Overview
Tresa Contafy provides robust XML processing capabilities for Mexican CFDI (Comprobante Fiscal Digital por Internet) documents. The system supports both CFDI 3.3 and 4.0 versions, automatically detecting the version and extracting all fiscal data with precision.
Supported Document Types
The parser handles three main types of CFDI documents:
- PUE (Pago en Una sola Exhibición) - Payment in a single installment
- PPD (Pago en Parcialidades o Diferido) - Payment in installments or deferred
- Complemento de Pago - Payment complement for PPD invoices
The parser automatically identifies the document type by analyzing the MetodoPago attribute and the presence of payment complement nodes.
Key Features
Automatic Version Detection
The system automatically detects whether a CFDI is version 3.3 or 4.0:
// From base.parser.ts:305
export function detectVersion(xml: Document): string {
if (xml["cfdi:Comprobante"]) return "4.0";
const comprobante = getComprobante(xml);
if (comprobante) {
const version = comprobante["@_Version"] ?? comprobante["@_version"];
if (version === "4.0") return "4.0";
return "3.3";
}
throw new Error("No se pudo detectar la versión del CFDI");
}
The parser extracts comprehensive information from CFDI documents:
UUID Extraction
Extracts the unique fiscal identifier from the TimbreFiscalDigital complement.// base.parser.ts:158
export function extractUUID(xml: Document): string
Tax Calculations
Automatically calculates IVA (VAT), ISR, and IVA withholdings.// base.parser.ts:73
export function extractIVA(xml: Document): number
// base.parser.ts:90 - Extracts ISR (001) and IVA (002) withholdings
export function extractRetenciones(xml: Document): { iva: number; isr: number }
Party Information
Extracts detailed information about both issuer and receiver.
- RFC (Tax ID)
- Name/Business name
- Fiscal regime (Régimen Fiscal)
Financial Data
Extracts all monetary values:
- Subtotal (before taxes)
- Total (with taxes)
- Tax amounts
- Withholdings
Parser Configuration
The XML parser is configured with specific options for optimal CFDI processing:
// From base.parser.ts:16
const defaultParser = new XMLParser({
ignoreAttributes: false, // Preserve XML attributes
attributeNamePrefix: "@_", // Prefix for attribute names
textNodeName: "#text", // Name for text nodes
parseAttributeValue: true, // Convert attribute values to primitives
parseTagValue: true, // Convert tag values to primitives
trimValues: true, // Remove whitespace
});
The parser uses the fast-xml-parser library, which is highly performant and handles large XML documents efficiently.
Invoice Processing
For income invoices (facturas de ingreso), use the parseInvoice function:
// From invoice.parser.ts:30
import { parseInvoice } from './parsers/invoice.parser';
const xmlBuffer = Buffer.from(xmlContent, 'utf-8');
const invoiceData = parseInvoice(xmlBuffer);
// Returns InvoiceData with:
// - uuid, fecha, tipo
// - total, subtotal, iva
// - rfcEmisor, rfcReceptor
// - nombreEmisor, nombreReceptor
// - regimenFiscalEmisor, regimenFiscalReceptor
// - concepto, mes, año
// - complementoPago (if COMPLEMENTO_PAGO type)
Invoice Validation
The parser validates the CFDI structure before processing:
// base.parser.ts:127
export function validateCFDI(xml: Document): boolean {
const comprobante = getComprobante(xml);
if (!comprobante) return false;
const emisor = comprobante["cfdi:Emisor"] ?? comprobante["Emisor"];
const receptor = comprobante["cfdi:Receptor"] ?? comprobante["Receptor"];
return !!(emisor && receptor);
}
Expense Processing
For expense documents (gastos/egresos), use the parseExpense function:
// From expense.parser.ts:45
import { parseExpense } from './parsers/expense.parser';
const xmlBuffer = Buffer.from(xmlContent, 'utf-8');
const expenseData = parseExpense(xmlBuffer);
// Returns ExpenseData with:
// - tipo_origen: 'XML'
// - is_paid: true for PUE, false for PPD
// - payment_date: fecha for PUE, null for PPD
// - All standard CFDI fields
The expense parser automatically determines the payment status based on the CFDI type:
- PUE:
is_paid = true, payment_date = fecha
- PPD:
is_paid = false, payment_date = null
Payment Complement Processing
For payment complements (complementos de pago), the parser extracts detailed payment information:
// From base.parser.ts:451
export function extractComplementoPago(xml: Document): ComplementoPago {
// Extracts:
// - Payment date, method, currency
// - Exchange rate
// - Payment amount
// - Related invoices (facturasRelacionadas)
// - Partial payment details (numParcialidad, saldos)
}
The parser extracts all related invoices from payment complements:
// From base.parser.ts:476
interface FacturaRelacionada {
uuid: string; // UUID of the related PPD invoice
monedaDR: string; // Currency
tipoCambioDR: number; // Exchange rate
metodoPagoDR: string; // Payment method
numParcialidad: number; // Installment number
impSaldoAnt: number; // Previous balance
impPagado: number; // Amount paid (subtotal)
impSaldoInsoluto: number; // Outstanding balance
}
Date and Timezone Handling
The parser handles dates with Mexico City timezone:
// From base.parser.ts:295
export function calculateMesYAno(fecha: Date): { mes: number; año: number } {
const fechaMexico = new Date(
fecha.toLocaleString("en-US", { timeZone: "America/Mexico_City" })
);
return { mes: fechaMexico.getMonth() + 1, año: fechaMexico.getFullYear() };
}
Error Handling
The parser includes comprehensive error handling:
The following errors will be thrown if validation fails:
- “El contenido XML no es válido” - Empty or invalid XML
- “No se encontró Comprobante” - Missing Comprobante node
- “No se encontró el complemento TimbreFiscalDigital” - Missing fiscal stamp
- “No se pudo extraer el UUID del timbre fiscal” - UUID not found
- “Fecha no encontrada en el CFDI” - Missing date
- “Fecha inválida” - Invalid date format
- “Estructura CFDI inválida: faltan Comprobante, Emisor o Receptor” - Invalid structure
Payroll Processing
For payroll documents (nómina), use the parsePayroll function:
// From payroll.parser.ts:109
import { parsePayroll } from './parsers/payroll.parser';
const xmlBuffer = Buffer.from(xmlContent, 'utf-8');
const payrollData = parsePayroll(xmlBuffer);
// Returns PayrollData with:
// - uuid
// - fecha_pago
// - percepciones_total
// - deducciones_total
// - otros_pagos_total
// - neto_pagado (calculated)
// - receptor: { nombre, rfc, curp, nss }
Payroll Calculations
The parser automatically calculates net pay:
// From payroll.parser.ts:127
const neto_pagado = percepciones_total - deducciones_total + otros_pagos_total;
Implementation Details
Base Parser Class
All parsers extend or use the BaseCFDIParser class located in src/parsers/base.parser.ts:507.
The base parser provides:
- XML parsing with
fast-xml-parser
- CFDI structure validation
- Version detection (3.3 vs 4.0)
- Common extraction methods
- Tax calculations
- UUID extraction
- Date handling
Source Code References
- Base parser:
src/parsers/base.parser.ts
- Invoice parser:
src/parsers/invoice.parser.ts
- Expense parser:
src/parsers/expense.parser.ts
- Payroll parser:
src/parsers/payroll.parser.ts
API Integration
The XML processing functionality is integrated into the API endpoints:
POST /api/invoices - Upload and parse invoice XML
POST /api/expenses - Upload and parse expense XML
POST /api/payroll - Upload and parse payroll XML
POST /api/payment-complements - Upload and parse payment complement XML
All XML uploads are processed through the parser before being stored in the database, ensuring data consistency and validation.