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>
30 KiB
30 KiB
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
-- 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
-- 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
// 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
// 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
// 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
// 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)