@arcasdk/pdf 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +114 -0
  2. package/lib/constants/index.d.ts +1 -0
  3. package/lib/constants/index.js +18 -0
  4. package/lib/constants/index.js.map +1 -0
  5. package/lib/constants/voucher.constants.d.ts +24 -0
  6. package/lib/constants/voucher.constants.js +99 -0
  7. package/lib/constants/voucher.constants.js.map +1 -0
  8. package/lib/generator/index.d.ts +1 -0
  9. package/lib/generator/index.js +18 -0
  10. package/lib/generator/index.js.map +1 -0
  11. package/lib/generator/invoice-pdf-generator.d.ts +11 -0
  12. package/lib/generator/invoice-pdf-generator.js +204 -0
  13. package/lib/generator/invoice-pdf-generator.js.map +1 -0
  14. package/lib/index.d.ts +6 -0
  15. package/lib/index.js +20 -0
  16. package/lib/index.js.map +1 -0
  17. package/lib/templates/arca-default/index.d.ts +1 -0
  18. package/lib/templates/arca-default/index.js +7 -0
  19. package/lib/templates/arca-default/index.js.map +1 -0
  20. package/lib/templates/arca-default/invoice-document.d.ts +3 -0
  21. package/lib/templates/arca-default/invoice-document.hbs +336 -0
  22. package/lib/templates/arca-default/invoice-document.js +14 -0
  23. package/lib/templates/arca-default/invoice-document.js.map +1 -0
  24. package/lib/templates/engine/index.d.ts +1 -0
  25. package/lib/templates/engine/index.js +8 -0
  26. package/lib/templates/engine/index.js.map +1 -0
  27. package/lib/templates/engine/template-engine.d.ts +5 -0
  28. package/lib/templates/engine/template-engine.js +88 -0
  29. package/lib/templates/engine/template-engine.js.map +1 -0
  30. package/lib/templates/index.d.ts +1 -0
  31. package/lib/templates/index.js +6 -0
  32. package/lib/templates/index.js.map +1 -0
  33. package/lib/types/index.d.ts +1 -0
  34. package/lib/types/index.js +18 -0
  35. package/lib/types/index.js.map +1 -0
  36. package/lib/types/invoice-data.types.d.ts +225 -0
  37. package/lib/types/invoice-data.types.js +3 -0
  38. package/lib/types/invoice-data.types.js.map +1 -0
  39. package/lib/utils/format.utils.d.ts +22 -0
  40. package/lib/utils/format.utils.js +56 -0
  41. package/lib/utils/format.utils.js.map +1 -0
  42. package/lib/utils/index.d.ts +3 -0
  43. package/lib/utils/index.js +20 -0
  44. package/lib/utils/index.js.map +1 -0
  45. package/lib/utils/number-to-words.utils.d.ts +1 -0
  46. package/lib/utils/number-to-words.utils.js +99 -0
  47. package/lib/utils/number-to-words.utils.js.map +1 -0
  48. package/lib/utils/qr.utils.d.ts +8 -0
  49. package/lib/utils/qr.utils.js +56 -0
  50. package/lib/utils/qr.utils.js.map +1 -0
  51. package/package.json +61 -0
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Datos del emisor (quien emite la factura)
3
+ */
4
+ export interface EmisorData {
5
+ razonSocial: string;
6
+ domicilioComercial: string;
7
+ condicionIva: string;
8
+ cuit: string;
9
+ iibb: string;
10
+ fechaInicioActividades: string;
11
+ /** Contacto del emisor (email, web, teléfono) */
12
+ contacto?: string;
13
+ }
14
+ /**
15
+ * Datos del receptor (a quien va dirigida la factura)
16
+ */
17
+ export interface ReceptorData {
18
+ razonSocial: string;
19
+ domicilio?: string;
20
+ condicionIva: string;
21
+ documentoTipo: string;
22
+ documentoNro: string;
23
+ /** CUIT del país destino con descripción (ej: "55000002126 (ESTADOS UNIDOS - Persona Jurídica)") */
24
+ cuitPais?: string;
25
+ /** ID impositivo del receptor en el país destino */
26
+ idImpositivo?: string;
27
+ }
28
+ /**
29
+ * Item individual de la factura
30
+ */
31
+ export interface InvoiceItem {
32
+ codigo?: string;
33
+ descripcion: string;
34
+ cantidad: number;
35
+ unidadMedida: string;
36
+ precioUnitario: number;
37
+ bonificacion?: number;
38
+ /** Importe de bonificación pre-calculado. Si no se provee, se calcula como precioUnitario * cantidad * (bonificacion / 100) */
39
+ importeBonificacion?: number;
40
+ subtotal: number;
41
+ alicuotaIva?: number;
42
+ }
43
+ /**
44
+ * Detalle de alícuota de IVA
45
+ */
46
+ export interface IvaDetail {
47
+ id: number;
48
+ descripcion: string;
49
+ baseImponible: number;
50
+ importe: number;
51
+ }
52
+ /**
53
+ * Detalle de otros tributos
54
+ */
55
+ export interface TributoDetail {
56
+ id: number;
57
+ descripcion: string;
58
+ baseImponible: number;
59
+ alicuota: number;
60
+ importe: number;
61
+ }
62
+ /**
63
+ * Comprobante asociado
64
+ */
65
+ export interface ComprobanteAsociado {
66
+ tipo: number;
67
+ puntoVenta: number;
68
+ numero: number;
69
+ cuit?: string;
70
+ fecha?: string;
71
+ }
72
+ /**
73
+ * Datos del comprobante electrónico para generar el PDF
74
+ * Independiente del package core - se puede construir manualmente
75
+ * o mapeando datos desde @arcasdk/core
76
+ */
77
+ export interface InvoiceData {
78
+ /** Datos del emisor */
79
+ emisor: EmisorData;
80
+ /** Datos del receptor */
81
+ receptor: ReceptorData;
82
+ /** Tipo de comprobante (ej: 1=Factura A, 6=Factura B, 11=Factura C) */
83
+ cbteTipo: number;
84
+ /** Descripción del tipo de comprobante (ej: "FACTURA") */
85
+ cbteDescripcion?: string;
86
+ /** Letra del comprobante (A, B, C, etc) */
87
+ cbteLetra: string;
88
+ /** Punto de venta */
89
+ puntoVenta: number;
90
+ /** Número de comprobante desde */
91
+ cbteDesde: number;
92
+ /** Número de comprobante hasta */
93
+ cbteHasta: number;
94
+ /** Fecha del comprobante (formato YYYY-MM-DD o YYYYMMDD) */
95
+ cbteFecha: string;
96
+ /** Concepto: 1=Productos, 2=Servicios, 3=Productos y Servicios */
97
+ concepto: number;
98
+ /** Moneda (ej: "PES" para pesos argentinos) */
99
+ moneda?: string;
100
+ /** Cotización de la moneda */
101
+ cotizacion?: number;
102
+ /** Condición de venta (ej: "Contado", "Cuenta Corriente") */
103
+ condicionVenta?: string;
104
+ /** Fecha desde del servicio (para conceptos 2 y 3) */
105
+ fechaServicioDesde?: string;
106
+ /** Fecha hasta del servicio (para conceptos 2 y 3) */
107
+ fechaServicioHasta?: string;
108
+ /** Fecha de vencimiento de pago (para conceptos 2 y 3) */
109
+ fechaVtoPago?: string;
110
+ /** Items de la factura */
111
+ items: InvoiceItem[];
112
+ /** Importe neto gravado */
113
+ importeNetoGravado: number;
114
+ /** Importe neto no gravado */
115
+ importeNetoNoGravado?: number;
116
+ /** Importe exento */
117
+ importeExento?: number;
118
+ /** Detalle de IVA por alícuota */
119
+ iva?: IvaDetail[];
120
+ /** Importe total de IVA */
121
+ importeIva: number;
122
+ /** Detalle de otros tributos */
123
+ tributos?: TributoDetail[];
124
+ /** Importe total de otros tributos */
125
+ importeTributos?: number;
126
+ /** Importe total */
127
+ importeTotal: number;
128
+ /** CAE (Código de Autorización Electrónico) */
129
+ cae: string;
130
+ /** Fecha de vencimiento del CAE (formato YYYY-MM-DD o YYYYMMDD) */
131
+ caeFechaVencimiento: string;
132
+ /** Comprobantes asociados */
133
+ cbtesAsociados?: ComprobanteAsociado[];
134
+ /** Observaciones */
135
+ observaciones?: string;
136
+ /** Divisa con descripción completa (ej: "USD - Dólar Estadounidense") */
137
+ divisa?: string;
138
+ /** País destino del comprobante */
139
+ destinoComprobante?: string;
140
+ /** Forma de pago (ej: "Transferencia SWIFT - Moneda Extranjera") */
141
+ formaPago?: string;
142
+ /** Incoterms */
143
+ incoterms?: string;
144
+ /** Fecha de pago (formato YYYYMMDD) */
145
+ fechaPago?: string;
146
+ /** Leyenda de condición IVA en exportación (ej: "IVA EXENTO OPERACIÓN DE EXPORTACIÓN") */
147
+ condicionIvaExportacion?: string;
148
+ /** Importe total expresado en pesos argentinos (para comprobantes en moneda extranjera).
149
+ * Si no se provee, se calcula como importeTotal * cotizacion */
150
+ importeTotalPesos?: number;
151
+ }
152
+ /**
153
+ * Props recibidas por cualquier template de factura.
154
+ * Implementar este contrato para crear templates personalizados.
155
+ */
156
+ export interface InvoiceTemplateProps {
157
+ data: InvoiceData;
158
+ options: ResolvedPdfOptions;
159
+ /** Data URL del QR code ya generado (undefined si includeQr=false) */
160
+ qrDataUrl?: string;
161
+ }
162
+ /**
163
+ * Opciones resueltas (con defaults aplicados) que recibe el template.
164
+ */
165
+ export type CopyType = "ORIGINAL" | "DUPLICADO" | "TRIPLICADO" | "CUADRUPLICADO";
166
+ export interface ResolvedPdfOptions {
167
+ pageSize: "A4" | "LETTER" | "LEGAL";
168
+ margin: number;
169
+ includeQr: boolean;
170
+ logo?: string;
171
+ logoWidth?: number;
172
+ footerText?: string;
173
+ copy?: CopyType;
174
+ copies?: CopyType[];
175
+ }
176
+ /**
177
+ * Opciones de configuración para la generación del PDF
178
+ */
179
+ export interface PdfGeneratorOptions {
180
+ /** Tamaño de página (default: "A4") */
181
+ pageSize?: "A4" | "LETTER" | "LEGAL";
182
+ /** Márgenes en puntos (default: 10) */
183
+ margin?: number;
184
+ /** Incluir QR code de ARCA (default: true) */
185
+ includeQr?: boolean;
186
+ /** Logo del emisor como data URL (ej: "data:image/png;base64,...") */
187
+ logo?: string;
188
+ /** Ancho del logo en puntos (default: 60) */
189
+ logoWidth?: number;
190
+ /** Texto adicional en el pie de página */
191
+ footerText?: string;
192
+ /** Banner de copia individual: "ORIGINAL" | "DUPLICADO" | "TRIPLICADO" | "CUADRUPLICADO" */
193
+ copy?: CopyType;
194
+ /**
195
+ * Generar múltiples copias en un solo PDF.
196
+ * Cada entrada genera una copia completa con su banner.
197
+ * Si se provee, ignora `copy`.
198
+ *
199
+ * @example
200
+ * copies: ["ORIGINAL", "DUPLICADO", "TRIPLICADO"]
201
+ */
202
+ copies?: CopyType[];
203
+ /**
204
+ * Template personalizado. Puede ser:
205
+ * - Una función que reciba InvoiceTemplateProps y retorne HTML string
206
+ * - Un string con template Handlebars (se compila automáticamente)
207
+ *
208
+ * Los templates Handlebars tienen acceso a helpers integrados:
209
+ * formatDate, formatCuit, formatCurrency, formatUnitPrice,
210
+ * voucherNumber, descTipo, numberToWords, currencyName, etc.
211
+ *
212
+ * @example
213
+ * // Como función
214
+ * const generator = new InvoicePdfGenerator({
215
+ * template: ({ data }) => `<h1>${data.emisor.razonSocial}</h1>`,
216
+ * });
217
+ *
218
+ * @example
219
+ * // Como template Handlebars
220
+ * const generator = new InvoicePdfGenerator({
221
+ * template: "<h1>{{data.emisor.razonSocial}}</h1>",
222
+ * });
223
+ */
224
+ template?: ((props: InvoiceTemplateProps) => string) | string;
225
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=invoice-data.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoice-data.types.js","sourceRoot":"","sources":["../../src/types/invoice-data.types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Formatea una fecha de YYYYMMDD o YYYY-MM-DD a DD/MM/YYYY
3
+ */
4
+ export declare function formatDate(date: string): string;
5
+ /**
6
+ * Formatea un número de comprobante con ceros a la izquierda
7
+ */
8
+ export declare function formatVoucherNumber(puntoVenta: number, numero: number): string;
9
+ /**
10
+ * Formatea un CUIT con guiones: XX-XXXXXXXX-X
11
+ */
12
+ export declare function formatCuit(cuit: string): string;
13
+ /**
14
+ * Formatea un monto como moneda con separador de miles
15
+ * Ejemplo: 1234567.89 -> "$ 1.234.567,89"
16
+ */
17
+ export declare function formatCurrency(amount: number, monedaId?: string): string;
18
+ /**
19
+ * Formatea un número con separador de miles (.) y decimales (,)
20
+ * Formato argentino: 1.234.567,89
21
+ */
22
+ export declare function formatNumber(value: number): string;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatDate = formatDate;
4
+ exports.formatVoucherNumber = formatVoucherNumber;
5
+ exports.formatCuit = formatCuit;
6
+ exports.formatCurrency = formatCurrency;
7
+ exports.formatNumber = formatNumber;
8
+ const voucher_constants_1 = require("../constants/voucher.constants");
9
+ /**
10
+ * Formatea una fecha de YYYYMMDD o YYYY-MM-DD a DD/MM/YYYY
11
+ */
12
+ function formatDate(date) {
13
+ const clean = date.replace(/-/g, "");
14
+ if (clean.length !== 8)
15
+ return date;
16
+ const day = clean.substring(6, 8);
17
+ const month = clean.substring(4, 6);
18
+ const year = clean.substring(0, 4);
19
+ return `${day}/${month}/${year}`;
20
+ }
21
+ /**
22
+ * Formatea un número de comprobante con ceros a la izquierda
23
+ */
24
+ function formatVoucherNumber(puntoVenta, numero) {
25
+ const pv = String(puntoVenta).padStart(5, "0");
26
+ const nro = String(numero).padStart(8, "0");
27
+ return `${pv}-${nro}`;
28
+ }
29
+ /**
30
+ * Formatea un CUIT con guiones: XX-XXXXXXXX-X
31
+ */
32
+ function formatCuit(cuit) {
33
+ const clean = cuit.replace(/-/g, "");
34
+ if (clean.length !== 11)
35
+ return cuit;
36
+ return `${clean.substring(0, 2)}-${clean.substring(2, 10)}-${clean.substring(10)}`;
37
+ }
38
+ /**
39
+ * Formatea un monto como moneda con separador de miles
40
+ * Ejemplo: 1234567.89 -> "$ 1.234.567,89"
41
+ */
42
+ function formatCurrency(amount, monedaId) {
43
+ const symbol = (monedaId && voucher_constants_1.CURRENCY_SYMBOL[monedaId]) || "$";
44
+ const formatted = formatNumber(amount);
45
+ return `${symbol}\u00a0${formatted}`;
46
+ }
47
+ /**
48
+ * Formatea un número con separador de miles (.) y decimales (,)
49
+ * Formato argentino: 1.234.567,89
50
+ */
51
+ function formatNumber(value) {
52
+ const [intPart, decPart] = value.toFixed(2).split(".");
53
+ const withDots = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ".");
54
+ return `${withDots},${decPart}`;
55
+ }
56
+ //# sourceMappingURL=format.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.utils.js","sourceRoot":"","sources":["../../src/utils/format.utils.ts"],"names":[],"mappings":";;AAKA,gCAOC;AAKD,kDAOC;AAKD,gCAIC;AAMD,wCAIC;AAMD,oCAIC;AArDD,wEAAmE;AAEnE;;GAEG;AACH,SAAgB,UAAU,CAAC,IAAY;IACrC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACrC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACnC,OAAO,GAAG,GAAG,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,SAAgB,mBAAmB,CACjC,UAAkB,EAClB,MAAc;IAEd,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5C,OAAO,GAAG,EAAE,IAAI,GAAG,EAAE,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,SAAgB,UAAU,CAAC,IAAY;IACrC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACrC,IAAI,KAAK,CAAC,MAAM,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACrC,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;AACrF,CAAC;AAED;;;GAGG;AACH,SAAgB,cAAc,CAAC,MAAc,EAAE,QAAiB;IAC9D,MAAM,MAAM,GAAG,CAAC,QAAQ,IAAI,mCAAe,CAAC,QAAQ,CAAC,CAAC,IAAI,GAAG,CAAC;IAC9D,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,GAAG,MAAM,SAAS,SAAS,EAAE,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,KAAa;IACxC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC;IAC/D,OAAO,GAAG,QAAQ,IAAI,OAAO,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./format.utils";
2
+ export * from "./qr.utils";
3
+ export * from "./number-to-words.utils";
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./format.utils"), exports);
18
+ __exportStar(require("./qr.utils"), exports);
19
+ __exportStar(require("./number-to-words.utils"), exports);
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iDAA+B;AAC/B,6CAA2B;AAC3B,0DAAwC"}
@@ -0,0 +1 @@
1
+ export declare function numberToWords(n: number): string;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.numberToWords = numberToWords;
4
+ const UNITS = [
5
+ "",
6
+ "un",
7
+ "dos",
8
+ "tres",
9
+ "cuatro",
10
+ "cinco",
11
+ "seis",
12
+ "siete",
13
+ "ocho",
14
+ "nueve",
15
+ "diez",
16
+ "once",
17
+ "doce",
18
+ "trece",
19
+ "catorce",
20
+ "quince",
21
+ "dieciséis",
22
+ "diecisiete",
23
+ "dieciocho",
24
+ "diecinueve",
25
+ "veinte",
26
+ "veintiún",
27
+ "veintidós",
28
+ "veintitrés",
29
+ "veinticuatro",
30
+ "veinticinco",
31
+ "veintiséis",
32
+ "veintisiete",
33
+ "veintiocho",
34
+ "veintinueve",
35
+ ];
36
+ const TENS = [
37
+ "",
38
+ "",
39
+ "",
40
+ "treinta",
41
+ "cuarenta",
42
+ "cincuenta",
43
+ "sesenta",
44
+ "setenta",
45
+ "ochenta",
46
+ "noventa",
47
+ ];
48
+ const HUNDREDS = [
49
+ "",
50
+ "ciento",
51
+ "doscientos",
52
+ "trescientos",
53
+ "cuatrocientos",
54
+ "quinientos",
55
+ "seiscientos",
56
+ "setecientos",
57
+ "ochocientos",
58
+ "novecientos",
59
+ ];
60
+ function intToWords(n) {
61
+ if (n === 0)
62
+ return "cero";
63
+ if (n < 30)
64
+ return UNITS[n];
65
+ if (n < 100) {
66
+ const t = Math.floor(n / 10);
67
+ const u = n % 10;
68
+ return u === 0 ? TENS[t] : `${TENS[t]} y ${UNITS[u]}`;
69
+ }
70
+ if (n === 100)
71
+ return "cien";
72
+ if (n < 1000) {
73
+ const h = Math.floor(n / 100);
74
+ const r = n % 100;
75
+ return r === 0 ? HUNDREDS[h] : `${HUNDREDS[h]} ${intToWords(r)}`;
76
+ }
77
+ if (n < 1000000) {
78
+ const thou = Math.floor(n / 1000);
79
+ const r = n % 1000;
80
+ const s = thou === 1 ? "mil" : `${intToWords(thou)} mil`;
81
+ return r === 0 ? s : `${s} ${intToWords(r)}`;
82
+ }
83
+ if (n < 1000000000) {
84
+ const mill = Math.floor(n / 1000000);
85
+ const r = n % 1000000;
86
+ const s = mill === 1 ? "un millón" : `${intToWords(mill)} millones`;
87
+ return r === 0 ? s : `${s} ${intToWords(r)}`;
88
+ }
89
+ return String(n);
90
+ }
91
+ function numberToWords(n) {
92
+ const cents = Math.round((n % 1) * 100);
93
+ const integer = Math.floor(n);
94
+ const centStr = `${String(cents).padStart(2, "0")}/100`;
95
+ if (integer === 0)
96
+ return `CERO CON ${centStr}`;
97
+ return `${intToWords(integer).toUpperCase()} CON ${centStr}`;
98
+ }
99
+ //# sourceMappingURL=number-to-words.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"number-to-words.utils.js","sourceRoot":"","sources":["../../src/utils/number-to-words.utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { InvoiceData } from "../types/invoice-data.types";
2
+ /**
3
+ * Genera la URL del QR de ARCA para un comprobante electrónico.
4
+ * El QR codifica los datos del comprobante en base64 como parámetro de la URL.
5
+ *
6
+ * @see https://www.afip.gob.ar/fe/qr/especificaciones.asp
7
+ */
8
+ export declare function buildArcaQrUrl(data: InvoiceData): string;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildArcaQrUrl = buildArcaQrUrl;
4
+ const ARCA_QR_BASE_URL = "https://www.afip.gob.ar/fe/qr/";
5
+ /**
6
+ * Genera la URL del QR de ARCA para un comprobante electrónico.
7
+ * El QR codifica los datos del comprobante en base64 como parámetro de la URL.
8
+ *
9
+ * @see https://www.afip.gob.ar/fe/qr/especificaciones.asp
10
+ */
11
+ function buildArcaQrUrl(data) {
12
+ const cuitEmisor = data.emisor.cuit.replace(/-/g, "");
13
+ const cuitReceptor = data.receptor.documentoNro.replace(/-/g, "");
14
+ const fecha = normalizeDate(data.cbteFecha);
15
+ const qrData = {
16
+ ver: 1,
17
+ fecha,
18
+ cuit: Number(cuitEmisor),
19
+ ptoVta: data.puntoVenta,
20
+ tipoCmp: data.cbteTipo,
21
+ nroCmp: data.cbteDesde,
22
+ importe: data.importeTotal,
23
+ moneda: data.moneda || "PES",
24
+ ctz: data.cotizacion || 1,
25
+ tipoDocRec: getDocTipoNumber(data.receptor.documentoTipo),
26
+ nroDocRec: Number(cuitReceptor) || 0,
27
+ tipoCodAut: "E",
28
+ codAut: Number(data.cae),
29
+ };
30
+ const jsonStr = JSON.stringify(qrData);
31
+ const base64 = Buffer.from(jsonStr).toString("base64");
32
+ return `${ARCA_QR_BASE_URL}?p=${base64}`;
33
+ }
34
+ function normalizeDate(date) {
35
+ const clean = date.replace(/-/g, "");
36
+ if (clean.length !== 8)
37
+ return date;
38
+ return `${clean.substring(0, 4)}-${clean.substring(4, 6)}-${clean.substring(6, 8)}`;
39
+ }
40
+ function getDocTipoNumber(docTipo) {
41
+ var _a;
42
+ const mapping = {
43
+ CUIT: 80,
44
+ CUIL: 86,
45
+ CDI: 87,
46
+ DNI: 96,
47
+ LE: 89,
48
+ LC: 90,
49
+ "CI Extranjera": 91,
50
+ Pasaporte: 94,
51
+ "CI Buenos Aires": 0,
52
+ "Sin Identificar": 99,
53
+ };
54
+ return (_a = mapping[docTipo]) !== null && _a !== void 0 ? _a : (Number(docTipo) || 99);
55
+ }
56
+ //# sourceMappingURL=qr.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr.utils.js","sourceRoot":"","sources":["../../src/utils/qr.utils.ts"],"names":[],"mappings":";;AAUA,wCAyBC;AAjCD,MAAM,gBAAgB,GAAG,gCAAgC,CAAC;AAE1D;;;;;GAKG;AACH,SAAgB,cAAc,CAAC,IAAiB;IAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAE5C,MAAM,MAAM,GAAG;QACb,GAAG,EAAE,CAAC;QACN,KAAK;QACL,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC;QACxB,MAAM,EAAE,IAAI,CAAC,UAAU;QACvB,OAAO,EAAE,IAAI,CAAC,QAAQ;QACtB,MAAM,EAAE,IAAI,CAAC,SAAS;QACtB,OAAO,EAAE,IAAI,CAAC,YAAY;QAC1B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;QAC5B,GAAG,EAAE,IAAI,CAAC,UAAU,IAAI,CAAC;QACzB,UAAU,EAAE,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC;QACzD,SAAS,EAAE,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC;QACpC,UAAU,EAAE,GAAG;QACf,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;KACzB,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEvD,OAAO,GAAG,gBAAgB,MAAM,MAAM,EAAE,CAAC;AAC3C,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACrC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AACtF,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAe;;IACvC,MAAM,OAAO,GAA2B;QACtC,IAAI,EAAE,EAAE;QACR,IAAI,EAAE,EAAE;QACR,GAAG,EAAE,EAAE;QACP,GAAG,EAAE,EAAE;QACP,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,eAAe,EAAE,EAAE;QACnB,SAAS,EAAE,EAAE;QACb,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,EAAE;KACtB,CAAC;IACF,OAAO,MAAA,OAAO,CAAC,OAAO,CAAC,mCAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACrD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@arcasdk/pdf",
3
+ "version": "0.2.0",
4
+ "description": "Arca TypeScript SDK - PDF invoice generator for ARCA electronic billing",
5
+ "source": "src/index.ts",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "scripts": {
9
+ "build": "npx tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && rsync -a --include='*/' --include='*.hbs' --exclude='*' src/ lib/",
10
+ "test": "jest --config jest.config.js",
11
+ "test:unit": "jest --config jest.config.js",
12
+ "test:coverage": "jest --config jest.config.js --coverage",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "dependencies": {
16
+ "handlebars": "^4.7.9",
17
+ "html2pdf.js": "^0.14.0",
18
+ "jsdom": "^29.1.1",
19
+ "pdf-lib": "^1.17.1",
20
+ "puppeteer": "^24.43.1",
21
+ "qrcode": "^1.5.4"
22
+ },
23
+ "devDependencies": {
24
+ "@jest/globals": "^30.0.0",
25
+ "@types/handlebars": "^4.0.40",
26
+ "@types/jest": "^30.0.0",
27
+ "@types/node": "^24.10.1",
28
+ "@types/qrcode": "^1.5.5",
29
+ "jest": "^30.0.0",
30
+ "ts-jest": "^29.0.0",
31
+ "ts-node": "^10.9.1",
32
+ "tsc-alias": "^1.8.16",
33
+ "tsconfig-paths": "^4.2.0",
34
+ "typescript": "^6.0.3"
35
+ },
36
+ "author": "valiuLab",
37
+ "license": "ISC",
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/ralcorta/arcasdk.git",
44
+ "directory": "packages/pdf"
45
+ },
46
+ "bugs": {
47
+ "url": "https://github.com/ralcorta/arcasdk/issues"
48
+ },
49
+ "homepage": "https://github.com/ralcorta/arcasdk#readme",
50
+ "files": [
51
+ "lib/**/*"
52
+ ],
53
+ "keywords": [
54
+ "factura-electronica",
55
+ "pdf",
56
+ "arca",
57
+ "afip",
58
+ "invoice",
59
+ "comprobante"
60
+ ]
61
+ }