feat: backend MVP (auth/rbac, catalog, orders, fulfillment, invoices) + specs + scaffolds
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# Proposal: cart-checkout-orders
|
||||
|
||||
## Why
|
||||
Shoppers must be able to collect items and place orders. Checkout splits the cart into one order per shop (B2B2C requirement), snapshots prices, and decrements stock atomically.
|
||||
|
||||
## What changes
|
||||
- Redis-backed cart per authenticated user (add/update/remove/list), merged SKU + product snapshot at read time.
|
||||
- Checkout: validate stock + published status, create orders (one per shop) with items, decrement stock in a transaction, clear cart.
|
||||
- Order lifecycle: pending_payment → paid (mock pay endpoint) → fulfilling → shipped → completed; customer cancel while pending_payment.
|
||||
- Customer APIs: my orders list/detail, cancel, mock-pay.
|
||||
|
||||
## Non-goals
|
||||
- Real payment gateways, partial refunds, guest checkout.
|
||||
|
||||
## Capabilities
|
||||
- `cart`: server-side cart.
|
||||
- `order`: checkout, order lifecycle, customer order APIs.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Spec delta: cart
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Server-side cart
|
||||
Authenticated shoppers SHALL have a Redis-backed cart keyed by user id, containing sku_id + qty entries.
|
||||
|
||||
#### Scenario: add and update
|
||||
- **WHEN** a shopper POSTs sku + qty, then PUTs a new qty
|
||||
- **THEN** GET /api/cart reflects the latest qty with current price/name snapshot
|
||||
|
||||
#### Scenario: unpurchasable SKU rejected
|
||||
- **WHEN** adding a SKU that is inactive or whose product is not published
|
||||
- **THEN** the API returns 400
|
||||
@@ -0,0 +1,32 @@
|
||||
# Spec delta: order
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Checkout splits by shop
|
||||
`POST /api/orders/checkout` SHALL create one order per distinct shop in the cart, in a single database transaction: stock decrement, order + item insert with price snapshots, cart clear. All amounts use the cart's SKU currencies converted into the buyer-chosen display currency at checkout time.
|
||||
|
||||
#### Scenario: two shops → two orders
|
||||
- **WHEN** the cart contains SKUs from shops A and B
|
||||
- **THEN** two orders are created, each with only its shop's items, and the cart is empty
|
||||
|
||||
#### Scenario: insufficient stock
|
||||
- **WHEN** any line's qty exceeds SKU stock
|
||||
- **THEN** the whole checkout returns 409 and no order is created and stock is unchanged
|
||||
|
||||
### Requirement: Order lifecycle
|
||||
Status transitions SHALL be: pending_payment → paid → fulfilling → shipped → completed; cancellable only from pending_payment, which MUST restore stock.
|
||||
|
||||
#### Scenario: cancel restores stock
|
||||
- **WHEN** a customer cancels a pending_payment order
|
||||
- **THEN** stock of each SKU increases by the ordered qty and status is cancelled
|
||||
|
||||
#### Scenario: illegal transition rejected
|
||||
- **WHEN** cancelling a paid order via the customer endpoint
|
||||
- **THEN** the API returns 409
|
||||
|
||||
### Requirement: Order ownership
|
||||
Customers SHALL see only their own orders; shop roles only their shop's orders; platform_admin sees all.
|
||||
|
||||
#### Scenario: cross-customer read denied
|
||||
- **WHEN** customer X requests customer Y's order id
|
||||
- **THEN** the API returns 404
|
||||
@@ -0,0 +1,17 @@
|
||||
# Tasks: cart-checkout-orders
|
||||
|
||||
## 1. Schema
|
||||
- [ ] Migration: orders, order_items; order status enum; order_no sequence
|
||||
|
||||
## 2. Cart (Redis)
|
||||
- [ ] GET /api/cart, POST /api/cart/items, PUT/DELETE /api/cart/items/{sku_id}
|
||||
- [ ] Cart read joins SKU/product snapshots; rejects inactive/unpublished SKUs
|
||||
|
||||
## 3. Checkout & orders
|
||||
- [ ] POST /api/orders/checkout (one order per shop, tx: stock decrement + order insert, cart clear)
|
||||
- [ ] GET /api/orders (mine, paged), GET /api/orders/{id}
|
||||
- [ ] POST /api/orders/{id}/pay (mock) and /cancel with state rules
|
||||
- [ ] GET /api/shop/orders for merchants; platform admin list
|
||||
|
||||
## 4. Tests
|
||||
- [ ] cargo test: multi-shop checkout splits orders, insufficient stock → 409, cancel rules, stock restored on cancel
|
||||
@@ -0,0 +1,18 @@
|
||||
# Proposal: catalog-i18n-currency
|
||||
|
||||
## Why
|
||||
Merchants must manage products (create, edit, publish/unpublish) and shoppers must browse a localized, multi-currency storefront. This phase delivers the catalog core plus the currency infrastructure every price display depends on.
|
||||
|
||||
## What changes
|
||||
- Schema: categories, products (JSONB i18n name/description), skus (price minor units + currency + stock), currencies (rate_to_base).
|
||||
- Public APIs: product list (paged, filter by category/q/shop), product detail by id/slug, category list, currency list + conversion endpoint.
|
||||
- Shop-admin APIs: product CRUD, SKU upsert, publish/unpublish transitions.
|
||||
- Seed: base currency USD, plus CNY/EUR/JPY; demo categories.
|
||||
|
||||
## Non-goals
|
||||
- Inventory reservations/warehouses, product variants matrix UI, image upload (URLs only).
|
||||
- Full-text search engines; ILIKE search is sufficient for MVP.
|
||||
|
||||
## Capabilities
|
||||
- `catalog`: categories, products, SKUs, publish lifecycle.
|
||||
- `currency`: currency registry and conversion.
|
||||
@@ -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
|
||||
@@ -0,0 +1,22 @@
|
||||
# Tasks: catalog-i18n-currency
|
||||
|
||||
## 1. Schema
|
||||
- [ ] Migration: currencies, categories, products, skus; product status enum
|
||||
- [ ] Seed currencies (USD base, CNY, EUR, JPY) + demo categories (bilingual)
|
||||
|
||||
## 2. Currency APIs
|
||||
- [ ] GET /api/currencies (public, enabled only)
|
||||
- [ ] GET /api/currencies/convert?amount_minor&from&to
|
||||
|
||||
## 3. Public catalog APIs
|
||||
- [ ] GET /api/products (paged; filters: category_id, q, shop_id; only published)
|
||||
- [ ] GET /api/products/{id_or_slug} (published only, with SKUs)
|
||||
- [ ] GET /api/categories
|
||||
|
||||
## 4. Shop-admin catalog APIs
|
||||
- [ ] GET/POST /api/shop/products, PUT /api/shop/products/{id}
|
||||
- [ ] POST /api/shop/products/{id}/publish | /unpublish
|
||||
- [ ] POST /api/shop/products/{id}/skus (upsert by sku_code)
|
||||
|
||||
## 5. Tests
|
||||
- [ ] cargo test: publish lifecycle visibility, i18n fields round-trip, currency conversion math, shop isolation
|
||||
@@ -0,0 +1,19 @@
|
||||
# Proposal: foundation-auth
|
||||
|
||||
## Why
|
||||
VMall needs a working backend skeleton and an identity layer before any commerce feature: users, roles (platform_admin / shop_owner / shop_staff / customer), and JWT-based auth that gates every protected endpoint.
|
||||
|
||||
## What changes
|
||||
- Rust axum API skeleton: config, structured errors, health/readiness endpoints, sqlx migrations on boot, Redis connection manager.
|
||||
- Users table with argon2 password hashes; shops table (needed for role scoping).
|
||||
- Auth endpoints: register (customer), login, me.
|
||||
- JWT issuance/validation middleware; role-guard helpers (`require_role`, shop-scoping for shop roles).
|
||||
- Seed: one platform admin account.
|
||||
|
||||
## Non-goals
|
||||
- OAuth / social login, refresh tokens, password reset flows.
|
||||
- Fine-grained permission tables beyond the four roles.
|
||||
|
||||
## Capabilities
|
||||
- `auth`: registration, login, token issuance, current-user lookup.
|
||||
- `rbac`: role model and enforcement on protected routes.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Spec delta: auth
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Customer registration
|
||||
The API SHALL provide `POST /api/auth/register` accepting email, password, display_name. New users are created with role `customer`. Duplicate emails MUST be rejected with 409.
|
||||
|
||||
#### Scenario: successful registration
|
||||
- **WHEN** a client posts a unique email with password ≥ 8 chars
|
||||
- **THEN** the API returns 201 with `{ token, user }` and the user can call `/api/auth/me` with the token
|
||||
|
||||
#### Scenario: duplicate email
|
||||
- **WHEN** the email already exists
|
||||
- **THEN** the API returns 409 with code `CONFLICT`
|
||||
|
||||
### Requirement: Login
|
||||
The API SHALL provide `POST /api/auth/login` issuing a signed JWT (24h TTL) containing user id and role.
|
||||
|
||||
#### Scenario: valid credentials
|
||||
- **WHEN** email + correct password are posted
|
||||
- **THEN** the API returns `{ token, user }`
|
||||
|
||||
#### Scenario: invalid credentials
|
||||
- **WHEN** the password is wrong or email unknown
|
||||
- **THEN** the API returns 401 with code `UNAUTHORIZED` and no token
|
||||
|
||||
### Requirement: Current user
|
||||
`GET /api/auth/me` SHALL return the authenticated user profile.
|
||||
|
||||
#### Scenario: missing token
|
||||
- **WHEN** no Bearer token is supplied
|
||||
- **THEN** the API returns 401
|
||||
@@ -0,0 +1,21 @@
|
||||
# Spec delta: rbac
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Role model
|
||||
The system SHALL support roles `platform_admin`, `shop_owner`, `shop_staff`, `customer`. Shop roles MUST carry a `shop_id` scope.
|
||||
|
||||
#### Scenario: seeded platform admin
|
||||
- **WHEN** migrations run on a fresh database
|
||||
- **THEN** a `platform_admin` account exists and can log in
|
||||
|
||||
### Requirement: Role enforcement
|
||||
Protected routes SHALL declare required roles; the API MUST reject requests with insufficient role using 403.
|
||||
|
||||
#### Scenario: customer hits admin route
|
||||
- **WHEN** a `customer` token calls an `/api/admin/*` route
|
||||
- **THEN** the API returns 403 with code `FORBIDDEN`
|
||||
|
||||
#### Scenario: shop scope isolation
|
||||
- **WHEN** a `shop_owner` of shop A accesses `/api/shop/*` resources of shop B
|
||||
- **THEN** the API returns 403 or 404, never the data
|
||||
@@ -0,0 +1,20 @@
|
||||
# Tasks: foundation-auth
|
||||
|
||||
## 1. Backend skeleton
|
||||
- [x] Cargo workspace, vmall-api crate (axum, sqlx, redis, tower-http)
|
||||
- [x] Config from env (DATABASE_URL, REDIS_URL, JWT_SECRET, PORT)
|
||||
- [x] Health + readiness endpoints (db/redis checked)
|
||||
- [x] sqlx migrate on boot; 0001_init migration
|
||||
|
||||
## 2. Identity schema
|
||||
- [ ] Migration: users, shops tables; role enum
|
||||
- [ ] Seed platform admin (admin@vmall.local / admin1234)
|
||||
|
||||
## 3. Auth API
|
||||
- [ ] POST /api/auth/register (customer role)
|
||||
- [ ] POST /api/auth/login → JWT + user
|
||||
- [ ] GET /api/auth/me (Bearer)
|
||||
- [ ] Auth extractor + require_role guard
|
||||
|
||||
## 4. Tests
|
||||
- [ ] cargo test: register/login/me happy path, wrong password, role guard denies customer on admin route
|
||||
@@ -0,0 +1,18 @@
|
||||
# Proposal: frontend-apps
|
||||
|
||||
## Why
|
||||
The API needs its three user-facing surfaces: customer storefront (mall), merchant console (shop-admin), and platform console (admin) — all Nuxt 3, all bilingual (en/zh), with the mall offering multi-currency display.
|
||||
|
||||
## What changes
|
||||
- Shared package @vmall/shared: API contract types, typed API client, en/zh locales, base stylesheet.
|
||||
- mall (port 3000): product browsing w/ search+category, product detail, cart, checkout, orders, shipments, invoices, login/register, currency switcher (display conversion).
|
||||
- shop-admin (3001): dashboard, product CRUD + publish/unpublish + SKU editing, order list, shipment creation/ship, invoice issue.
|
||||
- admin (3002): user role assignment, shop creation/suspension, order oversight, currency management (rates).
|
||||
|
||||
## Non-goals
|
||||
- SSR SEO tuning, design-system polish beyond the shared stylesheet, e2e test suites (smoke-verified in browser).
|
||||
|
||||
## Capabilities
|
||||
- `frontend-mall`: shopper experience.
|
||||
- `frontend-shop-admin`: merchant experience.
|
||||
- `frontend-admin`: platform operator experience.
|
||||
@@ -0,0 +1,17 @@
|
||||
# Spec delta: frontend-admin
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Platform user and shop management
|
||||
Platform admins SHALL assign user roles (with shop scope), create shops, and suspend/activate shops. Suspended shops' products MUST NOT be purchasable (enforced by API, reflected in UI).
|
||||
|
||||
#### Scenario: assign shop owner
|
||||
- **WHEN** an admin assigns role shop_owner with a shop to a user
|
||||
- **THEN** that user can log into shop-admin and manage that shop
|
||||
|
||||
### Requirement: Currency management
|
||||
Platform admins SHALL view currencies and update exchange rates; new rates affect subsequent conversions.
|
||||
|
||||
#### Scenario: rate update
|
||||
- **WHEN** an admin updates CNY rate_to_base
|
||||
- **THEN** the mall conversion endpoint returns amounts computed with the new rate
|
||||
@@ -0,0 +1,24 @@
|
||||
# Spec delta: frontend-mall
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Localized storefront
|
||||
The mall SHALL render UI strings and catalog content in en or zh from one switcher, defaulting to en.
|
||||
|
||||
#### Scenario: switch to Chinese
|
||||
- **WHEN** a shopper switches locale to zh
|
||||
- **THEN** navigation, buttons and product names render in Chinese without reload errors
|
||||
|
||||
### Requirement: Multi-currency display
|
||||
The mall SHALL offer a currency switcher (enabled currencies from the API) converting SKU prices for display; checkout uses the selected currency.
|
||||
|
||||
#### Scenario: switch currency
|
||||
- **WHEN** a shopper switches from USD to JPY on a product priced $10.00
|
||||
- **THEN** the displayed price reflects the API conversion rate (integer minor units)
|
||||
|
||||
### Requirement: Shopping flow
|
||||
A shopper SHALL be able to browse, view detail, add to cart, checkout with a shipping address, pay (mock), track shipments, confirm delivery, and request an invoice — all against the live API.
|
||||
|
||||
#### Scenario: end-to-end purchase
|
||||
- **WHEN** a registered shopper completes checkout on a non-empty cart
|
||||
- **THEN** orders appear under Orders and the cart is empty
|
||||
@@ -0,0 +1,17 @@
|
||||
# Spec delta: frontend-shop-admin
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Merchant product management
|
||||
Shop users SHALL manage only their own shop's products: create/edit bilingual content, manage SKUs, publish/unpublish with immediate effect on the storefront.
|
||||
|
||||
#### Scenario: publish visible in mall
|
||||
- **WHEN** a merchant publishes a product in shop-admin
|
||||
- **THEN** it appears in the mall product list for the matching locale
|
||||
|
||||
### Requirement: Merchant fulfillment
|
||||
Shop users SHALL see incoming orders, create shipments, mark them shipped, and issue requested invoices.
|
||||
|
||||
#### Scenario: ship an order
|
||||
- **WHEN** a merchant creates a shipment for a paid order and marks it shipped
|
||||
- **THEN** the shopper sees the shipment with tracking info
|
||||
@@ -0,0 +1,22 @@
|
||||
# Tasks: frontend-apps
|
||||
|
||||
## 1. Shared package
|
||||
- [x] @vmall/shared: types, API client, locales (en/zh), ui.css
|
||||
|
||||
## 2. Scaffolds
|
||||
- [x] Three Nuxt apps: config, i18n, pinia session store, api plugin, layouts
|
||||
|
||||
## 3. mall
|
||||
- [ ] Home: product grid + search + category filter + paging
|
||||
- [ ] Product detail with SKU picker, localized content, converted price
|
||||
- [ ] Cart page, checkout page (address form), orders list/detail, shipments, invoices, login/register
|
||||
|
||||
## 4. shop-admin
|
||||
- [ ] Login guard, dashboard, products list/new/edit (i18n fields, SKUs), publish/unpublish
|
||||
- [ ] Orders, shipment creation + mark shipped, invoices list + issue
|
||||
|
||||
## 5. admin
|
||||
- [ ] Login guard, users (role assignment), shops (create/suspend), orders, currencies (rate edit)
|
||||
|
||||
## 6. Verification
|
||||
- [ ] All three apps build; browser smoke: register→shop create→product publish→purchase→ship→invoice in en + zh
|
||||
@@ -0,0 +1,17 @@
|
||||
# Proposal: fulfillment-invoices
|
||||
|
||||
## Why
|
||||
After payment, merchants ship goods (发货单) and customers request invoices (发票). This phase closes the post-order loop of the MVP.
|
||||
|
||||
## What changes
|
||||
- Schema: shipments (carrier, tracking, status, per-item qty), invoices (title, tax_no, kind, amount, status).
|
||||
- Shipment lifecycle: created(pending) → shipped → delivered; order becomes fulfilling on first shipment, shipped when all items shipped, completed when all delivered (customer confirm allowed).
|
||||
- Merchant APIs: create shipment for paid/fulfilling orders, mark shipped, list shipments/invoices, issue invoice.
|
||||
- Customer APIs: my shipments, confirm delivery, request invoice per order, my invoices.
|
||||
|
||||
## Non-goals
|
||||
- Carrier API integrations, e-invoice/PDF generation, partial invoice amounts.
|
||||
|
||||
## Capabilities
|
||||
- `shipment`: 发货单 lifecycle.
|
||||
- `invoice`: 发票 request/issue lifecycle.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Spec delta: invoice
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Invoice request
|
||||
Customers SHALL request at most one open invoice (发票) per order, with title, kind (`personal | company`), and tax_no required for company invoices. Amount equals the order total in the order currency.
|
||||
|
||||
#### Scenario: company invoice requires tax number
|
||||
- **WHEN** requesting a company invoice without tax_no
|
||||
- **THEN** the API returns 400
|
||||
|
||||
#### Scenario: duplicate rejected
|
||||
- **WHEN** an order already has a requested or issued invoice
|
||||
- **THEN** a second request returns 409
|
||||
|
||||
### Requirement: Invoice issuance
|
||||
Merchants SHALL issue requested invoices of their own shop's orders; issuing sets invoice_no, issued_at and status `issued`.
|
||||
|
||||
#### Scenario: issue flow
|
||||
- **WHEN** the shop issues a requested invoice
|
||||
- **THEN** the customer sees status `issued` with an invoice number
|
||||
@@ -0,0 +1,23 @@
|
||||
# Spec delta: shipment
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Shipment creation
|
||||
Merchants SHALL create shipments (发货单) for their own orders in `paid` or `fulfilling` status, specifying carrier, tracking_no and per-item quantities.
|
||||
|
||||
#### Scenario: partial shipment
|
||||
- **WHEN** an order has 3 units of item X and a shipment covers 2
|
||||
- **THEN** a later shipment may cover the remaining 1; requesting more than the remainder returns 400
|
||||
|
||||
#### Scenario: status propagation
|
||||
- **WHEN** the first shipment is created for a paid order
|
||||
- **THEN** the order becomes `fulfilling`
|
||||
- **WHEN** all ordered quantities are covered by shipped shipments
|
||||
- **THEN** the order becomes `shipped`
|
||||
|
||||
### Requirement: Delivery confirmation
|
||||
Customers SHALL confirm delivery of their own shipments; when every shipment of an order is delivered the order becomes `completed`.
|
||||
|
||||
#### Scenario: confirm delivered
|
||||
- **WHEN** the customer confirms the only shipment of a shipped order
|
||||
- **THEN** shipment becomes `delivered` and the order `completed`
|
||||
@@ -0,0 +1,17 @@
|
||||
# Tasks: fulfillment-invoices
|
||||
|
||||
## 1. Schema
|
||||
- [ ] Migration: shipments, shipment_items, invoices; status enums; shipment_no/invoice_no sequences
|
||||
|
||||
## 2. Shipments
|
||||
- [ ] POST /api/shop/orders/{id}/shipments (qty validation vs unshipped remainder)
|
||||
- [ ] POST /api/shop/shipments/{id}/ship; order status propagation (fulfilling/shipped)
|
||||
- [ ] GET /api/shipments (customer), POST /api/shipments/{id}/confirm-delivered → order completed when done
|
||||
- [ ] GET /api/shop/shipments
|
||||
|
||||
## 3. Invoices
|
||||
- [ ] POST /api/orders/{id}/invoice (one open invoice per order; amount = order total)
|
||||
- [ ] GET /api/invoices (customer), GET /api/shop/invoices, POST /api/shop/invoices/{id}/issue
|
||||
|
||||
## 4. Tests
|
||||
- [ ] cargo test: partial shipment qty math, order status propagation, duplicate invoice rejected, issue flow
|
||||
Reference in New Issue
Block a user