Field Mapping: English (Invoice) ↔ Spanish (DTE JSON)
This document maps fields between the English Invoice interface (internal format) and the Spanish DteJson format (RPAfelApi format).
Architecture Overview
Current Approach: ✅ Recommended
- Source of Truth: English
Invoiceinterface (camelCase, flat structure) - Target Format: Spanish
DteJson(PascalCase, nested structure) - Transformation:
XmlToDteJsonService.convertInvoiceToDteJson() - When Used: When calling RPAfelApi provider
Field Mapping Reference
Root Level
| English (Invoice) | Spanish (DteJson) | Notes |
|---|---|---|
| N/A | ID: "DatosCertificados" | Constant value |
| N/A | DatosEmision | Wrapper object for all emission data |
| N/A | extra | Contains subDomain and TipoEspecial |
General Data
| English Path | Spanish Path | Transformation |
|---|---|---|
generalData.type | DatosEmision.DatosGenerales.Tipo | Direct mapping |
generalData.dateTimeIssue | DatosEmision.DatosGenerales.FechaHoraEmision | Direct mapping |
generalData.currencyCode | DatosEmision.DatosGenerales.CodigoMoneda | Defaults to "GTQ" if missing |
addendumData.internalReference | DatosEmision.DatosGenerales.rpaUUID | Passed as parameter |
felProviderData.providerName | DatosEmision.DatosGenerales.rpaDE_Empresa | Mapped via mapProviderName() |
| N/A | DatosEmision.DatosGenerales.rpaCertificador_Usuario | Set from business config |
| N/A | DatosEmision.DatosGenerales.rpaCertificador_Clave | Set from business config |
| N/A | DatosEmision.DatosGenerales.rpaFisco_Usuario | Empty string (default) |
Issuer Data (Emisor)
| English Path | Spanish Path | Transformation |
|---|---|---|
issuerData.taxId | DatosEmision.Emisor.NITEmisor | Direct mapping |
issuerData.taxName | DatosEmision.Emisor.NombreEmisor | Direct mapping |
issuerData.establishmentCode | DatosEmision.Emisor.CodigoEstablecimiento | Direct mapping |
issuerData.commercialName | DatosEmision.Emisor.NombreComercial | Direct mapping |
issuerData.vatAffiliationCode | DatosEmision.Emisor.AfiliacionIVA | Direct mapping |
issuerData.addressData.* | DatosEmision.Emisor.DireccionEmisor.* | See Address Mapping |
Receiver Data (Receptor)
| English Path | Spanish Path | Transformation |
|---|---|---|
receiverData.taxName | DatosEmision.Receptor.NombreReceptor | Direct mapping |
receiverData.taxId | DatosEmision.Receptor.IDReceptor | Direct mapping |
receiverData.taxpayerType | DatosEmision.Receptor.TipoEspecial | Defaults to "NIT" if missing |
receiverData.addressData.* | DatosEmision.Receptor.DireccionReceptor.* | See Address Mapping |
Address Data
| English Path | Spanish Path | Transformation |
|---|---|---|
addressData.address | Direccion | Direct mapping |
addressData.postalCode | CodigoPostal | Direct mapping |
addressData.municipality | Municipio | Direct mapping |
addressData.department | Departamento | Direct mapping |
addressData.country | Pais | Direct mapping |
Phrases
| English Path | Spanish Path | Transformation |
|---|---|---|
phrases[] | DatosEmision.Frases.Frase[] | Array wrapped in object |
phrases[].phraseType | DatosEmision.Frases.Frase[].TipoFrase | Direct mapping |
phrases[].scenarioCode | DatosEmision.Frases.Frase[].CodigoEscenario | Direct mapping |
Items
| English Path | Spanish Path | Transformation |
|---|---|---|
items[] | DatosEmision.Items.Item[] | Array wrapped in object |
index + 1 | DatosEmision.Items.Item[].NumeroLinea | Line number (1-based) |
items[].goodOrService | DatosEmision.Items.Item[].BienOServicio | Direct mapping |
items[].quantity | DatosEmision.Items.Item[].Cantidad | Converted to string |
items[].unitOfMeasure | DatosEmision.Items.Item[].UnidadMedida | Direct mapping |
items[].name | DatosEmision.Items.Item[].Descripcion | Direct mapping |
items[].unitPrice | DatosEmision.Items.Item[].PrecioUnitario | Converted to string (2 decimals) |
items[].amount | DatosEmision.Items.Item[].Precio | Converted to string (2 decimals) |
items[].discount | DatosEmision.Items.Item[].Descuento | Converted to string (2 decimals) |
items[].amount - items[].discount | DatosEmision.Items.Item[].Total | Calculated, string (2 decimals) |
items[].taxes[] | DatosEmision.Items.Item[].Impuestos.Impuesto[] | Array wrapped in object |
items[].taxes[].shortName | DatosEmision.Items.Item[].Impuestos.Impuesto[].NombreCorto | Direct mapping |
items[].taxes[].taxableUnitCode | DatosEmision.Items.Item[].Impuestos.Impuesto[].CodigoUnidadGravable | Direct mapping |
items[].taxes[].taxableAmount | DatosEmision.Items.Item[].Impuestos.Impuesto[].MontoGravable | Converted to string (2 decimals) |
items[].taxes[].taxAmount | DatosEmision.Items.Item[].Impuestos.Impuesto[].MontoImpuesto | Converted to string (2 decimals) |
Totals
| English Path | Spanish Path | Transformation |
|---|---|---|
total | DatosEmision.Totales.GranTotal | Converted to string (2 decimals) |
Calculated from items[].taxes[] | DatosEmision.Totales.TotalImpuestos.TotalImpuesto[] | Aggregated by tax name |
| N/A | DatosEmision.Totales.TotalImpuestos.TotalImpuesto[].NombreCorto | Tax name |
| N/A | DatosEmision.Totales.TotalImpuestos.TotalImpuesto[].TotalMontoImpuesto | Sum of tax amounts (string, 2 decimals) |
Extra Data
| English Path | Spanish Path | Transformation |
|---|---|---|
addendumData.version | extra.subDomain | Direct mapping, defaults to "" |
receiverData.taxpayerType | extra.TipoEspecial | Only included if value is special type (CUI, EXT, etc.). NOT set for "NIT" (invalid value) |
Fields NOT Mapped (English Only)
These fields exist in the English Invoice but are not included in the Spanish DTE JSON:
businessData.id- Used internally for business lookup, not sent to APIinvoiceNumber- Optional field, not used in DTE JSONdate- Not included in DTE JSON structureaddendumData.dateReference- Not included in DTE JSON structureaddendumData.validateInternalReference- Not included in DTE JSON structure
Structural Differences
1. Nesting
- English: Flat structure with top-level keys (
generalData,issuerData, etc.) - Spanish: Nested under
DatosEmisionwrapper
2. Array Wrapping
- English: Direct arrays (
phrases[],items[]) - Spanish: Arrays wrapped in objects (
Frases.Frase[],Items.Item[])
3. Naming Convention
- English: camelCase (
dateTimeIssue,taxId) - Spanish: PascalCase (
FechaHoraEmision,NITEmisor)
4. Data Types
- English: Numbers and strings as-is
- Spanish: Numbers converted to strings with 2 decimal places
Provider Name Mapping
| English (providerName) | Spanish (rpaDE_Empresa) |
|---|---|
digifact | Digifact |
infile | Infile |
fegora | Fegora |
guatefactura | GuateFactura |
rpafelapi | Digifact (default fallback) |
Usage Example
// English format (internal)
const invoice: Invoice = {
generalData: {
type: "FACT",
dateTimeIssue: "2025-10-21T22:13:59.079Z",
currencyCode: "GTQ"
},
// ... rest of invoice
};
// Convert to Spanish format (for API)
const dteJson = xmlToDteJsonService.convertInvoiceToDteJson(
invoice,
invoice.addendumData.internalReference
);
// Result: Spanish DTE JSON structure
// {
// ID: "DatosCertificados",
// DatosEmision: {
// DatosGenerales: {
// Tipo: "FACT",
// FechaHoraEmision: "2025-10-21T22:13:59.079Z",
// ...
// },
// ...
// }
// }
Best Practices
-
✅ Keep English as Source of Truth
- Always create/update the English
Invoicestructure first - Transform to Spanish only when needed (API calls)
- Always create/update the English
-
✅ Single Transformation Point
- Use
XmlToDteJsonServicefor all conversions - Don't create Spanish structure directly
- Use
-
✅ Type Safety
- Use TypeScript interfaces (
Invoice,DteJson) - Leverage type checking to catch mapping errors
- Use TypeScript interfaces (
-
✅ Maintainability
- Update this document when fields change
- Keep transformation logic centralized
-
✅ Testing
- Test transformations with real data
- Verify all fields are mapped correctly
- Test edge cases (missing fields, null values)
Reverse Conversion (Spanish → English)
The service now includes convertDteJsonToInvoice() method for reverse conversion.
Reverse Field Mapping
| Spanish Path | English Path | Transformation |
|---|---|---|
DatosEmision.DatosGenerales.Tipo | generalData.type | Direct mapping |
DatosEmision.DatosGenerales.FechaHoraEmision | generalData.dateTimeIssue | Direct mapping |
DatosEmision.DatosGenerales.CodigoMoneda | generalData.currencyCode | Defaults to "GTQ" if missing |
DatosEmision.DatosGenerales.rpaUUID | addendumData.internalReference | Direct mapping |
DatosEmision.DatosGenerales.rpaDE_Empresa | felProviderData.providerName | Reverse mapped via reverseMapProviderName() |
DatosEmision.Emisor.NITEmisor | issuerData.taxId | Direct mapping |
DatosEmision.Emisor.NombreEmisor | issuerData.taxName | Direct mapping |
DatosEmision.Emisor.CodigoEstablecimiento | issuerData.establishmentCode | Direct mapping |
DatosEmision.Emisor.NombreComercial | issuerData.commercialName | Direct mapping |
DatosEmision.Emisor.AfiliacionIVA | issuerData.vatAffiliationCode | Direct mapping |
DatosEmision.Emisor.DireccionEmisor.* | issuerData.addressData.* | See Address Reverse Mapping |
DatosEmision.Receptor.NombreReceptor | receiverData.taxName | Direct mapping |
DatosEmision.Receptor.IDReceptor | receiverData.taxId | Direct mapping |
DatosEmision.Receptor.TipoEspecial or extra.TipoEspecial | receiverData.taxpayerType | Defaults to "NIT" if missing |
DatosEmision.Receptor.DireccionReceptor.* | receiverData.addressData.* | See Address Reverse Mapping |
DatosEmision.Frases.Frase[] | phrases[] | Unwrapped from object |
DatosEmision.Frases.Frase[].TipoFrase | phrases[].phraseType | Direct mapping |
DatosEmision.Frases.Frase[].CodigoEscenario | phrases[].scenarioCode | Direct mapping |
DatosEmision.Items.Item[] | items[] | Unwrapped from object |
DatosEmision.Items.Item[].Descripcion | items[].name | Direct mapping |
DatosEmision.Items.Item[].Cantidad | items[].quantity | Parsed to number |
DatosEmision.Items.Item[].UnidadMedida | items[].unitOfMeasure | Direct mapping |
DatosEmision.Items.Item[].PrecioUnitario | items[].unitPrice | Parsed to number |
DatosEmision.Items.Item[].Precio | items[].amount | Parsed to number |
DatosEmision.Items.Item[].Descuento | items[].discount | Parsed to number |
DatosEmision.Items.Item[].BienOServicio | items[].goodOrService | Direct mapping |
DatosEmision.Items.Item[].Impuestos.Impuesto[] | items[].taxes[] | Unwrapped from object |
DatosEmision.Items.Item[].Impuestos.Impuesto[].NombreCorto | items[].taxes[].shortName | Direct mapping |
DatosEmision.Items.Item[].Impuestos.Impuesto[].CodigoUnidadGravable | items[].taxes[].taxableUnitCode | Direct mapping |
DatosEmision.Items.Item[].Impuestos.Impuesto[].MontoGravable | items[].taxes[].taxableAmount | Parsed to number |
DatosEmision.Items.Item[].Impuestos.Impuesto[].MontoImpuesto | items[].taxes[].taxAmount | Parsed to number |
DatosEmision.Totales.GranTotal | total | Parsed to number |
extra.subDomain | addendumData.version | Direct mapping |
DatosEmision.Referencias.Referencia[0].NumeroDocumento | originalInvoiceUuid | For NCRE/NDEB/ANULACION |
DatosEmision.Referencias.Referencia[0].MotivoAjuste | adjustmentReason | For NCRE/NDEB |
DatosEmision.Referencias.Referencia[0].MotivoAnulacion | cancellationReason | For ANULACION |
DatosEmision.Referencias.Referencia[0].FechaEmision | originalInvoiceDate | For ANULACION |
Address Reverse Mapping
| Spanish Path | English Path | Transformation |
|---|---|---|
Direccion | addressData.address | Direct mapping |
CodigoPostal | addressData.postalCode | Direct mapping |
Municipio | addressData.municipality | Direct mapping |
Departamento | addressData.department | Direct mapping |
Pais | addressData.country | Direct mapping |
Reverse Provider Name Mapping
| Spanish (rpaDE_Empresa) | English (providerName) |
|---|---|
Digifact | digifact |
Infile | infile |
Fegora | fegora |
GuateFactura | guatefactura |
| Other/Unknown | digifact (default) |
Fields Lost in Reverse Conversion
These fields exist in English Invoice but are not present in Spanish DTE JSON, so they will be:
- Set to default values
- Derived from other fields
- Or lost entirely
| English Field | Default/Derived Value |
|---|---|
invoiceNumber | Not available (lost) |
date | Derived from FechaHoraEmision (date portion only) |
addendumData.dateReference | Set to FechaHoraEmision |
addendumData.validateInternalReference | Set to "VALIDAR" (default) |
businessData.id | Must be provided as parameter to convertDteJsonToInvoice() |
Reverse Conversion Usage Example
// Spanish format (from API)
const dteJson: DteJson = {
ID: "DatosCertificados",
DatosEmision: {
DatosGenerales: {
Tipo: "FACT",
FechaHoraEmision: "2025-01-21T10:00:00.000Z",
...
},
...
}
};
// Convert back to English format (internal)
const invoice = xmlToDteJsonService.convertDteJsonToInvoice(
dteJson,
"business-123" // businessId must be provided
);
// Result: English Invoice structure
// {
// generalData: {
// type: "FACT",
// dateTimeIssue: "2025-01-21T10:00:00.000Z",
// ...
// },
// ...
// }
Reverse Conversion Notes
- Business ID Required:
convertDteJsonToInvoice()requiresbusinessIdas a parameter since it's not in DTE JSON - Data Type Conversion: String numbers are parsed back to numbers
- Array Unwrapping: Arrays wrapped in objects are unwrapped (e.g.,
Frases.Frase[]→phrases[]) - Missing Fields: Missing optional fields default to empty strings or empty arrays
- Validation: Reverse conversion validates that
DatosEmisionandDatosGeneralesexist
Last Updated: 2025-01-21 Maintained By: FEL Team