feat(ADM-018): completed feature
This commit is contained in:
295
project/specs-admin/050-orders/SPEC.md
Normal file
295
project/specs-admin/050-orders/SPEC.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# ADM-013 / ADM-014 / ADM-015 — Orders Spec
|
||||
|
||||
## Goal
|
||||
|
||||
Gestión completa de pedidos: listado, detalle, y transiciones de estado.
|
||||
|
||||
---
|
||||
|
||||
## 1. Order List (`/admin/orders`)
|
||||
|
||||
### API Contract
|
||||
|
||||
```
|
||||
GET /orders
|
||||
Authorization: session_token (admin role)
|
||||
|
||||
Query params:
|
||||
?offset=0&limit=20
|
||||
?state=PENDING
|
||||
?q=maria@ejemplo.com (búsqueda por email de cliente)
|
||||
|
||||
Response: OrderSummary[]
|
||||
```
|
||||
|
||||
```typescript
|
||||
interface OrderSummary {
|
||||
id: string;
|
||||
userId: string;
|
||||
state: OrderState;
|
||||
totalCents: number;
|
||||
currency: 'EUR';
|
||||
itemCount: number;
|
||||
createdAt: string; // ISO
|
||||
}
|
||||
```
|
||||
|
||||
### Desktop UX
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Pedidos [Estado ▼] [Búsqueda: ________] [🔍] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ #ID Cliente Total Estado Fecha │
|
||||
│ ──────────────────────────────────────────────────────────────── │
|
||||
│ abc-123 maria@email.com €45.83 ● Paid 15 ago 2026 │
|
||||
│ def-456 juan@email.com €23.10 ● Pending 14 ago 2026 │
|
||||
│ ghi-789 ana@email.com €89.00 ● Shipped 10 ago 2026 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ [← Anterior] Página 1 de 5 [Siguiente →] │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Filters
|
||||
|
||||
- **Estado**: PENDING, AWAITING_PAYMENT, PAID, PROCESSING, SHIPPED, DELIVERED, CANCELLED, REFUNDED, PARTIALLY_REFUNDED
|
||||
- **Búsqueda**: por email de cliente (o ID)
|
||||
- Estado por defecto: todos
|
||||
|
||||
### Responsive
|
||||
|
||||
Mobile: Cards en lugar de tabla
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ #abc-123 │
|
||||
│ maria@email.com │
|
||||
│ €45.83 · ● Paid │
|
||||
│ 15 ago 2026 │
|
||||
│ [Ver detalle →] │
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Order Detail (`/admin/orders/[id]`)
|
||||
|
||||
### API Contract
|
||||
|
||||
```
|
||||
GET /orders/:id/admin
|
||||
Authorization: session_token (admin role)
|
||||
|
||||
Response: Order (full)
|
||||
```
|
||||
|
||||
```typescript
|
||||
interface Order {
|
||||
id: string;
|
||||
userId: string;
|
||||
state: OrderState;
|
||||
subtotalCents: number;
|
||||
discountCents: number;
|
||||
taxCents: number;
|
||||
totalCents: number;
|
||||
currency: 'EUR';
|
||||
idempotencyKey: string | null;
|
||||
items: OrderItem[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
interface OrderItem {
|
||||
id: string;
|
||||
productId: string;
|
||||
variantId: string;
|
||||
sku: string;
|
||||
ean: string | null;
|
||||
name: string;
|
||||
unitPriceCents: number;
|
||||
discountCents: number;
|
||||
taxCents: number;
|
||||
quantity: number;
|
||||
}
|
||||
```
|
||||
|
||||
### Desktop UX — Layout
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ← Volver a pedidos │
|
||||
├───────────────────────────────────┬─────────────────────────────┤
|
||||
│ HEADER │ ACCIONES │
|
||||
│ #abc-123-def │ [Cambiar estado ▼] │
|
||||
│ ● Paid · 15 ago 2026 │ │
|
||||
├───────────────────────────────────┴─────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────┐ ┌─────────────────────────────┐ │
|
||||
│ │ RESUMEN DEL PEDIDO │ │ CLIENTE │ │
|
||||
│ │ │ │ maria@email.com │ │
|
||||
│ │ Subtotal €37.88 │ │ │ │
|
||||
│ │ Descuentos €0.00 │ │ ENVÍO │ │
|
||||
│ │ IVA €7.95 │ │ Calle Gran Vía 42 │ │
|
||||
│ │ ─────────────────── │ │ Madrid, 28013 │ │
|
||||
│ │ TOTAL €45.83 │ │ │ │
|
||||
│ └─────────────────────────┘ └─────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ PRODUCTOS PEDIDOS │ │
|
||||
│ │ ───────────────────────────────────────────────────── │ │
|
||||
│ │ Almendras Crudas 2 × €8.95 = €17.90 │ │
|
||||
│ │ Vitamina D3 + K2 2 × €9.99 = €19.98 │ │
|
||||
│ │ ───────────────────────────────────────────────────── │ │
|
||||
│ │ Envío estándar + €4.99 │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ HISTORIAL │ │
|
||||
│ │ ───────────────────────────────────────────────────── │ │
|
||||
│ │ ● 15 ago 2026 14:32 Creado │ │
|
||||
│ │ ● 15 ago 2026 14:35 Pagado │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Payment Transactions
|
||||
|
||||
```
|
||||
GET /payments/orders/:orderId/transactions
|
||||
Response: { transactions: PaymentTransaction[] }
|
||||
```
|
||||
|
||||
Mostrar en sección separada: fecha, método, monto, estado.
|
||||
|
||||
---
|
||||
|
||||
## 3. Order State Transitions
|
||||
|
||||
### API Contract
|
||||
|
||||
```
|
||||
POST /orders/:id/transitions/admin
|
||||
Authorization: session_token (admin role)
|
||||
|
||||
Body: { state: OrderState }
|
||||
|
||||
Response: Order (updated)
|
||||
```
|
||||
|
||||
### State Machine
|
||||
|
||||
```
|
||||
PENDING ──────────→ AWAITING_PAYMENT (customer paid)
|
||||
│ │
|
||||
├─→ CANCELLED └─→ PAID ──→ PROCESSING ──→ SHIPPED ──→ DELIVERED
|
||||
│ │ │
|
||||
│ ├─→ CANCELLED│
|
||||
│ │ ├─→ CANCELLED
|
||||
│ │ │
|
||||
│ ├─→ REFUNDED (full)
|
||||
│ └─→ PARTIALLY_REFUNDED
|
||||
│ │
|
||||
└─→ CANCELLED ←──────────────────────────────┘
|
||||
```
|
||||
|
||||
### Allowed transitions visible in UI
|
||||
|
||||
| Current State | Actions Available |
|
||||
|-------------------|------------------------------------------------------------|
|
||||
| PENDING | Marcar como Pagado · Cancelar pedido |
|
||||
| AWAITING_PAYMENT | Marcar como Pagado · Cancelar pedido |
|
||||
| PAID | Procesar pedido · Cancelar pedido · Reembolsar |
|
||||
| PROCESSING | Marcar como enviado · Cancelar pedido · Reembolsar |
|
||||
| SHIPPED | Marcar como entregado · Reembolso parcial |
|
||||
| DELIVERED | Reembolso parcial |
|
||||
| CANCELLED | (ninguna) |
|
||||
| REFUNDED | (ninguna) |
|
||||
| PARTIALLY_REFUNDED| (ninguna) |
|
||||
|
||||
### UX para transición
|
||||
|
||||
1. Dropdown o botones con las acciones válidas
|
||||
2. Al hacer click en acción → Dialog de confirmación:
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ Cambiar estado del pedido │
|
||||
│ │
|
||||
│ ¿Marcar este pedido como PAGADO? │
|
||||
│ │
|
||||
│ Estado actual: ● Pendiente │
|
||||
│ Nuevo estado: ● Pagado │
|
||||
│ │
|
||||
│ [Cancelar] [Confirmar] │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
3. Para Cancelar y Refund: campo obligatorio de "Motivo"
|
||||
4. Submit → esperar respuesta → refresh del detalle
|
||||
|
||||
### Cancellation UX
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ ⚠️ Cancelar pedido │
|
||||
│ │
|
||||
│ ¿Estás seguro de cancelar el pedido │
|
||||
│ #abc-123-def? │
|
||||
│ │
|
||||
│ Motivo * │
|
||||
│ ┌────────────────────────────────────┐│
|
||||
│ │ Cliente solicitó cancelación ││
|
||||
│ └────────────────────────────────────┘│
|
||||
│ │
|
||||
│ [Volver] [Cancelar pedido] │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Acceptance Criteria
|
||||
|
||||
### ADM-013 — Order List
|
||||
- [ ] Órdenes listadas con paginación (20 por página)
|
||||
- [ ] Filtro por estado funcional
|
||||
- [ ] Búsqueda por email/ID funcional
|
||||
- [ ] Link a detalle
|
||||
- [ ] Empty state si no hay pedidos
|
||||
- [ ] Loading skeleton
|
||||
|
||||
### ADM-014 — Order Detail
|
||||
- [ ] Resumen con todos los totales
|
||||
- [ ] Lista de productos con precios
|
||||
- [ ] Información de cliente
|
||||
- [ ] Línea de tiempo con historial de estados
|
||||
- [ ] Transacciones de pago visibles
|
||||
- [ ] Estado actual con badge de color + texto
|
||||
|
||||
### ADM-015 — State Transitions
|
||||
- [ ] Solo acciones válidas visibles según estado actual
|
||||
- [ ] Confirmación antes de ejecutar
|
||||
- [ ] Campo "Motivo" obligatorio para Cancelar y Refund
|
||||
- [ ] Refresh del detalle post-transición
|
||||
- [ ] Timeline actualizada
|
||||
- [ ] Toast de éxito/error
|
||||
|
||||
---
|
||||
|
||||
## 5. Tests
|
||||
|
||||
### Unit
|
||||
- State machine: verify only valid transitions shown
|
||||
- Transition formatter: state → label + color
|
||||
|
||||
### Component
|
||||
- OrderStatusBadge renders correct color for each state
|
||||
- TransitionDialog shows only valid actions
|
||||
- OrderTimeline renders in correct order
|
||||
|
||||
### Integration
|
||||
- Transition PENDING → PAID → PROCESSING → SHIPPED → DELIVERED
|
||||
- Attempt invalid transition → backend error shown
|
||||
|
||||
### E2E
|
||||
- Filter orders by status
|
||||
- View order detail
|
||||
- Transition order through valid states
|
||||
- Cancel order with reason
|
||||
- Error when backend rejects transition
|
||||
Reference in New Issue
Block a user