|
|
|
@@ -0,0 +1,80 @@
|
|
|
|
|
## 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
|