# 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
