568 lines
14 KiB
Markdown
568 lines
14 KiB
Markdown
# 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
|