facturas 0.12.4 → 0.13.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.
- package/README.md +41 -35
- package/dist/{chunk-YYDSBHMO.mjs → chunk-BPBTQDCO.mjs} +16 -173
- package/dist/chunk-BPBTQDCO.mjs.map +1 -0
- package/dist/{chunk-7V5S3ISG.mjs → chunk-KPWAQMR5.mjs} +766 -190
- package/dist/chunk-KPWAQMR5.mjs.map +1 -0
- package/dist/{chunk-GX5ERXT6.mjs → chunk-LQMNKQ2V.mjs} +1 -1
- package/dist/chunk-LQMNKQ2V.mjs.map +1 -0
- package/dist/{chunk-3EJINDBP.mjs → chunk-WN62NMUJ.mjs} +13 -13
- package/dist/chunk-WN62NMUJ.mjs.map +1 -0
- package/dist/cli.d.ts +5 -5
- package/dist/cli.mjs +4 -4
- package/dist/{client-tzfxAy7w.d.ts → client-bblLZeUb.d.ts} +83 -46
- package/dist/constants.d.ts +1 -1
- package/dist/constants.mjs +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/{fiscal-evidence-DXl_U1uz.d.ts → fiscal-evidence-DaCyGd5o.d.ts} +2 -2
- package/dist/{index-CT1ffEAL.d.ts → index-DAC3edtw.d.ts} +1 -1
- package/dist/index.d.ts +6 -7
- package/dist/index.mjs +9 -15
- package/dist/index.mjs.map +1 -1
- package/dist/padron.d.ts +2 -2
- package/dist/{types-SdBQAflw.d.ts → types-Su7QwloQ.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/wsfe.d.ts +255 -31
- package/dist/wsfe.mjs +1 -8
- package/dist/wsmtxca.d.ts +6 -6
- package/dist/wsmtxca.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-3EJINDBP.mjs.map +0 -1
- package/dist/chunk-7V5S3ISG.mjs.map +0 -1
- package/dist/chunk-GX5ERXT6.mjs.map +0 -1
- package/dist/chunk-YYDSBHMO.mjs.map +0 -1
- package/dist/chunk-ZZDL6I56.mjs +0 -169
- package/dist/chunk-ZZDL6I56.mjs.map +0 -1
- package/dist/decimal-CgWMOiAC.d.ts +0 -272
package/README.md
CHANGED
|
@@ -4,14 +4,16 @@
|
|
|
4
4
|
[](https://github.com/LaPyme/facturas/actions/workflows/ci.yml)
|
|
5
5
|
[](https://github.com/LaPyme/facturas/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
-
SDK de Node.js para ARCA / AFIP
|
|
8
|
-
|
|
7
|
+
SDK de Node.js para facturación electrónica con ARCA / AFIP. Emití facturas,
|
|
8
|
+
notas de crédito y notas de débito, y consultá el Padrón con una sola API.
|
|
9
9
|
|
|
10
10
|
- **Solo ESM**, Node.js **>= 20**
|
|
11
11
|
- **CLI incluido**: `npx facturas init` genera la clave y el CSR, y
|
|
12
12
|
`npx facturas check` nombra la capa de ARCA que falla
|
|
13
13
|
- **Integración directa con ARCA**, sin proxy ni dependencia alojada
|
|
14
|
-
- **
|
|
14
|
+
- **Una API de emisión**: el SDK deriva el pedido fiscal y mantiene los detalles
|
|
15
|
+
de cada servicio fuera del flujo normal
|
|
16
|
+
- **Login WSAA resuelto**: caché en memoria, stores de sesión persistentes,
|
|
15
17
|
deduplicación de logins en vuelo y recuperación de `coe.alreadyAuthenticated`
|
|
16
18
|
- **API pública en TypeScript estricto**, con nombres al estilo JS mapeados a
|
|
17
19
|
SOAP internamente
|
|
@@ -20,8 +22,8 @@ integración directa a WSFE y WSMTXCA.
|
|
|
20
22
|
- **Ejemplos copiables**, escritos para que los lean personas y agentes de
|
|
21
23
|
código
|
|
22
24
|
|
|
23
|
-
La documentación está en castellano, en [
|
|
24
|
-
[
|
|
25
|
+
La documentación está en castellano, en el [sitio de documentación](https://facturas-sdk.dev)
|
|
26
|
+
y en su [fuente Mintlify](./docs/index.mdx).
|
|
25
27
|
|
|
26
28
|
## Instalación
|
|
27
29
|
|
|
@@ -33,7 +35,7 @@ npm install facturas
|
|
|
33
35
|
|
|
34
36
|
## Emití tu primera factura
|
|
35
37
|
|
|
36
|
-
Definí las [variables de entorno](./docs/
|
|
38
|
+
Definí las [variables de entorno](./docs/reference/configuration.mdx#variables-de-entorno)
|
|
37
39
|
con tu CUIT, certificado y clave. No hace falta nada más: ni base de datos, ni
|
|
38
40
|
tabla, ni servicio externo.
|
|
39
41
|
|
|
@@ -52,22 +54,22 @@ const factura = await arca.issue({
|
|
|
52
54
|
|
|
53
55
|
Tratá siempre los cuatro resultados:
|
|
54
56
|
|
|
55
|
-
| Resultado |
|
|
57
|
+
| Resultado | Qué significa y qué hacer |
|
|
56
58
|
| --- | --- |
|
|
57
|
-
| `authorized` | Guardá el comprobante y el CAE. `recoveredByMatch: true` significa que el input guardado coincidió con la identidad consultada
|
|
59
|
+
| `authorized` | Guardá el comprobante y el CAE. `recoveredByMatch: true` significa que el input guardado coincidió con la identidad consultada. Esto prueba consistencia, no autoría. |
|
|
58
60
|
| `rejected` | Revisá los `issues` de ARCA. Una clave queda ligada a su input incluso después de un rechazo. |
|
|
59
61
|
| `indeterminate` | Conservá el número y la evidencia. Conciliá o repetí el input idéntico con su clave existente. |
|
|
60
62
|
| `conflict` | Hay otro comprobante en el número reservado. Detené el flujo e investigá. |
|
|
61
63
|
|
|
62
|
-
El paso a paso está en [Inicio rápido](./docs/
|
|
63
|
-
[Facturas](./docs/
|
|
64
|
+
El paso a paso está en [Inicio rápido](./docs/getting-started/quickstart.mdx).
|
|
65
|
+
El detalle está en [Facturas](./docs/guides/invoices.mdx).
|
|
64
66
|
|
|
65
67
|
## Reintentos seguros
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
consulta el número reservado
|
|
69
|
+
ARCA no recibe una clave de idempotencia como Stripe. Si una respuesta se
|
|
70
|
+
pierde, no puede distinguir un reintento de una emisión nueva. `facturas` te
|
|
71
|
+
permite guardar cada intento en un `store` con el ID estable de tu venta. Al
|
|
72
|
+
repetir esa clave, consulta el número reservado en vez de empezar otra emisión.
|
|
71
73
|
|
|
72
74
|
```ts
|
|
73
75
|
import { createArcaClient, createPostgresStore } from "facturas";
|
|
@@ -81,12 +83,13 @@ const factura = await arca.issue(input, { idempotencyKey: venta.id });
|
|
|
81
83
|
```
|
|
82
84
|
|
|
83
85
|
Hay adaptadores para Postgres, Redis, archivos y memoria, y podés escribir el
|
|
84
|
-
tuyo
|
|
85
|
-
serializan y cada una escribe su número. Ver
|
|
86
|
+
tuyo. Con ellos, dos emisiones simultáneas sobre el mismo punto de venta se
|
|
87
|
+
serializan y cada una escribe su número. Ver
|
|
88
|
+
[Evitar comprobantes duplicados](./docs/guides/avoid-duplicates.mdx).
|
|
86
89
|
|
|
87
90
|
## Nota de crédito
|
|
88
91
|
|
|
89
|
-
ARCA no anula comprobantes
|
|
92
|
+
ARCA no anula comprobantes. Una corrección es una nota de crédito y también es
|
|
90
93
|
un documento real que queda en los registros de ARCA. Lo habitual es la nota
|
|
91
94
|
parcial, que acredita las líneas que elegís.
|
|
92
95
|
|
|
@@ -101,31 +104,34 @@ const nota = await arca.issueCreditNote(
|
|
|
101
104
|
```
|
|
102
105
|
|
|
103
106
|
Con `all: true` acreditás el original completo. El modo es explícito y
|
|
104
|
-
obligatorio.
|
|
105
|
-
período](./docs/
|
|
106
|
-
|
|
107
|
+
obligatorio. También podés emitir [notas de débito y por
|
|
108
|
+
período](./docs/guides/credit-notes.mdx), tributos y FCE. `recover()` concilia
|
|
109
|
+
una reserva sin emitir.
|
|
107
110
|
|
|
108
111
|
## Documentación
|
|
109
112
|
|
|
110
|
-
- [Inicio rápido](./docs/
|
|
113
|
+
- [Inicio rápido](./docs/getting-started/quickstart.mdx): de cero a la primera factura y su
|
|
111
114
|
nota de crédito.
|
|
112
|
-
- [Habilitación en ARCA](./docs/
|
|
115
|
+
- [Habilitación en ARCA](./docs/getting-started/arca-setup.mdx): CUIT, certificado, punto
|
|
113
116
|
de venta y referencias oficiales.
|
|
114
|
-
- [CLI](./docs/cli.
|
|
115
|
-
- [Facturas](./docs/
|
|
116
|
-
contrato fiscal de
|
|
117
|
-
- [Notas de crédito](./docs/
|
|
117
|
+
- [CLI](./docs/getting-started/cli.mdx): `init`, `check` e `issue`, con la tabla de diagnósticos.
|
|
118
|
+
- [Facturas](./docs/guides/invoices.mdx): `issue()`, `preview()`, datos de la factura y
|
|
119
|
+
contrato fiscal de emisión.
|
|
120
|
+
- [Notas de crédito](./docs/guides/credit-notes.mdx): `issueCreditNote()`, modo
|
|
118
121
|
parcial y modo total.
|
|
119
|
-
- [
|
|
120
|
-
|
|
121
|
-
- [
|
|
122
|
-
|
|
123
|
-
- [
|
|
124
|
-
|
|
125
|
-
- [
|
|
126
|
-
|
|
122
|
+
- [Evitar comprobantes duplicados](./docs/guides/avoid-duplicates.mdx): claves para
|
|
123
|
+
reintentos, Postgres, Redis, archivos y memoria.
|
|
124
|
+
- [WSMTXCA para casos requeridos](./docs/guides/wsmtxca.mdx): configuración
|
|
125
|
+
avanzada para contribuyentes o puntos de venta que operan con ese servicio.
|
|
126
|
+
- [Configuración](./docs/reference/configuration.mdx): variables de entorno, opciones del
|
|
127
|
+
cliente, sesiones WSAA, logs, reintentos y límites de tiempo.
|
|
128
|
+
- [Módulos de transporte](./docs/reference/arca-services.mdx): `client.wsfe`,
|
|
129
|
+
`client.wsmtxca` y `client.padron` para lecturas, catálogos, estado del
|
|
130
|
+
servicio y Padrón.
|
|
131
|
+
- [Errores](./docs/reference/errors.mdx): clases de error y diagnóstico.
|
|
132
|
+
- [Referencia](./docs/reference/public-api.mdx): constantes, API pública con semver y
|
|
127
133
|
seguridad.
|
|
128
|
-
- [Ejemplos](./docs/
|
|
134
|
+
- [Ejemplos](./docs/reference/examples.mdx): índice de [examples/](./examples).
|
|
129
135
|
|
|
130
136
|
## Estado del proyecto
|
|
131
137
|
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ARCA_VAT_RATES
|
|
3
|
-
} from "./chunk-GX5ERXT6.mjs";
|
|
4
1
|
import {
|
|
5
2
|
classifyArcaAuthenticationError,
|
|
6
3
|
classifyArcaAuthenticationIssues,
|
|
@@ -23,14 +20,6 @@ var PERCENTAGE_SCALE = 100;
|
|
|
23
20
|
var MAX_ARCA_AMOUNT_MINOR_UNITS = 999999999999999n;
|
|
24
21
|
var MAX_ARCA_EXCHANGE_RATE_SCALED = 9999999999n;
|
|
25
22
|
var MAX_ARCA_PERCENTAGE_HUNDREDTHS = 99999n;
|
|
26
|
-
var VAT_RATE_BASIS_POINTS = {
|
|
27
|
-
0: 0n,
|
|
28
|
-
2.5: 250n,
|
|
29
|
-
5: 500n,
|
|
30
|
-
10.5: 1050n,
|
|
31
|
-
21: 2100n,
|
|
32
|
-
27: 2700n
|
|
33
|
-
};
|
|
34
23
|
function normalizeArcaAmountToMinorUnits(value, field) {
|
|
35
24
|
return normalizeScaledNumber({
|
|
36
25
|
value,
|
|
@@ -91,17 +80,6 @@ function arcaMinorUnitsToNumber(minorUnits, field) {
|
|
|
91
80
|
}
|
|
92
81
|
return Number(formatScaledInteger(minorUnits, 2));
|
|
93
82
|
}
|
|
94
|
-
function calculateVatMinorUnits(taxableMinorUnits, vatRate, field) {
|
|
95
|
-
const basisPoints = VAT_RATE_BASIS_POINTS[vatRate];
|
|
96
|
-
if (basisPoints === void 0) {
|
|
97
|
-
throw new ArcaInputError(`${field} is not a supported VAT rate.`, {
|
|
98
|
-
code: "ARCA_INPUT_INVALID_VALUE",
|
|
99
|
-
field,
|
|
100
|
-
expected: "one of 0, 2.5, 5, 10.5, 21, or 27"
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
return roundHalfEvenRatio(taxableMinorUnits * basisPoints, 10000n);
|
|
104
|
-
}
|
|
105
83
|
function roundHalfEvenRatio(numerator, denominator) {
|
|
106
84
|
if (numerator < 0n || denominator <= 0n) {
|
|
107
85
|
throw new RangeError(
|
|
@@ -226,13 +204,13 @@ function createWsfeService(options) {
|
|
|
226
204
|
const auth = await options.auth.login("wsfe", {
|
|
227
205
|
representedTaxId: input.representedTaxId,
|
|
228
206
|
forceRefresh: input.forceRefresh,
|
|
229
|
-
|
|
207
|
+
abortSignal: input.abortSignal
|
|
230
208
|
});
|
|
231
209
|
const response = await options.soap.execute({
|
|
232
210
|
service: "wsfe",
|
|
233
211
|
operation,
|
|
234
212
|
...retries === void 0 ? {} : { retries },
|
|
235
|
-
signal: input.
|
|
213
|
+
signal: input.abortSignal,
|
|
236
214
|
body: {
|
|
237
215
|
Auth: createWsfeAuth(
|
|
238
216
|
input.representedTaxId ?? options.config.taxId,
|
|
@@ -255,7 +233,7 @@ function createWsfeService(options) {
|
|
|
255
233
|
{
|
|
256
234
|
representedTaxId: input.representedTaxId,
|
|
257
235
|
forceRefresh,
|
|
258
|
-
|
|
236
|
+
abortSignal: input.abortSignal
|
|
259
237
|
},
|
|
260
238
|
body
|
|
261
239
|
);
|
|
@@ -279,14 +257,14 @@ function createWsfeService(options) {
|
|
|
279
257
|
salesPoint,
|
|
280
258
|
voucherType,
|
|
281
259
|
forceRefresh,
|
|
282
|
-
|
|
260
|
+
abortSignal
|
|
283
261
|
}) {
|
|
284
262
|
const result = await executeWsfeAuthenticatedOperation(
|
|
285
263
|
"FECompUltimoAutorizado",
|
|
286
264
|
{
|
|
287
265
|
representedTaxId,
|
|
288
266
|
forceRefresh,
|
|
289
|
-
|
|
267
|
+
abortSignal
|
|
290
268
|
},
|
|
291
269
|
{
|
|
292
270
|
PtoVta: salesPoint,
|
|
@@ -307,7 +285,7 @@ function createWsfeService(options) {
|
|
|
307
285
|
data,
|
|
308
286
|
voucherNumber,
|
|
309
287
|
forceRefresh,
|
|
310
|
-
|
|
288
|
+
abortSignal
|
|
311
289
|
}) {
|
|
312
290
|
const normalizedInput = normalizeWsfeVoucherInput(data);
|
|
313
291
|
return executeWsfeAuthorization({
|
|
@@ -315,7 +293,7 @@ function createWsfeService(options) {
|
|
|
315
293
|
data: normalizedInput,
|
|
316
294
|
voucherNumber,
|
|
317
295
|
forceRefresh,
|
|
318
|
-
|
|
296
|
+
abortSignal
|
|
319
297
|
}).then(({ outcome }) => outcome);
|
|
320
298
|
}
|
|
321
299
|
async function executeWsfeAuthorization({
|
|
@@ -323,13 +301,13 @@ function createWsfeService(options) {
|
|
|
323
301
|
data: normalizedInput,
|
|
324
302
|
voucherNumber,
|
|
325
303
|
forceRefresh,
|
|
326
|
-
|
|
304
|
+
abortSignal
|
|
327
305
|
}) {
|
|
328
306
|
const requestData = mapWsfeVoucherInput(normalizedInput, voucherNumber);
|
|
329
307
|
try {
|
|
330
308
|
const result = await executeWsfeAuthenticatedRawOperation(
|
|
331
309
|
"FECAESolicitar",
|
|
332
|
-
{ representedTaxId, forceRefresh,
|
|
310
|
+
{ representedTaxId, forceRefresh, abortSignal },
|
|
333
311
|
{
|
|
334
312
|
FeCAEReq: {
|
|
335
313
|
FeCabReq: {
|
|
@@ -360,7 +338,7 @@ function createWsfeService(options) {
|
|
|
360
338
|
salesPoint,
|
|
361
339
|
voucherType,
|
|
362
340
|
forceRefresh,
|
|
363
|
-
|
|
341
|
+
abortSignal
|
|
364
342
|
}) {
|
|
365
343
|
return executeWithAuthenticationRecovery({
|
|
366
344
|
service: "wsfe",
|
|
@@ -372,7 +350,7 @@ function createWsfeService(options) {
|
|
|
372
350
|
salesPoint,
|
|
373
351
|
voucherType,
|
|
374
352
|
forceRefresh: attemptForceRefresh,
|
|
375
|
-
|
|
353
|
+
abortSignal
|
|
376
354
|
})
|
|
377
355
|
});
|
|
378
356
|
}
|
|
@@ -382,12 +360,12 @@ function createWsfeService(options) {
|
|
|
382
360
|
salesPoint,
|
|
383
361
|
voucherType,
|
|
384
362
|
forceRefresh,
|
|
385
|
-
|
|
363
|
+
abortSignal
|
|
386
364
|
}) {
|
|
387
365
|
const operation = "FECompConsultar";
|
|
388
366
|
const result = await executeWsfeAuthenticatedRawOperation(
|
|
389
367
|
operation,
|
|
390
|
-
{ representedTaxId, forceRefresh,
|
|
368
|
+
{ representedTaxId, forceRefresh, abortSignal },
|
|
391
369
|
{
|
|
392
370
|
FeCompConsReq: {
|
|
393
371
|
CbteNro: number,
|
|
@@ -1462,150 +1440,15 @@ function getWsfeResultEntries(result, key) {
|
|
|
1462
1440
|
);
|
|
1463
1441
|
}
|
|
1464
1442
|
|
|
1465
|
-
// src/services/wsfe-amounts.ts
|
|
1466
|
-
var RATES = {
|
|
1467
|
-
0: { id: ARCA_VAT_RATES.IVA_0, basisPoints: 0n },
|
|
1468
|
-
2.5: { id: ARCA_VAT_RATES.IVA_2_5, basisPoints: 250n },
|
|
1469
|
-
5: { id: ARCA_VAT_RATES.IVA_5, basisPoints: 500n },
|
|
1470
|
-
10.5: { id: ARCA_VAT_RATES.IVA_10_5, basisPoints: 1050n },
|
|
1471
|
-
21: { id: ARCA_VAT_RATES.IVA_21, basisPoints: 2100n },
|
|
1472
|
-
27: { id: ARCA_VAT_RATES.IVA_27, basisPoints: 2700n }
|
|
1473
|
-
};
|
|
1474
|
-
function calculateWsfeAmounts(input) {
|
|
1475
|
-
if (!Array.isArray(input.items) || input.items.length === 0) {
|
|
1476
|
-
invalidItem("items", "a non-empty array of items");
|
|
1477
|
-
}
|
|
1478
|
-
const isVat = input.voucherClass === "A" || input.voucherClass === "B";
|
|
1479
|
-
if (!(isVat || input.voucherClass === "C")) {
|
|
1480
|
-
invalidItem("voucherClass", "A, B or C");
|
|
1481
|
-
}
|
|
1482
|
-
const totals = collectItems(input.items, isVat);
|
|
1483
|
-
let net = totals.net;
|
|
1484
|
-
let vat = 0n;
|
|
1485
|
-
const { exempt, untaxed, groups } = totals;
|
|
1486
|
-
const vatRates = [];
|
|
1487
|
-
for (const [rate, group] of groups) {
|
|
1488
|
-
const { id, basisPoints } = RATES[rate];
|
|
1489
|
-
const netFromGross = roundHalfEvenRatio(
|
|
1490
|
-
group.gross * 10000n,
|
|
1491
|
-
10000n + basisPoints
|
|
1492
|
-
);
|
|
1493
|
-
const base = group.net + netFromGross;
|
|
1494
|
-
const tax = roundHalfEvenRatio(group.net * basisPoints, 10000n) + group.gross - netFromGross;
|
|
1495
|
-
if (base === 0n) {
|
|
1496
|
-
continue;
|
|
1497
|
-
}
|
|
1498
|
-
net += base;
|
|
1499
|
-
vat += tax;
|
|
1500
|
-
vatRates.push({
|
|
1501
|
-
id,
|
|
1502
|
-
baseAmount: arcaMinorUnitsToNumber(base, "netAmount"),
|
|
1503
|
-
amount: arcaMinorUnitsToNumber(tax, "vatAmount")
|
|
1504
|
-
});
|
|
1505
|
-
}
|
|
1506
|
-
const computed = net + vat + exempt + untaxed;
|
|
1507
|
-
arcaMinorUnitsToNumber(computed, "totalAmount");
|
|
1508
|
-
const sent = input.total === void 0 ? computed : assertArcaMinorUnits(input.total, "total");
|
|
1509
|
-
const adjustment = sent - computed;
|
|
1510
|
-
const allowance = BigInt(vatRates.length);
|
|
1511
|
-
if (adjustment < -allowance || adjustment > allowance || vat + adjustment < 0n) {
|
|
1512
|
-
throw new ArcaInputError(
|
|
1513
|
-
"total does not match the computed amount within the VAT allowance.",
|
|
1514
|
-
{
|
|
1515
|
-
code: "ARCA_INPUT_AMOUNT_MISMATCH",
|
|
1516
|
-
field: "total",
|
|
1517
|
-
expected: `${computed} minor units (at most ${allowance} minor units of VAT adjustment, with non-negative VAT)`
|
|
1518
|
-
}
|
|
1519
|
-
);
|
|
1520
|
-
}
|
|
1521
|
-
return {
|
|
1522
|
-
data: {
|
|
1523
|
-
totalAmount: arcaMinorUnitsToNumber(sent, "totalAmount"),
|
|
1524
|
-
netAmount: arcaMinorUnitsToNumber(net, "netAmount"),
|
|
1525
|
-
vatAmount: arcaMinorUnitsToNumber(vat + adjustment, "vatAmount"),
|
|
1526
|
-
nonTaxableAmount: arcaMinorUnitsToNumber(untaxed, "nonTaxableAmount"),
|
|
1527
|
-
exemptAmount: arcaMinorUnitsToNumber(exempt, "exemptAmount"),
|
|
1528
|
-
taxAmount: 0,
|
|
1529
|
-
...isVat ? { vatRates } : {}
|
|
1530
|
-
},
|
|
1531
|
-
amounts: {
|
|
1532
|
-
computedTotal: Number(computed),
|
|
1533
|
-
sentTotal: Number(sent),
|
|
1534
|
-
vatAdjustment: Number(adjustment)
|
|
1535
|
-
}
|
|
1536
|
-
};
|
|
1537
|
-
}
|
|
1538
|
-
function collectItems(items, isVat) {
|
|
1539
|
-
let net = 0n;
|
|
1540
|
-
let exempt = 0n;
|
|
1541
|
-
let untaxed = 0n;
|
|
1542
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1543
|
-
for (const [index, item] of items.entries()) {
|
|
1544
|
-
const path = `items[${index}]`;
|
|
1545
|
-
if (item === null || typeof item !== "object" || Array.isArray(item)) {
|
|
1546
|
-
invalidItem(path, "an item object");
|
|
1547
|
-
}
|
|
1548
|
-
if (!isVat) {
|
|
1549
|
-
net += classCAmount(item, path);
|
|
1550
|
-
continue;
|
|
1551
|
-
}
|
|
1552
|
-
const { amount, field, rate } = vatItemAmount(item, path);
|
|
1553
|
-
if (rate === "exempt") {
|
|
1554
|
-
exempt += amount;
|
|
1555
|
-
continue;
|
|
1556
|
-
}
|
|
1557
|
-
if (rate === "untaxed") {
|
|
1558
|
-
untaxed += amount;
|
|
1559
|
-
continue;
|
|
1560
|
-
}
|
|
1561
|
-
const group = groups.get(rate) ?? { net: 0n, gross: 0n };
|
|
1562
|
-
group[field] += amount;
|
|
1563
|
-
groups.set(rate, group);
|
|
1564
|
-
}
|
|
1565
|
-
return { net, exempt, untaxed, groups };
|
|
1566
|
-
}
|
|
1567
|
-
function classCAmount(item, path) {
|
|
1568
|
-
if ("vat" in item || "net" in item || "gross" in item) {
|
|
1569
|
-
invalidItem("items", "amount items for a class C voucher");
|
|
1570
|
-
}
|
|
1571
|
-
return assertArcaMinorUnits(item.amount, `${path}.amount`);
|
|
1572
|
-
}
|
|
1573
|
-
function vatItemAmount(item, path) {
|
|
1574
|
-
if ("amount" in item || "net" in item === "gross" in item) {
|
|
1575
|
-
invalidItem(
|
|
1576
|
-
"items",
|
|
1577
|
-
"exactly one of net or gross, and vat, for a class A or B voucher"
|
|
1578
|
-
);
|
|
1579
|
-
}
|
|
1580
|
-
const field = "net" in item ? "net" : "gross";
|
|
1581
|
-
const amount = assertArcaMinorUnits(
|
|
1582
|
-
item[field],
|
|
1583
|
-
`${path}.${field}`
|
|
1584
|
-
);
|
|
1585
|
-
const rate = item.vat;
|
|
1586
|
-
if (rate !== "exempt" && rate !== "untaxed" && (typeof rate !== "number" || !Object.hasOwn(RATES, rate))) {
|
|
1587
|
-
invalidItem(`${path}.vat`, "0, 2.5, 5, 10.5, 21, 27, exempt, or untaxed");
|
|
1588
|
-
}
|
|
1589
|
-
return { amount, field, rate };
|
|
1590
|
-
}
|
|
1591
|
-
function invalidItem(field, expected) {
|
|
1592
|
-
throw new ArcaInputError(`${field} must be ${expected}.`, {
|
|
1593
|
-
code: "ARCA_INPUT_INVALID_VALUE",
|
|
1594
|
-
field,
|
|
1595
|
-
expected
|
|
1596
|
-
});
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
1443
|
export {
|
|
1600
1444
|
normalizeArcaAmountToMinorUnits,
|
|
1601
1445
|
serializeArcaExchangeRate,
|
|
1602
1446
|
assertArcaMinorUnits,
|
|
1603
1447
|
arcaMinorUnitsToNumber,
|
|
1604
|
-
|
|
1448
|
+
roundHalfEvenRatio,
|
|
1605
1449
|
isWithinArcaTolerance,
|
|
1606
1450
|
createWsfeService,
|
|
1607
1451
|
normalizeWsfeVoucherInput,
|
|
1608
|
-
normalizeWsfeDateInput
|
|
1609
|
-
calculateWsfeAmounts
|
|
1452
|
+
normalizeWsfeDateInput
|
|
1610
1453
|
};
|
|
1611
|
-
//# sourceMappingURL=chunk-
|
|
1454
|
+
//# sourceMappingURL=chunk-BPBTQDCO.mjs.map
|