6.4 KiB
ADDED Requirements
Requirement: Merchant freight template management
Shop users SHALL manage freight templates for their own shop only, scoped through own_shop, and platform administrators SHALL manage none implicitly. A template SHALL have a name, an optional always-free toggle, a pricing method of by_piece or by_weight, first-unit and additional-unit fees in i64 minor units of the shop currency, integer first/additional unit sizes (pieces for by_piece, grams for by_weight), and an optional free-shipping threshold in i64 minor units. At most one template per shop SHALL be the default, enforced by the database and preserved under concurrent default changes with guarded updates. Amounts SHALL be i64 minor units and weights integer grams; floating-point money is forbidden. Services SHALL return ApiResult<Dto>.
Scenario: create a template for the shop
- WHEN a shop user creates a by-weight template with a 500g first unit and minor-unit fees
- THEN the template is stored with integer fees and grams and is visible only to that shop's users
Scenario: cross-shop template access denied
- WHEN a shop user reads or edits another shop's freight template
- THEN the request is rejected as not found or forbidden
Scenario: single default preserved
- WHEN a shop user makes a second template the default while another default exists
- THEN exactly one template of that shop remains flagged as default
Requirement: Freight region rules
Each freight template SHALL optionally carry region rules that override its default fees for matching destination regions. A rule SHALL name a set of destination regions and its own first-unit and additional-unit fees and unit sizes in the same integer conventions. Region matching SHALL use a simplified region code table and text matching against the destination address's region fields; the spec scope of this simplification is exactly that table and matching, with no hierarchical region tree, adjacency, or postal-code logic. The rule set SHALL be edited with its template through the same merchant-scoped API.
Scenario: rule overrides default fees
- WHEN a template has a rule for a remote region and an order ships there
- THEN the rule's first-unit and additional-unit fees are used instead of the template's defaults
Scenario: no matching rule uses defaults
- WHEN an order ships to a region no rule names
- THEN the template's default fees apply
Requirement: Default freight template resolution
A product SHALL optionally reference one freight template of its own shop. When calculating shipping, each order item SHALL resolve its template from the product's template first, then from the shop's default template, and a shop order whose items resolve no template at all SHALL carry zero shipping fee. Product edits that point at a template of another shop SHALL be rejected.
Scenario: product template wins
- WHEN an order item's product references a freight template and the shop also has a default template
- THEN the product's template resolves for that item
Scenario: fallback to shop default
- WHEN an order item's product references no template and the shop has a default template
- THEN the shop's default template resolves for that item
Scenario: nothing resolves
- WHEN neither the products nor the shop resolve a template for a shop order
- THEN the shop order's shipping fee is zero
Requirement: Server-side shipping fee calculation
Checkout SHALL compute shipping entirely on the server and SHALL ignore or reject any client-supplied fee or total. For each shop order the server SHALL group items by resolved freight template and merge quantities (by_piece) or integer weights (by_weight) within each group, charging the first-unit fee once plus the additional-unit fee per extra unit, with part of an additional unit charging a full additional unit. Free shipping SHALL be decided before fees: an always-free template yields zero for its group, and a template whose free-shipping threshold is met by that group's merchandise subtotal also yields zero. A matching region rule SHALL override the group's default first/additional fees and unit sizes. The shop order's shipping fee SHALL be the sum of its groups' fees, and the order total SHALL include it.
Scenario: merged pieces across items
- WHEN two items of one shop resolve to the same by-piece template with quantities 2 and 3
- THEN the fee is one first-unit fee plus four additional-unit fees, not five separate shipments
Scenario: partial additional unit rounds up
- WHEN a by-weight group's merged weight exceeds the first unit by part of an additional unit
- THEN one full additional-unit fee is charged for that part
Scenario: free-shipping threshold wins
- WHEN a template group's merchandise subtotal meets the template's free-shipping threshold
- THEN that group contributes zero shipping fee regardless of region rules
Scenario: mixed templates in one shop order
- WHEN one shop order contains items resolving to two different templates
- THEN each group is computed independently and the shop order's fee is their sum
Scenario: region override beats default in a mixed order
- WHEN one group's destination matches a region rule and another group's does not
- THEN only the matching group uses the rule's fees and the other group uses default fees
Scenario: client amounts are not trusted
- WHEN a checkout request carries client-computed fees or totals different from the server calculation
- THEN the server-computed amounts are persisted and returned
Requirement: Shipping company dictionary and selection
The platform SHALL keep a dictionary of shipping companies with a code and bilingual {en, zh} name, and checkout and order surfaces SHALL never invent companies outside the dictionary. A merchant SHALL select one dictionary company when shipping an order, and the shipment SHALL record the selected company. The dictionary SHALL be readable through the shared contract so the merchant console lists real companies.
Scenario: ship with a dictionary company
- WHEN a shop user marks an order shipped and selects a shipping company
- THEN the shipment records that company and order surfaces show it
Scenario: unknown company rejected
- WHEN a ship request names a company absent from the dictionary
- THEN the request is rejected and the order is not marked shipped