feat: backend MVP (auth/rbac, catalog, orders, fulfillment, invoices) + specs + scaffolds
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
# Spec delta: catalog
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Localized product content
|
||||
Product and category names/descriptions SHALL be stored as JSONB maps keyed by locale (`en`, `zh`). The API MUST return the full map; clients pick the display locale.
|
||||
|
||||
#### Scenario: bilingual round-trip
|
||||
- **WHEN** a shop owner creates a product with name `{"en": "Mug", "zh": "马克杯"}`
|
||||
- **THEN** both public detail and shop-admin GET return the identical map
|
||||
|
||||
### Requirement: Publish lifecycle
|
||||
Products SHALL have status `draft | published | unpublished`. Only `published` products appear in public list/detail.
|
||||
|
||||
#### Scenario: publish then unpublish
|
||||
- **WHEN** a product is published
|
||||
- **THEN** it appears in `GET /api/products`
|
||||
- **WHEN** it is unpublished
|
||||
- **THEN** public detail returns 404 and it disappears from listings
|
||||
|
||||
#### Scenario: publish requires sellable SKU
|
||||
- **WHEN** publishing a product with no active SKU having price > 0
|
||||
- **THEN** the API returns 400
|
||||
|
||||
### Requirement: Shop isolation
|
||||
Shop-role users SHALL only see and mutate their own shop's products under `/api/shop/products`.
|
||||
|
||||
#### Scenario: cross-shop access denied
|
||||
- **WHEN** shop owner A requests `/api/shop/products/{id}` of shop B
|
||||
- **THEN** the API returns 404
|
||||
|
||||
### Requirement: SKU pricing
|
||||
Each SKU SHALL carry `price_minor` (integer minor units) and an ISO `currency` code; stock is a non-negative integer.
|
||||
|
||||
#### Scenario: negative stock rejected
|
||||
- **WHEN** upserting a SKU with stock < 0
|
||||
- **THEN** the API returns 400
|
||||
@@ -0,0 +1,21 @@
|
||||
# Spec delta: currency
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Currency registry
|
||||
The system SHALL maintain a currencies table: ISO code, localized name, symbol, exponent (minor units), enabled flag, and `rate_to_base` (NUMERIC). Exactly one currency is the base.
|
||||
|
||||
#### Scenario: seeded currencies
|
||||
- **WHEN** migrations finish
|
||||
- **THEN** USD exists as base with rate 1, and CNY/EUR/JPY exist with positive rates
|
||||
|
||||
### Requirement: Amount conversion
|
||||
`GET /api/currencies/convert` SHALL convert integer minor units between enabled currencies via base rates, rounding half-up to the target exponent.
|
||||
|
||||
#### Scenario: USD to JPY
|
||||
- **WHEN** converting 1000 minor USD (=$10.00) to JPY with rate 150
|
||||
- **THEN** the result is 1500 minor JPY (¥1500), an integer
|
||||
|
||||
#### Scenario: disabled currency rejected
|
||||
- **WHEN** converting to a disabled or unknown currency
|
||||
- **THEN** the API returns 400
|
||||
Reference in New Issue
Block a user