SRV-20 — Returns & after-sales (SAV) module
Return requests on delivered orders, approval, reception with automatic RETURN stock movements, credit notes; after-sales claims as tickets on order items with photos, resolved by replacement order, return, credit note, repair or rejection.
| Field | Details |
|---|---|
| Task code | SRV-20 |
| Phase | P1 — V1 build & integration |
| Product scope | V1 |
| Status | Done (Mon 19 Oct 2026) |
| Start | Mon 19 Oct 2026 |
| Due | Mon 19 Oct 2026 |
| Responsible | Anis — Full-stack developer |
| Co-responsible | — |
| Effort | 1.25 person-day(s) planned · 6 from scratch · size S |
| Area · app | Server · server |
| Depends on | SRV-16, SRV-24 |
| Unblocks | DSH-13 |
| Milestones | — |
Summary
Return requests on delivered orders, approval, reception with automatic RETURN stock movements, credit notes; after-sales claims as tickets on order items with photos, resolved by replacement order, return, credit note, repair or rejection.
Description
The 17 Oct decisions move returns and after-sales service into V1 (decisions 9 and 10; ORD-13, SUP-04, STK-03, FIN-08). This task implements return requests on delivered orders (ReturnRequest RET-YYYY-NNNNNN, ReturnItem) with the ReturnStatus lifecycle REQUESTED → APPROVED/REJECTED → IN_TRANSIT → RECEIVED → REFUNDED, within the return window and never above delivered minus already-returned quantities; back-office decision and reception (admin.returns.decide, admin.returns.receive) where reception posts RETURN stock movements automatically through the inventory hook and issues a CreditNote linked to the invoice. It also implements the resolution of after-sales claims (AFTER_SALES tickets with TicketItem lines, AfterSalesIssue and photos, opened through SRV-24) via admin.tickets.resolve with AfterSalesResolution: REPLACEMENT creates a zero-priced replacement order that still goes through the confirmation queue, RETURN creates an approved return, CREDIT_NOTE issues a credit note, REPAIR tracks the repair, REJECTED requires a reason. Out of scope: returns and SAV pages (DSH-13) and the reseller screens (MRA-10, WEB-04/05).
Subtasks
| # | Subtask | Owner |
|---|---|---|
| 1 | returns.create on a DELIVERED order: items, quantities, reason, photos; checks orders.returnWindowDays (RETURN_WINDOW_EXPIRED) and remaining quantities (RETURN_QTY_EXCEEDED); RET numbering | Anis |
| 2 | ReturnStatus transition table and guards; returns.list/get for the reseller; admin.returns.list/get with filters | Anis |
| 3 | admin.returns.decide: approve or reject with reason; IN_TRANSIT when pickup is arranged or the reseller ships | Anis |
| 4 | admin.returns.receive: received and accepted quantities per item, condition, target warehouse; RETURN stock movements through the inventory hook in the same transaction | Anis |
| 5 | CreditNote issuance (numbered, lines from order snapshots incl. VAT and pro-rated discounts) linked to the invoice; REFUNDED status; balance effect for billing | Anis |
| 6 | Order status DELIVERED → RETURNED when every line is fully returned; partial returns keep DELIVERED with returned quantities | Anis |
| 7 | admin.tickets.resolve for AFTER_SALES tickets: REPLACEMENT order (zero-priced lines, linked to ticket, lands in SUBMITTED), RETURN (approved return), CREDIT_NOTE, REPAIR, REJECTED with reason | Anis |
| 8 | Outbox events return.requested/approved/rejected/received/refunded and ticket.resolved to the reseller stream, notifications and the back-office stream | Anis |
| 9 | Credit note PDF on the billing PDF base | Anis |
| 10 | Return and claim payloads review for MRA-10 | Imad |
Deliverables
- apps/server/src/modules/orders/returns/{router,service,state-machine,events}.ts
- apps/server/src/modules/billing/credit-notes.{service,pdf}.ts
- apps/server/src/modules/support/after-sales/resolution.service.ts
- packages/contracts/src/{returns.ts,admin/returns.ts,admin/support.ts (resolve)}
Acceptance criteria
- A return request on an order delivered more than orders.returnWindowDays ago returns 422 RETURN_WINDOW_EXPIRED; quantities above delivered minus returned return 422 RETURN_QTY_EXCEEDED (ORD-13)
- Receiving a return writes one RETURN movement per accepted line and increases physical and available stock automatically (STK-03)
- Reception issues a credit note whose total equals the returned lines at their order snapshot prices incl. VAT, and the return becomes REFUNDED (FIN-08)
- Resolving an AFTER_SALES ticket with REPLACEMENT creates a zero-priced order linked to the ticket in the confirmation queue (SUP-04, ORD-14)
- Resolving with REJECTED without a reason returns 422 VALIDATION_FAILED
- Each return status change reaches the reseller live and as a notification (ORD-15)
Requirements covered
| Requirement | Title | Release |
|---|---|---|
| ORD-13 | Returns: request on a delivered order, back-office approval, reception; returned quantities re-enter stock automatically | V1 |
| SUP-04 | After-sales service (SAV): a reseller opens a claim on an order (items, quantities, issue, photos); the back-office handles it from diagnosis to resolution (replacement, return, credit note, rejection) | V1 |
| STK-03 | Reservation and deduction triggered by configurable order statuses; cancellations and returns put quantities back automatically | V1 |
| FIN-08 | Credit notes (avoirs) for returns and after-sales refunds; payment receipts | V1 |
Screens
| Screen | Name | App |
|---|---|---|
| BO-69 | Returns & after-sales claims | BO |
API surface
| Endpoint group | Operations |
|---|---|
| Reseller orders | 11 |
| Orders & delivery | 21 |
| Support & communication | 10 |
Test measures
- Table-driven tests for the ReturnStatus transitions and quantity rules
- DB tests against real Postgres for reception: stock movements, credit note totals, idempotent reception
- E2E on staging: reseller return request → approve → receive → credit note visible in the reseller finance view
Risks
- Defective returned items should not re-enter sellable stock — reception lets the operator route quantities to a non-selling (quarantine) warehouse; only sellable quantities raise availability
- Credit note legal format not yet validated — reuse the invoice legal mentions from DQA-09 and confirm with HOCO's accountant
KPI
- Delivered by Mon 19 Oct 2026 within 1.25 person-day(s).
- Every acceptance criterion above demonstrated before hand-off; linked requirements' acceptance criteria pass.
- Zero open critical defects, permission gaps or data-integrity regressions at sign-off.
Generated from content/plan/tasks.json by scripts/generate-team-tasks.mjs. Dates are computed by the scheduler (Sunday–Thursday weeks, holidays excluded) — change the plan, not this page.
SRV-24 — Support module — tickets & announcements
Tickets with types, related order and items, messages and internal notes, assignment and statuses; announcements with targeting; support contacts/FAQ; support regions with WhatsApp numbers resolved from the declared region or device location.
SRV-26 — Real-time events — back-office stream and reseller live order status (SSE)
Two permission-filtered SSE streams fed by the outbox: the back-office stream (orders, check-ins, tours, positions, stock alerts, pending accounts, returns, claims) and each user's own stream for the reseller app and web (order status changes, assisted orders, ticket replies, returns).