Some checks failed
ERP Core CI / Backend Lint (push) Has been cancelled
ERP Core CI / Backend Unit Tests (push) Has been cancelled
ERP Core CI / Backend Integration Tests (push) Has been cancelled
ERP Core CI / Frontend Lint (push) Has been cancelled
ERP Core CI / Frontend Unit Tests (push) Has been cancelled
ERP Core CI / Frontend E2E Tests (push) Has been cancelled
ERP Core CI / Database DDL Validation (push) Has been cancelled
ERP Core CI / Backend Build (push) Has been cancelled
ERP Core CI / Frontend Build (push) Has been cancelled
ERP Core CI / CI Success (push) Has been cancelled
Performance Tests / Lighthouse CI (push) Has been cancelled
Performance Tests / Bundle Size Analysis (push) Has been cancelled
Performance Tests / k6 Load Tests (push) Has been cancelled
Performance Tests / Performance Summary (push) Has been cancelled
- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8 - Actualizaciones en modulos CRM y OpenAPI Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
829 lines
30 KiB
Markdown
829 lines
30 KiB
Markdown
# PLAN DE EJECUCION COMPLETO - CIERRE DE GAPS
|
|
|
|
**ID:** PLAN-EXEC-COMPLETO-2026-01-10
|
|
**Fecha:** 2026-01-10
|
|
**Version:** 1.0
|
|
**Sistema:** SIMCO v3.5 + CAPVED
|
|
**Orquestador:** Claude Code - Opus 4.5
|
|
**Fase:** P (Planeacion) - FASE 3
|
|
|
|
---
|
|
|
|
## 1. CONTEXTO
|
|
|
|
### 1.1 Vinculacion
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| Proyecto | erp-core |
|
|
| Documento Base | ANALISIS-COMPLETO-PROYECTO-2026-01-10.md |
|
|
| Epic | EPIC-CIERRE-GAPS-2026-01 |
|
|
| Total GAPS | 33 |
|
|
| Story Points | 220 SP |
|
|
|
|
### 1.2 Principios SIMCO Aplicables
|
|
|
|
- @OP_BACKEND - Operaciones de backend
|
|
- @OP_FRONTEND - Operaciones de frontend
|
|
- @OP_DATABASE - Operaciones de base de datos
|
|
- @VALIDAR - Validacion obligatoria
|
|
- @DOC-PRIMERO - Documentacion primero
|
|
- @TAREA - Gestion de tareas
|
|
|
|
---
|
|
|
|
## 2. ESTRUCTURA DEL PLAN
|
|
|
|
### 2.1 Fases de Ejecucion
|
|
|
|
```
|
|
SPRINT 1 (GAPS CRITICOS) -> 45 SP -> Prioridad: CRITICA
|
|
SPRINT 2 (GAPS ALTOS) -> 47 SP -> Prioridad: ALTA
|
|
SPRINT 3 (GAPS MEDIOS - P1) -> 45 SP -> Prioridad: MEDIA
|
|
SPRINT 4 (GAPS MEDIOS - P2) -> 40 SP -> Prioridad: MEDIA
|
|
SPRINT 5 (GAPS BAJOS + E2E) -> 43 SP -> Prioridad: BAJA
|
|
```
|
|
|
|
### 2.2 Orden de Ejecucion por Sprint
|
|
|
|
```
|
|
Siempre: DATABASE -> BACKEND -> FRONTEND -> TESTS -> VALIDACION
|
|
```
|
|
|
|
---
|
|
|
|
## 3. SPRINT 1: GAPS CRITICOS (45 SP)
|
|
|
|
### 3.1 Objetivos
|
|
|
|
- Desbloquear funcionalidad core del ERP
|
|
- Seeds de datos maestros criticos
|
|
- Corregir calculo de impuestos
|
|
- Habilitar API services criticos en frontend
|
|
|
|
### 3.2 Tareas en Orden de Ejecucion
|
|
|
|
#### 3.2.1 DATABASE - Seeds Criticos (24 SP)
|
|
|
|
| ID | Tarea | Archivo | Dependencias | SP |
|
|
|----|-------|---------|--------------|-----|
|
|
| S1-DB-01 | Crear seed de secuencias | `seeds/05-sequences.sql` | 04-users.sql | 5 |
|
|
| S1-DB-02 | Crear seed de categorias producto | `seeds/06-product-categories.sql` | 05-sequences.sql | 3 |
|
|
| S1-DB-03 | Crear seed financiero (COA, journals, taxes) | `seeds/07-financial-setup.sql` | 05-sequences.sql | 8 |
|
|
| S1-DB-04 | Crear seed de inventario (warehouses, locations) | `seeds/08-inventory-setup.sql` | 06-product-categories.sql | 3 |
|
|
| S1-DB-05 | Crear seed de productos | `seeds/09-products.sql` | 08-inventory-setup.sql | 5 |
|
|
|
|
**Detalle S1-DB-01: Secuencias**
|
|
|
|
```sql
|
|
-- Archivo: seeds/05-sequences.sql
|
|
-- Contenido requerido:
|
|
INSERT INTO core.sequences (id, tenant_id, company_id, code, name, prefix, suffix, padding, next_number, step) VALUES
|
|
-- Ventas
|
|
('seq-so', 'tenant-1', 'company-1', 'SO', 'Sales Order', 'SO', '', 6, 1, 1),
|
|
('seq-qt', 'tenant-1', 'company-1', 'QT', 'Quotation', 'QT', '', 6, 1, 1),
|
|
-- Compras
|
|
('seq-po', 'tenant-1', 'company-1', 'PO', 'Purchase Order', 'PO', '', 6, 1, 1),
|
|
('seq-rfq', 'tenant-1', 'company-1', 'RFQ', 'Request for Quotation', 'RFQ', '', 6, 1, 1),
|
|
-- Financiero
|
|
('seq-inv', 'tenant-1', 'company-1', 'INV', 'Invoice', 'INV', '', 6, 1, 1),
|
|
('seq-bill', 'tenant-1', 'company-1', 'BILL', 'Vendor Bill', 'BILL', '', 6, 1, 1),
|
|
('seq-pay', 'tenant-1', 'company-1', 'PAY', 'Payment', 'PAY', '', 6, 1, 1),
|
|
('seq-je', 'tenant-1', 'company-1', 'JE', 'Journal Entry', 'JE', '', 6, 1, 1),
|
|
-- Inventario
|
|
('seq-pick', 'tenant-1', 'company-1', 'PICK', 'Picking', 'PICK', '', 6, 1, 1),
|
|
('seq-adj', 'tenant-1', 'company-1', 'ADJ', 'Stock Adjustment', 'ADJ', '', 6, 1, 1),
|
|
('seq-lot', 'tenant-1', 'company-1', 'LOT', 'Lot/Batch', 'LOT', '', 6, 1, 1),
|
|
-- Productos
|
|
('seq-prod', 'tenant-1', 'company-1', 'PROD', 'Product', 'PROD', '', 6, 1, 1),
|
|
-- Partners
|
|
('seq-cust', 'tenant-1', 'company-1', 'CUST', 'Customer', 'CUST', '', 6, 1, 1),
|
|
('seq-vend', 'tenant-1', 'company-1', 'VEND', 'Vendor', 'VEND', '', 6, 1, 1),
|
|
-- HR
|
|
('seq-emp', 'tenant-1', 'company-1', 'EMP', 'Employee', 'EMP', '', 6, 1, 1);
|
|
```
|
|
|
|
**Detalle S1-DB-03: Financial Setup**
|
|
|
|
```sql
|
|
-- Archivo: seeds/07-financial-setup.sql
|
|
-- Contenido requerido:
|
|
|
|
-- 1. Account Types (10 registros)
|
|
INSERT INTO financial.account_types (id, code, name, type, internal_group, balance_type) VALUES
|
|
('at-asset', 'ASSET', 'Activos', 'asset', 'asset', 'debit'),
|
|
('at-liability', 'LIABILITY', 'Pasivos', 'liability', 'liability', 'credit'),
|
|
('at-equity', 'EQUITY', 'Capital', 'equity', 'equity', 'credit'),
|
|
('at-revenue', 'REVENUE', 'Ingresos', 'revenue', 'income', 'credit'),
|
|
('at-expense', 'EXPENSE', 'Gastos', 'expense', 'expense', 'debit'),
|
|
('at-cogs', 'COGS', 'Costo de Ventas', 'expense', 'expense', 'debit'),
|
|
('at-bank', 'BANK', 'Bancos', 'asset', 'bank', 'debit'),
|
|
('at-cash', 'CASH', 'Efectivo', 'asset', 'cash', 'debit'),
|
|
('at-receivable', 'AR', 'Cuentas por Cobrar', 'asset', 'receivable', 'debit'),
|
|
('at-payable', 'AP', 'Cuentas por Pagar', 'liability', 'payable', 'credit');
|
|
|
|
-- 2. Chart of Accounts (50+ registros)
|
|
-- Ver detalle en archivo completo
|
|
|
|
-- 3. Journals (5 registros)
|
|
INSERT INTO financial.journals (id, tenant_id, company_id, code, name, type, default_account_id) VALUES
|
|
('jnl-sales', 'tenant-1', 'company-1', 'SAL', 'Ventas', 'sale', 'acc-sales'),
|
|
('jnl-purchase', 'tenant-1', 'company-1', 'PUR', 'Compras', 'purchase', 'acc-purchases'),
|
|
('jnl-bank', 'tenant-1', 'company-1', 'BNK', 'Banco', 'bank', 'acc-bank'),
|
|
('jnl-cash', 'tenant-1', 'company-1', 'CSH', 'Caja', 'cash', 'acc-cash'),
|
|
('jnl-misc', 'tenant-1', 'company-1', 'MISC', 'Miscelaneos', 'general', null);
|
|
|
|
-- 4. Fiscal Year (2 registros)
|
|
INSERT INTO financial.fiscal_years (id, tenant_id, company_id, name, date_from, date_to, state) VALUES
|
|
('fy-2025', 'tenant-1', 'company-1', 'Ejercicio 2025', '2025-01-01', '2025-12-31', 'open'),
|
|
('fy-2026', 'tenant-1', 'company-1', 'Ejercicio 2026', '2026-01-01', '2026-12-31', 'open');
|
|
|
|
-- 5. Fiscal Periods (24 registros - 2 anios x 12 meses)
|
|
-- Ver detalle en archivo completo
|
|
|
|
-- 6. Taxes (5 registros)
|
|
INSERT INTO financial.taxes (id, tenant_id, company_id, name, type_tax_use, amount_type, amount, tax_group_id, active) VALUES
|
|
('tax-iva-16', 'tenant-1', 'company-1', 'IVA 16%', 'sale', 'percent', 16.00, 'tg-iva', true),
|
|
('tax-iva-16-purchase', 'tenant-1', 'company-1', 'IVA 16% Compras', 'purchase', 'percent', 16.00, 'tg-iva', true),
|
|
('tax-iva-0', 'tenant-1', 'company-1', 'IVA 0%', 'sale', 'percent', 0.00, 'tg-iva', true),
|
|
('tax-isr-ret', 'tenant-1', 'company-1', 'Retencion ISR 10%', 'purchase', 'percent', -10.00, 'tg-isr', true),
|
|
('tax-iva-ret', 'tenant-1', 'company-1', 'Retencion IVA 2/3', 'purchase', 'percent', -10.67, 'tg-iva', true);
|
|
|
|
-- 7. Payment Terms (5 registros)
|
|
INSERT INTO financial.payment_terms (id, tenant_id, company_id, name, note, line_ids) VALUES
|
|
('pt-immediate', 'tenant-1', 'company-1', 'Pago Inmediato', 'Pago al contado', null),
|
|
('pt-15d', 'tenant-1', 'company-1', '15 Dias', 'Pago a 15 dias', null),
|
|
('pt-30d', 'tenant-1', 'company-1', '30 Dias', 'Pago a 30 dias', null),
|
|
('pt-60d', 'tenant-1', 'company-1', '60 Dias', 'Pago a 60 dias', null),
|
|
('pt-90d', 'tenant-1', 'company-1', '90 Dias', 'Pago a 90 dias', null);
|
|
|
|
-- 8. Payment Methods (5 registros)
|
|
INSERT INTO financial.payment_methods (id, tenant_id, company_id, name, code, payment_type) VALUES
|
|
('pm-cash', 'tenant-1', 'company-1', 'Efectivo', 'CASH', 'inbound'),
|
|
('pm-transfer', 'tenant-1', 'company-1', 'Transferencia', 'TRANSFER', 'inbound'),
|
|
('pm-check', 'tenant-1', 'company-1', 'Cheque', 'CHECK', 'inbound'),
|
|
('pm-card', 'tenant-1', 'company-1', 'Tarjeta', 'CARD', 'inbound'),
|
|
('pm-outbound', 'tenant-1', 'company-1', 'Pago a Proveedores', 'OUTBOUND', 'outbound');
|
|
```
|
|
|
|
#### 3.2.2 BACKEND - Correccion de TODOs Criticos (8 SP)
|
|
|
|
| ID | Tarea | Archivo | Linea | Descripcion | SP |
|
|
|----|-------|---------|-------|-------------|-----|
|
|
| S1-BE-01 | Implementar calculo de impuestos | `sales/quotations.service.ts` | 416 | Calcular taxes con TaxesService | 4 |
|
|
| S1-BE-02 | Implementar calculo de impuestos | `sales/orders.service.ts` | 466 | Calcular taxes con TaxesService | 4 |
|
|
|
|
**Detalle S1-BE-01: Implementacion de Calculo de Impuestos**
|
|
|
|
```typescript
|
|
// Archivo: backend/src/modules/sales/quotations.service.ts
|
|
// Linea 416 - Reemplazar:
|
|
// const amountTax = 0; // TODO: Calculate taxes
|
|
|
|
// Por:
|
|
private async calculateLineTaxes(line: QuotationLine, tenantId: string): Promise<number> {
|
|
if (!line.taxIds || line.taxIds.length === 0) {
|
|
return 0;
|
|
}
|
|
|
|
const taxes = await this.taxesService.findByIds(line.taxIds, tenantId);
|
|
let taxAmount = 0;
|
|
|
|
for (const tax of taxes) {
|
|
if (tax.amountType === 'percent') {
|
|
taxAmount += (line.priceSubtotal * tax.amount) / 100;
|
|
} else if (tax.amountType === 'fixed') {
|
|
taxAmount += tax.amount * line.productUomQty;
|
|
}
|
|
}
|
|
|
|
return taxAmount;
|
|
}
|
|
|
|
// Y actualizar recalculateTotals:
|
|
async recalculateTotals(quotationId: string, tenantId: string): Promise<void> {
|
|
const quotation = await this.findById(quotationId, tenantId);
|
|
|
|
let amountUntaxed = 0;
|
|
let amountTax = 0;
|
|
|
|
for (const line of quotation.lines) {
|
|
amountUntaxed += line.priceSubtotal;
|
|
amountTax += await this.calculateLineTaxes(line, tenantId);
|
|
}
|
|
|
|
const amountTotal = amountUntaxed + amountTax;
|
|
|
|
await this.quotationRepository.update(quotationId, {
|
|
amountUntaxed,
|
|
amountTax,
|
|
amountTotal,
|
|
updatedAt: new Date(),
|
|
});
|
|
}
|
|
```
|
|
|
|
#### 3.2.3 FRONTEND - API Services Criticos (13 SP)
|
|
|
|
| ID | Tarea | Archivo | Endpoints | SP |
|
|
|----|-------|---------|-----------|-----|
|
|
| S1-FE-01 | Crear Products API service | `features/products/api/products.api.ts` | 8 | 3 |
|
|
| S1-FE-02 | Crear Inventory API service | `features/inventory/api/inventory.api.ts` | 25 | 5 |
|
|
| S1-FE-03 | Crear Sales API service | `features/sales/api/sales.api.ts` | 20 | 5 |
|
|
|
|
**Detalle S1-FE-01: Products API Service**
|
|
|
|
```typescript
|
|
// Archivo: frontend/src/features/products/api/products.api.ts
|
|
import { axiosInstance } from '@/services/api/axios-instance';
|
|
import { API_ENDPOINTS } from '@/shared/constants/api-endpoints';
|
|
import { Product, CreateProductDto, UpdateProductDto, PaginatedResponse } from '../types';
|
|
|
|
export const productsApi = {
|
|
getAll: async (params?: Record<string, any>): Promise<PaginatedResponse<Product>> => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.INVENTORY.PRODUCTS, { params });
|
|
return response.data;
|
|
},
|
|
|
|
getById: async (id: string): Promise<Product> => {
|
|
const response = await axiosInstance.get(`${API_ENDPOINTS.INVENTORY.PRODUCTS}/${id}`);
|
|
return response.data;
|
|
},
|
|
|
|
getStock: async (id: string): Promise<any> => {
|
|
const response = await axiosInstance.get(`${API_ENDPOINTS.INVENTORY.PRODUCTS}/${id}/stock`);
|
|
return response.data;
|
|
},
|
|
|
|
create: async (data: CreateProductDto): Promise<Product> => {
|
|
const response = await axiosInstance.post(API_ENDPOINTS.INVENTORY.PRODUCTS, data);
|
|
return response.data;
|
|
},
|
|
|
|
update: async (id: string, data: UpdateProductDto): Promise<Product> => {
|
|
const response = await axiosInstance.put(`${API_ENDPOINTS.INVENTORY.PRODUCTS}/${id}`, data);
|
|
return response.data;
|
|
},
|
|
|
|
delete: async (id: string): Promise<void> => {
|
|
await axiosInstance.delete(`${API_ENDPOINTS.INVENTORY.PRODUCTS}/${id}`);
|
|
},
|
|
|
|
getByCategory: async (categoryId: string): Promise<Product[]> => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.INVENTORY.PRODUCTS, {
|
|
params: { categoryId }
|
|
});
|
|
return response.data.data;
|
|
},
|
|
|
|
search: async (query: string): Promise<Product[]> => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.INVENTORY.PRODUCTS, {
|
|
params: { search: query }
|
|
});
|
|
return response.data.data;
|
|
},
|
|
};
|
|
```
|
|
|
|
**Detalle S1-FE-02: Inventory API Service**
|
|
|
|
```typescript
|
|
// Archivo: frontend/src/features/inventory/api/inventory.api.ts
|
|
import { axiosInstance } from '@/services/api/axios-instance';
|
|
import { API_ENDPOINTS } from '@/shared/constants/api-endpoints';
|
|
|
|
export const inventoryApi = {
|
|
// Warehouses
|
|
warehouses: {
|
|
getAll: async (params?: Record<string, any>) => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.INVENTORY.WAREHOUSES, { params });
|
|
return response.data;
|
|
},
|
|
getById: async (id: string) => {
|
|
const response = await axiosInstance.get(`${API_ENDPOINTS.INVENTORY.WAREHOUSES}/${id}`);
|
|
return response.data;
|
|
},
|
|
getStock: async (id: string) => {
|
|
const response = await axiosInstance.get(`${API_ENDPOINTS.INVENTORY.WAREHOUSES}/${id}/stock`);
|
|
return response.data;
|
|
},
|
|
create: async (data: any) => {
|
|
const response = await axiosInstance.post(API_ENDPOINTS.INVENTORY.WAREHOUSES, data);
|
|
return response.data;
|
|
},
|
|
update: async (id: string, data: any) => {
|
|
const response = await axiosInstance.put(`${API_ENDPOINTS.INVENTORY.WAREHOUSES}/${id}`, data);
|
|
return response.data;
|
|
},
|
|
delete: async (id: string) => {
|
|
await axiosInstance.delete(`${API_ENDPOINTS.INVENTORY.WAREHOUSES}/${id}`);
|
|
},
|
|
},
|
|
|
|
// Locations
|
|
locations: {
|
|
getAll: async (params?: Record<string, any>) => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.INVENTORY.LOCATIONS, { params });
|
|
return response.data;
|
|
},
|
|
getById: async (id: string) => {
|
|
const response = await axiosInstance.get(`${API_ENDPOINTS.INVENTORY.LOCATIONS}/${id}`);
|
|
return response.data;
|
|
},
|
|
getStock: async (id: string) => {
|
|
const response = await axiosInstance.get(`${API_ENDPOINTS.INVENTORY.LOCATIONS}/${id}/stock`);
|
|
return response.data;
|
|
},
|
|
create: async (data: any) => {
|
|
const response = await axiosInstance.post(API_ENDPOINTS.INVENTORY.LOCATIONS, data);
|
|
return response.data;
|
|
},
|
|
update: async (id: string, data: any) => {
|
|
const response = await axiosInstance.put(`${API_ENDPOINTS.INVENTORY.LOCATIONS}/${id}`, data);
|
|
return response.data;
|
|
},
|
|
},
|
|
|
|
// Stock Moves
|
|
stockMoves: {
|
|
getAll: async (params?: Record<string, any>) => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.INVENTORY.STOCK_MOVES, { params });
|
|
return response.data;
|
|
},
|
|
getById: async (id: string) => {
|
|
const response = await axiosInstance.get(`${API_ENDPOINTS.INVENTORY.STOCK_MOVES}/${id}`);
|
|
return response.data;
|
|
},
|
|
},
|
|
|
|
// Pickings
|
|
pickings: {
|
|
getAll: async (params?: Record<string, any>) => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.INVENTORY.PICKINGS, { params });
|
|
return response.data;
|
|
},
|
|
getById: async (id: string) => {
|
|
const response = await axiosInstance.get(`${API_ENDPOINTS.INVENTORY.PICKINGS}/${id}`);
|
|
return response.data;
|
|
},
|
|
create: async (data: any) => {
|
|
const response = await axiosInstance.post(API_ENDPOINTS.INVENTORY.PICKINGS, data);
|
|
return response.data;
|
|
},
|
|
confirm: async (id: string) => {
|
|
const response = await axiosInstance.post(`${API_ENDPOINTS.INVENTORY.PICKINGS}/${id}/confirm`);
|
|
return response.data;
|
|
},
|
|
validate: async (id: string) => {
|
|
const response = await axiosInstance.post(`${API_ENDPOINTS.INVENTORY.PICKINGS}/${id}/validate`);
|
|
return response.data;
|
|
},
|
|
cancel: async (id: string) => {
|
|
const response = await axiosInstance.post(`${API_ENDPOINTS.INVENTORY.PICKINGS}/${id}/cancel`);
|
|
return response.data;
|
|
},
|
|
},
|
|
|
|
// Lots
|
|
lots: {
|
|
getAll: async (params?: Record<string, any>) => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.INVENTORY.LOTS, { params });
|
|
return response.data;
|
|
},
|
|
getById: async (id: string) => {
|
|
const response = await axiosInstance.get(`${API_ENDPOINTS.INVENTORY.LOTS}/${id}`);
|
|
return response.data;
|
|
},
|
|
create: async (data: any) => {
|
|
const response = await axiosInstance.post(API_ENDPOINTS.INVENTORY.LOTS, data);
|
|
return response.data;
|
|
},
|
|
getMovements: async (id: string) => {
|
|
const response = await axiosInstance.get(`${API_ENDPOINTS.INVENTORY.LOTS}/${id}/movements`);
|
|
return response.data;
|
|
},
|
|
},
|
|
|
|
// Adjustments
|
|
adjustments: {
|
|
getAll: async (params?: Record<string, any>) => {
|
|
const response = await axiosInstance.get(API_ENDPOINTS.INVENTORY.ADJUSTMENTS, { params });
|
|
return response.data;
|
|
},
|
|
getById: async (id: string) => {
|
|
const response = await axiosInstance.get(`${API_ENDPOINTS.INVENTORY.ADJUSTMENTS}/${id}`);
|
|
return response.data;
|
|
},
|
|
create: async (data: any) => {
|
|
const response = await axiosInstance.post(API_ENDPOINTS.INVENTORY.ADJUSTMENTS, data);
|
|
return response.data;
|
|
},
|
|
confirm: async (id: string) => {
|
|
const response = await axiosInstance.post(`${API_ENDPOINTS.INVENTORY.ADJUSTMENTS}/${id}/confirm`);
|
|
return response.data;
|
|
},
|
|
validate: async (id: string) => {
|
|
const response = await axiosInstance.post(`${API_ENDPOINTS.INVENTORY.ADJUSTMENTS}/${id}/validate`);
|
|
return response.data;
|
|
},
|
|
cancel: async (id: string) => {
|
|
const response = await axiosInstance.post(`${API_ENDPOINTS.INVENTORY.ADJUSTMENTS}/${id}/cancel`);
|
|
return response.data;
|
|
},
|
|
},
|
|
};
|
|
```
|
|
|
|
### 3.3 Validacion Sprint 1
|
|
|
|
| Tipo | Comando | Criterio Exito |
|
|
|------|---------|----------------|
|
|
| Seeds | `./scripts/recreate-database.sh --force` | 0 errores |
|
|
| Build Backend | `npm run build` | 0 errores |
|
|
| Lint Backend | `npm run lint` | 0 errores |
|
|
| Tests Backend | `npm test` | PASS all |
|
|
| Build Frontend | `npm run build` | 0 errores |
|
|
|
|
---
|
|
|
|
## 4. SPRINT 2: GAPS ALTOS (47 SP)
|
|
|
|
### 4.1 Objetivos
|
|
|
|
- Implementar email service productivo
|
|
- Completar permission middleware
|
|
- Tests para Sales y Purchases
|
|
- Tests para Audit (compliance)
|
|
- Seeds de listas de precio
|
|
|
|
### 4.2 Tareas en Orden de Ejecucion
|
|
|
|
#### 4.2.1 DATABASE - Seeds Adicionales (6 SP)
|
|
|
|
| ID | Tarea | Archivo | SP |
|
|
|----|-------|---------|-----|
|
|
| S2-DB-01 | Crear seed de listas de precio | `seeds/10-pricelists.sql` | 3 |
|
|
| S2-DB-02 | Actualizar seed de partners con direcciones | `seeds/11-sample-partners.sql` | 3 |
|
|
|
|
#### 4.2.2 BACKEND - Correccion de TODOs Altos (15 SP)
|
|
|
|
| ID | Tarea | Archivo | Linea | SP |
|
|
|----|-------|---------|-------|-----|
|
|
| S2-BE-01 | Implementar email service productivo | `shared/services/email.service.ts` | 66 | 5 |
|
|
| S2-BE-02 | Implementar envio de email cotizacion | `sales/quotations.service.ts` | 478 | 3 |
|
|
| S2-BE-03 | Completar permission middleware | `shared/middleware/auth.middleware.ts` | 81 | 5 |
|
|
| S2-BE-04 | Integrar notifications con scheduler | `reports/scheduler.service.ts` | 360 | 2 |
|
|
|
|
**Detalle S2-BE-01: Email Service Productivo**
|
|
|
|
```typescript
|
|
// Archivo: backend/src/shared/services/email.service.ts
|
|
// Linea 66 - Implementar SendGrid/Nodemailer/AWS SES
|
|
|
|
import { Injectable } from '@nestjs/common';
|
|
import * as nodemailer from 'nodemailer';
|
|
import { ConfigService } from '@nestjs/config';
|
|
|
|
@Injectable()
|
|
export class EmailService {
|
|
private transporter: nodemailer.Transporter;
|
|
|
|
constructor(private configService: ConfigService) {
|
|
const emailProvider = this.configService.get<string>('EMAIL_PROVIDER');
|
|
|
|
if (emailProvider === 'smtp') {
|
|
this.transporter = nodemailer.createTransport({
|
|
host: this.configService.get<string>('SMTP_HOST'),
|
|
port: this.configService.get<number>('SMTP_PORT'),
|
|
secure: this.configService.get<boolean>('SMTP_SECURE'),
|
|
auth: {
|
|
user: this.configService.get<string>('SMTP_USER'),
|
|
pass: this.configService.get<string>('SMTP_PASSWORD'),
|
|
},
|
|
});
|
|
} else if (emailProvider === 'sendgrid') {
|
|
// SendGrid implementation
|
|
this.transporter = nodemailer.createTransport({
|
|
service: 'SendGrid',
|
|
auth: {
|
|
user: 'apikey',
|
|
pass: this.configService.get<string>('SENDGRID_API_KEY'),
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
async sendEmail(options: {
|
|
to: string;
|
|
subject: string;
|
|
html: string;
|
|
attachments?: any[];
|
|
}): Promise<void> {
|
|
const from = this.configService.get<string>('EMAIL_FROM');
|
|
|
|
if (this.configService.get<string>('NODE_ENV') === 'development') {
|
|
console.log('[EMAIL-DEV] Would send:', { to: options.to, subject: options.subject });
|
|
return;
|
|
}
|
|
|
|
await this.transporter.sendMail({
|
|
from,
|
|
to: options.to,
|
|
subject: options.subject,
|
|
html: options.html,
|
|
attachments: options.attachments,
|
|
});
|
|
}
|
|
|
|
async sendQuotationEmail(quotation: any, recipientEmail: string): Promise<void> {
|
|
const html = this.generateQuotationHtml(quotation);
|
|
await this.sendEmail({
|
|
to: recipientEmail,
|
|
subject: `Cotizacion ${quotation.name} - ${quotation.companyName}`,
|
|
html,
|
|
});
|
|
}
|
|
|
|
private generateQuotationHtml(quotation: any): string {
|
|
return `
|
|
<h1>Cotizacion ${quotation.name}</h1>
|
|
<p>Estimado ${quotation.partnerName},</p>
|
|
<p>Adjunto encontrara nuestra cotizacion por un monto de ${quotation.currencySymbol}${quotation.amountTotal.toFixed(2)}.</p>
|
|
<p>Quedo a sus ordenes.</p>
|
|
`;
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 4.2.3 BACKEND - Tests Criticos (26 SP)
|
|
|
|
| ID | Tarea | Archivo | Tests Estimados | SP |
|
|
|----|-------|---------|-----------------|-----|
|
|
| S2-BE-05 | Tests orders.service | `sales/__tests__/orders.service.spec.ts` | 35 | 5 |
|
|
| S2-BE-06 | Tests quotations.service | `sales/__tests__/quotations.service.spec.ts` | 35 | 5 |
|
|
| S2-BE-07 | Tests pricelists.service | `sales/__tests__/pricelists.service.spec.ts` | 20 | 3 |
|
|
| S2-BE-08 | Tests purchases.service | `purchases/__tests__/purchases.service.spec.ts` | 30 | 5 |
|
|
| S2-BE-09 | Tests audit.service | `audit/__tests__/audit.service.spec.ts` | 25 | 3 |
|
|
| S2-BE-10 | Tests access-logs.service | `audit/__tests__/access-logs.service.spec.ts` | 20 | 3 |
|
|
| S2-BE-11 | Tests security-events.service | `audit/__tests__/security-events.service.spec.ts` | 15 | 2 |
|
|
|
|
### 4.3 Validacion Sprint 2
|
|
|
|
| Tipo | Comando | Criterio Exito |
|
|
|------|---------|----------------|
|
|
| Seeds | `./scripts/recreate-database.sh --force` | 0 errores |
|
|
| Tests Backend | `npm test -- --coverage` | >50% coverage |
|
|
| Email Test | Manual verification | Email received |
|
|
|
|
---
|
|
|
|
## 5. SPRINT 3: GAPS MEDIOS - Parte 1 (45 SP)
|
|
|
|
### 5.1 Objetivos
|
|
|
|
- Tests para HR, Reports, Projects
|
|
- Frontend API services para Projects, CRM
|
|
|
|
### 5.2 Tareas
|
|
|
|
#### 5.2.1 BACKEND - Tests (35 SP)
|
|
|
|
| ID | Tarea | Archivo | Tests | SP |
|
|
|----|-------|---------|-------|-----|
|
|
| S3-BE-01 | Tests employees.service | `hr/__tests__/employees.service.spec.ts` | 30 | 5 |
|
|
| S3-BE-02 | Tests contracts.service | `hr/__tests__/contracts.service.spec.ts` | 25 | 3 |
|
|
| S3-BE-03 | Tests leaves.service | `hr/__tests__/leaves.service.spec.ts` | 25 | 3 |
|
|
| S3-BE-04 | Tests payslips.service | `hr/__tests__/payslips.service.spec.ts` | 35 | 5 |
|
|
| S3-BE-05 | Tests departments.service | `hr/__tests__/departments.service.spec.ts` | 15 | 2 |
|
|
| S3-BE-06 | Tests reports.service | `reports/__tests__/reports.service.spec.ts` | 20 | 3 |
|
|
| S3-BE-07 | Tests dashboards.service | `reports/__tests__/dashboards.service.spec.ts` | 20 | 3 |
|
|
| S3-BE-08 | Tests report-builder.service | `reports/__tests__/report-builder.service.spec.ts` | 20 | 3 |
|
|
| S3-BE-09 | Tests projects.service | `projects/__tests__/projects.service.spec.ts` | 25 | 3 |
|
|
| S3-BE-10 | Tests tasks.service | `projects/__tests__/tasks.service.spec.ts` | 25 | 3 |
|
|
| S3-BE-11 | Tests timesheets.service | `projects/__tests__/timesheets.service.spec.ts` | 20 | 2 |
|
|
|
|
#### 5.2.2 FRONTEND - API Services (10 SP)
|
|
|
|
| ID | Tarea | Archivo | SP |
|
|
|----|-------|---------|-----|
|
|
| S3-FE-01 | Crear Projects API service | `features/projects/api/projects.api.ts` | 5 |
|
|
| S3-FE-02 | Crear CRM API service | `features/crm/api/crm.api.ts` | 5 |
|
|
|
|
---
|
|
|
|
## 6. SPRINT 4: GAPS MEDIOS - Parte 2 (40 SP)
|
|
|
|
### 6.1 Objetivos
|
|
|
|
- Tests para Financial, Inventory, CRM
|
|
- Frontend API services para HR, Purchases
|
|
|
|
### 6.2 Tareas
|
|
|
|
#### 6.2.1 BACKEND - Tests (30 SP)
|
|
|
|
| ID | Tarea | Archivo | Tests | SP |
|
|
|----|-------|---------|-------|-----|
|
|
| S4-BE-01 | Tests taxes.service | `financial/__tests__/taxes.service.spec.ts` | 25 | 3 |
|
|
| S4-BE-02 | Tests journals.service | `financial/__tests__/journals.service.spec.ts` | 20 | 3 |
|
|
| S4-BE-03 | Tests fiscalPeriods.service | `financial/__tests__/fiscalPeriods.service.spec.ts` | 20 | 3 |
|
|
| S4-BE-04 | Tests reconcile-models.service | `financial/__tests__/reconcile-models.service.spec.ts` | 25 | 5 |
|
|
| S4-BE-05 | Tests warehouses.service | `inventory/__tests__/warehouses.service.spec.ts` | 20 | 3 |
|
|
| S4-BE-06 | Tests locations.service | `inventory/__tests__/locations.service.spec.ts` | 20 | 3 |
|
|
| S4-BE-07 | Tests pickings.service | `inventory/__tests__/pickings.service.spec.ts` | 30 | 5 |
|
|
| S4-BE-08 | Tests leads.service | `crm/__tests__/leads.service.spec.ts` | 25 | 3 |
|
|
| S4-BE-09 | Tests opportunities.service | `crm/__tests__/opportunities.service.spec.ts` | 25 | 2 |
|
|
|
|
#### 6.2.2 FRONTEND - API Services (10 SP)
|
|
|
|
| ID | Tarea | Archivo | SP |
|
|
|----|-------|---------|-----|
|
|
| S4-FE-01 | Crear HR API service | `features/hr/api/hr.api.ts` | 5 |
|
|
| S4-FE-02 | Crear Purchases API service | `features/purchases/api/purchases.api.ts` | 5 |
|
|
|
|
---
|
|
|
|
## 7. SPRINT 5: GAPS BAJOS + E2E (43 SP)
|
|
|
|
### 7.1 Objetivos
|
|
|
|
- Tests restantes (System, Shared, minor services)
|
|
- Tests E2E con Playwright
|
|
- Seeds opcionales (HR, CRM, Projects)
|
|
|
|
### 7.2 Tareas
|
|
|
|
#### 7.2.1 BACKEND - Tests Restantes (14 SP)
|
|
|
|
| ID | Tarea | Archivo | Tests | SP |
|
|
|----|-------|---------|-------|-----|
|
|
| S5-BE-01 | Tests token.service | `auth/__tests__/token.service.spec.ts` | 20 | 3 |
|
|
| S5-BE-02 | Tests base.service | `shared/__tests__/base.service.spec.ts` | 15 | 2 |
|
|
| S5-BE-03 | Tests email.service | `shared/__tests__/email.service.spec.ts` | 15 | 2 |
|
|
| S5-BE-04 | Tests cache.service | `shared/__tests__/cache.service.spec.ts` | 15 | 2 |
|
|
| S5-BE-05 | Tests notifications.service | `system/__tests__/notifications.service.spec.ts` | 15 | 2 |
|
|
| S5-BE-06 | Tests companies.service | `companies/__tests__/companies.service.spec.ts` | 15 | 3 |
|
|
|
|
#### 7.2.2 E2E TESTS (21 SP)
|
|
|
|
| ID | Tarea | Archivo | Flujos | SP |
|
|
|----|-------|---------|--------|-----|
|
|
| S5-E2E-01 | Setup Playwright | `e2e/playwright.config.ts` | - | 3 |
|
|
| S5-E2E-02 | Auth E2E tests | `e2e/tests/auth.spec.ts` | Login, Register, MFA | 5 |
|
|
| S5-E2E-03 | Sales E2E tests | `e2e/tests/sales.spec.ts` | Quote to Order | 5 |
|
|
| S5-E2E-04 | Inventory E2E tests | `e2e/tests/inventory.spec.ts` | Stock movement | 5 |
|
|
| S5-E2E-05 | Financial E2E tests | `e2e/tests/financial.spec.ts` | Invoice to Payment | 3 |
|
|
|
|
#### 7.2.3 DATABASE - Seeds Opcionales (8 SP)
|
|
|
|
| ID | Tarea | Archivo | SP |
|
|
|----|-------|---------|-----|
|
|
| S5-DB-01 | Seeds HR demo | `seeds/demo/hr-demo.sql` | 3 |
|
|
| S5-DB-02 | Seeds CRM demo | `seeds/demo/crm-demo.sql` | 2 |
|
|
| S5-DB-03 | Seeds Projects demo | `seeds/demo/projects-demo.sql` | 3 |
|
|
|
|
---
|
|
|
|
## 8. RESUMEN DEL PLAN
|
|
|
|
### 8.1 Por Sprint
|
|
|
|
| Sprint | Foco | Story Points | Archivos |
|
|
|--------|------|--------------|----------|
|
|
| Sprint 1 | GAPS Criticos | 45 SP | 11 archivos |
|
|
| Sprint 2 | GAPS Altos | 47 SP | 13 archivos |
|
|
| Sprint 3 | GAPS Medios P1 | 45 SP | 13 archivos |
|
|
| Sprint 4 | GAPS Medios P2 | 40 SP | 11 archivos |
|
|
| Sprint 5 | GAPS Bajos + E2E | 43 SP | 14 archivos |
|
|
| **TOTAL** | - | **220 SP** | **62 archivos** |
|
|
|
|
### 8.2 Por Tipo de Trabajo
|
|
|
|
| Tipo | Story Points | % Total |
|
|
|------|--------------|---------|
|
|
| Database Seeds | 38 SP | 17% |
|
|
| Backend TODOs | 23 SP | 10% |
|
|
| Backend Tests | 96 SP | 44% |
|
|
| Frontend APIs | 41 SP | 19% |
|
|
| E2E Tests | 21 SP | 10% |
|
|
|
|
### 8.3 Cobertura Final Esperada
|
|
|
|
| Metrica | Actual | Esperado Post-Plan |
|
|
|---------|--------|-------------------|
|
|
| Servicios con tests | 39.5% | 95%+ |
|
|
| Tablas con seeds | 14.1% | 80%+ |
|
|
| Frontend API coverage | 60% | 100% |
|
|
| E2E flows | 0 | 4 flujos criticos |
|
|
|
|
---
|
|
|
|
## 9. DEPENDENCIAS CRITICAS
|
|
|
|
### 9.1 Dependencias entre Tareas
|
|
|
|
```
|
|
S1-DB-01 (sequences)
|
|
└── S1-DB-02 (categories)
|
|
└── S1-DB-03 (financial)
|
|
└── S1-BE-01 (tax calculation)
|
|
└── S1-BE-02 (tax calculation)
|
|
└── S1-DB-04 (inventory)
|
|
└── S1-DB-05 (products)
|
|
└── S1-FE-01 (products api)
|
|
└── S1-FE-02 (inventory api)
|
|
|
|
S2-BE-01 (email service)
|
|
└── S2-BE-02 (quotation email)
|
|
|
|
S2-BE-03 (permission middleware)
|
|
└── Depends on: roles/permissions seeds (existentes)
|
|
```
|
|
|
|
### 9.2 Archivos con Multiples Dependencias
|
|
|
|
| Archivo | Dependencias | Impacto si Falla |
|
|
|---------|-------------|------------------|
|
|
| `seeds/05-sequences.sql` | Todos los documentos | CRITICO |
|
|
| `seeds/07-financial-setup.sql` | Invoices, Payments, Taxes | CRITICO |
|
|
| `sales/quotations.service.ts` | Taxes, Email, Partners | ALTO |
|
|
| `shared/services/email.service.ts` | Quotations, Reports, Notifications | ALTO |
|
|
|
|
---
|
|
|
|
## 10. CRITERIOS DE ACEPTACION POR SPRINT
|
|
|
|
### Sprint 1
|
|
|
|
- [ ] Seeds ejecutan sin errores en recreate-database.sh
|
|
- [ ] COA tiene 50+ cuentas correctamente jerarquizadas
|
|
- [ ] Impuestos calculan correctamente en quotations y orders
|
|
- [ ] Frontend Products API consume endpoint correctamente
|
|
- [ ] Frontend Inventory API consume endpoints correctamente
|
|
- [ ] Frontend Sales API consume endpoints correctamente
|
|
|
|
### Sprint 2
|
|
|
|
- [ ] Email service envia correos en produccion
|
|
- [ ] Permission middleware valida permisos desde DB
|
|
- [ ] Tests Sales tienen >80% coverage
|
|
- [ ] Tests Purchases tienen >80% coverage
|
|
- [ ] Tests Audit tienen >80% coverage
|
|
|
|
### Sprint 3
|
|
|
|
- [ ] Tests HR tienen >80% coverage
|
|
- [ ] Tests Reports tienen >80% coverage
|
|
- [ ] Tests Projects tienen >80% coverage
|
|
- [ ] Frontend Projects API funcional
|
|
- [ ] Frontend CRM API funcional
|
|
|
|
### Sprint 4
|
|
|
|
- [ ] Tests Financial adicionales >80% coverage
|
|
- [ ] Tests Inventory adicionales >80% coverage
|
|
- [ ] Tests CRM tienen >80% coverage
|
|
- [ ] Frontend HR API funcional
|
|
- [ ] Frontend Purchases API funcional
|
|
|
|
### Sprint 5
|
|
|
|
- [ ] Cobertura total backend >90%
|
|
- [ ] E2E Auth flow pasa
|
|
- [ ] E2E Sales flow pasa
|
|
- [ ] E2E Inventory flow pasa
|
|
- [ ] E2E Financial flow pasa
|
|
|
|
---
|
|
|
|
## 11. RIESGOS Y MITIGACION
|
|
|
|
| Riesgo | Probabilidad | Impacto | Mitigacion |
|
|
|--------|-------------|---------|------------|
|
|
| Tax calculation compleja | MEDIA | ALTO | Usar TaxesService existente, solo integrar |
|
|
| Email provider config | BAJA | MEDIO | Usar nodemailer con SMTP generico |
|
|
| Tests E2E inestables | ALTA | MEDIO | Usar fixtures, evitar datos dinamicos |
|
|
| Seeds conflicto IDs | MEDIA | ALTO | Usar UUIDs generados, no hardcoded |
|
|
| Dependencias circulares | BAJA | ALTO | Revisar imports antes de implementar |
|
|
|
|
---
|
|
|
|
## 12. DOCUMENTACION REQUERIDA
|
|
|
|
### Por Sprint
|
|
|
|
| Sprint | Documentos a Actualizar |
|
|
|--------|------------------------|
|
|
| Sprint 1 | MASTER_INVENTORY.yml, REPORTE-EJECUCION-S1.md |
|
|
| Sprint 2 | MASTER_INVENTORY.yml, REPORTE-EJECUCION-S2.md |
|
|
| Sprint 3 | MASTER_INVENTORY.yml, REPORTE-EJECUCION-S3.md |
|
|
| Sprint 4 | MASTER_INVENTORY.yml, REPORTE-EJECUCION-S4.md |
|
|
| Sprint 5 | MASTER_INVENTORY.yml, REPORTE-FINAL.md |
|
|
|
|
---
|
|
|
|
**Documento generado por:** ORQUESTADOR (Claude Code Opus 4.5)
|
|
**Sistema:** SIMCO v3.5 + CAPVED
|
|
**Fase:** P (Planeacion) - COMPLETADA
|
|
**Siguiente fase:** V (Validacion de Plan)
|