feat(freight): shop freight templates, server-side checkout fees, company dictionary (add-freight-templates)

This commit is contained in:
Chengdong Zhang
2026-09-24 15:03:51 +08:00
parent 5b426486ac
commit 473d19d089
44 changed files with 2409 additions and 67 deletions
+32 -1
View File
@@ -777,6 +777,8 @@ export function createMockApi(): ApiClient {
}
discount = Math.min(coupon.amount_minor, subtotal);
}
// Same deterministic rule as quoteShipping: flat fee under the free threshold.
const shippingFee = subtotal - discount >= 20000 ? 0 : 599;
const order: Order = {
id: `o-${Date.now()}-${shopId}`,
order_no: nextOrderNo(),
@@ -784,8 +786,9 @@ export function createMockApi(): ApiClient {
user_id: MOCK_USER.id,
status: "pending_payment",
currency,
total_minor: subtotal - discount,
total_minor: subtotal - discount + shippingFee,
discount_minor: discount,
shipping_fee_minor: shippingFee,
refund_total_minor: 0,
coupon_id: couponId ?? null,
group_activity_id: groupHere ? activity.id : null,
@@ -879,6 +882,30 @@ export function createMockApi(): ApiClient {
listMyShipments: () => Promise.resolve(state.shipments.map((s) => ({ ...s }))),
// Deterministic fixture rule: a flat per-shop fee, free at/over 200 major.
quoteShipping: (_address, currency) => {
const byShop = new Map<string, number>();
for (const item of state.cart) {
byShop.set(item.shop_id, (byShop.get(item.shop_id) ?? 0) + item.unit_price_minor * item.qty);
}
const shops = [...byShop.entries()].map(([shop_id, subtotal]) => ({
shop_id,
fee_minor: subtotal >= 20000 ? 0 : 599,
}));
return Promise.resolve({
currency,
shops,
total_minor: shops.reduce((sum, s) => sum + s.fee_minor, 0),
});
},
listShippingCompanies: () =>
Promise.resolve([
{ code: "sf-express", name: { en: "SF Express", zh: "顺丰速运" }, active: true },
{ code: "zto", name: { en: "ZTO Express", zh: "中通快递" }, active: true },
{ code: "ups", name: { en: "UPS", zh: "联合包裹" }, active: true },
]),
requestInvoice: (orderId: string, title: string, taxNo: string | null, kind: InvoiceKind) => {
const order = state.orders.find((o) => o.id === orderId);
if (!order) return Promise.reject(new ApiError(404, "NOT_FOUND", "Order not found"));
@@ -1338,6 +1365,10 @@ export function createMockApi(): ApiClient {
confirmAftersaleReceipt: (_id: string) => unsupported(),
refundAftersale: (_id: string) => unsupported(),
addAftersaleMessage: (_id: string, _body: AftersaleMessageBody) => unsupported(),
listFreightTemplates: () => unsupported(),
createFreightTemplate: () => unsupported(),
updateFreightTemplate: () => unsupported(),
deleteFreightTemplate: () => unsupported(),
},
admin: {
listUsers: () => unsupported(),
+2 -1
View File
@@ -1146,9 +1146,10 @@ export function seedOrders(userId: string): MockOrderSeed {
user_id: userId,
status,
currency: BASE_CURRENCY,
total_minor: total,
total_minor: total + 599,
discount_minor: 0,
refund_total_minor: 0,
shipping_fee_minor: 599,
coupon_id: null,
group_activity_id: null,
group_id: null,