Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/TI-Sin-Problemas/erpnext_mexico_compliance/llms.txt

Use this file to discover all available pages before exploring further.

Sales Invoices are stamped as CFDI Tipo Ingreso documents (TipoDeComprobante.INGRESO). When a Sales Invoice is stamped, the app assembles a cfdi40.Comprobante from the invoice data, signs it with the selected Digital Signing Certificate, and sends it to the TI Sin Problemas web service for PAC stamping. The resulting UUID and stamped XML are stored directly on the Sales Invoice record.

Requirements

All of the following conditions must be satisfied before a Sales Invoice can be stamped:
  • The Company address must have a Zip Code (pincode on the Address DocType).
  • The Customer must have a valid Mexican Tax ID (RFC) in the tax_id field.
  • The Customer must have a SAT Tax Regime (mx_tax_regime) set.
  • The Customer’s billing address must have a Zip Code.
  • Every Invoice Item must have a SAT Product or Service Key (mx_product_service_key).
  • The UOM of every Invoice Item must have a SAT UOM Key (mx_uom_key).
  • The Mode of Payment on the invoice must have a SAT Payment Method code assigned.
  • The invoice must have a SAT Payment Option (mx_payment_option) selected.
  • At least one Digital Signing Certificate must exist for the Company issuing the invoice.
  • You must have available stamp credits in your TI Sin Problemas account.
Items with a zero amount are skipped when building the CFDI Conceptos list. All non-zero items must individually satisfy the SAT key requirements above.

Stamping a Sales Invoice

1

Submit the Sales Invoice

Complete and submit the Sales Invoice in ERPNext as you normally would. The document must reach Submitted status before it can be stamped.
2

Locate the Stamp CFDI button

Once the invoice is submitted, a Stamp CFDI button appears on the form. This button is only visible on submitted, un-stamped invoices.
3

Select the Digital Signing Certificate

If prompted, select the Digital Signing Certificate (CSD) to use for this invoice. Only certificates associated with the invoice’s Company are shown.
4

Click Stamp CFDI

The app validates the company address, customer details, and all line items, then signs the CFDI and posts it to tisp_apps.api.v1.cfdi.stamp. If any validation fails, a descriptive error is shown before any API call is made.
5

Stamping succeeds

On success, the mx_stamped_xml field is populated with the full SAT-stamped XML (including the TimbreFiscalDigital complement), mx_uuid is set to the CFDI UUID, and mx_cfdi_status becomes Valid. A green confirmation alert is displayed.
If stamp_on_submit is enabled in CFDI Stamping Settings, steps 2–5 happen automatically at the moment of submission. See the next section for details.

Auto-stamp on submit

In CFDI Stamping Settings, the Stamp on Submit (stamp_on_submit) toggle controls automatic stamping. When enabled, the on_submit hook of the Sales Invoice override fires immediately after the document is submitted:
super().on_submit()
settings: CFDIStampingSettings = frappe.get_single("CFDI Stamping Settings")
if settings.stamp_on_submit:
    csd = frappe.get_value("Default CSD", {"company": self.company}, "csd")
    # If stamping fails, do not block the submission of the sales invoice
    try:
        self.stamp_cfdi(csd)
    except Exception as e:
        frappe.msgprint(str(e), title=_("CFDI Stamping Error"))
The certificate used is the Default CSD configured for the Company in CFDI Stamping Settings. If stamping fails for any reason (validation error, network issue, insufficient credits), a warning message is shown but the submission itself is not rolled back — the invoice remains submitted so you can correct the issue and stamp manually.

CFDI field mapping

The table below shows how ERPNext fields are translated into CFDI 4.0 XML elements when the get_cfdi_voucher method assembles the cfdi40.Comprobante:
ERPNext FieldCFDI Element
Company (Emisor RFC from CSD)Emisor/Rfc
Company Address Zip CodeLugarExpedicion
Customer Tax ID (tax_id)Receptor/Rfc
Customer Name — uppercasedReceptor/Nombre
Customer Address Zip CodeReceptor/DomicilioFiscalReceptor
Customer SAT Tax Regime (mx_tax_regime)Receptor/RegimenFiscalReceptor
SAT CFDI Use (mx_cfdi_use)Receptor/UsoCFDI
SAT Payment Option (mx_payment_option)MetodoPago
SAT Payment Method code (mx_payment_mode)FormaPago
CurrencyMoneda
Conversion Rate (when not MXN)TipoCambio
Posting Date + Posting TimeFecha
Item SAT Product/Service KeyConcepto/ClaveProdServ
Item UOM SAT KeyConcepto/ClaveUnidad
Item CodeConcepto/NoIdentificacion
Item QuantityConcepto/Cantidad
Item Rate + Discount Amount (pre-discount unit price)Concepto/ValorUnitario
Item Discount AmountConcepto/Descuento

The mx_related_sales_invoices child table allows you to link one or more previously stamped CFDIs to the current invoice using an SAT relationship type (e.g., substitute for a cancelled invoice, credit note, etc.). Each row contains:
  • Sales Invoice — the related ERPNext Sales Invoice.
  • UUID — automatically read from the related invoice’s mx_uuid field.
  • SAT Relationship Type — the SAT catalog code for the relationship (e.g., 04 for substitution).
At validation time, the app checks that every row in mx_related_sales_invoices has a UUID. If any related invoice has not yet been stamped, saving the current invoice is blocked with an error. When the CFDI voucher is assembled, each unique relationship type becomes a cfdi40.CfdiRelacionados element containing the UUIDs for that type:
related_documents = {}
for rsi in self.mx_related_sales_invoices:
    related_documents.setdefault(rsi.sat_relationship_type, []).append(rsi.uuid)

cfdi_relacionados=[cfdi40.CfdiRelacionados(k, v) for k, v in related_documents.items()]

Addenda

The SAT allows issuers to append custom XML content inside a <cfdi:Addenda> element. The app supports addenda at two levels:
  • Customer-level (mx_addenda on the Customer DocType) — default addenda applied to all invoices for that customer.
  • Invoice-level (mx_addenda on the Sales Invoice) — per-invoice addenda that overrides or supplements the customer default.
The content of mx_addenda is treated as a Jinja template rendered with the current document as the doc context variable:
rendered_addenda = frappe.render_template(self.mx_addenda, {"doc": self})
The rendered XML string is parsed and appended as a child of a <cfdi:Addenda> element inside the SAT namespace (http://www.sat.gob.mx/cfd/4). The addenda is injected into the stamped XML after the PAC stamps the document — the final mx_stamped_xml stored on the invoice includes the addenda.
Use Jinja expressions like {{ doc.customer }} or {{ doc.grand_total }} inside your addenda template to embed document-specific values.

Download CFDI files

Once a Sales Invoice is stamped, the Download CFDI Files button packages both the stamped XML and the rendered PDF into a ZIP archive named {invoice_name}_CFDI.zip. The archive contains:
  • {invoice_name}_CFDI.xml — the full SAT-stamped XML with TimbreFiscalDigital.
  • {invoice_name}_CFDI.pdf — the CFDI PDF representation.
You can also use the View PDF file button (under CFDI Actions) to open the PDF directly in the browser without downloading the archive.

Build docs developers (and LLMs) love