erp-retail/orchestration/planes/fase-2-analisis-modulos/ANALISIS-RT-002-pos.md

501 lines
12 KiB
Markdown

# 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<POSSession>;
@Post('sessions/:id/close')
closeSession(@Param('id') id: string, @Body() dto: CloseSessionDto): Promise<POSSession>;
@Get('sessions/active')
getActiveSession(): Promise<POSSession>;
// Orders
@Post('orders')
createOrder(@Body() dto: CreateOrderDto): Promise<POSOrder>;
@Post('orders/:id/lines')
addLine(@Param('id') id: string, @Body() dto: AddLineDto): Promise<POSOrderLine>;
@Delete('orders/:id/lines/:lineId')
removeLine(@Param('id') id: string, @Param('lineId') lineId: string): Promise<void>;
@Post('orders/:id/discount')
applyDiscount(@Param('id') id: string, @Body() dto: DiscountDto): Promise<POSOrder>;
@Post('orders/:id/pay')
pay(@Param('id') id: string, @Body() dto: PaymentDto): Promise<POSOrder>;
// Products
@Get('products/search')
searchProducts(@Query('q') query: string): Promise<Product[]>;
@Get('products/barcode/:code')
getByBarcode(@Param('code') code: string): Promise<Product>;
// Sync
@Post('sync')
syncOfflineOrders(@Body() dto: SyncDto): Promise<SyncResult>;
}
```
### 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<boolean>;
print(ticket: TicketData): Promise<void>;
openDrawer(): Promise<void>;
cut(): Promise<void>;
}
// 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