# ANALISIS MODULO RT-010: FACTURACION CFDI 4.0

**Fecha:** 2025-12-18
**Fase:** 2 - Analisis por Modulo
**Modulo:** RT-010 Facturacion
**Herencia:** 60%
**Story Points:** 35
**Prioridad:** P0

---

## 1. DESCRIPCION GENERAL

### 1.1 Proposito
Generacion de comprobantes fiscales digitales (CFDI 4.0) para ventas en POS y e-commerce, con timbrado en tiempo real y portal de autofactura.

### 1.2 Funcionalidades Principales

| Funcionalidad | Descripcion | Criticidad |
|---------------|-------------|------------|
| Facturacion POS | Al cerrar venta | Critica |
| Publico general | RFC generico | Alta |
| Autofactura | Portal publico | Alta |
| Notas de credito | Por devolucion | Alta |
| Cancelacion | Dentro de plazo | Media |
| Reportes | Fiscales | Media |

---

## 2. HERENCIA DEL CORE

### 2.1 Componentes Heredados (60%)

| Componente Core | % Uso | Accion |
|-----------------|-------|--------|
| financial.invoices | 80% | EXTENDER |
| financial.invoice_lines | 80% | EXTENDER |
| financial.payments | 100% | HEREDAR |
| core.partners | 100% | HEREDAR |

### 2.2 Servicios a Heredar

```typescript
import { InvoicesService } from '@erp-core/financial';
import { PaymentsService } from '@erp-core/financial';
import { PartnersService } from '@erp-core/core';
```

### 2.3 Servicios a Extender

```typescript
class CFDIInvoiceService extends InvoicesService {
  // Generacion CFDI
  async generateCFDI(posOrderId: string): Promise<CFDI>;
  async timbrar(cfdi: CFDI): Promise<TimbradoResult>;

  // Cancelacion
  async cancelCFDI(invoiceId: string, motivo: string): Promise<CancelResult>;
}
```

---

## 3. COMPONENTES NUEVOS

### 3.1 Estructura CFDI 4.0

```typescript
interface CFDI40 {
  // Comprobante
  version: '4.0';
  serie: string;
  folio: string;
  fecha: Date;
  formaPago: string;    // 01 Efectivo, 04 Tarjeta, etc.
  metodoPago: 'PUE' | 'PPD';  // Pago en una exhibicion / Parcialidades
  tipoDeComprobante: 'I' | 'E' | 'T' | 'N' | 'P';  // Ingreso, Egreso, Traslado, Nomina, Pago
  lugarExpedicion: string;  // CP

  // Emisor
  emisor: {
    rfc: string;
    nombre: string;
    regimenFiscal: string;  // 601, 612, etc.
  };

  // Receptor
  receptor: {
    rfc: string;
    nombre: string;
    domicilioFiscalReceptor: string;  // CP
    regimenFiscalReceptor: string;
    usoCFDI: string;  // G03 Gastos en general, etc.
  };

  // Conceptos
  conceptos: CFDIConcepto[];

  // Impuestos
  impuestos: {
    totalImpuestosTrasladados: number;
    totalImpuestosRetenidos: number;
    traslados: CFDITraslado[];
    retenciones?: CFDIRetencion[];
  };

  // Totales
  subTotal: number;
  descuento?: number;
  total: number;

  // Timbre (despues de timbrar)
  timbreFiscalDigital?: {
    version: '1.1';
    uuid: string;
    fechaTimbrado: Date;
    rfcProvCertif: string;
    selloCFD: string;
    selloSAT: string;
    noCertificadoSAT: string;
  };
}

interface CFDIConcepto {
  claveProdServ: string;    // Clave SAT (ej: 01010101)
  noIdentificacion?: string; // SKU
  cantidad: number;
  claveUnidad: string;       // E48, H87, etc.
  unidad?: string;           // Descripcion unidad
  descripcion: string;
  valorUnitario: number;
  importe: number;
  descuento?: number;
  objetoImp: '01' | '02' | '03';  // No objeto, Si objeto, Si objeto no desglosado
  impuestos?: {
    traslados: CFDITraslado[];
    retenciones?: CFDIRetencion[];
  };
}

interface CFDITraslado {
  base: number;
  impuesto: '002';  // IVA
  tipoFactor: 'Tasa' | 'Cuota' | 'Exento';
  tasaOCuota: number;  // 0.160000
  importe: number;
}
```

### 3.2 Servicios Backend

| Servicio | Metodos Principales |
|----------|-------------------|
| CFDIService | generate(), timbrar(), cancel(), validate() |
| CFDIBuilderService | fromPOSOrder(), fromEcommerceOrder() |
| PACService | timbrar(), consultar(), cancelar() |
| XMLService | buildXML(), parseXML(), validate() |
| PDFService | generatePDF() |
| AutoFacturaService | validateTicket(), generateFromTicket() |

### 3.3 Controladores

```typescript
@Controller('cfdi')
export class CFDIController {
  // Generacion desde POS
  @Post('pos/:orderId')
  generateFromPOS(@Param('orderId') orderId: string,
                  @Body() dto: CFDIRequestDto): Promise<CFDI>;

  // Generacion desde E-commerce
  @Post('ecommerce/:orderId')
  generateFromEcommerce(@Param('orderId') orderId: string): Promise<CFDI>;

  // Factura publico general
  @Post('public/:orderId')
  generatePublicInvoice(@Param('orderId') orderId: string): Promise<CFDI>;

  // Consulta
  @Get(':id')
  getCFDI(@Param('id') id: string): Promise<CFDI>;

  @Get(':id/xml')
  getXML(@Param('id') id: string): Promise<string>;

  @Get(':id/pdf')
  getPDF(@Param('id') id: string): Promise<Buffer>;

  // Cancelacion
  @Post(':id/cancel')
  cancel(@Param('id') id: string, @Body() dto: CancelDto): Promise<CancelResult>;

  // Notas de credito
  @Post('credit-note')
  createCreditNote(@Body() dto: CreditNoteDto): Promise<CFDI>;

  // Reportes
  @Get('report/monthly')
  getMonthlyReport(@Query() filters: ReportFilters): Promise<CFDIReport>;
}

// API Publica - Autofactura
@Controller('autofactura')
export class AutofacturaController {
  @Get('validate/:ticketNumber')
  validateTicket(@Param('ticketNumber') ticketNumber: string): Promise<TicketValidation>;

  @Post('generate')
  generate(@Body() dto: AutofacturaDto): Promise<CFDI>;

  @Get('download/:uuid')
  download(@Param('uuid') uuid: string): Promise<{ xml: string; pdf: Buffer }>;
}
```

---

## 4. INTEGRACION PAC

### 4.1 Proveedores Soportados

```typescript
interface PACProvider {
  name: string;
  timbrar(xml: string): Promise<TimbradoResult>;
  consultar(uuid: string): Promise<ConsultaResult>;
  cancelar(uuid: string, motivo: string): Promise<CancelResult>;
}

// Implementaciones
class FinkokPAC implements PACProvider {
  private readonly wsdlUrl = 'https://demo-facturacion.finkok.com/servicios/soap/stamp.wsdl';
  // ...
}

class FacturamaPAC implements PACProvider {
  private readonly apiUrl = 'https://api.facturama.mx/3/cfdis';
  // ...
}

class SWsapienPAC implements PACProvider {
  private readonly apiUrl = 'https://services.test.sw.com.mx/cfdi40/issue';
  // ...
}
```

### 4.2 Configuracion

```yaml
cfdi_config:
  pac:
    primary: "finkok"
    backup: "facturama"
    timeout: 10000  # 10 segundos
    retries: 3

  emisor:
    rfc: "XAXX010101000"  # Configurable por tenant
    nombre: "EMPRESA DEMO SA DE CV"
    regimen: "601"
    cp: "06600"

  certificados:
    cer_path: "/certs/{tenant}/cer.cer"
    key_path: "/certs/{tenant}/key.key"
    key_password: "${CFDI_KEY_PASSWORD}"

  almacenamiento:
    xml_path: "/storage/cfdi/{tenant}/{year}/{month}/"
    retention_years: 5
```

---

## 5. FLUJOS DE NEGOCIO

### 5.1 Facturacion en POS

```
1. Cliente solicita factura al cerrar venta
      ↓
2. Capturar datos fiscales:
   - RFC
   - Nombre o Razon Social
   - Regimen Fiscal
   - Uso CFDI
   - CP Domicilio Fiscal
      ↓
3. Validar RFC contra lista SAT (opcional)
      ↓
4. Construir XML CFDI 4.0
      ↓
5. Firmar con certificado
      ↓
6. Enviar a PAC para timbrado
      ↓
7. Si exito:
   a. Guardar XML timbrado
   b. Generar PDF
   c. Enviar por email
   d. Imprimir (opcional)
      ↓
8. Si fallo:
   a. Reintentar con PAC backup
   b. Si falla: marcar para reintento posterior
```

### 5.2 Autofactura

```
1. Cliente accede al portal publico
      ↓
2. Ingresa numero de ticket
      ↓
3. Sistema valida:
   - Ticket existe
   - No esta facturado
   - Dentro de plazo (30 dias)
      ↓
4. Muestra detalle de compra
      ↓
5. Cliente ingresa datos fiscales
      ↓
6. Genera y timbra CFDI
      ↓
7. Descarga XML y PDF
```

### 5.3 Cancelacion

```
1. Solicitar cancelacion
      ↓
2. Validar:
   - Dentro de plazo (30 dias)
   - No tiene pagos aplicados
      ↓
3. Seleccionar motivo:
   - 01: Con documento relacionado (sustitucion)
   - 02: Sin documento relacionado
   - 03: No se llevo a cabo
   - 04: Error en datos
      ↓
4. Enviar solicitud a PAC
      ↓
5. Si requiere aceptacion receptor:
   - Esperar aceptacion
      ↓
6. Confirmar cancelacion
```

---

## 6. TABLAS DDL

### 6.1 Tablas Nuevas

```sql
-- Configuracion CFDI por tenant
CREATE TABLE retail.cfdi_config (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) UNIQUE,
  emisor_rfc VARCHAR(13) NOT NULL,
  emisor_nombre VARCHAR(255) NOT NULL,
  emisor_regimen VARCHAR(3) NOT NULL,
  emisor_cp VARCHAR(5) NOT NULL,
  pac_provider VARCHAR(20) NOT NULL DEFAULT 'finkok',
  pac_user VARCHAR(100),
  pac_password_encrypted TEXT,
  cer_path TEXT,
  key_path TEXT,
  key_password_encrypted TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ
);

-- CFDIs emitidos
CREATE TABLE retail.cfdis (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id),

  -- Relacion origen
  source_type VARCHAR(20) NOT NULL,  -- pos_order, ecommerce_order
  source_id UUID NOT NULL,

  -- Datos comprobante
  serie VARCHAR(10),
  folio VARCHAR(20),
  uuid VARCHAR(36),  -- UUID del SAT
  fecha_emision TIMESTAMPTZ NOT NULL,
  tipo_comprobante CHAR(1) NOT NULL,  -- I, E, T, N, P
  forma_pago VARCHAR(2),
  metodo_pago VARCHAR(3),

  -- Receptor
  receptor_rfc VARCHAR(13) NOT NULL,
  receptor_nombre VARCHAR(255) NOT NULL,
  receptor_regimen VARCHAR(3),
  receptor_cp VARCHAR(5),
  uso_cfdi VARCHAR(4),

  -- Totales
  subtotal DECIMAL(12,2) NOT NULL,
  descuento DECIMAL(12,2) DEFAULT 0,
  total_impuestos DECIMAL(12,2) NOT NULL,
  total DECIMAL(12,2) NOT NULL,

  -- Estado
  status VARCHAR(20) NOT NULL DEFAULT 'vigente',  -- vigente, cancelado
  cancel_date TIMESTAMPTZ,
  cancel_reason VARCHAR(2),

  -- Archivos
  xml_content TEXT,
  xml_path TEXT,
  pdf_path TEXT,

  -- Timbre
  fecha_timbrado TIMESTAMPTZ,
  rfc_pac VARCHAR(13),
  sello_cfd TEXT,
  sello_sat TEXT,
  no_certificado_sat VARCHAR(20),

  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ,

  UNIQUE(tenant_id, uuid)
);

-- Indices
CREATE INDEX idx_cfdis_source ON retail.cfdis(source_type, source_id);
CREATE INDEX idx_cfdis_uuid ON retail.cfdis(uuid);
CREATE INDEX idx_cfdis_fecha ON retail.cfdis(fecha_emision);
CREATE INDEX idx_cfdis_receptor ON retail.cfdis(receptor_rfc);
```

---

## 7. DEPENDENCIAS

### 7.1 Dependencias de Core

| Modulo | Estado | Requerido Para |
|--------|--------|---------------|
| MGN-010 Financial | 70% | Facturas base |

### 7.2 Dependencias de Retail

| Modulo | Tipo |
|--------|------|
| RT-001 Fundamentos | Prerequisito |
| RT-002 POS | Origen de facturas |
| RT-009 E-commerce | Origen de facturas |

### 7.3 Dependencias Externas

| Servicio | Proposito |
|----------|-----------|
| PAC (Finkok, Facturama) | Timbrado |
| SAT | Validacion RFC (opcional) |

---

## 8. CRITERIOS DE ACEPTACION

### 8.1 Funcionales

- [ ] Generar factura desde POS
- [ ] Generar factura publico general
- [ ] Timbrar en < 5 segundos
- [ ] Retry automatico con PAC backup
- [ ] Generar PDF
- [ ] Enviar por email
- [ ] Portal de autofactura
- [ ] Cancelar CFDI
- [ ] Generar nota de credito
- [ ] Almacenar XMLs 5 anos

### 8.2 Compliance

- [ ] CFDI 4.0 valido
- [ ] Catalogo SAT actualizado
- [ ] Certificados vigentes
- [ ] Timbre valido

---

## 9. ESTIMACION DETALLADA

| Componente | SP Backend | SP Frontend | Total |
|------------|-----------|-------------|-------|
| Entities + Migrations | 3 | - | 3 |
| CFDIService | 5 | - | 5 |
| CFDIBuilderService | 5 | - | 5 |
| PACService | 5 | - | 5 |
| XMLService | 3 | - | 3 |
| PDFService | 2 | - | 2 |
| AutofacturaService | 3 | - | 3 |
| Controllers | 3 | - | 3 |
| Autofactura Portal | - | 4 | 4 |
| Config UI | - | 2 | 2 |
| **TOTAL** | **29** | **6** | **35** |

---

## 10. CATALOGOS SAT REQUERIDOS

| Catalogo | Uso |
|----------|-----|
| c_FormaPago | 01 Efectivo, 04 Tarjeta, etc. |
| c_MetodoPago | PUE, PPD |
| c_UsoCFDI | G01, G03, etc. |
| c_RegimenFiscal | 601, 612, etc. |
| c_ClaveProdServ | Claves de productos |
| c_ClaveUnidad | E48, H87, etc. |
| c_TipoDeComprobante | I, E, T, N, P |
| c_Impuesto | 002 IVA |
| c_TasaOCuota | 0.160000, 0.000000 |
| c_Moneda | MXN, USD |

---

**Estado:** ANALISIS COMPLETO
**Bloqueado por:** RT-001, RT-002 o RT-009
