# ANALISIS MODULO RT-009: E-COMMERCE

**Fecha:** 2025-12-18
**Fase:** 2 - Analisis por Modulo
**Modulo:** RT-009 E-commerce
**Herencia:** 20%
**Story Points:** 55
**Prioridad:** P2

---

## 1. DESCRIPCION GENERAL

### 1.1 Proposito
Tienda online integrada con inventario, carrito de compras, checkout, pasarelas de pago y opciones de entrega.

### 1.2 Funcionalidades Principales

| Funcionalidad | Descripcion | Criticidad |
|---------------|-------------|------------|
| Catalogo online | Navegacion y busqueda | Alta |
| Carrito | Persistente | Alta |
| Checkout | Flujo completo | Critica |
| Pagos | Multiples pasarelas | Critica |
| Envio | Multiples opciones | Alta |
| Pedidos | Gestion backoffice | Alta |

---

## 2. HERENCIA DEL CORE

### 2.1 Componentes Heredados (20%)

| Componente Core | % Uso | Accion |
|-----------------|-------|--------|
| inventory.products | 100% | HEREDAR |
| sales.pricelists | 100% | HEREDAR |
| core.partners | 100% | HEREDAR |

### 2.2 Servicios a Heredar

```typescript
import { ProductsService } from '@erp-core/inventory';
import { PricelistsService } from '@erp-core/sales';
import { PartnersService } from '@erp-core/core';
```

---

## 3. COMPONENTES NUEVOS

### 3.1 Entidades (TypeORM)

```typescript
// 1. EcommerceOrder - Pedido online
@Entity('ecommerce_orders', { schema: 'retail' })
export class EcommerceOrder {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column('uuid')
  tenantId: string;

  @Column()
  orderNumber: string;

  @ManyToOne(() => Partner)
  customer: Partner;

  @Column({ type: 'enum', enum: EcommerceOrderStatus })
  status: EcommerceOrderStatus;
  // pending, paid, preparing, shipped, ready_pickup, delivered, cancelled

  @Column({ type: 'timestamptz' })
  orderDate: Date;

  // Totales
  @Column({ type: 'decimal', precision: 12, scale: 2 })
  subtotal: number;

  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  discountAmount: number;

  @Column({ type: 'decimal', precision: 12, scale: 2 })
  shippingCost: number;

  @Column({ type: 'decimal', precision: 12, scale: 2 })
  taxAmount: number;

  @Column({ type: 'decimal', precision: 12, scale: 2 })
  total: number;

  // Pago
  @Column({ type: 'enum', enum: PaymentStatus })
  paymentStatus: PaymentStatus;

  @Column({ nullable: true })
  paymentMethod: string;

  @Column({ nullable: true })
  paymentReference: string;

  // Entrega
  @Column({ type: 'enum', enum: DeliveryMethod })
  deliveryMethod: DeliveryMethod;  // shipping, pickup

  @ManyToOne(() => Branch, { nullable: true })
  pickupBranch: Branch;  // Si es pickup

  @Column({ type: 'jsonb', nullable: true })
  shippingAddress: Address;

  @Column({ nullable: true })
  trackingNumber: string;

  @OneToMany(() => EcommerceOrderLine, line => line.order)
  lines: EcommerceOrderLine[];

  @Column({ nullable: true })
  notes: string;
}

// 2. EcommerceOrderLine
@Entity('ecommerce_order_lines', { schema: 'retail' })
export class EcommerceOrderLine {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @ManyToOne(() => EcommerceOrder)
  order: EcommerceOrder;

  @ManyToOne(() => Product)
  product: Product;

  @Column()
  productName: string;

  @Column({ type: 'decimal', precision: 12, scale: 4 })
  quantity: number;

  @Column({ type: 'decimal', precision: 12, scale: 2 })
  unitPrice: number;

  @Column({ type: 'decimal', precision: 12, scale: 2 })
  total: number;
}

// 3. Cart - Carrito (session-based)
@Entity('carts', { schema: 'retail' })
export class Cart {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column('uuid')
  tenantId: string;

  @ManyToOne(() => Partner, { nullable: true })
  customer: Partner;  // null si guest

  @Column({ nullable: true })
  sessionId: string;  // Para guests

  @OneToMany(() => CartItem, item => item.cart)
  items: CartItem[];

  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  subtotal: number;

  @Column({ type: 'timestamptz' })
  createdAt: Date;

  @Column({ type: 'timestamptz' })
  updatedAt: Date;

  @Column({ type: 'timestamptz', nullable: true })
  expiresAt: Date;
}

// 4. CartItem
@Entity('cart_items', { schema: 'retail' })
export class CartItem {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @ManyToOne(() => Cart)
  cart: Cart;

  @ManyToOne(() => Product)
  product: Product;

  @Column({ type: 'decimal', precision: 12, scale: 4 })
  quantity: number;

  @Column({ type: 'decimal', precision: 12, scale: 2 })
  unitPrice: number;

  @Column({ type: 'decimal', precision: 12, scale: 2 })
  total: number;
}

// 5. ShippingRate
@Entity('shipping_rates', { schema: 'retail' })
export class ShippingRate {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column('uuid')
  tenantId: string;

  @Column()
  name: string;  // "Envio estandar", "Express"

  @Column()
  carrier: string;  // "Fedex", "DHL", "Estafeta"

  @Column({ type: 'decimal', precision: 12, scale: 2 })
  baseRate: number;

  @Column({ type: 'decimal', precision: 12, scale: 2, nullable: true })
  freeShippingMinimum: number;

  @Column({ type: 'int', nullable: true })
  estimatedDays: number;

  @Column({ type: 'boolean', default: true })
  isActive: boolean;
}
```

### 3.2 Servicios Backend

| Servicio | Metodos Principales |
|----------|-------------------|
| CatalogService | search(), getByCategory(), getProduct() |
| CartService | create(), addItem(), updateItem(), removeItem(), clear() |
| CheckoutService | validate(), calculateTotals(), processPayment() |
| PaymentGatewayService | createPayment(), capture(), refund() |
| ShippingService | calculateRates(), createShipment(), track() |
| EcommerceOrderService | create(), updateStatus(), getByCustomer() |

### 3.3 Controladores

```typescript
// API Publica (Storefront)
@Controller('store')
export class StorefrontController {
  // Catalogo
  @Get('products')
  getProducts(@Query() filters: CatalogFilters): Promise<PaginatedProducts>;

  @Get('products/:id')
  getProduct(@Param('id') id: string): Promise<ProductDetail>;

  @Get('products/search')
  searchProducts(@Query('q') query: string): Promise<Product[]>;

  @Get('categories')
  getCategories(): Promise<Category[]>;

  // Carrito
  @Get('cart')
  getCart(): Promise<Cart>;

  @Post('cart/items')
  addToCart(@Body() dto: AddToCartDto): Promise<Cart>;

  @Put('cart/items/:id')
  updateCartItem(@Param('id') id: string, @Body() dto: UpdateCartDto): Promise<Cart>;

  @Delete('cart/items/:id')
  removeFromCart(@Param('id') id: string): Promise<Cart>;

  // Checkout
  @Post('checkout/validate')
  validateCheckout(@Body() dto: CheckoutDto): Promise<CheckoutValidation>;

  @Get('checkout/shipping-rates')
  getShippingRates(@Query() dto: ShippingQuery): Promise<ShippingRate[]>;

  @Post('checkout/complete')
  completeCheckout(@Body() dto: CompleteCheckoutDto): Promise<EcommerceOrder>;

  // Pagos
  @Post('payments/create')
  createPayment(@Body() dto: PaymentDto): Promise<PaymentIntent>;

  @Post('payments/webhook')
  handleWebhook(@Body() payload: any): Promise<void>;

  // Pedidos (autenticado)
  @Get('orders')
  @UseGuards(AuthGuard)
  getMyOrders(): Promise<EcommerceOrder[]>;

  @Get('orders/:id')
  @UseGuards(AuthGuard)
  getOrder(@Param('id') id: string): Promise<EcommerceOrder>;
}

// API Backoffice
@Controller('ecommerce')
export class EcommerceController {
  // Gestion de pedidos
  @Get('orders')
  getOrders(@Query() filters: OrderFilters): Promise<PaginatedOrders>;

  @Put('orders/:id/status')
  updateStatus(@Param('id') id: string, @Body() dto: StatusDto): Promise<EcommerceOrder>;

  @Post('orders/:id/ship')
  markAsShipped(@Param('id') id: string, @Body() dto: ShipDto): Promise<EcommerceOrder>;

  // Configuracion
  @Get('shipping-rates')
  getShippingRates(): Promise<ShippingRate[]>;

  @Post('shipping-rates')
  createShippingRate(@Body() dto: CreateRateDto): Promise<ShippingRate>;
}
```

---

## 4. INTEGRACIONES EXTERNAS

### 4.1 Pasarelas de Pago

```typescript
interface PaymentGateway {
  name: string;
  createPayment(order: EcommerceOrder): Promise<PaymentIntent>;
  capturePayment(paymentId: string): Promise<PaymentResult>;
  refund(paymentId: string, amount: number): Promise<RefundResult>;
}

// Implementaciones
class StripeGateway implements PaymentGateway { ... }
class ConektaGateway implements PaymentGateway { ... }
class MercadoPagoGateway implements PaymentGateway { ... }
```

### 4.2 Proveedores de Envio

```typescript
interface ShippingProvider {
  name: string;
  calculateRate(origin: Address, destination: Address, weight: number): Promise<ShippingQuote>;
  createShipment(order: EcommerceOrder): Promise<Shipment>;
  getTracking(trackingNumber: string): Promise<TrackingInfo>;
}

// Implementaciones
class FedexProvider implements ShippingProvider { ... }
class DHLProvider implements ShippingProvider { ... }
class EstafetaProvider implements ShippingProvider { ... }
```

---

## 5. FLUJOS DE NEGOCIO

### 5.1 Flujo de Compra

```
1. Cliente navega catalogo
      ↓
2. Agrega productos al carrito
      ↓
3. Revisa carrito
      ↓
4. Inicia checkout
      ↓
5. Login/registro (o guest)
      ↓
6. Selecciona direccion de envio
      ↓
7. Selecciona metodo de envio
      ↓
8. Ve resumen con totales
      ↓
9. Selecciona metodo de pago
      ↓
10. Procesa pago
      ↓
11. Confirmacion de pedido
      ↓
12. Email de confirmacion
```

### 5.2 Flujo de Gestion de Pedido

```
1. Pedido creado (PENDING)
      ↓
2. Pago confirmado (PAID)
      ↓
3. Preparando (PREPARING)
   - Reservar stock
   - Imprimir etiqueta
      ↓
4. Enviado (SHIPPED) o Listo para recoger (READY_PICKUP)
   - Tracking disponible
      ↓
5. Entregado (DELIVERED)
      ↓
6. Fin (o cancelado en cualquier punto)
```

---

## 6. TABLAS DDL

### 6.1 Tablas Nuevas

```sql
-- Pedidos
CREATE TABLE retail.ecommerce_orders (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id),
  order_number VARCHAR(20) NOT NULL,
  customer_id UUID NOT NULL REFERENCES core.partners(id),
  status VARCHAR(20) NOT NULL DEFAULT 'pending',
  order_date TIMESTAMPTZ NOT NULL DEFAULT NOW(),

  subtotal DECIMAL(12,2) NOT NULL,
  discount_amount DECIMAL(12,2) DEFAULT 0,
  shipping_cost DECIMAL(12,2) NOT NULL DEFAULT 0,
  tax_amount DECIMAL(12,2) NOT NULL DEFAULT 0,
  total DECIMAL(12,2) NOT NULL,

  payment_status VARCHAR(20),
  payment_method VARCHAR(50),
  payment_reference VARCHAR(100),

  delivery_method VARCHAR(20) NOT NULL,
  pickup_branch_id UUID REFERENCES retail.branches(id),
  shipping_address JSONB,
  tracking_number VARCHAR(50),

  notes TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ,

  UNIQUE(tenant_id, order_number)
);

-- Lineas
CREATE TABLE retail.ecommerce_order_lines (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id),
  order_id UUID NOT NULL REFERENCES retail.ecommerce_orders(id) ON DELETE CASCADE,
  product_id UUID NOT NULL REFERENCES inventory.products(id),
  product_name VARCHAR(255) NOT NULL,
  quantity DECIMAL(12,4) NOT NULL,
  unit_price DECIMAL(12,2) NOT NULL,
  total DECIMAL(12,2) NOT NULL
);

-- Carritos
CREATE TABLE retail.carts (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id),
  customer_id UUID REFERENCES core.partners(id),
  session_id VARCHAR(100),
  subtotal DECIMAL(12,2) DEFAULT 0,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ,
  expires_at TIMESTAMPTZ
);

CREATE TABLE retail.cart_items (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id),
  cart_id UUID NOT NULL REFERENCES retail.carts(id) ON DELETE CASCADE,
  product_id UUID NOT NULL REFERENCES inventory.products(id),
  quantity DECIMAL(12,4) NOT NULL,
  unit_price DECIMAL(12,2) NOT NULL,
  total DECIMAL(12,2) NOT NULL
);

-- Tarifas de envio
CREATE TABLE retail.shipping_rates (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id),
  name VARCHAR(100) NOT NULL,
  carrier VARCHAR(50) NOT NULL,
  base_rate DECIMAL(12,2) NOT NULL,
  free_shipping_minimum DECIMAL(12,2),
  estimated_days INT,
  is_active BOOLEAN DEFAULT TRUE,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
```

---

## 7. DEPENDENCIAS

### 7.1 Dependencias de Retail

| Modulo | Tipo |
|--------|------|
| RT-001 Fundamentos | Prerequisito |
| RT-003 Inventario | Stock |
| RT-005 Clientes | Clientes y puntos |
| RT-006 Precios | Precios online |

### 7.2 Dependencias Externas

| Servicio | Proposito |
|----------|-----------|
| Stripe/Conekta | Pagos |
| Fedex/DHL | Envios |
| SendGrid | Emails |

### 7.3 Bloquea a

| Modulo | Razon |
|--------|-------|
| RT-010 Facturacion | Facturas de pedidos |

---

## 8. CRITERIOS DE ACEPTACION

### 8.1 Storefront

- [ ] Navegar catalogo
- [ ] Buscar productos
- [ ] Ver detalle de producto
- [ ] Stock en tiempo real
- [ ] Agregar al carrito
- [ ] Carrito persistente
- [ ] Checkout como guest o registrado
- [ ] Multiples metodos de pago
- [ ] Envio o pickup
- [ ] Confirmacion por email

### 8.2 Backoffice

- [ ] Ver pedidos
- [ ] Actualizar estado
- [ ] Generar etiqueta envio
- [ ] Registrar tracking
- [ ] Cancelar pedido

---

## 9. ESTIMACION DETALLADA

| Componente | SP Backend | SP Frontend | Total |
|------------|-----------|-------------|-------|
| Entities + Migrations | 5 | - | 5 |
| CatalogService | 3 | - | 3 |
| CartService | 5 | - | 5 |
| CheckoutService | 5 | - | 5 |
| PaymentGatewayService | 8 | - | 8 |
| ShippingService | 5 | - | 5 |
| OrderService | 5 | - | 5 |
| Storefront UI | - | 13 | 13 |
| Backoffice UI | - | 6 | 6 |
| **TOTAL** | **36** | **19** | **55** |

---

**Estado:** ANALISIS COMPLETO
**Bloqueado por:** RT-001, RT-003, RT-005, RT-006
