Files
vmall/openspec/changes/add-wallet-settlement/specs/settlement/spec.md
T

57 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## ADDED Requirements
### Requirement: Idempotent periodic statement generation
A settlement statement SHALL be generated manually for one shop, one period kind (week or month), and one closed period. For the same shop, period kind, and period start at most one statement SHALL exist, enforced by the database. A repeated generation request SHALL return the existing statement unchanged instead of recomputing or duplicating it. No scheduled batch job SHALL generate statements.
#### Scenario: repeat generation is idempotent
- **WHEN** a statement already exists for a shop and period and generation is requested again
- **THEN** the existing statement is returned unchanged and no second row is created
#### Scenario: generation is manual only
- **WHEN** no generation request is made for a closed period
- **THEN** no statement exists for that period
### Requirement: Immutable amount snapshot
A generated statement SHALL snapshot the contributing order count, the gross total of confirmed-received orders in the period, the deduction total of completed refunds against those orders (`aftersales` rows with status `refunded`, per-order totals from `add-aftersale-refunds`), the platform commission rate at generation time, the commission amount, and the payable amount where payable = gross − refunds − commission. All amounts SHALL be integer minor units computed with integer arithmetic, and the commission rate SHALL be an integer basis-point value. The snapshot SHALL NOT change after generation even when later orders, refunds, or commission-rate changes occur.
#### Scenario: refunds reduce the payable amount
- **WHEN** a confirmed-received order in the period has a completed refund of 2000 minor units
- **THEN** the statement's refund deduction includes those 2000 minor units and the payable amount is reduced accordingly
#### Scenario: commission arithmetic is integral
- **WHEN** the commission rate is 500 basis points and gross minus refunds is 10000 minor units
- **THEN** the commission snapshot is 500 minor units and the payable snapshot is 9500 minor units with no floating-point arithmetic
#### Scenario: snapshot survives rate changes
- **WHEN** the platform commission rate changes after a statement was generated
- **THEN** the generated statement keeps its snapshotted rate and amounts
### Requirement: Platform commission rate configuration
The commission rate SHALL be a single platform-level setting stored as integer basis points and readable and editable by platform admins. A rate change SHALL affect only statements generated after the change.
#### Scenario: new rate applies to new statements
- **WHEN** an admin sets the commission rate and a statement is generated afterwards
- **THEN** that statement snapshots the new rate
### Requirement: One-time payout confirmation
A statement SHALL transition exactly once from `pending` to `confirmed` through a guarded status transition requiring the `pending` status, triggered by a platform admin confirming the payout; a repeated confirmation SHALL return 409. Confirmation SHALL record the confirming admin and timestamp and SHALL credit the payable amount to the shop owner's available account with exactly one ledger entry referencing the statement.
#### Scenario: confirm pays out once
- **WHEN** an admin confirms a pending statement
- **THEN** the statement becomes confirmed and the shop owner's available balance increases by the payable amount with one ledger entry referencing the statement
#### Scenario: double confirmation conflicts
- **WHEN** an admin confirms an already confirmed statement
- **THEN** the API returns 409 and no second ledger entry is written
### Requirement: Shop-scoped statement visibility
Statements and their breakdown SHALL be scoped to one shop. Through the shop's own-shop scope, shop users SHALL read and generate statements only for their own shop and SHALL never observe another shop's statements, contributing orders, or refund lines; platform admins SHALL observe every shop.
#### Scenario: merchant sees own shop only
- **WHEN** a shop user lists settlement statements
- **THEN** only statements of their own shop appear
#### Scenario: detail breakdown is shop-scoped
- **WHEN** a shop user opens a statement detail
- **THEN** the contributing orders and refunded amounts are visible for that shop only