Skip to main content

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");
}

Data Extraction

The parser extracts comprehensive information from CFDI documents:
1

UUID Extraction

Extracts the unique fiscal identifier from the TimbreFiscalDigital complement.
// base.parser.ts:158
export function extractUUID(xml: Document): string
2

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 }
3

Party Information

Extracts detailed information about both issuer and receiver.
  • RFC (Tax ID)
  • Name/Business name
  • Fiscal regime (Régimen Fiscal)
4

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.

Build docs developers (and LLMs) love