电商订单管理与税费运费处理
1. 订单管理系统现状与问题
当前,订单管理页面无法正常工作,需要对其进行修改以适配新功能。在将客户订单添加到系统的过程中,新增代码较为简单,数据和业务层的订单处理函数现在将客户 ID 作为参数,并分配给订单。不过,在实现更多新的订单代码后,才能为客户提供更多信息,如发送确认电子邮件。
2. 客户订单管理的更新
由于数据库已将订单与客户关联,接下来需要更新订单管理页面,因为旧页面已无法正常工作。这涉及对数据层和业务层进行各种修改,以在管理系统中提供新的数据结构和访问代码。由于更改范围广泛,将分别对数据层、业务层和表示层进行处理。
3. 数据层的修改
需要对数据库中的存储过程进行更新和创建新的存储过程,具体如下:
-
更新的存储过程
:
-
orders_get_most_recent_orders
-
orders_get_orders_between_dates
-
orders_get_orders_by_status
-
orders_get_order_info
-
orders_update_order
-
新创建的存储过程
:
-
orders_get_by_customer_id
-
orders_get_order_short_details
-
customer_get_customers_list
更新数据层的具体步骤
:
1. 使用 phpMyAdmin 创建以下存储过程,执行每个步骤的代码前不要忘记设置
$$
分隔符。
2. 删除旧的
orders_get_most_recent_orders
存储过程,并创建新的:
-- Drop orders_get_most_recent_orders stored procedure
DROP PROCEDURE orders_get_most_recent_orders$$
-- Create orders_get_most_recent_orders stored procedure
CREATE PROCEDURE orders_get_most_recent_orders(IN inHowMany INT)
BEGIN
PREPARE statement FROM
"SELECT o.order_id, o.total_amount, o.created_on,
o.shipped_on, o.status, c.name
FROM orders o
INNER JOIN customer c
ON o.customer_id = c.customer_id
ORDER BY o.created_on DESC
LIMIT ?";
SET @p1 = inHowMany;
EXECUTE statement USING @p1;
END$$
-
删除旧的
orders_get_orders_between_dates存储过程,并创建新的:
-- Drop orders_get_orders_between_dates stored procedure
DROP PROCEDURE orders_get_orders_between_dates$$
-- Create orders_get_orders_between_dates stored procedure
CREATE PROCEDURE orders_get_orders_between_dates(
IN inStartDate DATETIME, IN inEndDate DATETIME)
BEGIN
SELECT o.order_id, o.total_amount, o.created_on,
o.shipped_on, o.status, c.name
FROM orders o
INNER JOIN customer c
ON o.customer_id = c.customer_id
WHERE o.created_on >= inStartDate AND o.created_on <= inEndDate
ORDER BY o.created_on DESC;
END$$
-
删除旧的
orders_get_orders_by_status存储过程,并创建新的:
-- Drop orders_get_orders_by_status stored procedure
DROP PROCEDURE orders_get_orders_by_status$$
-- Create orders_get_orders_by_status stored procedure
CREATE PROCEDURE orders_get_orders_by_status(IN inStatus INT)
BEGIN
SELECT o.order_id, o.total_amount, o.created_on,
o.shipped_on, o.status, c.name
FROM orders o
INNER JOIN customer c
ON o.customer_id = c.customer_id
WHERE o.status = inStatus
ORDER BY o.created_on DESC;
END$$
-
删除旧的
orders_get_order_info存储过程,并创建新的:
-- Drop orders_get_order_info stored procedure
DROP PROCEDURE orders_get_order_info$$
-- Create orders_get_order_info stored procedure
CREATE PROCEDURE orders_get_order_info(IN inOrderId INT)
BEGIN
SELECT order_id, total_amount, created_on, shipped_on, status,
comments, customer_id, auth_code, reference
FROM orders
WHERE order_id = inOrderId;
END$$
-
删除旧的
orders_update_order存储过程,并创建新的:
-- Drop orders_update_order stored procedure
DROP PROCEDURE orders_update_order$$
-- Create orders_update_order stored procedure
CREATE PROCEDURE orders_update_order(IN inOrderId INT, IN inStatus INT,
IN inComments VARCHAR(255), IN inAuthCode VARCHAR(50),
IN inReference VARCHAR(50))
BEGIN
DECLARE currentStatus INT;
SELECT status
FROM orders
WHERE order_id = inOrderId
INTO currentStatus;
IF inStatus != currentStatus AND (inStatus = 0 OR inStatus = 1) THEN
UPDATE orders SET shipped_on = NULL WHERE order_id = inOrderId;
ELSEIF inStatus != currentStatus AND inStatus = 2 THEN
UPDATE orders SET shipped_on = NOW() WHERE order_id = inOrderId;
END IF;
UPDATE orders
SET status = inStatus, comments = inComments,
auth_code = inAuthCode, reference = inReference
WHERE order_id = inOrderId;
END$$
-
执行以下代码创建
orders_get_orders_by_customer_id存储过程:
-- Create orders_get_by_customer_id stored procedure
CREATE PROCEDURE orders_get_by_customer_id(IN inCustomerId INT)
BEGIN
SELECT o.order_id, o.total_amount, o.created_on,
o.shipped_on, o.status, c.name
FROM orders o
INNER JOIN customer c
ON o.customer_id = c.customer_id
WHERE o.customer_id = inCustomerId
ORDER BY o.created_on DESC;
END$$
-
执行以下代码创建
orders_get_order_short_details存储过程:
-- Create orders_get_order_short_details stored procedure
CREATE PROCEDURE orders_get_order_short_details(IN inOrderId INT)
BEGIN
SELECT o.order_id, o.total_amount, o.created_on,
o.shipped_on, o.status, c.name
FROM orders o
INNER JOIN customer c
ON o.customer_id = c.customer_id
WHERE o.order_id = inOrderId;
END$$
-
执行以下代码创建
customer_get_customers_list存储过程:
-- Create customer_get_customers_list stored procedure
CREATE PROCEDURE customer_get_customers_list()
BEGIN
SELECT customer_id, name FROM customer ORDER BY name ASC;
END$$
4. 业务层的修改
需要对业务层进行一些更改,包括修改
Orders
类的
UpdateOrder()
方法,并向
Orders
和
Customers
类添加三个新方法:
-
GetByCustomerId()
-
GetOrderShortDetails()
-
GetCustomersList()
更新业务层的具体步骤
:
1. 在
business/orders.php
的
Orders
类中添加
GetByCustomerId()
方法:
// Gets all orders placed by a specified customer
public static function GetByCustomerId($customerId)
{
// Build the SQL query
$sql = 'CALL orders_get_by_customer_id(:customer_id)';
// Build the parameters array
$params = array (':customer_id' => $customerId);
// Execute the query and return the results
return DatabaseHandler::GetAll($sql, $params);
}
-
在
business/orders.php的Orders类中添加GetOrderShortDetails()方法:
// Get short details for an order
public static function GetOrderShortDetails($orderId)
{
// Build the SQL query
$sql = 'CALL orders_get_order_short_details(:order_id)';
// Build the parameters array
$params = array (':order_id' => $orderId);
// Execute the query and return the results
return DatabaseHandler::GetAll($sql, $params);
}
-
修改
Orders类的UpdateOrder()方法:
// Updates order details
public static function UpdateOrder($orderId, $status, $comments,
$authCode, $reference)
{
// Build the SQL query
$sql = 'CALL orders_update_order(:order_id, :status, :comments,
:auth_code, :reference)';
// Build the parameters array
$params = array (':order_id' => $orderId,
':status' => $status,
':comments' => $comments,
':auth_code' => $authCode,
':reference' => $reference);
// Execute the query
DatabaseHandler::Execute($sql, $params);
}
-
在
business/customer.php的Customer类中添加GetCustomersList()方法:
// Gets all customers names with their associated id
public static function GetCustomersList()
{
// Build the SQL query
$sql = 'CALL customer_get_customers_list()';
// Execute the query and return the results
return DatabaseHandler::GetAll($sql);
}
5. 表示层的修改
需要更新表示层以利用新的数据层和业务层功能。目前不会对订单管理代码进行大规模更改,因为在完成新的订单处理系统后还会进行修改。
更新表示层的具体步骤
:
1. 在
presentation/templates/admin_orders.tpl
中添加以下代码:
{* admin_orders.tpl *}
{load_presentation_object filename="admin_orders" assign="obj"}
{if $obj->mErrorMessage}<p class="error">{$obj->mErrorMessage}</p>{/if}
<form method="get" action="{$obj->mLinkToAdmin}">
<input name="Page" type="hidden" value="Orders" />
<p>
<font class="bold-text">Show orders by customer</font>
<select name="customer_id">
{section name=i loop=$obj->mCustomers}
<option value="{$obj->mCustomers[i].customer_id}"
{if $obj->mCustomers[i].customer_id == $obj->mCustomerId}
selected="selected"
{/if}>
{$obj->mCustomers[i].name}
</option>
{/section}
</select>
<input type="submit" name="submitByCustomer" value="Go!" />
</p>
<p>
<font class="bold-text">Get by order ID</font>
<input name="orderId" type="text" value="{$obj->mOrderId}" />
<input type="submit" name="submitByOrderId" value="Go!" />
</p>
<p>
<font class="bold-text">Show the most recent</font>
<input name="recordCount" type="text" value="{$obj->mRecordCount}" />
...
{section name=i loop=$obj->mOrders}
{assign var=status value=$obj->mOrders[i].status}
<tr>
<td>{$obj->mOrders[i].order_id}</td>
<td>{$obj->mOrders[i].created_on|date_format:"%Y-%m-%d %T"}</td>
<td>{$obj->mOrders[i].shipped_on|date_format:"%Y-%m-%d %T"}</td>
<td>{$obj->mOrderStatusOptions[$status]}</td>
<td>{$obj->mOrders[i].name}</td>
<td align="right">
<a href="{$obj->mOrders[i].link_to_order_details_admin}">View Details</a>
</td>
</tr>
{/section}
</table>
{/if}
-
在
presentation/admin_orders.php的AdminOrders类中添加以下成员:
public $mLinkToAdmin;
public $mCustomers;
public $mCustomerId;
public $mOrderId;
-
在
presentation/admin_orders.php的AdminOrders类的init()方法中添加以下代码:
// If "Show orders by status" filter is in action ...
if (isset ($_GET['submitOrdersByStatus']))
{
$this->mSelectedStatus = $_GET['status'];
$this->mOrders = Orders::GetOrdersByStatus($this->mSelectedStatus);
}
// If the "Show orders by customer ID" filter is in action ...
if (isset ($_GET['submitByCustomer']))
{
if (empty ($_GET['customer_id']))
$this->mErrorMessage = 'No customer has been selected';
else
{
$this->mCustomerId = $_GET['customer_id'];
$this->mOrders = Orders::GetByCustomerId($this->mCustomerId);
}
}
// If the "Get order by ID" filter is in action ...
if (isset ($_GET['submitByOrderId']))
{
if (empty ($_GET['orderId']))
$this->mErrorMessage = 'You must enter an order ID.';
else
{
$this->mOrderId = $_GET['orderId'];
$this->mOrders = Orders::GetOrderShortDetails($this->mOrderId);
}
}
$this->mCustomers = Customer::GetCustomersList();
if (is_array($this->mOrders) && count($this->mOrders) == 0)
$this->mErrorMessage =
'No orders found matching your searching criteria!';
-
在
presentation/admin_order_details.php的AdminOrderDetails类中添加新成员:
public $mLinkToAdmin;
public $mLinkToOrdersAdmin;
public $mCustomerInfo;
-
在
AdminOrderDetails类的init()方法中修改更新订单的代码:
// Initializes class members
public function init()
{
if (isset ($_GET['submitUpdate']))
{
Orders::UpdateOrder($this->mOrderId, $_GET['status'],
$_GET['comments'], $_GET['authCode'], $_GET['reference']);
}
-
在
AdminOrderDetails类的init()方法中添加读取客户数据的代码:
$this->mOrderInfo = Orders::GetOrderInfo($this->mOrderId);
$this->mOrderDetails = Orders::GetOrderDetails($this->mOrderId);
$this->mCustomerInfo = Customer::Get($this->mOrderInfo['customer_id']);
// Value which specifies whether to enable or disable edit mode
if (isset ($_GET['submitEdit']))
-
修改
presentation/templates/admin_order_details.tpl:
<tr>
<td class="bold-text">Status: </td>
<td>
<select name="status"
{if ! $obj->mEditEnabled} disabled="disabled" {/if} >
{html_options options=$obj->mOrderStatusOptions
selected=$obj->mOrderInfo.status}
</select>
</td>
</tr>
<tr>
<td class="bold-text">Authorization Code: </td>
<td>
<input name="authCode" type="text" size="50"
value="{$obj->mOrderInfo.auth_code}"
{if ! $obj->mEditEnabled} disabled="disabled" {/if} />
<td>
</tr>
<tr>
<td class="bold-text">Reference Number: </td>
<td>
<input name="reference" type="text" size="50"
value="{$obj->mOrderInfo.reference}"
{if ! $obj->mEditEnabled} disabled="disabled" {/if} />
<td>
</tr>
<tr>
<td class="bold-text">Comments: </td>
<td>
<input name="comments" type="text" size="50"
value="{$obj->mOrderInfo.comments}"
{if ! $obj->mEditEnabled} disabled="disabled" {/if} />
<td>
</tr>
<tr>
<td class="bold-text">Customer Name: </td>
<td>{$obj->mCustomerInfo.name}</td>
</tr>
<tr>
<td class="bold-text" valign="top">Shipping Address: </td>
<td>
{$obj->mCustomerInfo.address_1}<br />
{if $obj->mCustomerInfo.address_2}
{$obj->mCustomerInfo.address_2}<br />
{/if}
{$obj->mCustomerInfo.city}<br />
{$obj->mCustomerInfo.region}<br />
{$obj->mCustomerInfo.postal_code}<br />
{$obj->mCustomerInfo.country}<br />
</td>
</tr>
<tr>
<td class="bold-text">Customer Email: </td>
<td>{$obj->mCustomerInfo.email}</td>
</tr>
</table>
-
更新
admin.php,添加对对称加密、安全卡和客户业务层类的引用:
require_once BUSINESS_DIR . 'shopping_cart.php';
require_once BUSINESS_DIR . 'orders.php';
require_once BUSINESS_DIR . 'symmetric_crypt.php';
require_once BUSINESS_DIR . 'secure_card.php';
require_once BUSINESS_DIR . 'customer.php';
- 加载网站,确保新添加的代码正常工作。
6. 税费和运费处理
许多电子商务网站都需要添加税费和运费。这一功能的实现复杂度取决于具体需求,这里将提供基本且可扩展的功能来评估税费和运费。
7. 税费问题
- 历史背景 :早期电子商务网站的税收执行不力,许多网站完全忽略税收,尤其是国际订单。随着人们对电子商务的认识增加,税务机构开始重视这一问题,提出并实施了一系列解决方案。
- 关键概念 :“关联”(nexus)是税收的关键概念,即企业在征税管辖区有足够的业务存在才需要征税。国际运输时,除非公司在目的地国家有重要业务,否则通常无需负责税收;国内运输则可能需要负责。
-
其他关键问题
:
- 税收取决于发货地和收货地。
- 适用国家规则。
- 销售的产品类型很重要。
- 简单的税收方案 :数据库表将包含各种适用的税率信息,目前税率选择取决于客户的运输区域,所有产品按相同税率征税。
8. 运费问题
- 处理方式 :运费处理相对税费较简单,但也可根据需求复杂化。如果有与邮政服务的既有关系,可尽量保持原有方式;如果是新开始或调整业务,有多种选择。
-
选择方案
:
- 不考虑运费,如数字下载业务。
- 将运费包含在产品成本中。
- 收取固定费用。
- 考虑产品的重量和尺寸计算精确费用,部分运输公司提供 API 简化计算。
- 简单的运费方案 :为数据库中的每个运输区域提供多种运输选项,每个选项有相应费用,该费用直接添加到订单成本中。
9. 实施税费和运费处理
需要对系统进行多项修改,包括添加新的数据库表(
tax
和
shipping
)、修改
orders
表、添加新的数据库函数、修改业务层类以及在表示层提供用户选择运输方式的方法。理想情况下,应清除
orders
和
order_detail
表的内容,或考虑旧订单不包含税费和运费的情况。
以下是数据层修改的流程图:
graph TD;
A[开始] --> B[更新存储过程];
B --> C[创建新存储过程];
C --> D[完成数据层修改];
以下是业务层修改的表格:
| 类 | 方法 | 说明 |
| ---- | ---- | ---- |
| Orders | GetByCustomerId() | 获取指定客户的所有订单 |
| Orders | GetOrderShortDetails() | 获取订单的简短详情 |
| Orders | UpdateOrder() | 更新订单详情 |
| Customers | GetCustomersList() | 获取所有客户列表 |
电商订单管理与税费运费处理
10. 具体实施步骤
为了实现税费和运费的处理,下面将详细介绍具体的操作步骤。
10.1 数据库表的添加与修改
-
添加新表
:在数据库中添加
tax和shipping表,用于存储税费和运费的相关信息。 -
修改
orders表 :根据新的需求,对orders表进行相应的修改,可能需要添加新的字段来记录税费和运费信息。
10.2 数据库函数的添加与修改
- 添加新函数 :编写新的数据库函数,用于计算税费和运费。例如,可以创建一个函数根据客户的运输区域和订单信息计算税费,另一个函数根据运输区域和产品重量计算运费。
- 修改现有函数 :对现有的订单处理函数进行修改,使其能够调用新的税费和运费计算函数,并将计算结果更新到订单信息中。
10.3 业务层类的修改
-
修改
Orders类 :在Orders类中添加新的方法,用于调用数据库函数计算税费和运费,并将计算结果保存到订单对象中。例如:
// 计算并更新订单的税费和运费
public static function CalculateTaxAndShipping($orderId)
{
// 调用数据库函数计算税费
$tax = DatabaseHandler::CallFunction('calculate_tax', $orderId);
// 调用数据库函数计算运费
$shipping = DatabaseHandler::CallFunction('calculate_shipping', $orderId);
// 更新订单信息
Orders::UpdateOrder($orderId, null, null, null, null, $tax, $shipping);
}
- 修改其他相关类 :根据具体需求,对其他相关的业务层类进行修改,确保整个业务流程能够正确处理税费和运费。
10.4 表示层的修改
- 添加用户选择运输方式的界面 :在订单确认页面或结算页面添加一个下拉框或单选框,让用户选择运输方式。根据用户的选择,调用业务层的方法计算相应的运费。
<select name="shipping_method">
{section name=i loop=$obj->mShippingMethods}
<option value="{$obj->mShippingMethods[i].id}">{$obj->mShippingMethods[i].name} - {$obj->mShippingMethods[i].price}</option>
{/section}
</select>
- 显示税费和运费信息 :在订单详情页面和结算页面显示计算好的税费和运费信息,让用户清楚了解订单的总成本。
<tr>
<td class="bold-text">税费: </td>
<td>{$obj->mOrderInfo.tax}</td>
</tr>
<tr>
<td class="bold-text">运费: </td>
<td>{$obj->mOrderInfo.shipping}</td>
</tr>
11. 测试与验证
在完成所有修改后,需要对系统进行全面的测试,确保税费和运费的计算和处理功能正常工作。
11.1 测试用例设计
- 正常情况测试 :创建不同类型的订单,选择不同的运输区域和运输方式,验证税费和运费的计算结果是否正确。
- 边界情况测试 :测试极端情况,如订单金额为零、运输区域为空等,确保系统能够正确处理这些情况。
- 异常情况测试 :模拟网络故障、数据库错误等异常情况,验证系统的容错能力和错误处理机制。
11.2 测试流程
graph TD;
A[开始测试] --> B[创建测试订单];
B --> C[选择运输区域和方式];
C --> D[计算税费和运费];
D --> E[验证计算结果];
E --> F{结果是否正确};
F -- 是 --> G[测试通过];
F -- 否 --> H[记录错误信息];
H --> I[修复问题];
I --> B;
12. 总结与展望
通过以上步骤,我们实现了电商订单管理系统中税费和运费的处理功能。采用简单而可扩展的方案,为系统的后续发展奠定了基础。
在未来,可以根据实际业务需求,进一步完善税费和运费的计算逻辑。例如,考虑不同产品类型的税率差异,根据订单重量和体积动态调整运费计算方式等。同时,还可以集成更多的运输公司 API,提供更多的运输选项和实时的运费报价,提升用户体验。
以下是表示层修改的操作列表:
1. 在订单确认页面添加运输方式选择界面。
2. 在订单详情页面显示税费和运费信息。
3. 验证用户选择的运输方式是否正确更新到订单信息中。
以下是测试用例的表格:
| 测试类型 | 测试用例描述 | 预期结果 |
| ---- | ---- | ---- |
| 正常情况测试 | 创建订单,选择不同运输区域和方式 | 税费和运费计算正确 |
| 边界情况测试 | 订单金额为零,选择运输方式 | 系统正常处理,计算结果合理 |
| 异常情况测试 | 模拟网络故障,提交订单 | 系统提示错误信息,不影响数据完整性 |
超级会员免费看
1132

被折叠的 条评论
为什么被折叠?



