438 lines
11 KiB
Markdown
438 lines
11 KiB
Markdown
# ANALISIS MODULO RT-003: INVENTARIO MULTI-SUCURSAL
|
|
|
|
**Fecha:** 2025-12-18
|
|
**Fase:** 2 - Analisis por Modulo
|
|
**Modulo:** RT-003 Inventario
|
|
**Herencia:** 60%
|
|
**Story Points:** 42
|
|
**Prioridad:** P0
|
|
|
|
---
|
|
|
|
## 1. DESCRIPCION GENERAL
|
|
|
|
### 1.1 Proposito
|
|
Gestion de inventario distribuido entre multiples sucursales con transferencias, conteos ciclicos y alertas de reorden.
|
|
|
|
### 1.2 Funcionalidades Principales
|
|
|
|
| Funcionalidad | Descripcion | Criticidad |
|
|
|---------------|-------------|------------|
|
|
| Stock por sucursal | Inventario independiente | Critica |
|
|
| Transferencias | Entre sucursales | Alta |
|
|
| Alertas reorden | Stock minimo | Alta |
|
|
| Conteos ciclicos | ABC | Media |
|
|
| Kardex | Historial movimientos | Media |
|
|
| Reservas | Stock apartado | Media |
|
|
|
|
---
|
|
|
|
## 2. HERENCIA DEL CORE
|
|
|
|
### 2.1 Componentes Heredados (60%)
|
|
|
|
| Componente Core | % Uso | Accion |
|
|
|-----------------|-------|--------|
|
|
| inventory.products | 100% | HEREDAR |
|
|
| inventory.product_variants | 100% | HEREDAR |
|
|
| inventory.warehouses | 100% | HEREDAR |
|
|
| inventory.locations | 100% | HEREDAR |
|
|
| inventory.lots | 100% | HEREDAR |
|
|
| inventory.stock_moves | 80% | EXTENDER |
|
|
| inventory.pickings | 60% | EXTENDER |
|
|
|
|
### 2.2 Servicios a Heredar
|
|
|
|
```typescript
|
|
import { ProductsService } from '@erp-core/inventory';
|
|
import { WarehousesService } from '@erp-core/inventory';
|
|
import { LocationsService } from '@erp-core/inventory';
|
|
import { LotsService } from '@erp-core/inventory';
|
|
```
|
|
|
|
### 2.3 Servicios a Extender
|
|
|
|
```typescript
|
|
// Extender para multi-sucursal
|
|
class RetailInventoryService extends InventoryService {
|
|
// Stock por sucursal
|
|
async getStockByBranch(branchId: string, productId: string): Promise<BranchStock>;
|
|
|
|
// Transferencias
|
|
async createTransfer(dto: CreateTransferDto): Promise<StockTransfer>;
|
|
async confirmTransfer(id: string): Promise<StockTransfer>;
|
|
async receiveTransfer(id: string, dto: ReceiveDto): Promise<StockTransfer>;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 3. COMPONENTES NUEVOS
|
|
|
|
### 3.1 Entidades (TypeORM)
|
|
|
|
```typescript
|
|
// 1. BranchStock - Stock por sucursal
|
|
@Entity('branch_stock', { schema: 'retail' })
|
|
export class BranchStock {
|
|
@PrimaryGeneratedColumn('uuid')
|
|
id: string;
|
|
|
|
@Column('uuid')
|
|
tenantId: string;
|
|
|
|
@ManyToOne(() => Branch)
|
|
branch: Branch;
|
|
|
|
@ManyToOne(() => Product)
|
|
product: Product;
|
|
|
|
@Column({ type: 'decimal', precision: 12, scale: 4 })
|
|
quantityOnHand: number;
|
|
|
|
@Column({ type: 'decimal', precision: 12, scale: 4, default: 0 })
|
|
quantityReserved: number;
|
|
|
|
@Column({ type: 'decimal', precision: 12, scale: 4 })
|
|
@Generated('quantityOnHand - quantityReserved')
|
|
quantityAvailable: number;
|
|
|
|
@Column({ type: 'decimal', precision: 12, scale: 4, nullable: true })
|
|
reorderPoint: number;
|
|
|
|
@Column({ type: 'decimal', precision: 12, scale: 4, nullable: true })
|
|
maxStock: number;
|
|
|
|
@Column({ type: 'date', nullable: true })
|
|
lastCountDate: Date;
|
|
}
|
|
|
|
// 2. StockTransfer - Transferencia
|
|
@Entity('stock_transfers', { schema: 'retail' })
|
|
export class StockTransfer {
|
|
@PrimaryGeneratedColumn('uuid')
|
|
id: string;
|
|
|
|
@Column('uuid')
|
|
tenantId: string;
|
|
|
|
@Column()
|
|
transferNumber: string;
|
|
|
|
@ManyToOne(() => Branch)
|
|
sourceBranch: Branch;
|
|
|
|
@ManyToOne(() => Branch)
|
|
destinationBranch: Branch;
|
|
|
|
@Column({ type: 'enum', enum: TransferStatus })
|
|
status: TransferStatus;
|
|
|
|
@Column({ type: 'timestamptz' })
|
|
requestDate: Date;
|
|
|
|
@Column({ type: 'timestamptz', nullable: true })
|
|
shipDate: Date;
|
|
|
|
@Column({ type: 'timestamptz', nullable: true })
|
|
receiveDate: Date;
|
|
|
|
@ManyToOne(() => User)
|
|
requestedBy: User;
|
|
|
|
@ManyToOne(() => User, { nullable: true })
|
|
shippedBy: User;
|
|
|
|
@ManyToOne(() => User, { nullable: true })
|
|
receivedBy: User;
|
|
|
|
@OneToMany(() => StockTransferLine, line => line.transfer)
|
|
lines: StockTransferLine[];
|
|
}
|
|
|
|
// 3. StockTransferLine
|
|
@Entity('stock_transfer_lines', { schema: 'retail' })
|
|
export class StockTransferLine {
|
|
@PrimaryGeneratedColumn('uuid')
|
|
id: string;
|
|
|
|
@ManyToOne(() => StockTransfer)
|
|
transfer: StockTransfer;
|
|
|
|
@ManyToOne(() => Product)
|
|
product: Product;
|
|
|
|
@Column({ type: 'decimal', precision: 12, scale: 4 })
|
|
quantityRequested: number;
|
|
|
|
@Column({ type: 'decimal', precision: 12, scale: 4, nullable: true })
|
|
quantityShipped: number;
|
|
|
|
@Column({ type: 'decimal', precision: 12, scale: 4, nullable: true })
|
|
quantityReceived: number;
|
|
}
|
|
```
|
|
|
|
### 3.2 Servicios Backend
|
|
|
|
| Servicio | Metodos Principales |
|
|
|----------|-------------------|
|
|
| BranchStockService | getStock(), updateStock(), reserveStock(), releaseStock() |
|
|
| TransferService | create(), confirm(), ship(), receive(), cancel() |
|
|
| ReorderService | checkReorderPoints(), generateSuggestions() |
|
|
| CycleCountService | createCount(), recordCount(), approveAdjustment() |
|
|
| KardexService | getMovements(), getProductHistory() |
|
|
|
|
### 3.3 Controladores
|
|
|
|
```typescript
|
|
@Controller('inventory')
|
|
export class RetailInventoryController {
|
|
// Stock por sucursal
|
|
@Get('branches/:branchId/stock')
|
|
getBranchStock(@Param('branchId') branchId: string): Promise<BranchStock[]>;
|
|
|
|
@Get('branches/:branchId/stock/:productId')
|
|
getProductStock(@Param('branchId') branchId: string,
|
|
@Param('productId') productId: string): Promise<BranchStock>;
|
|
|
|
@Get('stock/all/:productId')
|
|
getStockAllBranches(@Param('productId') productId: string): Promise<BranchStock[]>;
|
|
|
|
// Transferencias
|
|
@Post('transfers')
|
|
createTransfer(@Body() dto: CreateTransferDto): Promise<StockTransfer>;
|
|
|
|
@Post('transfers/:id/confirm')
|
|
confirmTransfer(@Param('id') id: string): Promise<StockTransfer>;
|
|
|
|
@Post('transfers/:id/ship')
|
|
shipTransfer(@Param('id') id: string, @Body() dto: ShipDto): Promise<StockTransfer>;
|
|
|
|
@Post('transfers/:id/receive')
|
|
receiveTransfer(@Param('id') id: string, @Body() dto: ReceiveDto): Promise<StockTransfer>;
|
|
|
|
// Alertas
|
|
@Get('alerts/reorder')
|
|
getReorderAlerts(): Promise<ReorderAlert[]>;
|
|
|
|
// Kardex
|
|
@Get('kardex/:productId')
|
|
getKardex(@Param('productId') productId: string,
|
|
@Query() filters: KardexFilters): Promise<KardexEntry[]>;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4. FLUJOS DE NEGOCIO
|
|
|
|
### 4.1 Flujo de Transferencia
|
|
|
|
```
|
|
1. Sucursal destino solicita transferencia
|
|
↓
|
|
2. Estado: DRAFT → PENDING (pendiente aprobacion)
|
|
↓
|
|
3. Sucursal origen aprueba
|
|
↓
|
|
4. Estado: PENDING → APPROVED
|
|
↓
|
|
5. Sucursal origen prepara y envia
|
|
↓
|
|
6. Estado: APPROVED → IN_TRANSIT
|
|
↓
|
|
7. Stock origen: - cantidad_enviada
|
|
↓
|
|
8. Sucursal destino recibe
|
|
↓
|
|
9. Validar cantidades (diferencias)
|
|
↓
|
|
10. Estado: IN_TRANSIT → RECEIVED
|
|
↓
|
|
11. Stock destino: + cantidad_recibida
|
|
```
|
|
|
|
### 4.2 Flujo de Conteo Ciclico
|
|
|
|
```
|
|
1. Generar conteo (clasificacion ABC)
|
|
↓
|
|
2. Asignar a contador
|
|
↓
|
|
3. Conteo ciego (sin ver stock sistema)
|
|
↓
|
|
4. Registrar cantidades
|
|
↓
|
|
5. Comparar vs sistema
|
|
↓
|
|
6. Si diferencia:
|
|
a. Registrar motivo
|
|
b. Aprobar ajuste (supervisor)
|
|
c. Aplicar ajuste
|
|
↓
|
|
7. Actualizar fecha ultimo conteo
|
|
```
|
|
|
|
---
|
|
|
|
## 5. TABLAS DDL
|
|
|
|
### 5.1 Tablas Nuevas
|
|
|
|
```sql
|
|
-- Definidas en 03-retail-tables.sql
|
|
CREATE TABLE retail.branch_stock (...);
|
|
CREATE TABLE retail.stock_transfers (...);
|
|
CREATE TABLE retail.stock_transfer_lines (...);
|
|
```
|
|
|
|
### 5.2 Indices
|
|
|
|
```sql
|
|
-- Stock por sucursal
|
|
CREATE UNIQUE INDEX idx_branch_stock_branch_product
|
|
ON retail.branch_stock(branch_id, product_id);
|
|
|
|
-- Transferencias
|
|
CREATE INDEX idx_transfers_source ON retail.stock_transfers(source_branch_id);
|
|
CREATE INDEX idx_transfers_dest ON retail.stock_transfers(destination_branch_id);
|
|
CREATE INDEX idx_transfers_status ON retail.stock_transfers(status);
|
|
```
|
|
|
|
### 5.3 Triggers
|
|
|
|
```sql
|
|
-- Actualizar stock al confirmar venta
|
|
CREATE OR REPLACE FUNCTION retail.update_stock_on_sale()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
UPDATE retail.branch_stock
|
|
SET quantity_on_hand = quantity_on_hand - NEW.quantity
|
|
WHERE branch_id = (SELECT branch_id FROM retail.pos_sessions WHERE id =
|
|
(SELECT session_id FROM retail.pos_orders WHERE id = NEW.order_id))
|
|
AND product_id = NEW.product_id;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
---
|
|
|
|
## 6. DEPENDENCIAS
|
|
|
|
### 6.1 Dependencias de Core
|
|
|
|
| Modulo | Estado | Requerido Para |
|
|
|--------|--------|---------------|
|
|
| MGN-011 Inventory | 60% | Base de productos y stock |
|
|
| MGN-005 Catalogs | 0% | Productos base |
|
|
|
|
### 6.2 Dependencias de Retail
|
|
|
|
| Modulo | Tipo |
|
|
|--------|------|
|
|
| RT-001 Fundamentos | Prerequisito |
|
|
| RT-004 Compras | Recepciones |
|
|
|
|
### 6.3 Bloquea a
|
|
|
|
| Modulo | Razon |
|
|
|--------|-------|
|
|
| RT-002 POS | Stock disponible |
|
|
| RT-009 E-commerce | Stock online |
|
|
|
|
---
|
|
|
|
## 7. ESPECIFICACIONES APLICABLES
|
|
|
|
### 7.1 Del Core
|
|
|
|
| SPEC | Aplicacion |
|
|
|------|------------|
|
|
| SPEC-INVENTARIOS-CICLICOS | Conteos |
|
|
| SPEC-TRAZABILIDAD-LOTES-SERIES | Productos con lote |
|
|
| SPEC-VALORACION-INVENTARIO | Costeo |
|
|
|
|
### 7.2 Configuracion
|
|
|
|
```yaml
|
|
inventory_config:
|
|
reorder:
|
|
check_interval: "1h"
|
|
notification: ["email", "push"]
|
|
|
|
cycle_count:
|
|
classification:
|
|
A: { frequency: "weekly", threshold: 0.8 } # 80% del valor
|
|
B: { frequency: "monthly", threshold: 0.15 }
|
|
C: { frequency: "quarterly", threshold: 0.05 }
|
|
|
|
transfers:
|
|
require_approval: true
|
|
auto_reserve_on_request: true
|
|
```
|
|
|
|
---
|
|
|
|
## 8. CRITERIOS DE ACEPTACION
|
|
|
|
### 8.1 Funcionales
|
|
|
|
- [ ] Consultar stock por sucursal
|
|
- [ ] Ver stock en todas las sucursales
|
|
- [ ] Crear solicitud de transferencia
|
|
- [ ] Aprobar transferencia
|
|
- [ ] Enviar transferencia (actualiza stock origen)
|
|
- [ ] Recibir transferencia (actualiza stock destino)
|
|
- [ ] Registrar diferencias en recepcion
|
|
- [ ] Alertas de stock bajo automaticas
|
|
- [ ] Conteo ciclico con clasificacion ABC
|
|
- [ ] Ver kardex de movimientos
|
|
|
|
### 8.2 Performance
|
|
|
|
- [ ] Consulta stock < 500ms
|
|
- [ ] Listado sucursales < 1s
|
|
|
|
---
|
|
|
|
## 9. RIESGOS
|
|
|
|
| Riesgo | Probabilidad | Impacto | Mitigacion |
|
|
|--------|--------------|---------|------------|
|
|
| Inconsistencia stock | Media | Critico | Transacciones atomicas |
|
|
| Performance en listados | Media | Medio | Indices + paginacion |
|
|
|
|
---
|
|
|
|
## 10. ESTIMACION DETALLADA
|
|
|
|
| Componente | SP Backend | SP Frontend | Total |
|
|
|------------|-----------|-------------|-------|
|
|
| Entities + Migrations | 3 | - | 3 |
|
|
| BranchStockService | 5 | - | 5 |
|
|
| TransferService | 8 | - | 8 |
|
|
| ReorderService | 3 | - | 3 |
|
|
| CycleCountService | 5 | - | 5 |
|
|
| Controllers | 3 | - | 3 |
|
|
| Stock Pages | - | 8 | 8 |
|
|
| Transfer Pages | - | 5 | 5 |
|
|
| Alerts UI | - | 2 | 2 |
|
|
| **TOTAL** | **27** | **15** | **42** |
|
|
|
|
---
|
|
|
|
## 11. REFERENCIAS
|
|
|
|
| Documento | Ubicacion |
|
|
|-----------|-----------|
|
|
| Epica RT-003 | docs/08-epicas/EPIC-RT-003-inventario.md |
|
|
| Modulo Core Inventory | erp-core/backend/src/modules/inventory/ |
|
|
| SPEC Conteos | erp-core/docs/04-modelado/especificaciones-tecnicas/ |
|
|
|
|
---
|
|
|
|
**Estado:** ANALISIS COMPLETO
|
|
**Bloqueado por:** RT-001 Fundamentos, MGN-011 Inventory (core)
|