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
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)
// 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
// 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
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
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
-- 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
8.2 Backoffice
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