# ANALISIS MODULO RT-002: PUNTO DE VENTA (POS) **Fecha:** 2025-12-18 **Fase:** 2 - Analisis por Modulo **Modulo:** RT-002 POS **Herencia:** 20% **Story Points:** 55 **Prioridad:** P0 (Critico) --- ## 1. DESCRIPCION GENERAL ### 1.1 Proposito Terminal de punto de venta para operacion en tienda fisica con soporte offline, integracion de hardware y venta rapida en mostrador. ### 1.2 Funcionalidades Principales | Funcionalidad | Descripcion | Criticidad | |---------------|-------------|------------| | Venta rapida | Escaneo y cobro < 30s | Critica | | Modo offline | Operacion sin internet | Critica | | Multi-pago | Efectivo, tarjeta, mixto | Alta | | Descuentos | Manuales y automaticos | Alta | | Hardware | Impresora, lector, cajon | Alta | | Tickets | Impresion automatica | Alta | --- ## 2. HERENCIA DEL CORE ### 2.1 Componentes Heredados (20%) | Componente Core | Uso en POS | |-----------------|------------| | inventory.products | Catalogo de productos | | sales.pricelists | Precios base | | core.partners | Clientes | | auth.users | Cajeros | ### 2.2 Tablas Reutilizadas | Tabla Core | Relacion | |------------|----------| | inventory.products | FK en pos_order_lines.product_id | | core.partners | FK en pos_orders.customer_id | | auth.users | FK en pos_sessions.user_id | | sales.pricelists | Consulta de precios | --- ## 3. COMPONENTES NUEVOS ### 3.1 Entidades (TypeORM) ```typescript // 1. POSSession - Sesion de caja @Entity('pos_sessions', { schema: 'retail' }) export class POSSession { @PrimaryGeneratedColumn('uuid') id: string; @Column('uuid') tenantId: string; @ManyToOne(() => Branch) branch: Branch; @ManyToOne(() => CashRegister) cashRegister: CashRegister; @ManyToOne(() => User) user: User; @Column({ type: 'enum', enum: POSSessionStatus }) status: POSSessionStatus; @Column({ type: 'timestamptz' }) openingDate: Date; @Column({ type: 'decimal', precision: 12, scale: 2 }) openingBalance: number; @Column({ type: 'timestamptz', nullable: true }) closingDate: Date; @Column({ type: 'decimal', precision: 12, scale: 2, nullable: true }) closingBalance: number; @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 }) totalSales: number; @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 }) totalRefunds: number; } // 2. POSOrder - Venta @Entity('pos_orders', { schema: 'retail' }) export class POSOrder { @PrimaryGeneratedColumn('uuid') id: string; @Column('uuid') tenantId: string; @ManyToOne(() => POSSession) session: POSSession; @Column() orderNumber: string; @Column({ type: 'timestamptz' }) orderDate: Date; @ManyToOne(() => Partner, { nullable: true }) customer: Partner; @Column({ type: 'enum', enum: POSOrderStatus }) status: POSOrderStatus; @Column({ type: 'decimal', precision: 12, scale: 2 }) subtotal: number; @Column({ type: 'decimal', precision: 12, scale: 2 }) discountAmount: number; @Column({ type: 'decimal', precision: 12, scale: 2 }) taxAmount: number; @Column({ type: 'decimal', precision: 12, scale: 2 }) total: number; @Column({ type: 'enum', enum: PaymentMethod }) paymentMethod: PaymentMethod; @OneToMany(() => POSOrderLine, line => line.order) lines: POSOrderLine[]; } // 3. POSOrderLine - Linea de venta @Entity('pos_order_lines', { schema: 'retail' }) export class POSOrderLine { @PrimaryGeneratedColumn('uuid') id: string; @ManyToOne(() => POSOrder) order: POSOrder; @ManyToOne(() => Product) product: Product; @Column() productName: string; @Column({ nullable: true }) barcode: string; @Column({ type: 'decimal', precision: 12, scale: 4 }) quantity: number; @Column({ type: 'decimal', precision: 12, scale: 2 }) unitPrice: number; @Column({ type: 'decimal', precision: 5, scale: 2, default: 0 }) discountPercent: number; @Column({ type: 'decimal', precision: 12, scale: 2 }) subtotal: number; @Column({ type: 'decimal', precision: 12, scale: 2 }) total: number; } ``` ### 3.2 Servicios Backend | Servicio | Metodos Principales | |----------|-------------------| | POSSessionService | openSession(), closeSession(), getActiveSession() | | POSOrderService | createOrder(), addLine(), removeLine(), applyDiscount(), pay() | | POSPaymentService | processPayment(), processMixedPayment(), refund() | | POSPrintService | printTicket(), openCashDrawer() | | POSSyncService | syncOfflineOrders(), resolveConflicts() | ### 3.3 Controladores ```typescript @Controller('pos') export class POSController { // Sessions @Post('sessions/open') openSession(@Body() dto: OpenSessionDto): Promise; @Post('sessions/:id/close') closeSession(@Param('id') id: string, @Body() dto: CloseSessionDto): Promise; @Get('sessions/active') getActiveSession(): Promise; // Orders @Post('orders') createOrder(@Body() dto: CreateOrderDto): Promise; @Post('orders/:id/lines') addLine(@Param('id') id: string, @Body() dto: AddLineDto): Promise; @Delete('orders/:id/lines/:lineId') removeLine(@Param('id') id: string, @Param('lineId') lineId: string): Promise; @Post('orders/:id/discount') applyDiscount(@Param('id') id: string, @Body() dto: DiscountDto): Promise; @Post('orders/:id/pay') pay(@Param('id') id: string, @Body() dto: PaymentDto): Promise; // Products @Get('products/search') searchProducts(@Query('q') query: string): Promise; @Get('products/barcode/:code') getByBarcode(@Param('code') code: string): Promise; // Sync @Post('sync') syncOfflineOrders(@Body() dto: SyncDto): Promise; } ``` ### 3.4 Frontend (PWA) | Pagina/Componente | Descripcion | |-------------------|-------------| | POSPage | Pantalla principal de venta | | ProductSearch | Busqueda de productos | | Cart | Carrito de compra | | PaymentModal | Modal de pago | | TicketPreview | Vista previa de ticket | | SessionStatus | Estado de sesion | | OfflineIndicator | Indicador modo offline | | HardwareStatus | Estado de hardware | --- ## 4. REQUERIMIENTOS TECNICOS ### 4.1 PWA y Modo Offline ```yaml pwa_config: manifest: name: "Retail POS" short_name: "POS" display: "fullscreen" orientation: "landscape" service_worker: cache_strategy: "cache-first" assets_to_cache: - "/pos/**" - "/api/products" - "/api/pricelists" indexed_db: stores: - products - prices - customers - offline_orders - pending_sync sync: strategy: "background-sync" retry_interval: "30s" max_retries: 10 ``` ### 4.2 Performance | Operacion | Objetivo | Implementacion | |-----------|----------|----------------| | Busqueda producto | < 50ms | Indice + Cache Redis | | Agregar al carrito | < 100ms | Operacion local | | Calcular total | < 10ms | Calculo en memoria | | Procesar pago | < 3s | Async + optimistic UI | ### 4.3 Integracion Hardware ```typescript // Interfaz de Hardware interface POSHardware { printer: TicketPrinter; scanner: BarcodeScanner; drawer: CashDrawer; cardReader?: CardReader; } // Impresora ESC/POS interface TicketPrinter { connect(): Promise; print(ticket: TicketData): Promise; openDrawer(): Promise; cut(): Promise; } // Lector de codigo de barras interface BarcodeScanner { onScan(callback: (barcode: string) => void): void; } ``` --- ## 5. TABLAS DDL ### 5.1 Tablas Nuevas ```sql -- Ya definidas en 03-retail-tables.sql CREATE TABLE retail.pos_sessions (...); CREATE TABLE retail.pos_orders (...); CREATE TABLE retail.pos_order_lines (...); CREATE TABLE retail.pos_payments (...); CREATE TABLE retail.cash_registers (...); ``` ### 5.2 ENUMs ```sql CREATE TYPE retail.pos_session_status AS ENUM ('opening', 'open', 'closing', 'closed'); CREATE TYPE retail.pos_order_status AS ENUM ('draft', 'paid', 'done', 'cancelled', 'refunded'); CREATE TYPE retail.payment_method AS ENUM ('cash', 'card', 'transfer', 'credit', 'mixed'); ``` ### 5.3 Indices Criticos ```sql -- Performance para busqueda CREATE INDEX idx_pos_orders_session ON retail.pos_orders(session_id); CREATE INDEX idx_pos_orders_date ON retail.pos_orders(order_date); CREATE INDEX idx_pos_order_lines_order ON retail.pos_order_lines(order_id); CREATE INDEX idx_pos_order_lines_product ON retail.pos_order_lines(product_id); -- Busqueda por codigo de barras CREATE INDEX idx_product_barcodes_code ON retail.product_barcodes(barcode); ``` --- ## 6. DEPENDENCIAS ### 6.1 Dependencias de Core | Modulo | Requerido Para | |--------|---------------| | MGN-001 Auth | Autenticacion cajeros | | MGN-011 Inventory | Productos y stock | | MGN-013 Sales | Precios | | MGN-005 Catalogs | Partners (clientes) | ### 6.2 Dependencias de Retail | Modulo | Tipo | |--------|------| | RT-001 Fundamentos | Prerequisito | | RT-003 Inventario | Stock por sucursal | | RT-006 Precios | Promociones | | RT-005 Clientes | Programa lealtad | ### 6.3 Dependencias Externas | Servicio | Proposito | |----------|-----------| | Redis | Cache de productos | | WebSocket | Sync tiempo real | --- ## 7. FLUJOS DE NEGOCIO ### 7.1 Flujo de Venta ``` 1. Cajero abre sesion (fondo inicial) ↓ 2. Cliente presenta productos ↓ 3. Escanear/buscar productos ↓ 4. Agregar al carrito ↓ 5. Aplicar descuentos (si aplica) ↓ 6. Seleccionar forma de pago ↓ 7. Procesar pago ↓ 8. Imprimir ticket ↓ 9. Abrir cajon (si efectivo) ↓ 10. Entregar cambio y productos ``` ### 7.2 Flujo Offline ``` 1. Detectar perdida de conexion ↓ 2. Activar modo offline ↓ 3. Usar cache local (IndexedDB) ↓ 4. Almacenar ventas en cola ↓ 5. Detectar reconexion ↓ 6. Sincronizar cola con servidor ↓ 7. Resolver conflictos (si existen) ↓ 8. Confirmar sincronizacion ``` --- ## 8. CRITERIOS DE ACEPTACION ### 8.1 Funcionales - [ ] Abrir sesion con fondo inicial - [ ] Buscar productos por nombre/SKU - [ ] Escanear codigo de barras - [ ] Agregar/quitar productos del carrito - [ ] Calcular subtotal, descuento, impuesto, total - [ ] Aplicar descuento manual (con limite) - [ ] Procesar pago efectivo con cambio - [ ] Procesar pago tarjeta - [ ] Procesar pago mixto - [ ] Imprimir ticket automaticamente - [ ] Cerrar sesion con arqueo ### 8.2 Modo Offline - [ ] Funcionar sin conexion 24+ horas - [ ] Cache de productos disponible - [ ] Almacenar ventas localmente - [ ] Sincronizar al reconectar - [ ] No perder transacciones ### 8.3 Performance - [ ] Busqueda < 50ms - [ ] Calculo totales < 10ms - [ ] Registro venta < 100ms - [ ] Venta completa < 30s --- ## 9. RIESGOS | Riesgo | Probabilidad | Impacto | Mitigacion | |--------|--------------|---------|------------| | PWA complejo | Alta | Alto | Spike tecnico primero | | Hardware variado | Media | Medio | Drivers genericos | | Sync conflicts | Media | Alto | Estrategia last-write-wins | | Performance | Media | Critico | Indices + Cache | --- ## 10. ESTIMACION DETALLADA | Componente | SP Backend | SP Frontend | Total | |------------|-----------|-------------|-------| | Entities + Migrations | 3 | - | 3 | | POSSessionService | 5 | - | 5 | | POSOrderService | 8 | - | 8 | | POSPaymentService | 5 | - | 5 | | POSController | 3 | - | 3 | | PWA Setup | - | 5 | 5 | | POSPage + Cart | - | 8 | 8 | | PaymentModal | - | 3 | 3 | | Offline Sync | 5 | 5 | 10 | | Hardware Integration | 5 | - | 5 | | **TOTAL** | **34** | **21** | **55** | --- ## 11. REFERENCIAS | Documento | Ubicacion | |-----------|-----------| | Epica RT-002 | docs/08-epicas/EPIC-RT-002-pos.md | | Modulo definicion | docs/02-definicion-modulos/RT-002-pos/ | | DDL Retail | database/init/03-retail-tables.sql | | Directiva POS | orchestration/directivas/DIRECTIVA-PUNTO-VENTA.md | --- **Estado:** ANALISIS COMPLETO **Bloqueado por:** RT-001 Fundamentos, RT-006 Precios