facturas 0.20.5 → 0.22.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 +4 -2
- package/dist/{chunk-C2KX652X.mjs → chunk-5CGIRDZY.mjs} +5 -2
- package/dist/chunk-5CGIRDZY.mjs.map +1 -0
- package/dist/{chunk-VEKUMXHW.mjs → chunk-67ISWKGE.mjs} +5 -4
- package/dist/{chunk-B2M64SIK.mjs → chunk-7MNVZSW6.mjs} +13 -4
- package/dist/chunk-7MNVZSW6.mjs.map +1 -0
- package/dist/{chunk-UOIZQMA6.mjs → chunk-FSJHE4VS.mjs} +3 -3
- package/dist/{chunk-EXJMG54W.mjs → chunk-GQGYO2Z2.mjs} +4 -4
- package/dist/{chunk-YPQJRSTA.mjs → chunk-HLSFMWRT.mjs} +2 -2
- package/dist/{chunk-NT2A6YMO.mjs → chunk-Q7IURW54.mjs} +12 -1
- package/dist/chunk-Q7IURW54.mjs.map +1 -0
- package/dist/{chunk-KBEV7QTU.mjs → chunk-R4MOVPYT.mjs} +2848 -297
- package/dist/chunk-R4MOVPYT.mjs.map +1 -0
- package/dist/cli.d.ts +4 -4
- package/dist/cli.mjs +11 -11
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-BgWH_jKT.d.ts → client-ghHzZdu8.d.ts} +613 -73
- package/dist/constants.d.ts +3 -0
- package/dist/constants.mjs +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.mjs +3 -1
- package/dist/{fiscal-evidence-Bg4o9nxt.d.ts → fiscal-evidence-DjG8zB3o.d.ts} +21 -5
- package/dist/{index-DAC3edtw.d.ts → index-BSTxy3nN.d.ts} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.mjs +35 -16
- package/dist/index.mjs.map +1 -1
- package/dist/padron.d.ts +2 -2
- package/dist/padron.mjs +3 -3
- package/dist/{types-Su7QwloQ.d.ts → types-CbvsjIhf.d.ts} +14 -5
- package/dist/types.d.ts +1 -1
- package/dist/wsfe.d.ts +3 -3
- package/dist/wsfe.mjs +4 -4
- package/dist/wsmtxca.d.ts +3 -3
- package/dist/wsmtxca.mjs +4 -4
- package/package.json +1 -1
- package/dist/chunk-B2M64SIK.mjs.map +0 -1
- package/dist/chunk-C2KX652X.mjs.map +0 -1
- package/dist/chunk-KBEV7QTU.mjs.map +0 -1
- package/dist/chunk-NT2A6YMO.mjs.map +0 -1
- /package/dist/{chunk-VEKUMXHW.mjs.map → chunk-67ISWKGE.mjs.map} +0 -0
- /package/dist/{chunk-UOIZQMA6.mjs.map → chunk-FSJHE4VS.mjs.map} +0 -0
- /package/dist/{chunk-EXJMG54W.mjs.map → chunk-GQGYO2Z2.mjs.map} +0 -0
- /package/dist/{chunk-YPQJRSTA.mjs.map → chunk-HLSFMWRT.mjs.map} +0 -0
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
ARCA_RECEIVER_CONDITION_IDS,
|
|
8
8
|
ARCA_VAT_RATES,
|
|
9
9
|
ARCA_VOUCHER_TYPES
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-5CGIRDZY.mjs";
|
|
11
11
|
import {
|
|
12
12
|
createPadronService
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-FSJHE4VS.mjs";
|
|
14
14
|
import {
|
|
15
15
|
arcaMinorUnitsToNumber,
|
|
16
16
|
assertArcaMinorUnits,
|
|
@@ -20,24 +20,32 @@ import {
|
|
|
20
20
|
normalizeWsfeDateInput,
|
|
21
21
|
normalizeWsfeVoucherInput,
|
|
22
22
|
roundHalfEvenRatio,
|
|
23
|
+
serializeArcaAmount,
|
|
23
24
|
serializeArcaExchangeRate,
|
|
24
25
|
toIsoDate
|
|
25
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-67ISWKGE.mjs";
|
|
26
27
|
import {
|
|
27
28
|
createWsmtxcaService
|
|
28
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-GQGYO2Z2.mjs";
|
|
30
|
+
import {
|
|
31
|
+
classifyArcaAuthenticationError,
|
|
32
|
+
classifyArcaAuthenticationIssues,
|
|
33
|
+
createArcaAuthenticationEvidence,
|
|
34
|
+
executeWithAuthenticationRecovery
|
|
35
|
+
} from "./chunk-7MNVZSW6.mjs";
|
|
29
36
|
import {
|
|
30
37
|
ArcaConfigurationError,
|
|
31
38
|
ArcaError,
|
|
32
39
|
ArcaInputError,
|
|
33
40
|
ArcaInvalidSoapResponseError,
|
|
41
|
+
ArcaLockTimeoutError,
|
|
34
42
|
ArcaServiceError,
|
|
35
43
|
ArcaSoapFaultError,
|
|
36
44
|
ArcaTransportError,
|
|
37
45
|
createResponseBodyDiagnostic,
|
|
38
46
|
createSafeErrorDiagnostic,
|
|
39
47
|
toArcaSafeErrorMetadata
|
|
40
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-Q7IURW54.mjs";
|
|
41
49
|
|
|
42
50
|
// src/config.ts
|
|
43
51
|
var ARCA_ENVIRONMENTS = ["production", "test"];
|
|
@@ -154,6 +162,16 @@ var ARCA_SERVICE_CONFIG = {
|
|
|
154
162
|
soapActionBase: "http://ar.gov.afip.dif.FEV1/",
|
|
155
163
|
useLegacyTlsSecurityLevel0: true
|
|
156
164
|
},
|
|
165
|
+
wsfex: {
|
|
166
|
+
namespace: "http://ar.gov.afip.dif.fexv1/",
|
|
167
|
+
endpoint: {
|
|
168
|
+
production: "https://servicios1.afip.gov.ar/wsfexv1/service.asmx",
|
|
169
|
+
test: "https://wswhomo.afip.gov.ar/wsfexv1/service.asmx"
|
|
170
|
+
},
|
|
171
|
+
soapVersion: "1.1",
|
|
172
|
+
soapActionBase: "http://ar.gov.afip.dif.fexv1/",
|
|
173
|
+
useLegacyTlsSecurityLevel0: true
|
|
174
|
+
},
|
|
157
175
|
wsmtxca: {
|
|
158
176
|
namespace: "http://impl.service.wsmtxca.afip.gov.ar/service/",
|
|
159
177
|
endpoint: {
|
|
@@ -302,7 +320,16 @@ function voucherFamily(type) {
|
|
|
302
320
|
});
|
|
303
321
|
}
|
|
304
322
|
var KINDS = ["invoice", "debit_note", "credit_note"];
|
|
323
|
+
var EXPORT_TYPES = [19, 20, 21];
|
|
305
324
|
function describeVoucherType(voucherType) {
|
|
325
|
+
const exportIndex = EXPORT_TYPES.indexOf(voucherType);
|
|
326
|
+
if (exportIndex !== -1) {
|
|
327
|
+
return {
|
|
328
|
+
family: "export",
|
|
329
|
+
voucherClass: "E",
|
|
330
|
+
kind: KINDS[exportIndex]
|
|
331
|
+
};
|
|
332
|
+
}
|
|
306
333
|
const { family, voucherClass, types } = voucherFamily(voucherType);
|
|
307
334
|
return {
|
|
308
335
|
family,
|
|
@@ -698,20 +725,17 @@ function receiverDocument(document) {
|
|
|
698
725
|
invalid("document.type", "an ARCA document type");
|
|
699
726
|
}
|
|
700
727
|
const number = digits(document.number, "document.number", 0, 20);
|
|
701
|
-
if (document.type === 99 || number === 0) {
|
|
702
|
-
return {};
|
|
703
|
-
}
|
|
704
728
|
return { tipoDocRec: document.type, nroDocRec: number };
|
|
705
729
|
}
|
|
706
730
|
function digits(value, field, min, max) {
|
|
707
|
-
const
|
|
708
|
-
if (!/^\d*$/.test(
|
|
731
|
+
const text2 = String(value).trim();
|
|
732
|
+
if (!/^\d*$/.test(text2) || text2.length < min || text2.length > max) {
|
|
709
733
|
invalid(field, min === max ? `${min} digits` : `${min} to ${max} digits`);
|
|
710
734
|
}
|
|
711
|
-
if (
|
|
735
|
+
if (text2 === "") {
|
|
712
736
|
return 0;
|
|
713
737
|
}
|
|
714
|
-
const parsed = Number(
|
|
738
|
+
const parsed = Number(text2);
|
|
715
739
|
if (!Number.isSafeInteger(parsed)) {
|
|
716
740
|
invalid(field, "a safe integer");
|
|
717
741
|
}
|
|
@@ -1174,10 +1198,10 @@ function deriveWsfeInvoice(input, now = /* @__PURE__ */ new Date()) {
|
|
|
1174
1198
|
const headerTotal = Number(
|
|
1175
1199
|
normalizeArcaAmountToMinorUnits(data.netAmount, "net") + normalizeArcaAmountToMinorUnits(data.vatAmount, "vat") + normalizeArcaAmountToMinorUnits(data.exemptAmount, "exempt") + normalizeArcaAmountToMinorUnits(data.nonTaxableAmount, "untaxed") + normalizeArcaAmountToMinorUnits(data.taxAmount, "tax")
|
|
1176
1200
|
);
|
|
1177
|
-
const
|
|
1178
|
-
data.totalAmount =
|
|
1201
|
+
const reviewedTotal2 = input.amounts ? input.total : void 0;
|
|
1202
|
+
data.totalAmount = reviewedTotal2 === void 0 ? headerTotal / 100 : minor(reviewedTotal2, "total");
|
|
1179
1203
|
amounts.computedTotal += headerTotal - amounts.sentTotal;
|
|
1180
|
-
amounts.sentTotal =
|
|
1204
|
+
amounts.sentTotal = reviewedTotal2 ?? headerTotal;
|
|
1181
1205
|
if (receiver.receiverVatConditionId === 5 && receiver.documentType === ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL) {
|
|
1182
1206
|
const [whole, fraction = ""] = currency.exchangeRate.split(".");
|
|
1183
1207
|
const rate = BigInt(whole) * 1000000n + BigInt(fraction.padEnd(6, "0"));
|
|
@@ -1289,11 +1313,11 @@ function issueDocumentNumber(value, field, min, max) {
|
|
|
1289
1313
|
if (typeof value !== "number" && typeof value !== "string") {
|
|
1290
1314
|
invalid2(field, `a positive document number with ${min} to ${max} digits`);
|
|
1291
1315
|
}
|
|
1292
|
-
const
|
|
1293
|
-
if (!/^\d+$/.test(
|
|
1316
|
+
const text2 = String(value);
|
|
1317
|
+
if (!/^\d+$/.test(text2) || text2.length < min || text2.length > max || !Number.isSafeInteger(Number(text2)) || Number(text2) <= 0) {
|
|
1294
1318
|
invalid2(field, `a positive document number with ${min} to ${max} digits`);
|
|
1295
1319
|
}
|
|
1296
|
-
return Number(
|
|
1320
|
+
return Number(text2);
|
|
1297
1321
|
}
|
|
1298
1322
|
function deriveCurrency(input) {
|
|
1299
1323
|
const currency = input.currency === void 0 ? "ARS" : input.currency;
|
|
@@ -1406,6 +1430,16 @@ function voucherDateWindow(input) {
|
|
|
1406
1430
|
if (!(now instanceof Date) || Number.isNaN(now.getTime())) {
|
|
1407
1431
|
invalid2("now", "a valid Date");
|
|
1408
1432
|
}
|
|
1433
|
+
if (EXPORT_TYPES.includes(input.voucherType)) {
|
|
1434
|
+
if (input.service !== void 0) {
|
|
1435
|
+
invalid2("service", "absent: export vouchers always use WSFEX");
|
|
1436
|
+
}
|
|
1437
|
+
const { from: from2, to: to2 } = exportDateWindow(
|
|
1438
|
+
concept === "services",
|
|
1439
|
+
buenosAiresDate(now)
|
|
1440
|
+
);
|
|
1441
|
+
return { from: isoDay(from2), to: isoDay(to2) };
|
|
1442
|
+
}
|
|
1409
1443
|
voucherFamily(input.voucherType);
|
|
1410
1444
|
const { from, to } = dateWindow(
|
|
1411
1445
|
{ concept: CONCEPT_IDS[concept], voucherType: input.voucherType },
|
|
@@ -1433,18 +1467,32 @@ function dateWindow(data, service, today) {
|
|
|
1433
1467
|
function assertVoucherDateWindow(data, service, today) {
|
|
1434
1468
|
const { from, to } = dateWindow(data, service, today);
|
|
1435
1469
|
if (data.voucherDate < from || data.voucherDate > to) {
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
window
|
|
1444
|
-
}
|
|
1445
|
-
);
|
|
1470
|
+
throwOutsideWindow(from, to, today);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
function assertExportDateWindow(voucherDate, services, today) {
|
|
1474
|
+
const { from, to } = exportDateWindow(services, today);
|
|
1475
|
+
if (voucherDate < from || voucherDate > to) {
|
|
1476
|
+
throwOutsideWindow(from, to, today);
|
|
1446
1477
|
}
|
|
1447
1478
|
}
|
|
1479
|
+
function exportDateWindow(services, today) {
|
|
1480
|
+
const from = addDays(today, -5);
|
|
1481
|
+
const to = addDays(today, 5);
|
|
1482
|
+
return { from, to: services ? minDate(to, endOfMonth(today)) : to };
|
|
1483
|
+
}
|
|
1484
|
+
function throwOutsideWindow(from, to, today) {
|
|
1485
|
+
const window = { from: isoDay(from), to: isoDay(to) };
|
|
1486
|
+
throw new ArcaInputError(
|
|
1487
|
+
`date must be from ${window.from} through ${window.to}, the window ARCA accepts on ${isoDay(today)}.`,
|
|
1488
|
+
{
|
|
1489
|
+
code: "ARCA_INPUT_DATE_OUTSIDE_WINDOW",
|
|
1490
|
+
field: "date",
|
|
1491
|
+
expected: `a date from ${window.from} through ${window.to}`,
|
|
1492
|
+
window
|
|
1493
|
+
}
|
|
1494
|
+
);
|
|
1495
|
+
}
|
|
1448
1496
|
function isoDay(date) {
|
|
1449
1497
|
return toIsoDate(date) ?? date;
|
|
1450
1498
|
}
|
|
@@ -1545,74 +1593,1138 @@ function deriveNumericReceiver(to) {
|
|
|
1545
1593
|
};
|
|
1546
1594
|
}
|
|
1547
1595
|
|
|
1548
|
-
// src/
|
|
1549
|
-
|
|
1550
|
-
function
|
|
1551
|
-
|
|
1596
|
+
// src/services/wsfex.ts
|
|
1597
|
+
function createWsfexService(options) {
|
|
1598
|
+
async function executeWsfexRawOperation(operation, input, body = {}, authFields = {}) {
|
|
1599
|
+
const auth2 = await options.auth.login("wsfex", {
|
|
1600
|
+
representedTaxId: input.representedTaxId,
|
|
1601
|
+
forceRefresh: input.forceRefresh,
|
|
1602
|
+
abortSignal: input.abortSignal
|
|
1603
|
+
});
|
|
1604
|
+
const response = await options.soap.execute({
|
|
1605
|
+
service: "wsfex",
|
|
1606
|
+
operation,
|
|
1607
|
+
signal: input.abortSignal,
|
|
1608
|
+
body: {
|
|
1609
|
+
Auth: {
|
|
1610
|
+
...createWsfexAuth(
|
|
1611
|
+
input.representedTaxId ?? options.config.taxId,
|
|
1612
|
+
auth2.token,
|
|
1613
|
+
auth2.sign
|
|
1614
|
+
),
|
|
1615
|
+
...authFields
|
|
1616
|
+
},
|
|
1617
|
+
...body
|
|
1618
|
+
}
|
|
1619
|
+
});
|
|
1620
|
+
return unwrapWsfexOperationEnvelope(operation, response.result);
|
|
1621
|
+
}
|
|
1622
|
+
function executeWsfexOperation(operation, input, body = {}, authFields = {}) {
|
|
1623
|
+
return executeWithAuthenticationRecovery({
|
|
1624
|
+
service: "wsfex",
|
|
1625
|
+
operation,
|
|
1626
|
+
forceRefresh: input.forceRefresh,
|
|
1627
|
+
async execute(forceRefresh) {
|
|
1628
|
+
const result = await executeWsfexRawOperation(
|
|
1629
|
+
operation,
|
|
1630
|
+
{
|
|
1631
|
+
representedTaxId: input.representedTaxId,
|
|
1632
|
+
forceRefresh,
|
|
1633
|
+
abortSignal: input.abortSignal
|
|
1634
|
+
},
|
|
1635
|
+
body,
|
|
1636
|
+
authFields
|
|
1637
|
+
);
|
|
1638
|
+
throwForWsfexOperationError(operation, result);
|
|
1639
|
+
return result;
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
async function getWsfexResultRows(operation, rowKey, input) {
|
|
1644
|
+
const result = await executeWsfexOperation(operation, {
|
|
1645
|
+
representedTaxId: input.representedTaxId,
|
|
1646
|
+
forceRefresh: input.forceRefresh
|
|
1647
|
+
});
|
|
1648
|
+
return getWsfexRows(result.FEXResultGet, rowKey);
|
|
1649
|
+
}
|
|
1650
|
+
async function getWsfexCatalog(operation, rowKey, fields, input) {
|
|
1651
|
+
const rows2 = await getWsfexResultRows(operation, rowKey, input);
|
|
1652
|
+
return rows2.flatMap((row) => {
|
|
1653
|
+
const id = readWsfexText(row[fields.id]);
|
|
1654
|
+
return id === void 0 ? [] : [{ id, description: readWsfexText(row[fields.description]) ?? "" }];
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1657
|
+
function issue(input) {
|
|
1658
|
+
const request = createWsfexRequest(input);
|
|
1659
|
+
return executeWsfexAuthorization(input, request);
|
|
1660
|
+
}
|
|
1661
|
+
async function executeWsfexAuthorization(input, request) {
|
|
1662
|
+
try {
|
|
1663
|
+
const result = await executeWsfexRawOperation(
|
|
1664
|
+
"FEXAuthorize",
|
|
1665
|
+
{
|
|
1666
|
+
representedTaxId: input.representedTaxId,
|
|
1667
|
+
forceRefresh: input.forceRefresh,
|
|
1668
|
+
abortSignal: input.abortSignal
|
|
1669
|
+
},
|
|
1670
|
+
{ Cmp: request }
|
|
1671
|
+
);
|
|
1672
|
+
return classifyWsfexAuthorization(result);
|
|
1673
|
+
} catch (error) {
|
|
1674
|
+
return createWsfexIndeterminateOutcome(error);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
function lookupVoucher2(input) {
|
|
1678
|
+
return executeWithAuthenticationRecovery({
|
|
1679
|
+
service: "wsfex",
|
|
1680
|
+
operation: "FEXGetCMP",
|
|
1681
|
+
forceRefresh: input.forceRefresh,
|
|
1682
|
+
execute: (forceRefresh) => lookupVoucherOnce({ ...input, forceRefresh })
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
async function lookupVoucherOnce(input) {
|
|
1686
|
+
const operation = "FEXGetCMP";
|
|
1687
|
+
const result = await executeWsfexRawOperation(operation, input, {
|
|
1688
|
+
// ClsFEXGetCMP spells it `Cbte_tipo`; the manual's `Cbte_Tipo` would
|
|
1689
|
+
// be dropped by the server and consult type 0.
|
|
1690
|
+
Cmp: {
|
|
1691
|
+
Cbte_tipo: input.voucherType,
|
|
1692
|
+
Punto_vta: input.salesPoint,
|
|
1693
|
+
Cbte_nro: input.number
|
|
1694
|
+
}
|
|
1695
|
+
});
|
|
1696
|
+
const errors = extractWsfexErrors(result, operation);
|
|
1697
|
+
if (errors.length > 0 && errors.every((issue2) => issue2.code === "1020")) {
|
|
1698
|
+
return {
|
|
1699
|
+
kind: "not_found",
|
|
1700
|
+
service: "wsfex",
|
|
1701
|
+
operation,
|
|
1702
|
+
errors,
|
|
1703
|
+
observations: [],
|
|
1704
|
+
raw: result
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
if (errors.length > 0) {
|
|
1708
|
+
throw createWsfexServiceError(operation, errors);
|
|
1709
|
+
}
|
|
1710
|
+
const raw = toWsfexRecord(result.FEXResultGet);
|
|
1711
|
+
if (!raw) {
|
|
1712
|
+
throw new ArcaServiceError("WSFEX did not return the consulted voucher", {
|
|
1713
|
+
service: "wsfex",
|
|
1714
|
+
operation
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
return {
|
|
1718
|
+
kind: "found",
|
|
1719
|
+
service: "wsfex",
|
|
1720
|
+
operation,
|
|
1721
|
+
voucher: mapWsfexVoucherInfo(raw),
|
|
1722
|
+
observations: extractWsfexReasons(
|
|
1723
|
+
raw.Motivos_Obs,
|
|
1724
|
+
operation,
|
|
1725
|
+
"observation"
|
|
1726
|
+
),
|
|
1727
|
+
raw: result
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
return {
|
|
1731
|
+
issue,
|
|
1732
|
+
async getLastRequestId(input = {}) {
|
|
1733
|
+
const operation = "FEXGetLast_ID";
|
|
1734
|
+
const result = await executeWsfexOperation(operation, input);
|
|
1735
|
+
return parseWsfexInteger(toWsfexRecord(result.FEXResultGet)?.Id, {
|
|
1736
|
+
operation,
|
|
1737
|
+
field: "Id",
|
|
1738
|
+
min: 0,
|
|
1739
|
+
max: WSFEX_MAX_REQUEST_ID
|
|
1740
|
+
});
|
|
1741
|
+
},
|
|
1742
|
+
async getLastVoucherNumber({ salesPoint, voucherType, ...input }) {
|
|
1743
|
+
const operation = "FEXGetLast_CMP";
|
|
1744
|
+
const result = await executeWsfexOperation(
|
|
1745
|
+
operation,
|
|
1746
|
+
input,
|
|
1747
|
+
{},
|
|
1748
|
+
{ Pto_venta: salesPoint, Cbte_Tipo: voucherType }
|
|
1749
|
+
);
|
|
1750
|
+
return parseWsfexInteger(
|
|
1751
|
+
toWsfexRecord(result.FEXResult_LastCMP)?.Cbte_nro,
|
|
1752
|
+
{ operation, field: "Cbte_nro", min: 0, max: 99999999 }
|
|
1753
|
+
);
|
|
1754
|
+
},
|
|
1755
|
+
lookupVoucher: lookupVoucher2,
|
|
1756
|
+
async getSalesPoints(input = {}) {
|
|
1757
|
+
const rows2 = await getWsfexResultRows(
|
|
1758
|
+
"FEXGetPARAM_PtoVenta",
|
|
1759
|
+
"ClsFEXResponse_PtoVenta",
|
|
1760
|
+
input
|
|
1761
|
+
);
|
|
1762
|
+
return rows2.map(mapWsfexSalesPoint);
|
|
1763
|
+
},
|
|
1764
|
+
getCountries(input = {}) {
|
|
1765
|
+
return getWsfexCatalog(
|
|
1766
|
+
"FEXGetPARAM_DST_pais",
|
|
1767
|
+
"ClsFEXResponse_DST_pais",
|
|
1768
|
+
{ id: "DST_Codigo", description: "DST_Ds" },
|
|
1769
|
+
input
|
|
1770
|
+
);
|
|
1771
|
+
},
|
|
1772
|
+
async getCountryTaxIds(input = {}) {
|
|
1773
|
+
const rows2 = await getWsfexResultRows(
|
|
1774
|
+
"FEXGetPARAM_DST_CUIT",
|
|
1775
|
+
"ClsFEXResponse_DST_cuit",
|
|
1776
|
+
input
|
|
1777
|
+
);
|
|
1778
|
+
return rows2.flatMap((row) => {
|
|
1779
|
+
const taxId = readWsfexText(row.DST_CUIT);
|
|
1780
|
+
return taxId === void 0 ? [] : [{ taxId, description: readWsfexText(row.DST_Ds) ?? "" }];
|
|
1781
|
+
});
|
|
1782
|
+
},
|
|
1783
|
+
getCurrencies(input = {}) {
|
|
1784
|
+
return getWsfexCatalog(
|
|
1785
|
+
"FEXGetPARAM_MON",
|
|
1786
|
+
"ClsFEXResponse_Mon",
|
|
1787
|
+
{ id: "Mon_Id", description: "Mon_Ds" },
|
|
1788
|
+
input
|
|
1789
|
+
);
|
|
1790
|
+
},
|
|
1791
|
+
getIncoterms(input = {}) {
|
|
1792
|
+
return getWsfexCatalog(
|
|
1793
|
+
"FEXGetPARAM_Incoterms",
|
|
1794
|
+
"ClsFEXResponse_Inc",
|
|
1795
|
+
{ id: "Inc_Id", description: "Inc_Ds" },
|
|
1796
|
+
input
|
|
1797
|
+
);
|
|
1798
|
+
},
|
|
1799
|
+
getUnits(input = {}) {
|
|
1800
|
+
return getWsfexCatalog(
|
|
1801
|
+
"FEXGetPARAM_UMed",
|
|
1802
|
+
"ClsFEXResponse_UMed",
|
|
1803
|
+
{ id: "Umed_Id", description: "Umed_Ds" },
|
|
1804
|
+
input
|
|
1805
|
+
);
|
|
1806
|
+
},
|
|
1807
|
+
getLanguages(input = {}) {
|
|
1808
|
+
return getWsfexCatalog(
|
|
1809
|
+
"FEXGetPARAM_Idiomas",
|
|
1810
|
+
"ClsFEXResponse_Idi",
|
|
1811
|
+
{ id: "Idi_Id", description: "Idi_Ds" },
|
|
1812
|
+
input
|
|
1813
|
+
);
|
|
1814
|
+
},
|
|
1815
|
+
getVoucherTypes(input = {}) {
|
|
1816
|
+
return getWsfexCatalog(
|
|
1817
|
+
"FEXGetPARAM_Cbte_Tipo",
|
|
1818
|
+
"ClsFEXResponse_Cbte_Tipo",
|
|
1819
|
+
{ id: "Cbte_Id", description: "Cbte_Ds" },
|
|
1820
|
+
input
|
|
1821
|
+
);
|
|
1822
|
+
},
|
|
1823
|
+
getExportTypes(input = {}) {
|
|
1824
|
+
return getWsfexCatalog(
|
|
1825
|
+
"FEXGetPARAM_Tipo_Expo",
|
|
1826
|
+
"ClsFEXResponse_Tex",
|
|
1827
|
+
{ id: "Tex_Id", description: "Tex_Ds" },
|
|
1828
|
+
input
|
|
1829
|
+
);
|
|
1830
|
+
},
|
|
1831
|
+
async getActivities(input = {}) {
|
|
1832
|
+
const rows2 = await getWsfexResultRows(
|
|
1833
|
+
"FEXGetPARAM_Actividades",
|
|
1834
|
+
"ClsFEXResponse_ActividadTipo",
|
|
1835
|
+
input
|
|
1836
|
+
);
|
|
1837
|
+
return rows2.flatMap((row) => {
|
|
1838
|
+
const id = toWsfexActivityId(row.Id);
|
|
1839
|
+
return id === void 0 ? [] : [{ id, description: readWsfexText(row.Desc) ?? "" }];
|
|
1840
|
+
});
|
|
1841
|
+
},
|
|
1842
|
+
async getExchangeRate({ currencyId, date, ...input }) {
|
|
1843
|
+
const operation = "FEXGetPARAM_Ctz";
|
|
1844
|
+
const quotationDate = date === void 0 ? void 0 : toIsoDate(normalizeWsfexDate(date, "date"));
|
|
1845
|
+
const result = await executeWsfexOperation(operation, input, {
|
|
1846
|
+
Mon_id: currencyId,
|
|
1847
|
+
...optionalWsfexField("FchCotiz", quotationDate)
|
|
1848
|
+
});
|
|
1849
|
+
const raw = toWsfexRecord(result.FEXResultGet);
|
|
1850
|
+
const rate = canonicalizeWsfexDecimal(raw?.Mon_ctz);
|
|
1851
|
+
const rateDate = toIsoDate(readWsfexText(raw?.Mon_fecha));
|
|
1852
|
+
if (rate === void 0 || rateDate === void 0) {
|
|
1853
|
+
throw new ArcaInvalidSoapResponseError("Invalid WSFEX exchange rate", {
|
|
1854
|
+
service: "wsfex",
|
|
1855
|
+
operation
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
return { currencyId, rate, date: rateDate };
|
|
1859
|
+
},
|
|
1860
|
+
async checkPermit({ id, destination, ...input }) {
|
|
1861
|
+
const operation = "FEXCheck_Permiso";
|
|
1862
|
+
const result = await executeWsfexOperation(operation, input, {
|
|
1863
|
+
ID_Permiso: id,
|
|
1864
|
+
Dst_merc: destination
|
|
1865
|
+
});
|
|
1866
|
+
const status = readWsfexText(
|
|
1867
|
+
toWsfexRecord(result.FEXResultGet)?.Status
|
|
1868
|
+
)?.toUpperCase();
|
|
1869
|
+
if (status === "OK" || status === "NO") {
|
|
1870
|
+
return status === "OK";
|
|
1871
|
+
}
|
|
1872
|
+
throw new ArcaInvalidSoapResponseError("Invalid WSFEX permit status", {
|
|
1873
|
+
service: "wsfex",
|
|
1874
|
+
operation
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
};
|
|
1552
1878
|
}
|
|
1553
|
-
|
|
1554
|
-
|
|
1879
|
+
var WSFEX_MAX_REQUEST_ID = 999999999999999;
|
|
1880
|
+
var WSFEX_VOUCHER_TYPES = [19, 20, 21];
|
|
1881
|
+
var WSFEX_EXPORT_TYPES = [1, 2, 4];
|
|
1882
|
+
var WSFEX_LANGUAGES = [1, 2, 3];
|
|
1883
|
+
var WSFEX_ITEM_DECIMAL_PATTERN = /^(\d{1,12})(?:\.(\d{1,6}))?$/;
|
|
1884
|
+
var WSFEX_TAX_ID_PATTERN = /^\d{11}$/;
|
|
1885
|
+
function assertWsfexVoucherInput(voucher) {
|
|
1886
|
+
createWsfexRequest({ ...voucher, id: 0, number: 1 });
|
|
1555
1887
|
}
|
|
1556
|
-
function
|
|
1557
|
-
return
|
|
1888
|
+
function createWsfexRequest(input) {
|
|
1889
|
+
return {
|
|
1890
|
+
Id: assertWsfexInteger(input.id, "id", 0, WSFEX_MAX_REQUEST_ID),
|
|
1891
|
+
...optionalWsfexField(
|
|
1892
|
+
"Fecha_cbte",
|
|
1893
|
+
normalizeOptionalWsfexDate(input.voucherDate, "voucherDate")
|
|
1894
|
+
),
|
|
1895
|
+
Cbte_Tipo: assertWsfexCode(
|
|
1896
|
+
input.voucherType,
|
|
1897
|
+
"voucherType",
|
|
1898
|
+
WSFEX_VOUCHER_TYPES
|
|
1899
|
+
),
|
|
1900
|
+
Punto_vta: assertWsfexInteger(input.salesPoint, "salesPoint", 1, 99999),
|
|
1901
|
+
Cbte_nro: assertWsfexInteger(input.number, "number", 1, 99999999),
|
|
1902
|
+
Tipo_expo: assertWsfexCode(
|
|
1903
|
+
input.exportType,
|
|
1904
|
+
"exportType",
|
|
1905
|
+
WSFEX_EXPORT_TYPES
|
|
1906
|
+
),
|
|
1907
|
+
// ARCA expects the element present and empty for notes and services.
|
|
1908
|
+
Permiso_existente: assertOptionalWsfexFlag(input.permitExists, "permitExists") ?? "",
|
|
1909
|
+
...optionalWsfexField(
|
|
1910
|
+
"Permisos",
|
|
1911
|
+
createWsfexList(input.permits, "Permiso", createWsfexPermit)
|
|
1912
|
+
),
|
|
1913
|
+
Dst_cmp: assertWsfexInteger(input.destination, "destination", 1, 999),
|
|
1914
|
+
Cliente: requireWsfexText(input.receiverName, "receiverName"),
|
|
1915
|
+
...optionalWsfexField(
|
|
1916
|
+
"Cuit_pais_cliente",
|
|
1917
|
+
assertOptionalWsfexTaxId(
|
|
1918
|
+
input.receiverCountryTaxId,
|
|
1919
|
+
"receiverCountryTaxId"
|
|
1920
|
+
)
|
|
1921
|
+
),
|
|
1922
|
+
Domicilio_cliente: requireWsfexText(
|
|
1923
|
+
input.receiverAddress,
|
|
1924
|
+
"receiverAddress"
|
|
1925
|
+
),
|
|
1926
|
+
...optionalWsfexField(
|
|
1927
|
+
"Id_impositivo",
|
|
1928
|
+
optionalWsfexText(input.receiverTaxId, "receiverTaxId")
|
|
1929
|
+
),
|
|
1930
|
+
Moneda_Id: requireWsfexText(input.currencyId, "currencyId"),
|
|
1931
|
+
...optionalWsfexField(
|
|
1932
|
+
"Moneda_ctz",
|
|
1933
|
+
input.exchangeRate === void 0 ? void 0 : serializeArcaExchangeRate(input.exchangeRate, "exchangeRate")
|
|
1934
|
+
),
|
|
1935
|
+
...optionalWsfexField(
|
|
1936
|
+
"CanMisMonExt",
|
|
1937
|
+
assertOptionalWsfexFlag(
|
|
1938
|
+
input.sameCurrencyForeignCancellation,
|
|
1939
|
+
"sameCurrencyForeignCancellation"
|
|
1940
|
+
)
|
|
1941
|
+
),
|
|
1942
|
+
...optionalWsfexField(
|
|
1943
|
+
"Obs_comerciales",
|
|
1944
|
+
optionalWsfexText(input.commercialObservations, "commercialObservations")
|
|
1945
|
+
),
|
|
1946
|
+
Imp_total: serializeArcaAmount(input.totalAmount, "totalAmount"),
|
|
1947
|
+
...optionalWsfexField(
|
|
1948
|
+
"Obs",
|
|
1949
|
+
optionalWsfexText(input.observations, "observations")
|
|
1950
|
+
),
|
|
1951
|
+
...optionalWsfexField(
|
|
1952
|
+
"Cmps_asoc",
|
|
1953
|
+
createWsfexList(
|
|
1954
|
+
input.associatedVouchers,
|
|
1955
|
+
"Cmp_asoc",
|
|
1956
|
+
createWsfexAssociatedVoucher
|
|
1957
|
+
)
|
|
1958
|
+
),
|
|
1959
|
+
...optionalWsfexField(
|
|
1960
|
+
"Forma_pago",
|
|
1961
|
+
optionalWsfexText(input.paymentTerms, "paymentTerms")
|
|
1962
|
+
),
|
|
1963
|
+
...optionalWsfexField(
|
|
1964
|
+
"Incoterms",
|
|
1965
|
+
optionalWsfexText(input.incoterms, "incoterms")
|
|
1966
|
+
),
|
|
1967
|
+
...optionalWsfexField(
|
|
1968
|
+
"Incoterms_Ds",
|
|
1969
|
+
optionalWsfexText(input.incotermsDetail, "incotermsDetail")
|
|
1970
|
+
),
|
|
1971
|
+
Idioma_cbte: assertWsfexCode(input.language, "language", WSFEX_LANGUAGES),
|
|
1972
|
+
Items: createWsfexItems(input.items),
|
|
1973
|
+
...optionalWsfexField(
|
|
1974
|
+
"Fecha_pago",
|
|
1975
|
+
normalizeOptionalWsfexDate(input.paymentDate, "paymentDate")
|
|
1976
|
+
),
|
|
1977
|
+
...optionalWsfexField(
|
|
1978
|
+
"Actividades",
|
|
1979
|
+
createWsfexList(input.activities, "Actividad", createWsfexActivity)
|
|
1980
|
+
)
|
|
1981
|
+
};
|
|
1558
1982
|
}
|
|
1559
|
-
function
|
|
1560
|
-
|
|
1983
|
+
function createWsfexList(rows2, key, map) {
|
|
1984
|
+
if (rows2 === void 0 || rows2.length === 0) {
|
|
1985
|
+
return void 0;
|
|
1986
|
+
}
|
|
1987
|
+
return { [key]: rows2.map(map) };
|
|
1561
1988
|
}
|
|
1562
|
-
function
|
|
1563
|
-
return
|
|
1989
|
+
function createWsfexPermit(permit, index) {
|
|
1990
|
+
return {
|
|
1991
|
+
Id_permiso: requireWsfexText(permit.id, `permits[${index}].id`),
|
|
1992
|
+
Dst_merc: assertWsfexInteger(
|
|
1993
|
+
permit.destination,
|
|
1994
|
+
`permits[${index}].destination`,
|
|
1995
|
+
1,
|
|
1996
|
+
999
|
|
1997
|
+
)
|
|
1998
|
+
};
|
|
1564
1999
|
}
|
|
1565
|
-
function
|
|
1566
|
-
|
|
1567
|
-
|
|
2000
|
+
function createWsfexAssociatedVoucher(voucher, index) {
|
|
2001
|
+
const field = `associatedVouchers[${index}]`;
|
|
2002
|
+
return {
|
|
2003
|
+
Cbte_tipo: assertWsfexInteger(
|
|
2004
|
+
voucher.voucherType,
|
|
2005
|
+
`${field}.voucherType`,
|
|
2006
|
+
1,
|
|
2007
|
+
999
|
|
2008
|
+
),
|
|
2009
|
+
Cbte_punto_vta: assertWsfexInteger(
|
|
2010
|
+
voucher.salesPoint,
|
|
2011
|
+
`${field}.salesPoint`,
|
|
2012
|
+
1,
|
|
2013
|
+
99999
|
|
2014
|
+
),
|
|
2015
|
+
Cbte_nro: assertWsfexInteger(
|
|
2016
|
+
voucher.number,
|
|
2017
|
+
`${field}.number`,
|
|
2018
|
+
1,
|
|
2019
|
+
999999999
|
|
2020
|
+
),
|
|
2021
|
+
...optionalWsfexField(
|
|
2022
|
+
"Cbte_cuit",
|
|
2023
|
+
assertOptionalWsfexTaxId(voucher.issuerTaxId, `${field}.issuerTaxId`)
|
|
2024
|
+
)
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
function createWsfexItems(items) {
|
|
2028
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
2029
|
+
throw new ArcaInputError("items must contain at least one item.", {
|
|
2030
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
2031
|
+
field: "items",
|
|
2032
|
+
expected: "from 1 to 9999 items"
|
|
2033
|
+
});
|
|
1568
2034
|
}
|
|
1569
|
-
|
|
1570
|
-
|
|
2035
|
+
return { Item: items.map(createWsfexItem) };
|
|
2036
|
+
}
|
|
2037
|
+
function createWsfexItem(item, index) {
|
|
2038
|
+
const field = `items[${index}]`;
|
|
2039
|
+
return {
|
|
2040
|
+
...optionalWsfexField(
|
|
2041
|
+
"Pro_codigo",
|
|
2042
|
+
optionalWsfexText(item.code, `${field}.code`)
|
|
2043
|
+
),
|
|
2044
|
+
Pro_ds: requireWsfexText(item.description, `${field}.description`),
|
|
2045
|
+
...optionalWsfexField(
|
|
2046
|
+
"Pro_qty",
|
|
2047
|
+
serializeWsfexItemDecimal(item.quantity, `${field}.quantity`)
|
|
2048
|
+
),
|
|
2049
|
+
Pro_umed: assertWsfexInteger(item.unit, `${field}.unit`, 0, 99),
|
|
2050
|
+
...optionalWsfexField(
|
|
2051
|
+
"Pro_precio_uni",
|
|
2052
|
+
serializeWsfexItemDecimal(item.unitPrice, `${field}.unitPrice`)
|
|
2053
|
+
),
|
|
2054
|
+
...optionalWsfexField(
|
|
2055
|
+
"Pro_bonificacion",
|
|
2056
|
+
serializeWsfexItemDecimal(item.discount, `${field}.discount`)
|
|
2057
|
+
),
|
|
2058
|
+
Pro_total_item: serializeWsfexSignedAmount(item.amount, `${field}.amount`)
|
|
2059
|
+
};
|
|
2060
|
+
}
|
|
2061
|
+
function createWsfexActivity(activity, index) {
|
|
2062
|
+
const field = `activities[${index}].id`;
|
|
2063
|
+
if (typeof activity.id !== "string" || !/^\d{1,6}$/.test(activity.id)) {
|
|
2064
|
+
throw new ArcaInputError(`${field} must be an activity code.`, {
|
|
2065
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
2066
|
+
field,
|
|
2067
|
+
expected: "a code of up to 6 digits from getActivities()"
|
|
2068
|
+
});
|
|
1571
2069
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
2070
|
+
return { Id: Number.parseInt(activity.id, 10) };
|
|
2071
|
+
}
|
|
2072
|
+
function serializeWsfexSignedAmount(value, field) {
|
|
2073
|
+
return typeof value === "number" && value < 0 ? `-${serializeArcaAmount(-value, field)}` : serializeArcaAmount(value, field);
|
|
2074
|
+
}
|
|
2075
|
+
function serializeWsfexItemDecimal(value, field) {
|
|
2076
|
+
if (value === void 0) {
|
|
2077
|
+
return void 0;
|
|
2078
|
+
}
|
|
2079
|
+
const text2 = typeof value === "number" && Number.isFinite(value) ? String(value) : value;
|
|
2080
|
+
const match = typeof text2 === "string" ? WSFEX_ITEM_DECIMAL_PATTERN.exec(text2.trim()) : null;
|
|
2081
|
+
if (!match) {
|
|
2082
|
+
throw new ArcaInputError(
|
|
2083
|
+
`${field} must be a non-negative decimal with at most 12 integer and 6 fractional digits.`,
|
|
2084
|
+
{
|
|
2085
|
+
code: "ARCA_INPUT_INVALID_AMOUNT",
|
|
2086
|
+
field,
|
|
2087
|
+
expected: "a non-negative decimal with up to 12 integer and 6 fractional digits"
|
|
2088
|
+
}
|
|
1575
2089
|
);
|
|
1576
2090
|
}
|
|
1577
|
-
return
|
|
2091
|
+
return formatWsfexDecimal(match[1] ?? "0", match[2]);
|
|
1578
2092
|
}
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
2093
|
+
function formatWsfexDecimal(integerPart, fractionPart = "") {
|
|
2094
|
+
const integer = integerPart.replace(/^0+(?=\d)/, "");
|
|
2095
|
+
const fraction = fractionPart.replace(/0+$/, "");
|
|
2096
|
+
return fraction ? `${integer}.${fraction}` : integer;
|
|
2097
|
+
}
|
|
2098
|
+
function canonicalizeWsfexDecimal(value) {
|
|
2099
|
+
const match = /^(\d+)(?:\.(\d+))?$/.exec(readWsfexText(value) ?? "");
|
|
2100
|
+
return match ? formatWsfexDecimal(match[1] ?? "0", match[2]) : void 0;
|
|
2101
|
+
}
|
|
2102
|
+
function optionalWsfexField(key, value) {
|
|
2103
|
+
return value === void 0 ? {} : { [key]: value };
|
|
2104
|
+
}
|
|
2105
|
+
function assertWsfexInteger(value, field, min, max) {
|
|
2106
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= min && value <= max) {
|
|
2107
|
+
return value;
|
|
1584
2108
|
}
|
|
2109
|
+
return throwWsfexInvalidInput(
|
|
2110
|
+
value,
|
|
2111
|
+
field,
|
|
2112
|
+
`an integer from ${min} to ${max}`
|
|
2113
|
+
);
|
|
1585
2114
|
}
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
2115
|
+
function assertWsfexCode(value, field, allowed) {
|
|
2116
|
+
if (allowed.includes(value)) {
|
|
2117
|
+
return value;
|
|
2118
|
+
}
|
|
2119
|
+
return throwWsfexInvalidInput(value, field, `one of ${allowed.join(", ")}`);
|
|
2120
|
+
}
|
|
2121
|
+
function assertOptionalWsfexFlag(value, field) {
|
|
2122
|
+
if (value === void 0 || value === "S" || value === "N") {
|
|
2123
|
+
return value;
|
|
2124
|
+
}
|
|
2125
|
+
return throwWsfexInvalidInput(value, field, "S or N");
|
|
2126
|
+
}
|
|
2127
|
+
function assertOptionalWsfexTaxId(value, field) {
|
|
2128
|
+
if (value === void 0 || typeof value === "string" && WSFEX_TAX_ID_PATTERN.test(value)) {
|
|
2129
|
+
return value;
|
|
2130
|
+
}
|
|
2131
|
+
return throwWsfexInvalidInput(value, field, "an 11-digit CUIT string");
|
|
2132
|
+
}
|
|
2133
|
+
function requireWsfexText(value, field) {
|
|
2134
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
2135
|
+
return value;
|
|
2136
|
+
}
|
|
2137
|
+
return throwWsfexInvalidInput(value, field, "a non-empty string");
|
|
2138
|
+
}
|
|
2139
|
+
function optionalWsfexText(value, field) {
|
|
2140
|
+
if (value === void 0 || typeof value === "string") {
|
|
2141
|
+
return value;
|
|
2142
|
+
}
|
|
2143
|
+
return throwWsfexInvalidInput(value, field, "a string");
|
|
2144
|
+
}
|
|
2145
|
+
function throwWsfexInvalidInput(value, field, expected) {
|
|
2146
|
+
throw new ArcaInputError(
|
|
2147
|
+
value === void 0 ? `${field} is required.` : `${field} must be ${expected}.`,
|
|
2148
|
+
{
|
|
2149
|
+
code: value === void 0 ? "ARCA_INPUT_MISSING_FIELD" : "ARCA_INPUT_INVALID_VALUE",
|
|
2150
|
+
field,
|
|
2151
|
+
expected
|
|
1594
2152
|
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
2153
|
+
);
|
|
2154
|
+
}
|
|
2155
|
+
function normalizeOptionalWsfexDate(value, field) {
|
|
2156
|
+
return value === void 0 ? void 0 : normalizeWsfexDate(value, field);
|
|
2157
|
+
}
|
|
2158
|
+
function normalizeWsfexDate(value, field) {
|
|
2159
|
+
const text2 = typeof value === "string" ? value.trim() : "";
|
|
2160
|
+
const [, year = "", month = "", day = ""] = /^(\d{4})(\d{2})(\d{2})$/.exec(text2) ?? /^(\d{4})-(\d{2})-(\d{2})$/.exec(text2) ?? [];
|
|
2161
|
+
const candidate = new Date(
|
|
2162
|
+
Date.UTC(Number(year), Number(month) - 1, Number(day))
|
|
2163
|
+
);
|
|
2164
|
+
if (year !== "" && candidate.getUTCFullYear() === Number(year) && candidate.getUTCMonth() === Number(month) - 1 && candidate.getUTCDate() === Number(day)) {
|
|
2165
|
+
return `${year}${month}${day}`;
|
|
2166
|
+
}
|
|
2167
|
+
throw new ArcaInputError(
|
|
2168
|
+
`Invalid WSFEX ${field}: expected an existing YYYYMMDD or YYYY-MM-DD date`,
|
|
2169
|
+
{
|
|
2170
|
+
code: "ARCA_INPUT_INVALID_DATE",
|
|
2171
|
+
field,
|
|
2172
|
+
expected: "an existing YYYYMMDD or YYYY-MM-DD calendar date"
|
|
1607
2173
|
}
|
|
1608
|
-
|
|
2174
|
+
);
|
|
2175
|
+
}
|
|
2176
|
+
function createWsfexAuth(representedTaxId, token, sign) {
|
|
2177
|
+
return {
|
|
2178
|
+
Token: token,
|
|
2179
|
+
Sign: sign,
|
|
2180
|
+
Cuit: Number.parseInt(String(representedTaxId), 10)
|
|
1609
2181
|
};
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
2182
|
+
}
|
|
2183
|
+
function unwrapWsfexOperationEnvelope(operation, response) {
|
|
2184
|
+
const operationResponse = response[`${operation}Response`];
|
|
2185
|
+
const result = operationResponse?.[`${operation}Result`] ?? response[`${operation}Result`] ?? response;
|
|
2186
|
+
return result;
|
|
2187
|
+
}
|
|
2188
|
+
function throwForWsfexOperationError(operation, result) {
|
|
2189
|
+
const errors = extractWsfexErrors(result, operation);
|
|
2190
|
+
if (errors.length > 0) {
|
|
2191
|
+
throw createWsfexServiceError(operation, errors);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
function createWsfexServiceError(operation, issues) {
|
|
2195
|
+
const authenticationError = classifyArcaAuthenticationIssues(issues, {
|
|
2196
|
+
service: "wsfex",
|
|
2197
|
+
operation
|
|
2198
|
+
});
|
|
2199
|
+
if (authenticationError) {
|
|
2200
|
+
return authenticationError;
|
|
2201
|
+
}
|
|
2202
|
+
const firstIssue = issues[0];
|
|
2203
|
+
return new ArcaServiceError(
|
|
2204
|
+
firstIssue ? formatWsfexIssue(firstIssue) : "WSFEX returned a service error",
|
|
2205
|
+
{
|
|
2206
|
+
service: "wsfex",
|
|
2207
|
+
operation,
|
|
2208
|
+
...firstIssue?.code === void 0 ? {} : { serviceCode: firstIssue.code },
|
|
2209
|
+
issues
|
|
2210
|
+
}
|
|
2211
|
+
);
|
|
2212
|
+
}
|
|
2213
|
+
function formatWsfexIssue(issue) {
|
|
2214
|
+
return issue.code ? `(${issue.code}) ${issue.message}` : issue.message;
|
|
2215
|
+
}
|
|
2216
|
+
function extractWsfexErrors(result, operation, resultLevel) {
|
|
2217
|
+
const error = toWsfexRecord(result.FEXErr);
|
|
2218
|
+
const code = readWsfexText(error?.ErrCode);
|
|
2219
|
+
if (code === void 0 || /^0+$/.test(code)) {
|
|
2220
|
+
return [];
|
|
2221
|
+
}
|
|
2222
|
+
return [
|
|
2223
|
+
{
|
|
2224
|
+
service: "wsfex",
|
|
2225
|
+
operation,
|
|
2226
|
+
source: "error",
|
|
2227
|
+
category: getWsfexErrorCategory(operation, code),
|
|
2228
|
+
code,
|
|
2229
|
+
message: readWsfexText(error?.ErrMsg) ?? readWsfexText(error?.Errmsg) ?? "Unknown WSFEX error",
|
|
2230
|
+
...resultLevel === void 0 ? {} : { resultLevel }
|
|
2231
|
+
}
|
|
2232
|
+
];
|
|
2233
|
+
}
|
|
2234
|
+
function getWsfexErrorCategory(operation, code) {
|
|
2235
|
+
if (operation !== "FEXAuthorize") {
|
|
2236
|
+
return "unknown";
|
|
2237
|
+
}
|
|
2238
|
+
return WSFEX_AUTHORIZATION_INFRASTRUCTURE_CODES.has(code) ? "infrastructure" : "business";
|
|
2239
|
+
}
|
|
2240
|
+
var WSFEX_AUTHORIZATION_INFRASTRUCTURE_CODES = /* @__PURE__ */ new Set([
|
|
2241
|
+
"500",
|
|
2242
|
+
"501",
|
|
2243
|
+
"502",
|
|
2244
|
+
"505",
|
|
2245
|
+
"1000",
|
|
2246
|
+
"1001"
|
|
2247
|
+
]);
|
|
2248
|
+
function extractWsfexReasons(value, operation, category) {
|
|
2249
|
+
const text2 = readWsfexText(value);
|
|
2250
|
+
if (text2 === void 0) {
|
|
2251
|
+
return [];
|
|
2252
|
+
}
|
|
2253
|
+
const base = {
|
|
2254
|
+
service: "wsfex",
|
|
2255
|
+
operation,
|
|
2256
|
+
source: "observation",
|
|
2257
|
+
category,
|
|
2258
|
+
resultLevel: "header"
|
|
2259
|
+
};
|
|
2260
|
+
if (!/^\d+(?:\s*[,;|\s]\s*\d+)*$/.test(text2)) {
|
|
2261
|
+
return [{ ...base, message: text2 }];
|
|
2262
|
+
}
|
|
2263
|
+
return text2.split(/\D+/).filter(Boolean).map((token) => {
|
|
2264
|
+
const code = String(Number.parseInt(token, 10));
|
|
2265
|
+
return { ...base, code, message: code };
|
|
2266
|
+
});
|
|
2267
|
+
}
|
|
2268
|
+
function classifyWsfexAuthorization(result) {
|
|
2269
|
+
const operation = "FEXAuthorize";
|
|
2270
|
+
const authorization = toWsfexRecord(result.FEXResultAuth);
|
|
2271
|
+
const resultCode = normalizeWsfexResult(authorization?.Resultado);
|
|
2272
|
+
const echo = readWsfexEcho(authorization);
|
|
2273
|
+
const reprocessed = readWsfexReprocessed(authorization?.Reproceso);
|
|
2274
|
+
const base = {
|
|
2275
|
+
service: "wsfex",
|
|
2276
|
+
operation,
|
|
2277
|
+
results: resultCode === void 0 ? {} : { header: resultCode },
|
|
2278
|
+
errors: extractWsfexErrors(result, operation, "header"),
|
|
2279
|
+
observations: extractWsfexReasons(
|
|
2280
|
+
authorization?.Motivos_Obs,
|
|
2281
|
+
operation,
|
|
2282
|
+
resultCode === "R" ? "business" : "observation"
|
|
2283
|
+
),
|
|
2284
|
+
raw: result
|
|
2285
|
+
};
|
|
2286
|
+
assignWsfexValue(base, "reprocessed", reprocessed);
|
|
2287
|
+
assignWsfexValue(base, "echo", echo);
|
|
2288
|
+
const context = {
|
|
2289
|
+
base,
|
|
2290
|
+
resultCode,
|
|
2291
|
+
cae: readWsfexText(authorization?.Cae),
|
|
2292
|
+
caeExpiry: readWsfexText(authorization?.Fch_venc_Cae),
|
|
2293
|
+
voucherNumber: echo?.number
|
|
2294
|
+
};
|
|
2295
|
+
return base.errors.length > 0 ? classifyWsfexErrorAnswer(context) : classifyWsfexResultAnswer(context);
|
|
2296
|
+
}
|
|
2297
|
+
function classifyWsfexErrorAnswer(context) {
|
|
2298
|
+
if (context.cae || context.resultCode === "A") {
|
|
2299
|
+
return createWsfexStructuredIndeterminate(
|
|
2300
|
+
context,
|
|
2301
|
+
"contradictory_response"
|
|
2302
|
+
);
|
|
2303
|
+
}
|
|
2304
|
+
const authenticationError = classifyArcaAuthenticationIssues(
|
|
2305
|
+
context.base.errors,
|
|
2306
|
+
{ service: "wsfex", operation: context.base.operation }
|
|
2307
|
+
);
|
|
2308
|
+
if (authenticationError) {
|
|
2309
|
+
return {
|
|
2310
|
+
...createWsfexStructuredIndeterminate(context, "authentication_rejected"),
|
|
2311
|
+
authentication: createArcaAuthenticationEvidence(authenticationError)
|
|
2312
|
+
};
|
|
2313
|
+
}
|
|
2314
|
+
if (context.base.errors.some((issue) => issue.category === "infrastructure")) {
|
|
2315
|
+
return createWsfexStructuredIndeterminate(context, "incomplete_response");
|
|
2316
|
+
}
|
|
2317
|
+
return {
|
|
2318
|
+
...context.base,
|
|
2319
|
+
kind: "rejected",
|
|
2320
|
+
result: "R",
|
|
2321
|
+
resultLevel: "header"
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
function classifyWsfexResultAnswer(context) {
|
|
2325
|
+
const { cae, caeExpiry, voucherNumber } = context;
|
|
2326
|
+
if (context.resultCode === "A") {
|
|
2327
|
+
return cae && caeExpiry && voucherNumber !== void 0 ? {
|
|
2328
|
+
...context.base,
|
|
2329
|
+
kind: "authorized",
|
|
2330
|
+
result: "A",
|
|
2331
|
+
resultLevel: "header",
|
|
2332
|
+
cae,
|
|
2333
|
+
caeExpiry,
|
|
2334
|
+
voucherNumber
|
|
2335
|
+
} : createWsfexStructuredIndeterminate(context, "incomplete_response");
|
|
2336
|
+
}
|
|
2337
|
+
if (context.resultCode === "R") {
|
|
2338
|
+
return cae ? createWsfexStructuredIndeterminate(context, "contradictory_response") : {
|
|
2339
|
+
...context.base,
|
|
2340
|
+
kind: "rejected",
|
|
2341
|
+
result: "R",
|
|
2342
|
+
resultLevel: "header"
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
return createWsfexStructuredIndeterminate(context, "incomplete_response");
|
|
2346
|
+
}
|
|
2347
|
+
function createWsfexStructuredIndeterminate(context, reason) {
|
|
2348
|
+
const outcome = {
|
|
2349
|
+
...context.base,
|
|
2350
|
+
kind: "indeterminate",
|
|
2351
|
+
reason
|
|
2352
|
+
};
|
|
2353
|
+
assignWsfexValue(outcome, "result", context.resultCode);
|
|
2354
|
+
if (context.resultCode !== void 0) {
|
|
2355
|
+
outcome.resultLevel = "header";
|
|
2356
|
+
}
|
|
2357
|
+
assignWsfexValue(outcome, "cae", context.cae);
|
|
2358
|
+
assignWsfexValue(outcome, "caeExpiry", context.caeExpiry);
|
|
2359
|
+
return outcome;
|
|
2360
|
+
}
|
|
2361
|
+
function createWsfexIndeterminateOutcome(error) {
|
|
2362
|
+
const operation = "FEXAuthorize";
|
|
2363
|
+
const authenticationError = classifyArcaAuthenticationError(error, {
|
|
2364
|
+
service: "wsfex",
|
|
2365
|
+
operation
|
|
2366
|
+
});
|
|
2367
|
+
return {
|
|
2368
|
+
kind: "indeterminate",
|
|
2369
|
+
service: "wsfex",
|
|
2370
|
+
operation,
|
|
2371
|
+
results: {},
|
|
2372
|
+
reason: authenticationError ? "authentication_rejected" : getWsfexIndeterminateReason(error),
|
|
2373
|
+
...authenticationError ? {
|
|
2374
|
+
authentication: createArcaAuthenticationEvidence(authenticationError)
|
|
2375
|
+
} : {},
|
|
2376
|
+
errors: [],
|
|
2377
|
+
observations: []
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
function getWsfexIndeterminateReason(error) {
|
|
2381
|
+
if (error instanceof ArcaTransportError) {
|
|
2382
|
+
return "transport_error";
|
|
2383
|
+
}
|
|
2384
|
+
if (error instanceof ArcaSoapFaultError) {
|
|
2385
|
+
return "soap_fault";
|
|
2386
|
+
}
|
|
2387
|
+
if (error instanceof ArcaInvalidSoapResponseError) {
|
|
2388
|
+
return "invalid_response";
|
|
2389
|
+
}
|
|
2390
|
+
return "unexpected_error";
|
|
2391
|
+
}
|
|
2392
|
+
function readWsfexEcho(authorization) {
|
|
2393
|
+
if (!authorization) {
|
|
2394
|
+
return void 0;
|
|
2395
|
+
}
|
|
2396
|
+
const echo = {};
|
|
2397
|
+
assignWsfexValue(
|
|
2398
|
+
echo,
|
|
2399
|
+
"id",
|
|
2400
|
+
readWsfexInteger(authorization.Id, 0, WSFEX_MAX_REQUEST_ID)
|
|
2401
|
+
);
|
|
2402
|
+
assignWsfexValue(
|
|
2403
|
+
echo,
|
|
2404
|
+
"salesPoint",
|
|
2405
|
+
readWsfexInteger(authorization.Punto_vta, 1, 99999)
|
|
2406
|
+
);
|
|
2407
|
+
assignWsfexValue(
|
|
2408
|
+
echo,
|
|
2409
|
+
"voucherType",
|
|
2410
|
+
readWsfexInteger(authorization.Cbte_tipo ?? authorization.Cbte_Tipo, 1, 999)
|
|
2411
|
+
);
|
|
2412
|
+
assignWsfexValue(
|
|
2413
|
+
echo,
|
|
2414
|
+
"number",
|
|
2415
|
+
readWsfexInteger(authorization.Cbte_nro, 1, 99999999)
|
|
2416
|
+
);
|
|
2417
|
+
assignWsfexValue(echo, "date", readWsfexText(authorization.Fch_cbte));
|
|
2418
|
+
return Object.keys(echo).length > 0 ? echo : void 0;
|
|
2419
|
+
}
|
|
2420
|
+
function readWsfexReprocessed(value) {
|
|
2421
|
+
const flag = normalizeWsfexResult(value);
|
|
2422
|
+
return flag === "S" || flag === "N" ? flag === "S" : void 0;
|
|
2423
|
+
}
|
|
2424
|
+
function mapWsfexVoucherInfo(raw) {
|
|
2425
|
+
const operation = "FEXGetCMP";
|
|
2426
|
+
const voucher = {
|
|
2427
|
+
voucherType: parseWsfexInteger(raw.Cbte_tipo ?? raw.Cbte_Tipo, {
|
|
2428
|
+
operation,
|
|
2429
|
+
field: "Cbte_tipo",
|
|
2430
|
+
min: 1,
|
|
2431
|
+
max: 999
|
|
2432
|
+
}),
|
|
2433
|
+
salesPoint: parseWsfexInteger(raw.Punto_vta, {
|
|
2434
|
+
operation,
|
|
2435
|
+
field: "Punto_vta",
|
|
2436
|
+
min: 1,
|
|
2437
|
+
max: 99999
|
|
2438
|
+
}),
|
|
2439
|
+
number: parseWsfexInteger(raw.Cbte_nro, {
|
|
2440
|
+
operation,
|
|
2441
|
+
field: "Cbte_nro",
|
|
2442
|
+
min: 1,
|
|
2443
|
+
max: 99999999
|
|
2444
|
+
}),
|
|
2445
|
+
destination: parseWsfexNumber(raw.Dst_cmp, operation, "Dst_cmp"),
|
|
2446
|
+
receiverName: readWsfexText(raw.Cliente) ?? "",
|
|
2447
|
+
receiverAddress: readWsfexText(raw.Domicilio_cliente) ?? "",
|
|
2448
|
+
currencyId: readWsfexText(raw.Moneda_Id) ?? "",
|
|
2449
|
+
totalAmount: parseWsfexNumber(raw.Imp_total, operation, "Imp_total"),
|
|
2450
|
+
items: getWsfexRows(raw.Items, "Item").map(mapWsfexLookupItem)
|
|
2451
|
+
};
|
|
2452
|
+
assignWsfexValue(
|
|
2453
|
+
voucher,
|
|
2454
|
+
"id",
|
|
2455
|
+
readWsfexInteger(raw.Id, 0, WSFEX_MAX_REQUEST_ID)
|
|
2456
|
+
);
|
|
2457
|
+
assignWsfexValue(voucher, "voucherDate", readWsfexText(raw.Fecha_cbte));
|
|
2458
|
+
assignWsfexValue(voucher, "exportType", readWsfexNumber(raw.Tipo_expo));
|
|
2459
|
+
assignWsfexValue(
|
|
2460
|
+
voucher,
|
|
2461
|
+
"permitExists",
|
|
2462
|
+
readWsfexFlag(raw.Permiso_existente)
|
|
2463
|
+
);
|
|
2464
|
+
assignWsfexValue(
|
|
2465
|
+
voucher,
|
|
2466
|
+
"permits",
|
|
2467
|
+
mapWsfexLookupList(raw.Permisos, "Permiso", mapWsfexLookupPermit)
|
|
2468
|
+
);
|
|
2469
|
+
assignWsfexValue(
|
|
2470
|
+
voucher,
|
|
2471
|
+
"receiverCountryTaxId",
|
|
2472
|
+
readWsfexTaxId(raw.Cuit_pais_cliente)
|
|
2473
|
+
);
|
|
2474
|
+
assignWsfexValue(voucher, "receiverTaxId", readWsfexText(raw.Id_impositivo));
|
|
2475
|
+
assignWsfexValue(
|
|
2476
|
+
voucher,
|
|
2477
|
+
"exchangeRate",
|
|
2478
|
+
canonicalizeWsfexDecimal(raw.Moneda_ctz)
|
|
2479
|
+
);
|
|
2480
|
+
assignWsfexValue(
|
|
2481
|
+
voucher,
|
|
2482
|
+
"sameCurrencyForeignCancellation",
|
|
2483
|
+
readWsfexFlag(raw.CanMisMonExt)
|
|
2484
|
+
);
|
|
2485
|
+
assignWsfexValue(
|
|
2486
|
+
voucher,
|
|
2487
|
+
"commercialObservations",
|
|
2488
|
+
readWsfexText(raw.Obs_comerciales)
|
|
2489
|
+
);
|
|
2490
|
+
assignWsfexValue(voucher, "observations", readWsfexText(raw.Obs));
|
|
2491
|
+
assignWsfexValue(
|
|
2492
|
+
voucher,
|
|
2493
|
+
"associatedVouchers",
|
|
2494
|
+
mapWsfexLookupList(
|
|
2495
|
+
raw.Cmps_asoc,
|
|
2496
|
+
"Cmp_asoc",
|
|
2497
|
+
mapWsfexLookupAssociatedVoucher
|
|
2498
|
+
)
|
|
2499
|
+
);
|
|
2500
|
+
assignWsfexValue(voucher, "paymentTerms", readWsfexText(raw.Forma_pago));
|
|
2501
|
+
assignWsfexValue(voucher, "incoterms", readWsfexText(raw.Incoterms));
|
|
2502
|
+
assignWsfexValue(voucher, "incotermsDetail", readWsfexText(raw.Incoterms_Ds));
|
|
2503
|
+
assignWsfexValue(voucher, "language", readWsfexNumber(raw.Idioma_cbte));
|
|
2504
|
+
assignWsfexValue(voucher, "paymentDate", readWsfexText(raw.Fecha_pago));
|
|
2505
|
+
assignWsfexValue(
|
|
2506
|
+
voucher,
|
|
2507
|
+
"activities",
|
|
2508
|
+
mapWsfexLookupList(raw.Actividades, "Actividad", (row) => {
|
|
2509
|
+
const id = toWsfexActivityId(row.Id);
|
|
2510
|
+
return id === void 0 ? void 0 : { id };
|
|
2511
|
+
})
|
|
2512
|
+
);
|
|
2513
|
+
assignWsfexValue(voucher, "result", normalizeWsfexResult(raw.Resultado));
|
|
2514
|
+
assignWsfexValue(voucher, "cae", readWsfexText(raw.Cae));
|
|
2515
|
+
assignWsfexValue(voucher, "caeExpiry", readWsfexText(raw.Fch_venc_Cae));
|
|
2516
|
+
assignWsfexValue(voucher, "authorizedAt", readWsfexText(raw.Fecha_cbte_cae));
|
|
2517
|
+
return voucher;
|
|
2518
|
+
}
|
|
2519
|
+
function mapWsfexLookupItem(row) {
|
|
2520
|
+
const operation = "FEXGetCMP";
|
|
2521
|
+
const code = readWsfexText(row.Pro_codigo);
|
|
2522
|
+
const quantity = canonicalizeWsfexDecimal(row.Pro_qty);
|
|
2523
|
+
const unitPrice = canonicalizeWsfexDecimal(row.Pro_precio_uni);
|
|
2524
|
+
const discount = canonicalizeWsfexDecimal(row.Pro_bonificacion);
|
|
2525
|
+
return {
|
|
2526
|
+
...code === void 0 ? {} : { code },
|
|
2527
|
+
description: readWsfexText(row.Pro_ds) ?? "",
|
|
2528
|
+
...quantity === void 0 ? {} : { quantity },
|
|
2529
|
+
unit: parseWsfexNumber(row.Pro_umed, operation, "Pro_umed"),
|
|
2530
|
+
...unitPrice === void 0 ? {} : { unitPrice },
|
|
2531
|
+
...discount === void 0 ? {} : { discount },
|
|
2532
|
+
amount: parseWsfexNumber(row.Pro_total_item, operation, "Pro_total_item")
|
|
2533
|
+
};
|
|
2534
|
+
}
|
|
2535
|
+
function mapWsfexLookupPermit(row) {
|
|
2536
|
+
const id = readWsfexText(row.Id_permiso);
|
|
2537
|
+
const destination = readWsfexNumber(row.Dst_merc);
|
|
2538
|
+
return id === void 0 || destination === void 0 ? void 0 : { id, destination };
|
|
2539
|
+
}
|
|
2540
|
+
function mapWsfexLookupAssociatedVoucher(row) {
|
|
2541
|
+
const voucherType = readWsfexNumber(row.Cbte_tipo ?? row.Cbte_Tipo);
|
|
2542
|
+
const salesPoint = readWsfexNumber(row.Cbte_punto_vta);
|
|
2543
|
+
const number = readWsfexNumber(row.Cbte_nro);
|
|
2544
|
+
if (voucherType === void 0 || salesPoint === void 0 || number === void 0) {
|
|
2545
|
+
return void 0;
|
|
2546
|
+
}
|
|
2547
|
+
const issuerTaxId2 = readWsfexTaxId(row.Cbte_cuit);
|
|
2548
|
+
return {
|
|
2549
|
+
voucherType,
|
|
2550
|
+
salesPoint,
|
|
2551
|
+
number,
|
|
2552
|
+
...issuerTaxId2 === void 0 ? {} : { issuerTaxId: issuerTaxId2 }
|
|
2553
|
+
};
|
|
2554
|
+
}
|
|
2555
|
+
function mapWsfexLookupList(container, key, map) {
|
|
2556
|
+
const rows2 = getWsfexRows(container, key);
|
|
2557
|
+
if (rows2.length === 0) {
|
|
2558
|
+
return void 0;
|
|
2559
|
+
}
|
|
2560
|
+
const result = [];
|
|
2561
|
+
for (const row of rows2) {
|
|
2562
|
+
const mapped = map(row);
|
|
2563
|
+
if (mapped === void 0) {
|
|
2564
|
+
return void 0;
|
|
2565
|
+
}
|
|
2566
|
+
result.push(mapped);
|
|
2567
|
+
}
|
|
2568
|
+
return result;
|
|
2569
|
+
}
|
|
2570
|
+
function mapWsfexSalesPoint(row) {
|
|
2571
|
+
const deactivatedAt = toIsoDate(readWsfexText(row.Pve_FchBaja));
|
|
2572
|
+
return {
|
|
2573
|
+
number: parseWsfexInteger(row.Pve_Nro, {
|
|
2574
|
+
operation: "FEXGetPARAM_PtoVenta",
|
|
2575
|
+
field: "Pve_Nro",
|
|
2576
|
+
min: 1,
|
|
2577
|
+
max: 99999
|
|
2578
|
+
}),
|
|
2579
|
+
blocked: normalizeWsfexResult(row.Pve_Bloqueado) === "S",
|
|
2580
|
+
...deactivatedAt === void 0 ? {} : { deactivatedAt }
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
function toWsfexActivityId(value) {
|
|
2584
|
+
const text2 = readWsfexText(value);
|
|
2585
|
+
return text2 !== void 0 && /^\d+$/.test(text2) && Number(text2) !== 0 ? text2.padStart(6, "0") : void 0;
|
|
2586
|
+
}
|
|
2587
|
+
function readWsfexTaxId(value) {
|
|
2588
|
+
const text2 = readWsfexText(value);
|
|
2589
|
+
return text2 !== void 0 && /^\d+$/.test(text2) && Number(text2) !== 0 ? text2 : void 0;
|
|
2590
|
+
}
|
|
2591
|
+
function readWsfexFlag(value) {
|
|
2592
|
+
const flag = normalizeWsfexResult(value);
|
|
2593
|
+
return flag === "S" || flag === "N" ? flag : void 0;
|
|
2594
|
+
}
|
|
2595
|
+
function getWsfexRows(container, key) {
|
|
2596
|
+
const value = toWsfexRecord(container)?.[key];
|
|
2597
|
+
if (value === void 0) {
|
|
2598
|
+
return [];
|
|
2599
|
+
}
|
|
2600
|
+
return (Array.isArray(value) ? value : [value]).flatMap((row) => {
|
|
2601
|
+
const record = toWsfexRecord(row);
|
|
2602
|
+
return record ? [record] : [];
|
|
2603
|
+
});
|
|
2604
|
+
}
|
|
2605
|
+
function toWsfexRecord(value) {
|
|
2606
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2607
|
+
return void 0;
|
|
2608
|
+
}
|
|
2609
|
+
const record = value;
|
|
2610
|
+
return Object.keys(record).some((key) => !key.startsWith("@_")) ? record : void 0;
|
|
2611
|
+
}
|
|
2612
|
+
function readWsfexText(value) {
|
|
2613
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
2614
|
+
return void 0;
|
|
2615
|
+
}
|
|
2616
|
+
const text2 = String(value).trim();
|
|
2617
|
+
return text2 || void 0;
|
|
2618
|
+
}
|
|
2619
|
+
function readWsfexNumber(value) {
|
|
2620
|
+
const text2 = readWsfexText(value);
|
|
2621
|
+
return text2 !== void 0 && /^-?\d+(?:\.\d+)?$/.test(text2) ? Number(text2) : void 0;
|
|
2622
|
+
}
|
|
2623
|
+
function readWsfexInteger(value, min, max) {
|
|
2624
|
+
const text2 = readWsfexText(value);
|
|
2625
|
+
if (text2 === void 0 || !/^\d+$/.test(text2)) {
|
|
2626
|
+
return void 0;
|
|
2627
|
+
}
|
|
2628
|
+
const parsed = Number(text2);
|
|
2629
|
+
return Number.isSafeInteger(parsed) && parsed >= min && parsed <= max ? parsed : void 0;
|
|
2630
|
+
}
|
|
2631
|
+
function parseWsfexInteger(value, options) {
|
|
2632
|
+
const parsed = readWsfexInteger(value, options.min, options.max);
|
|
2633
|
+
if (parsed === void 0) {
|
|
2634
|
+
throw new ArcaInvalidSoapResponseError(`Invalid WSFEX ${options.field}`, {
|
|
2635
|
+
service: "wsfex",
|
|
2636
|
+
operation: options.operation
|
|
2637
|
+
});
|
|
2638
|
+
}
|
|
2639
|
+
return parsed;
|
|
2640
|
+
}
|
|
2641
|
+
function parseWsfexNumber(value, operation, field) {
|
|
2642
|
+
const parsed = readWsfexNumber(value);
|
|
2643
|
+
if (parsed === void 0) {
|
|
2644
|
+
throw new ArcaInvalidSoapResponseError(`Invalid WSFEX ${field}`, {
|
|
2645
|
+
service: "wsfex",
|
|
2646
|
+
operation
|
|
2647
|
+
});
|
|
2648
|
+
}
|
|
2649
|
+
return parsed;
|
|
2650
|
+
}
|
|
2651
|
+
function normalizeWsfexResult(value) {
|
|
2652
|
+
return readWsfexText(value)?.toUpperCase();
|
|
2653
|
+
}
|
|
2654
|
+
function assignWsfexValue(target, key, value) {
|
|
2655
|
+
if (value !== void 0) {
|
|
2656
|
+
target[key] = value;
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
// src/store/types.ts
|
|
2661
|
+
import { createHash } from "crypto";
|
|
2662
|
+
function attemptKey(environment, taxId, key) {
|
|
2663
|
+
return `arca:v1:attempt:${environment}:${taxId}:${key}`;
|
|
2664
|
+
}
|
|
2665
|
+
function sequenceKey(environment, taxId, salesPoint, voucherType) {
|
|
2666
|
+
return `arca:v1:sequence:${environment}:${taxId}:${salesPoint}:${voucherType}`;
|
|
2667
|
+
}
|
|
2668
|
+
function sequenceLockKey(environment, taxId, salesPoint, voucherType) {
|
|
2669
|
+
return `arca:v1:lock:sequence:${environment}:${taxId}:${salesPoint}:${voucherType}`;
|
|
2670
|
+
}
|
|
2671
|
+
function settledKey(environment, taxId, key) {
|
|
2672
|
+
return `arca:v1:settled:${environment}:${taxId}:${key}`;
|
|
2673
|
+
}
|
|
2674
|
+
function canonicalHash(input) {
|
|
2675
|
+
return createHash("sha256").update(JSON.stringify(canonical(input))).digest("hex");
|
|
2676
|
+
}
|
|
2677
|
+
function canonical(value) {
|
|
2678
|
+
if (Array.isArray(value)) {
|
|
2679
|
+
return value.map(canonical);
|
|
2680
|
+
}
|
|
2681
|
+
if (value instanceof Date) {
|
|
2682
|
+
return value.toJSON();
|
|
2683
|
+
}
|
|
2684
|
+
if (value !== null && typeof value === "object") {
|
|
2685
|
+
return Object.fromEntries(
|
|
2686
|
+
Object.entries(value).filter(([, item]) => item !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, item]) => [key, canonical(item)])
|
|
2687
|
+
);
|
|
2688
|
+
}
|
|
2689
|
+
return value;
|
|
2690
|
+
}
|
|
2691
|
+
async function storeCall(fn) {
|
|
2692
|
+
try {
|
|
2693
|
+
return await fn();
|
|
2694
|
+
} catch (cause) {
|
|
2695
|
+
throw new ArcaConfigurationError("ARCA store operation failed.", { cause });
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
// src/services/wsfe-identity.ts
|
|
2700
|
+
function matchWsfeVoucherIdentity(sent, number, found) {
|
|
2701
|
+
let missing;
|
|
2702
|
+
const compare = (field, expected, actual, normalize) => {
|
|
2703
|
+
if (actual === void 0 || actual === null || expected === void 0) {
|
|
2704
|
+
missing ??= field;
|
|
2705
|
+
return void 0;
|
|
2706
|
+
}
|
|
2707
|
+
try {
|
|
2708
|
+
const left = normalize ? normalize(expected) : expected;
|
|
2709
|
+
const right = normalize ? normalize(actual) : actual;
|
|
2710
|
+
if (left !== right) {
|
|
2711
|
+
return {
|
|
2712
|
+
matches: false,
|
|
2713
|
+
evidence: "conflict",
|
|
2714
|
+
reason: `${field} differs from the sent input`
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
} catch {
|
|
2718
|
+
missing ??= field;
|
|
2719
|
+
}
|
|
2720
|
+
return void 0;
|
|
2721
|
+
};
|
|
2722
|
+
const checks = [
|
|
2723
|
+
["voucherType", sent.voucherType, found.voucherType],
|
|
2724
|
+
["salesPoint", sent.salesPoint, found.salesPoint],
|
|
2725
|
+
["number", number, found.voucherNumber, normalizeVoucherNumber],
|
|
2726
|
+
[
|
|
2727
|
+
"date",
|
|
1616
2728
|
sent.voucherDate,
|
|
1617
2729
|
found.voucherDate,
|
|
1618
2730
|
(value) => normalizeWsfeDateInput(value, "date")
|
|
@@ -1718,11 +2830,11 @@ function normalizeVoucherNumber(value) {
|
|
|
1718
2830
|
return value;
|
|
1719
2831
|
}
|
|
1720
2832
|
function normalizeDocument(value) {
|
|
1721
|
-
const
|
|
1722
|
-
if (!/^\d+$/.test(
|
|
2833
|
+
const text2 = String(value);
|
|
2834
|
+
if (!/^\d+$/.test(text2)) {
|
|
1723
2835
|
throw new Error("Invalid document number");
|
|
1724
2836
|
}
|
|
1725
|
-
return BigInt(
|
|
2837
|
+
return BigInt(text2);
|
|
1726
2838
|
}
|
|
1727
2839
|
function compareVatRates(expected, actual) {
|
|
1728
2840
|
if (actual === void 0) {
|
|
@@ -2144,19 +3256,19 @@ async function postXmlOnce({
|
|
|
2144
3256
|
});
|
|
2145
3257
|
}
|
|
2146
3258
|
return await new Promise((resolve, reject) => {
|
|
2147
|
-
let
|
|
3259
|
+
let settled2 = false;
|
|
2148
3260
|
const settleResolve = (response) => {
|
|
2149
|
-
if (
|
|
3261
|
+
if (settled2) {
|
|
2150
3262
|
return;
|
|
2151
3263
|
}
|
|
2152
|
-
|
|
3264
|
+
settled2 = true;
|
|
2153
3265
|
resolve(response);
|
|
2154
3266
|
};
|
|
2155
3267
|
const settleReject = (error) => {
|
|
2156
|
-
if (
|
|
3268
|
+
if (settled2) {
|
|
2157
3269
|
return;
|
|
2158
3270
|
}
|
|
2159
|
-
|
|
3271
|
+
settled2 = true;
|
|
2160
3272
|
reject(error);
|
|
2161
3273
|
};
|
|
2162
3274
|
const request = https.request(
|
|
@@ -2701,7 +3813,7 @@ async function withWsaaSessionStoreLock(config, key, service, fn) {
|
|
|
2701
3813
|
if (entered) {
|
|
2702
3814
|
throw error;
|
|
2703
3815
|
}
|
|
2704
|
-
if (error instanceof ArcaConfigurationError) {
|
|
3816
|
+
if (error instanceof ArcaConfigurationError || error instanceof ArcaLockTimeoutError) {
|
|
2705
3817
|
throw error;
|
|
2706
3818
|
}
|
|
2707
3819
|
throw new ArcaConfigurationError(
|
|
@@ -2804,25 +3916,1343 @@ function createArcaLogger(config) {
|
|
|
2804
3916
|
}
|
|
2805
3917
|
};
|
|
2806
3918
|
}
|
|
2807
|
-
function resolveArcaLogLevel(level) {
|
|
2808
|
-
if (isArcaLogLevel(level)) {
|
|
2809
|
-
return level;
|
|
3919
|
+
function resolveArcaLogLevel(level) {
|
|
3920
|
+
if (isArcaLogLevel(level)) {
|
|
3921
|
+
return level;
|
|
3922
|
+
}
|
|
3923
|
+
const envLevel = process.env.ARCA_LOG_LEVEL?.trim().toLowerCase();
|
|
3924
|
+
if (isArcaLogLevel(envLevel)) {
|
|
3925
|
+
return envLevel;
|
|
3926
|
+
}
|
|
3927
|
+
return "warn";
|
|
3928
|
+
}
|
|
3929
|
+
function shouldLog(threshold, messageLevel) {
|
|
3930
|
+
return ARCA_LOG_LEVELS.indexOf(messageLevel) >= ARCA_LOG_LEVELS.indexOf(threshold);
|
|
3931
|
+
}
|
|
3932
|
+
function isArcaLogLevel(value) {
|
|
3933
|
+
return ARCA_LOG_LEVELS.includes(value);
|
|
3934
|
+
}
|
|
3935
|
+
function defaultArcaLog(level, message, ...args) {
|
|
3936
|
+
const method = level === "debug" ? console.debug : level === "info" ? console.info : level === "warn" ? console.warn : console.error;
|
|
3937
|
+
method(message, ...args);
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
// src/store/lock.ts
|
|
3941
|
+
import { randomUUID } from "crypto";
|
|
3942
|
+
var ARCA_LEASE_MS = 6e4;
|
|
3943
|
+
var RENEW_MS = 2e4;
|
|
3944
|
+
var POLL_MS = 50;
|
|
3945
|
+
var MAX_WAIT_MS = 2 * ARCA_LEASE_MS;
|
|
3946
|
+
async function withLease(driver, fn, { signal } = {}) {
|
|
3947
|
+
const owner = randomUUID();
|
|
3948
|
+
const deadline = Date.now() + MAX_WAIT_MS;
|
|
3949
|
+
throwIfAborted(signal);
|
|
3950
|
+
let held = await driver.acquire(owner);
|
|
3951
|
+
while (!held) {
|
|
3952
|
+
if (Date.now() >= deadline) {
|
|
3953
|
+
throw new ArcaLockTimeoutError(
|
|
3954
|
+
"ARCA store lock stayed held; no work was attempted.",
|
|
3955
|
+
{ reason: "held" }
|
|
3956
|
+
);
|
|
3957
|
+
}
|
|
3958
|
+
await delay2(POLL_MS + Math.floor(Math.random() * POLL_MS), signal);
|
|
3959
|
+
throwIfAborted(signal);
|
|
3960
|
+
held = await driver.acquire(owner);
|
|
3961
|
+
}
|
|
3962
|
+
if (signal?.aborted) {
|
|
3963
|
+
await driver.release(owner).catch(() => void 0);
|
|
3964
|
+
throwIfAborted(signal);
|
|
3965
|
+
}
|
|
3966
|
+
const renewal = setInterval(() => {
|
|
3967
|
+
driver.renew(owner).catch(() => void 0);
|
|
3968
|
+
}, RENEW_MS);
|
|
3969
|
+
renewal.unref?.();
|
|
3970
|
+
try {
|
|
3971
|
+
return await fn();
|
|
3972
|
+
} finally {
|
|
3973
|
+
clearInterval(renewal);
|
|
3974
|
+
await driver.release(owner).catch(() => void 0);
|
|
3975
|
+
}
|
|
3976
|
+
}
|
|
3977
|
+
function throwIfAborted(signal) {
|
|
3978
|
+
if (signal?.aborted) {
|
|
3979
|
+
throw new ArcaLockTimeoutError(
|
|
3980
|
+
"Stopped waiting for an ARCA store lock: the caller's signal aborted; no work was attempted.",
|
|
3981
|
+
{ reason: "aborted", cause: signal.reason }
|
|
3982
|
+
);
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
function delay2(ms, signal) {
|
|
3986
|
+
if (signal?.aborted) {
|
|
3987
|
+
return Promise.resolve();
|
|
3988
|
+
}
|
|
3989
|
+
return new Promise((resolve) => {
|
|
3990
|
+
const wake = () => {
|
|
3991
|
+
clearTimeout(timer);
|
|
3992
|
+
resolve();
|
|
3993
|
+
};
|
|
3994
|
+
const timer = setTimeout(() => {
|
|
3995
|
+
signal?.removeEventListener("abort", wake);
|
|
3996
|
+
resolve();
|
|
3997
|
+
}, ms);
|
|
3998
|
+
timer.unref?.();
|
|
3999
|
+
signal?.addEventListener("abort", wake, { once: true });
|
|
4000
|
+
});
|
|
4001
|
+
}
|
|
4002
|
+
|
|
4003
|
+
// src/services/issuance-wsfex.ts
|
|
4004
|
+
var LANGUAGES = { es: 1, en: 2, pt: 3 };
|
|
4005
|
+
var EXPORT_KINDS = { goods: 1, services: 2, other: 4 };
|
|
4006
|
+
var UNPRICED_UNITS = [0, 97, 99];
|
|
4007
|
+
function isExportVoucherType(voucherType) {
|
|
4008
|
+
return typeof voucherType === "number" && EXPORT_TYPES.includes(voucherType);
|
|
4009
|
+
}
|
|
4010
|
+
function isExportInput(input) {
|
|
4011
|
+
return input !== null && typeof input === "object" && "export" in input;
|
|
4012
|
+
}
|
|
4013
|
+
function isExportNoteInput(input) {
|
|
4014
|
+
if (input === null || typeof input !== "object" || !("for" in input)) {
|
|
4015
|
+
return false;
|
|
4016
|
+
}
|
|
4017
|
+
const target = input.for;
|
|
4018
|
+
return target !== null && typeof target === "object" && !Array.isArray(target) && isExportVoucherType(target.voucherType);
|
|
4019
|
+
}
|
|
4020
|
+
function deriveExportInvoice(input, now = /* @__PURE__ */ new Date()) {
|
|
4021
|
+
assertIssueObject(input, "input");
|
|
4022
|
+
assertIssueKeys(
|
|
4023
|
+
input,
|
|
4024
|
+
[
|
|
4025
|
+
"salesPoint",
|
|
4026
|
+
"to",
|
|
4027
|
+
"export",
|
|
4028
|
+
"items",
|
|
4029
|
+
"total",
|
|
4030
|
+
"date",
|
|
4031
|
+
"currency",
|
|
4032
|
+
"exchangeRate",
|
|
4033
|
+
"paidInForeignCurrency",
|
|
4034
|
+
"paymentTerms",
|
|
4035
|
+
"language",
|
|
4036
|
+
"observations",
|
|
4037
|
+
"commercialObservations"
|
|
4038
|
+
],
|
|
4039
|
+
"input"
|
|
4040
|
+
);
|
|
4041
|
+
assertSalesPoint2(input.salesPoint, "salesPoint");
|
|
4042
|
+
const receiver = deriveReceiver2(input.to);
|
|
4043
|
+
const exported = deriveExport(input.export);
|
|
4044
|
+
const voucherDate = normalizeWsfeDateInput(
|
|
4045
|
+
input.date ?? buenosAiresDate(now),
|
|
4046
|
+
"date"
|
|
4047
|
+
);
|
|
4048
|
+
if (exported.paymentDate !== void 0 && exported.paymentDate < voucherDate) {
|
|
4049
|
+
invalid3("export.paymentDate", "on or after the voucher date");
|
|
4050
|
+
}
|
|
4051
|
+
const currency = deriveCurrency2(input.currency, input.exchangeRate);
|
|
4052
|
+
if (input.paidInForeignCurrency !== void 0) {
|
|
4053
|
+
if (typeof input.paidInForeignCurrency !== "boolean") {
|
|
4054
|
+
invalid3("paidInForeignCurrency", "a boolean");
|
|
4055
|
+
}
|
|
4056
|
+
if (currency.currencyId === ARCA_CURRENCY_IDS.ARS) {
|
|
4057
|
+
invalid3("paidInForeignCurrency", "absent for an invoice in pesos");
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
const { items, total } = deriveItems(input.items, "items");
|
|
4061
|
+
const reviewed = reviewedTotal(input.total, total);
|
|
4062
|
+
const data = {
|
|
4063
|
+
voucherType: ARCA_VOUCHER_TYPES.FACTURA_E,
|
|
4064
|
+
salesPoint: input.salesPoint,
|
|
4065
|
+
voucherDate,
|
|
4066
|
+
exportType: exported.exportType,
|
|
4067
|
+
...exported.permitExists === void 0 ? {} : { permitExists: exported.permitExists },
|
|
4068
|
+
...exported.permits === void 0 ? {} : { permits: exported.permits },
|
|
4069
|
+
...receiver,
|
|
4070
|
+
...currency,
|
|
4071
|
+
...input.paidInForeignCurrency === void 0 ? {} : {
|
|
4072
|
+
sameCurrencyForeignCancellation: input.paidInForeignCurrency ? "S" : "N"
|
|
4073
|
+
},
|
|
4074
|
+
...optionalText(
|
|
4075
|
+
input.commercialObservations,
|
|
4076
|
+
"commercialObservations",
|
|
4077
|
+
4e3
|
|
4078
|
+
),
|
|
4079
|
+
totalAmount: reviewed / 100,
|
|
4080
|
+
...optionalText(input.observations, "observations", 1e3),
|
|
4081
|
+
paymentTerms: text(input.paymentTerms, "paymentTerms", 50),
|
|
4082
|
+
...exported.incoterms === void 0 ? {} : { incoterms: exported.incoterms },
|
|
4083
|
+
...exported.incotermsDetail === void 0 ? {} : { incotermsDetail: exported.incotermsDetail },
|
|
4084
|
+
language: deriveLanguage(input.language),
|
|
4085
|
+
items,
|
|
4086
|
+
...exported.paymentDate === void 0 ? {} : { paymentDate: exported.paymentDate }
|
|
4087
|
+
};
|
|
4088
|
+
assertWsfexVoucherInput(data);
|
|
4089
|
+
return {
|
|
4090
|
+
data,
|
|
4091
|
+
amounts: { computedTotal: total, sentTotal: reviewed, vatAdjustment: 0 }
|
|
4092
|
+
};
|
|
4093
|
+
}
|
|
4094
|
+
function deriveExportNote(original, input, kind, now = /* @__PURE__ */ new Date()) {
|
|
4095
|
+
assertIssueKeys(
|
|
4096
|
+
input,
|
|
4097
|
+
[
|
|
4098
|
+
"for",
|
|
4099
|
+
"salesPoint",
|
|
4100
|
+
"date",
|
|
4101
|
+
"exchangeRate",
|
|
4102
|
+
"observations",
|
|
4103
|
+
"commercialObservations",
|
|
4104
|
+
"items",
|
|
4105
|
+
...kind === "creditNote" ? ["all"] : []
|
|
4106
|
+
],
|
|
4107
|
+
"input",
|
|
4108
|
+
kind === "creditNote" ? "issueCreditNote()" : "issueDebitNote()"
|
|
4109
|
+
);
|
|
4110
|
+
const exportType = noteExportType(original, kind);
|
|
4111
|
+
const salesPoint = input.salesPoint ?? original.salesPoint;
|
|
4112
|
+
assertSalesPoint2(salesPoint, "salesPoint");
|
|
4113
|
+
const voucherDate = normalizeWsfeDateInput(
|
|
4114
|
+
input.date ?? buenosAiresDate(now),
|
|
4115
|
+
"date"
|
|
4116
|
+
);
|
|
4117
|
+
if (exportType === 2 && original.voucherDate !== void 0 && voucherDate < original.voucherDate) {
|
|
4118
|
+
invalid3("date", "on or after the original's date for services");
|
|
4119
|
+
}
|
|
4120
|
+
const exchangeRate2 = noteRate(original, input.exchangeRate, exportType);
|
|
4121
|
+
const lines = noteLines(original, input);
|
|
4122
|
+
const data = {
|
|
4123
|
+
voucherType: kind === "creditNote" ? ARCA_VOUCHER_TYPES.NOTA_CREDITO_E : ARCA_VOUCHER_TYPES.NOTA_DEBITO_E,
|
|
4124
|
+
salesPoint,
|
|
4125
|
+
voucherDate,
|
|
4126
|
+
exportType,
|
|
4127
|
+
...inheritedReceiver(original),
|
|
4128
|
+
currencyId: original.currencyId,
|
|
4129
|
+
...exchangeRate2 === void 0 ? {} : { exchangeRate: exchangeRate2 },
|
|
4130
|
+
...optionalText(
|
|
4131
|
+
input.commercialObservations,
|
|
4132
|
+
"commercialObservations",
|
|
4133
|
+
4e3
|
|
4134
|
+
),
|
|
4135
|
+
totalAmount: lines.total / 100,
|
|
4136
|
+
...optionalText(input.observations, "observations", 1e3),
|
|
4137
|
+
associatedVouchers: [
|
|
4138
|
+
{
|
|
4139
|
+
voucherType: original.voucherType,
|
|
4140
|
+
salesPoint: original.salesPoint,
|
|
4141
|
+
number: original.number
|
|
4142
|
+
}
|
|
4143
|
+
],
|
|
4144
|
+
...original.incoterms === void 0 ? {} : { incoterms: original.incoterms },
|
|
4145
|
+
language: original.language === 2 || original.language === 3 ? original.language : 1,
|
|
4146
|
+
items: lines.items
|
|
4147
|
+
};
|
|
4148
|
+
assertWsfexVoucherInput(data);
|
|
4149
|
+
return {
|
|
4150
|
+
data,
|
|
4151
|
+
amounts: {
|
|
4152
|
+
computedTotal: lines.total,
|
|
4153
|
+
sentTotal: lines.total,
|
|
4154
|
+
vatAdjustment: 0
|
|
4155
|
+
}
|
|
4156
|
+
};
|
|
4157
|
+
}
|
|
4158
|
+
function noteExportType(original, kind) {
|
|
4159
|
+
if (original.result !== void 0 && original.result !== "A") {
|
|
4160
|
+
invalid3("for", "an authorized voucher");
|
|
4161
|
+
}
|
|
4162
|
+
const allowed = kind === "creditNote" ? [ARCA_VOUCHER_TYPES.FACTURA_E, ARCA_VOUCHER_TYPES.NOTA_DEBITO_E] : [ARCA_VOUCHER_TYPES.FACTURA_E];
|
|
4163
|
+
if (!allowed.includes(original.voucherType)) {
|
|
4164
|
+
invalid3(
|
|
4165
|
+
"for.voucherType",
|
|
4166
|
+
kind === "creditNote" ? "19 or 20" : "19, a Factura E"
|
|
4167
|
+
);
|
|
4168
|
+
}
|
|
4169
|
+
const exportType = original.exportType;
|
|
4170
|
+
if (exportType !== 1 && exportType !== 2 && exportType !== 4) {
|
|
4171
|
+
throw new ArcaConfigurationError(
|
|
4172
|
+
"The original voucher reports no export type."
|
|
4173
|
+
);
|
|
4174
|
+
}
|
|
4175
|
+
return exportType;
|
|
4176
|
+
}
|
|
4177
|
+
function noteRate(original, rate, exportType) {
|
|
4178
|
+
if (rate === void 0) {
|
|
4179
|
+
return original.exchangeRate;
|
|
4180
|
+
}
|
|
4181
|
+
if (exportType === 2) {
|
|
4182
|
+
invalid3("exchangeRate", "absent: services notes keep the original's");
|
|
4183
|
+
}
|
|
4184
|
+
return deriveCurrency2(
|
|
4185
|
+
original.currencyId === ARCA_CURRENCY_IDS.ARS ? "ARS" : { id: original.currencyId },
|
|
4186
|
+
rate
|
|
4187
|
+
).exchangeRate;
|
|
4188
|
+
}
|
|
4189
|
+
function noteLines(original, input) {
|
|
4190
|
+
const all = "all" in input ? input.all : void 0;
|
|
4191
|
+
if (input.items === void 0 === (all === void 0)) {
|
|
4192
|
+
throw new ArcaInputError(
|
|
4193
|
+
"issueCreditNote needs exactly one mode: items for a partial note, or all: true for the whole original.",
|
|
4194
|
+
{
|
|
4195
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
4196
|
+
field: input.items === void 0 ? "items" : "all",
|
|
4197
|
+
expected: "exactly one of items or all: true"
|
|
4198
|
+
}
|
|
4199
|
+
);
|
|
4200
|
+
}
|
|
4201
|
+
if (all === void 0) {
|
|
4202
|
+
return deriveItems(input.items, "items");
|
|
4203
|
+
}
|
|
4204
|
+
if (all !== true) {
|
|
4205
|
+
invalid3("all", "true");
|
|
4206
|
+
}
|
|
4207
|
+
if (original.items.length === 0) {
|
|
4208
|
+
throw new ArcaInputError(
|
|
4209
|
+
"ARCA reports no lines for the original, so all: true has nothing to credit; list the items instead.",
|
|
4210
|
+
{
|
|
4211
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
4212
|
+
field: "all",
|
|
4213
|
+
expected: "items, since the original reports no lines"
|
|
4214
|
+
}
|
|
4215
|
+
);
|
|
4216
|
+
}
|
|
4217
|
+
return { items: original.items, total: originalTotal(original) };
|
|
4218
|
+
}
|
|
4219
|
+
function inheritedReceiver(original) {
|
|
4220
|
+
return {
|
|
4221
|
+
destination: original.destination,
|
|
4222
|
+
receiverName: original.receiverName,
|
|
4223
|
+
...original.receiverCountryTaxId === void 0 ? {} : { receiverCountryTaxId: original.receiverCountryTaxId },
|
|
4224
|
+
receiverAddress: original.receiverAddress,
|
|
4225
|
+
...original.receiverTaxId === void 0 ? {} : { receiverTaxId: original.receiverTaxId }
|
|
4226
|
+
};
|
|
4227
|
+
}
|
|
4228
|
+
function assertExportWindow(data, now = /* @__PURE__ */ new Date()) {
|
|
4229
|
+
assertExportDateWindow(
|
|
4230
|
+
data.voucherDate ?? buenosAiresDate(now),
|
|
4231
|
+
data.exportType === 2,
|
|
4232
|
+
buenosAiresDate(now)
|
|
4233
|
+
);
|
|
4234
|
+
}
|
|
4235
|
+
function exportPreview(prepared, originals) {
|
|
4236
|
+
return {
|
|
4237
|
+
voucherClass: "E",
|
|
4238
|
+
voucherType: prepared.data.voucherType,
|
|
4239
|
+
date: isoDate(prepared.data.voucherDate),
|
|
4240
|
+
header: exportHeader(prepared.data),
|
|
4241
|
+
amounts: { ...prepared.amounts },
|
|
4242
|
+
request: structuredClone(prepared.data),
|
|
4243
|
+
service: "wsfex",
|
|
4244
|
+
...originals === void 0 ? {} : { originals }
|
|
4245
|
+
};
|
|
4246
|
+
}
|
|
4247
|
+
function createExportIssuance(wsfex, context) {
|
|
4248
|
+
return {
|
|
4249
|
+
issue: (prepare, operation, hashInput, options) => issueExport(wsfex, context, prepare, operation, hashInput, options),
|
|
4250
|
+
async original(coordinates, options) {
|
|
4251
|
+
const result = await wsfex.lookupVoucher({
|
|
4252
|
+
...auth(options),
|
|
4253
|
+
...coordinates
|
|
4254
|
+
});
|
|
4255
|
+
return result.kind === "found" ? result.voucher : null;
|
|
4256
|
+
},
|
|
4257
|
+
async lookup(coordinates, options) {
|
|
4258
|
+
const result = await wsfex.lookupVoucher({
|
|
4259
|
+
...auth(options),
|
|
4260
|
+
...coordinates
|
|
4261
|
+
});
|
|
4262
|
+
return result.kind === "found" ? exportSummary(result.voucher) : null;
|
|
4263
|
+
},
|
|
4264
|
+
lastAuthorized: (sequence, options) => wsfex.getLastVoucherNumber({ ...auth(options), ...sequence }),
|
|
4265
|
+
recover: (key, json, options) => recoverExport(wsfex, context, key, json, options)
|
|
4266
|
+
};
|
|
4267
|
+
}
|
|
4268
|
+
async function recoverExport(wsfex, context, key, json, options) {
|
|
4269
|
+
const first = readExportRecord(json);
|
|
4270
|
+
const issuer = first.representedTaxId ?? context.taxId;
|
|
4271
|
+
if (options.representedTaxId !== void 0 && String(options.representedTaxId) !== issuer) {
|
|
4272
|
+
throw new ArcaInputError(
|
|
4273
|
+
"Reservation belongs to another represented taxpayer",
|
|
4274
|
+
{ code: "ARCA_INPUT_IDEMPOTENCY_MISMATCH" }
|
|
4275
|
+
);
|
|
4276
|
+
}
|
|
4277
|
+
const consulted = {
|
|
4278
|
+
...options,
|
|
4279
|
+
...first.representedTaxId === void 0 ? {} : { representedTaxId: first.representedTaxId }
|
|
4280
|
+
};
|
|
4281
|
+
return await exportLock(
|
|
4282
|
+
context,
|
|
4283
|
+
issuer,
|
|
4284
|
+
options
|
|
4285
|
+
)(async () => {
|
|
4286
|
+
const store = context.store;
|
|
4287
|
+
const latest = store ? await storeCall(
|
|
4288
|
+
() => store.get(attemptKey(context.environment, context.taxId, key))
|
|
4289
|
+
) : null;
|
|
4290
|
+
const record = latest === null ? first : readExportRecord(latest);
|
|
4291
|
+
const outcome = await consultReservation(wsfex, record, consulted, issuer);
|
|
4292
|
+
return {
|
|
4293
|
+
...outcome,
|
|
4294
|
+
service: "wsfex",
|
|
4295
|
+
...options.include?.request ? {
|
|
4296
|
+
request: {
|
|
4297
|
+
...structuredClone(record.sent),
|
|
4298
|
+
id: record.requestId,
|
|
4299
|
+
number: record.number
|
|
4300
|
+
}
|
|
4301
|
+
} : {}
|
|
4302
|
+
};
|
|
4303
|
+
});
|
|
4304
|
+
}
|
|
4305
|
+
async function consultReservation(wsfex, record, options, issuer) {
|
|
4306
|
+
const attempted = {
|
|
4307
|
+
salesPoint: record.salesPoint,
|
|
4308
|
+
voucherType: record.voucherType,
|
|
4309
|
+
number: record.number
|
|
4310
|
+
};
|
|
4311
|
+
const attempt = {
|
|
4312
|
+
service: "wsfex",
|
|
4313
|
+
operation: "FEXAuthorize",
|
|
4314
|
+
kind: "indeterminate",
|
|
4315
|
+
reason: "incomplete_response",
|
|
4316
|
+
results: {},
|
|
4317
|
+
errors: [],
|
|
4318
|
+
observations: []
|
|
4319
|
+
};
|
|
4320
|
+
const total = Math.round(record.sent.totalAmount * 100);
|
|
4321
|
+
const amounts = { computedTotal: total, sentTotal: total, vatAdjustment: 0 };
|
|
4322
|
+
let found;
|
|
4323
|
+
try {
|
|
4324
|
+
const result = await wsfex.lookupVoucher({
|
|
4325
|
+
...auth(options),
|
|
4326
|
+
...attempted
|
|
4327
|
+
});
|
|
4328
|
+
found = result.kind === "found" ? result.voucher : null;
|
|
4329
|
+
} catch (error) {
|
|
4330
|
+
return {
|
|
4331
|
+
kind: "indeterminate",
|
|
4332
|
+
attempted,
|
|
4333
|
+
attempt,
|
|
4334
|
+
lookup: options.abortSignal?.aborted ? { kind: "aborted" } : { kind: "failed", error: toArcaSafeErrorMetadata(error) }
|
|
4335
|
+
};
|
|
4336
|
+
}
|
|
4337
|
+
return settled(
|
|
4338
|
+
attempt,
|
|
4339
|
+
attempt,
|
|
4340
|
+
found,
|
|
4341
|
+
record.requestId,
|
|
4342
|
+
attempted,
|
|
4343
|
+
record.sent,
|
|
4344
|
+
(authorization) => issuedVoucher(
|
|
4345
|
+
record.sent,
|
|
4346
|
+
attempted,
|
|
4347
|
+
record.requestId,
|
|
4348
|
+
authorization,
|
|
4349
|
+
amounts,
|
|
4350
|
+
issuer
|
|
4351
|
+
)
|
|
4352
|
+
);
|
|
4353
|
+
}
|
|
4354
|
+
function exportLock(context, issuer, options) {
|
|
4355
|
+
const key = `arca:v1:lock:wsfex:${context.environment}:${issuer}`;
|
|
4356
|
+
return (fn) => context.store?.withLock ? context.store.withLock(key, fn, { signal: options.abortSignal }) : withLocalLock(key, fn, options.abortSignal);
|
|
4357
|
+
}
|
|
4358
|
+
function auth(options) {
|
|
4359
|
+
return {
|
|
4360
|
+
...options.representedTaxId === void 0 ? {} : { representedTaxId: options.representedTaxId },
|
|
4361
|
+
...options.forceRefresh === void 0 ? {} : { forceRefresh: options.forceRefresh },
|
|
4362
|
+
...options.abortSignal === void 0 ? {} : { abortSignal: options.abortSignal }
|
|
4363
|
+
};
|
|
4364
|
+
}
|
|
4365
|
+
async function issueExport(wsfex, context, prepare, operation, hashInput, options) {
|
|
4366
|
+
if (options.service !== void 0) {
|
|
4367
|
+
invalid3("options.service", "absent: export vouchers always use WSFEX");
|
|
4368
|
+
}
|
|
4369
|
+
if (options.number !== void 0) {
|
|
4370
|
+
invalid3("options.number", "absent: WSFEX numbers are read under a lock");
|
|
4371
|
+
}
|
|
4372
|
+
const representedTaxId = options.representedTaxId === void 0 ? void 0 : String(options.representedTaxId);
|
|
4373
|
+
const issuer = representedTaxId ?? context.taxId;
|
|
4374
|
+
const lock = exportLock(context, issuer, options);
|
|
4375
|
+
const idempotencyKey = options.idempotencyKey;
|
|
4376
|
+
if (idempotencyKey === void 0 || !context.store) {
|
|
4377
|
+
const prepared2 = await prepare();
|
|
4378
|
+
assertExportWindow(prepared2.data);
|
|
4379
|
+
return lock(() => fresh(prepared2));
|
|
4380
|
+
}
|
|
4381
|
+
const store = context.store;
|
|
4382
|
+
const recordKey = attemptKey(
|
|
4383
|
+
context.environment,
|
|
4384
|
+
context.taxId,
|
|
4385
|
+
idempotencyKey
|
|
4386
|
+
);
|
|
4387
|
+
const inputHash = canonicalHash({
|
|
4388
|
+
input: hashInput,
|
|
4389
|
+
representedTaxId,
|
|
4390
|
+
service: "wsfex"
|
|
4391
|
+
});
|
|
4392
|
+
const existing = await storeCall(() => store.get(recordKey));
|
|
4393
|
+
if (existing !== null) {
|
|
4394
|
+
return lock(
|
|
4395
|
+
async () => replay(await storeCall(() => store.get(recordKey)) ?? existing)
|
|
4396
|
+
);
|
|
4397
|
+
}
|
|
4398
|
+
const prepared = await prepare();
|
|
4399
|
+
assertExportWindow(prepared.data);
|
|
4400
|
+
return lock(async () => {
|
|
4401
|
+
const prior = await storeCall(() => store.get(recordKey));
|
|
4402
|
+
return prior === null ? fresh(prepared) : replay(prior);
|
|
4403
|
+
});
|
|
4404
|
+
async function fresh(prepared2) {
|
|
4405
|
+
const { data } = prepared2;
|
|
4406
|
+
const number = await wsfex.getLastVoucherNumber({
|
|
4407
|
+
...auth(options),
|
|
4408
|
+
salesPoint: data.salesPoint,
|
|
4409
|
+
voucherType: data.voucherType
|
|
4410
|
+
}) + 1;
|
|
4411
|
+
const id = await nextRequestId();
|
|
4412
|
+
if (idempotencyKey === void 0 || !context.store) {
|
|
4413
|
+
return submit({ id, number, data, fresh: true });
|
|
4414
|
+
}
|
|
4415
|
+
const record = {
|
|
4416
|
+
v: 2,
|
|
4417
|
+
operation,
|
|
4418
|
+
service: "wsfex",
|
|
4419
|
+
...representedTaxId === void 0 ? {} : { representedTaxId },
|
|
4420
|
+
salesPoint: data.salesPoint,
|
|
4421
|
+
voucherType: data.voucherType,
|
|
4422
|
+
number,
|
|
4423
|
+
requestId: id,
|
|
4424
|
+
inputHash,
|
|
4425
|
+
sent: data,
|
|
4426
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4427
|
+
};
|
|
4428
|
+
const store2 = context.store;
|
|
4429
|
+
if (await storeCall(() => store2.add(recordKey, JSON.stringify(record)))) {
|
|
4430
|
+
return submit({ id, number, data, fresh: true, record });
|
|
4431
|
+
}
|
|
4432
|
+
const winner = await storeCall(() => store2.get(recordKey));
|
|
4433
|
+
if (winner === null) {
|
|
4434
|
+
throw new ArcaConfigurationError(
|
|
4435
|
+
"ARCA reservation disappeared after atomic creation lost."
|
|
4436
|
+
);
|
|
4437
|
+
}
|
|
4438
|
+
return replay(winner);
|
|
4439
|
+
}
|
|
4440
|
+
function replay(json) {
|
|
4441
|
+
const stored = readExportRecord(json);
|
|
4442
|
+
if (stored.operation !== operation || stored.inputHash !== inputHash || stored.representedTaxId !== representedTaxId) {
|
|
4443
|
+
throw new ArcaInputError(
|
|
4444
|
+
"The idempotency key was already used with different input or operation.",
|
|
4445
|
+
{
|
|
4446
|
+
code: "ARCA_INPUT_IDEMPOTENCY_MISMATCH",
|
|
4447
|
+
field: "options.idempotencyKey"
|
|
4448
|
+
}
|
|
4449
|
+
);
|
|
4450
|
+
}
|
|
4451
|
+
return submit({
|
|
4452
|
+
id: stored.requestId,
|
|
4453
|
+
number: stored.number,
|
|
4454
|
+
data: stored.sent,
|
|
4455
|
+
fresh: false,
|
|
4456
|
+
record: stored
|
|
4457
|
+
});
|
|
4458
|
+
}
|
|
4459
|
+
async function submit(claim) {
|
|
4460
|
+
const first = await attemptOnce(claim, claim.id, claim.fresh);
|
|
4461
|
+
if (first !== "collided") {
|
|
4462
|
+
return first;
|
|
4463
|
+
}
|
|
4464
|
+
const id = await nextRequestId();
|
|
4465
|
+
await moveRecord(claim.record, id);
|
|
4466
|
+
const second = await attemptOnce(claim, id, true);
|
|
4467
|
+
if (second === "collided") {
|
|
4468
|
+
throw new ArcaConfigurationError("WSFEX answered a reused id twice.");
|
|
4469
|
+
}
|
|
4470
|
+
return second;
|
|
4471
|
+
}
|
|
4472
|
+
async function attemptOnce(claim, id, mayCollide) {
|
|
4473
|
+
const { number, data } = claim;
|
|
4474
|
+
const attempted = {
|
|
4475
|
+
salesPoint: data.salesPoint,
|
|
4476
|
+
voucherType: data.voucherType,
|
|
4477
|
+
number
|
|
4478
|
+
};
|
|
4479
|
+
const finish = (outcome2) => ({
|
|
4480
|
+
...outcome2,
|
|
4481
|
+
service: "wsfex",
|
|
4482
|
+
...options.include?.request ? { request: { ...structuredClone(data), id, number } } : {}
|
|
4483
|
+
});
|
|
4484
|
+
const outcome = await send(data, id, number);
|
|
4485
|
+
const attempt = evidence(outcome);
|
|
4486
|
+
const direct = answered(outcome, attempt, id, attempted);
|
|
4487
|
+
if (direct !== void 0) {
|
|
4488
|
+
return finish(
|
|
4489
|
+
direct.kind === "authorized" ? { ...direct, voucher: issued(data, attempted, id, direct.voucher) } : direct
|
|
4490
|
+
);
|
|
4491
|
+
}
|
|
4492
|
+
const consulted = await consult(outcome, attempt, attempted);
|
|
4493
|
+
if ("outcome" in consulted) {
|
|
4494
|
+
return finish(consulted.outcome);
|
|
4495
|
+
}
|
|
4496
|
+
const { found } = consulted;
|
|
4497
|
+
if (mayCollide && outcome.kind === "authorized" && outcome.reprocessed === true && found === null) {
|
|
4498
|
+
return "collided";
|
|
4499
|
+
}
|
|
4500
|
+
return finish(
|
|
4501
|
+
settled(
|
|
4502
|
+
outcome,
|
|
4503
|
+
attempt,
|
|
4504
|
+
found,
|
|
4505
|
+
id,
|
|
4506
|
+
attempted,
|
|
4507
|
+
data,
|
|
4508
|
+
(voucher) => issued(data, attempted, id, voucher)
|
|
4509
|
+
)
|
|
4510
|
+
);
|
|
4511
|
+
}
|
|
4512
|
+
async function consult(outcome, attempt, attempted) {
|
|
4513
|
+
const aborted = () => ({
|
|
4514
|
+
outcome: {
|
|
4515
|
+
kind: "indeterminate",
|
|
4516
|
+
attempted,
|
|
4517
|
+
attempt,
|
|
4518
|
+
lookup: { kind: "aborted" }
|
|
4519
|
+
}
|
|
4520
|
+
});
|
|
4521
|
+
if (options.abortSignal?.aborted && outcome.kind !== "authorized") {
|
|
4522
|
+
return aborted();
|
|
4523
|
+
}
|
|
4524
|
+
try {
|
|
4525
|
+
const result = await wsfex.lookupVoucher({
|
|
4526
|
+
...auth(options),
|
|
4527
|
+
...attempted
|
|
4528
|
+
});
|
|
4529
|
+
return { found: result.kind === "found" ? result.voucher : null };
|
|
4530
|
+
} catch (error) {
|
|
4531
|
+
return options.abortSignal?.aborted ? aborted() : {
|
|
4532
|
+
outcome: {
|
|
4533
|
+
kind: "indeterminate",
|
|
4534
|
+
attempted,
|
|
4535
|
+
attempt,
|
|
4536
|
+
lookup: { kind: "failed", error: toArcaSafeErrorMetadata(error) }
|
|
4537
|
+
}
|
|
4538
|
+
};
|
|
4539
|
+
}
|
|
4540
|
+
}
|
|
4541
|
+
async function nextRequestId() {
|
|
4542
|
+
const received = await wsfex.getLastRequestId(auth(options));
|
|
4543
|
+
const key = requestIdKey(context.environment, issuer);
|
|
4544
|
+
const store2 = context.store;
|
|
4545
|
+
const issued2 = store2 ? readRequestIdMarker(await storeCall(() => store2.get(key))) : localRequestIds.get(key) ?? 0;
|
|
4546
|
+
const id = Math.max(received, issued2) + 1;
|
|
4547
|
+
if (id > MAX_REQUEST_ID) {
|
|
4548
|
+
throw new ArcaConfigurationError("WSFEX request ids are exhausted.");
|
|
4549
|
+
}
|
|
4550
|
+
if (store2) {
|
|
4551
|
+
const marker = { v: 1, id };
|
|
4552
|
+
await storeCall(() => store2.set(key, JSON.stringify(marker)));
|
|
4553
|
+
} else {
|
|
4554
|
+
localRequestIds.set(key, id);
|
|
4555
|
+
}
|
|
4556
|
+
return id;
|
|
4557
|
+
}
|
|
4558
|
+
function send(data, id, number) {
|
|
4559
|
+
const once = (forceRefresh) => wsfex.issue({
|
|
4560
|
+
...data,
|
|
4561
|
+
id,
|
|
4562
|
+
number,
|
|
4563
|
+
...auth({
|
|
4564
|
+
...options,
|
|
4565
|
+
...forceRefresh ? { forceRefresh: true } : {}
|
|
4566
|
+
})
|
|
4567
|
+
});
|
|
4568
|
+
return once().then(
|
|
4569
|
+
(outcome) => outcome.kind === "indeterminate" && outcome.reason === "authentication_rejected" && options.forceRefresh !== true ? once(true) : outcome
|
|
4570
|
+
);
|
|
4571
|
+
}
|
|
4572
|
+
async function moveRecord(record, id) {
|
|
4573
|
+
const store2 = context.store;
|
|
4574
|
+
if (record === void 0 || store2 === void 0) {
|
|
4575
|
+
return;
|
|
4576
|
+
}
|
|
4577
|
+
const moved = { ...record, requestId: id };
|
|
4578
|
+
await storeCall(() => store2.set(recordKey, JSON.stringify(moved)));
|
|
4579
|
+
}
|
|
4580
|
+
function issued(data, attempted, id, authorization) {
|
|
4581
|
+
const total = Math.round(data.totalAmount * 100);
|
|
4582
|
+
return issuedVoucher(
|
|
4583
|
+
data,
|
|
4584
|
+
attempted,
|
|
4585
|
+
id,
|
|
4586
|
+
authorization,
|
|
4587
|
+
{ computedTotal: total, sentTotal: total, vatAdjustment: 0 },
|
|
4588
|
+
issuer
|
|
4589
|
+
);
|
|
4590
|
+
}
|
|
4591
|
+
function evidence(outcome) {
|
|
4592
|
+
const { raw, ...rest } = outcome;
|
|
4593
|
+
return options.include?.rawResponse && raw !== void 0 ? { ...rest, rawResponse: raw } : rest;
|
|
4594
|
+
}
|
|
4595
|
+
}
|
|
4596
|
+
function issuedVoucher(data, attempted, id, authorization, amounts, issuer) {
|
|
4597
|
+
const date = isoDate(data.voucherDate);
|
|
4598
|
+
return {
|
|
4599
|
+
...attempted,
|
|
4600
|
+
voucherClass: "E",
|
|
4601
|
+
requestId: id,
|
|
4602
|
+
date,
|
|
4603
|
+
header: exportHeader(data),
|
|
4604
|
+
cae: authorization.cae,
|
|
4605
|
+
caeExpiry: isoDate(authorization.caeExpiry) ?? "",
|
|
4606
|
+
amounts: { ...amounts },
|
|
4607
|
+
...exportQr(issuer, attempted, data, date, authorization.cae)
|
|
4608
|
+
};
|
|
4609
|
+
}
|
|
4610
|
+
function answered(outcome, attempt, id, attempted) {
|
|
4611
|
+
if (outcome.kind === "authorized" && !outcome.reprocessed && echoes(outcome, id, attempted)) {
|
|
4612
|
+
return {
|
|
4613
|
+
kind: "authorized",
|
|
4614
|
+
recoveredByMatch: false,
|
|
4615
|
+
voucher: { cae: outcome.cae, caeExpiry: outcome.caeExpiry },
|
|
4616
|
+
authorization: attempt
|
|
4617
|
+
};
|
|
4618
|
+
}
|
|
4619
|
+
if (outcome.kind === "rejected" && !hasError(outcome, "1535")) {
|
|
4620
|
+
return {
|
|
4621
|
+
kind: "rejected",
|
|
4622
|
+
attempted,
|
|
4623
|
+
issues: [...outcome.errors, ...outcome.observations],
|
|
4624
|
+
authorization: attempt
|
|
4625
|
+
};
|
|
4626
|
+
}
|
|
4627
|
+
return void 0;
|
|
4628
|
+
}
|
|
4629
|
+
function settled(outcome, attempt, found, id, attempted, sent, issue) {
|
|
4630
|
+
if (found !== null && found.id === id && sameVoucher(found, sent)) {
|
|
4631
|
+
const authorized = outcome.kind === "authorized" ? outcome : void 0;
|
|
4632
|
+
const cae = found.cae ?? authorized?.cae;
|
|
4633
|
+
const caeExpiry = found.caeExpiry ?? authorized?.caeExpiry;
|
|
4634
|
+
if (found.result !== "A" || !cae || !caeExpiry) {
|
|
4635
|
+
return {
|
|
4636
|
+
kind: "indeterminate",
|
|
4637
|
+
attempted,
|
|
4638
|
+
attempt,
|
|
4639
|
+
lookup: {
|
|
4640
|
+
kind: "incomplete",
|
|
4641
|
+
reason: "ARCA reports no approved result, CAE and CAE expiry"
|
|
4642
|
+
}
|
|
4643
|
+
};
|
|
4644
|
+
}
|
|
4645
|
+
const voucher = issue({ cae, caeExpiry });
|
|
4646
|
+
return authorized ? {
|
|
4647
|
+
kind: "authorized",
|
|
4648
|
+
recoveredByMatch: false,
|
|
4649
|
+
voucher,
|
|
4650
|
+
authorization: attempt
|
|
4651
|
+
} : {
|
|
4652
|
+
kind: "authorized",
|
|
4653
|
+
recoveredByMatch: true,
|
|
4654
|
+
voucher,
|
|
4655
|
+
attempt,
|
|
4656
|
+
lookup: exportSummary(found)
|
|
4657
|
+
};
|
|
4658
|
+
}
|
|
4659
|
+
if (found !== null) {
|
|
4660
|
+
return {
|
|
4661
|
+
kind: "conflict",
|
|
4662
|
+
attempted,
|
|
4663
|
+
attempt,
|
|
4664
|
+
found: exportSummary(found),
|
|
4665
|
+
reason: found.id === id ? "ARCA stored different fiscal fields under this request id" : "Another request holds this voucher number"
|
|
4666
|
+
};
|
|
4667
|
+
}
|
|
4668
|
+
if (outcome.kind === "rejected") {
|
|
4669
|
+
return {
|
|
4670
|
+
kind: "rejected",
|
|
4671
|
+
attempted,
|
|
4672
|
+
issues: [...outcome.errors, ...outcome.observations],
|
|
4673
|
+
authorization: attempt
|
|
4674
|
+
};
|
|
4675
|
+
}
|
|
4676
|
+
return {
|
|
4677
|
+
kind: "indeterminate",
|
|
4678
|
+
attempted,
|
|
4679
|
+
attempt,
|
|
4680
|
+
lookup: { kind: "not_found" }
|
|
4681
|
+
};
|
|
4682
|
+
}
|
|
4683
|
+
function echoes(outcome, id, attempted) {
|
|
4684
|
+
const echo = outcome.echo ?? {};
|
|
4685
|
+
return (echo.id === void 0 || echo.id === id) && (echo.salesPoint === void 0 || echo.salesPoint === attempted.salesPoint) && (echo.voucherType === void 0 || echo.voucherType === attempted.voucherType) && (echo.number === void 0 || echo.number === attempted.number);
|
|
4686
|
+
}
|
|
4687
|
+
function hasError(outcome, code) {
|
|
4688
|
+
return outcome.errors.some((issue) => issue.code === code);
|
|
4689
|
+
}
|
|
4690
|
+
function sameVoucher(found, sent) {
|
|
4691
|
+
return canonicalHash(fiscalIdentity(found)) === canonicalHash(fiscalIdentity(sent));
|
|
4692
|
+
}
|
|
4693
|
+
function fiscalIdentity(voucher) {
|
|
4694
|
+
return {
|
|
4695
|
+
voucherType: voucher.voucherType,
|
|
4696
|
+
salesPoint: voucher.salesPoint,
|
|
4697
|
+
voucherDate: trimmed(voucher.voucherDate),
|
|
4698
|
+
exportType: voucher.exportType,
|
|
4699
|
+
permitExists: trimmed(voucher.permitExists),
|
|
4700
|
+
permits: (voucher.permits ?? []).map((permit) => [
|
|
4701
|
+
trimmed(permit.id),
|
|
4702
|
+
permit.destination
|
|
4703
|
+
]),
|
|
4704
|
+
destination: voucher.destination,
|
|
4705
|
+
receiverName: trimmed(voucher.receiverName),
|
|
4706
|
+
receiverCountryTaxId: trimmed(voucher.receiverCountryTaxId),
|
|
4707
|
+
receiverAddress: trimmed(voucher.receiverAddress),
|
|
4708
|
+
receiverTaxId: trimmed(voucher.receiverTaxId),
|
|
4709
|
+
currencyId: trimmed(voucher.currencyId),
|
|
4710
|
+
exchangeRate: decimal(voucher.exchangeRate),
|
|
4711
|
+
sameCurrencyForeignCancellation: trimmed(
|
|
4712
|
+
voucher.sameCurrencyForeignCancellation
|
|
4713
|
+
),
|
|
4714
|
+
commercialObservations: trimmed(voucher.commercialObservations),
|
|
4715
|
+
totalAmount: decimal(voucher.totalAmount),
|
|
4716
|
+
observations: trimmed(voucher.observations),
|
|
4717
|
+
// ARCA may fill the issuer's CUIT; the original is named by coordinates.
|
|
4718
|
+
associatedVouchers: (voucher.associatedVouchers ?? []).map((original) => [
|
|
4719
|
+
original.voucherType,
|
|
4720
|
+
original.salesPoint,
|
|
4721
|
+
original.number
|
|
4722
|
+
]),
|
|
4723
|
+
paymentTerms: trimmed(voucher.paymentTerms),
|
|
4724
|
+
incoterms: trimmed(voucher.incoterms),
|
|
4725
|
+
incotermsDetail: trimmed(voucher.incotermsDetail),
|
|
4726
|
+
language: voucher.language,
|
|
4727
|
+
items: voucher.items.map((item) => [
|
|
4728
|
+
trimmed(item.code),
|
|
4729
|
+
trimmed(item.description),
|
|
4730
|
+
decimal(item.quantity ?? 0),
|
|
4731
|
+
item.unit,
|
|
4732
|
+
decimal(item.unitPrice ?? "0"),
|
|
4733
|
+
decimal(item.discount ?? 0),
|
|
4734
|
+
decimal(item.amount)
|
|
4735
|
+
]),
|
|
4736
|
+
paymentDate: trimmed(voucher.paymentDate),
|
|
4737
|
+
activities: (voucher.activities ?? []).map(
|
|
4738
|
+
(activity) => trimmed(activity.id)?.padStart(6, "0")
|
|
4739
|
+
)
|
|
4740
|
+
};
|
|
4741
|
+
}
|
|
4742
|
+
function trimmed(value) {
|
|
4743
|
+
return value?.replace(/\r\n?/g, "\n").trim() || void 0;
|
|
4744
|
+
}
|
|
4745
|
+
function decimal(value) {
|
|
4746
|
+
if (value === void 0) {
|
|
4747
|
+
return;
|
|
4748
|
+
}
|
|
4749
|
+
const written = typeof value === "number" ? value.toFixed(6) : value.trim();
|
|
4750
|
+
const match = /^(-?)(\d+)(?:\.(\d+))?$/.exec(written);
|
|
4751
|
+
if (!match) {
|
|
4752
|
+
return written;
|
|
4753
|
+
}
|
|
4754
|
+
const whole = (match[2] ?? "0").replace(/^0+(?=\d)/, "");
|
|
4755
|
+
const fraction = (match[3] ?? "").replace(/0+$/, "");
|
|
4756
|
+
const sign = whole === "0" && fraction === "" ? "" : match[1] ?? "";
|
|
4757
|
+
return `${sign}${whole}${fraction ? `.${fraction}` : ""}`;
|
|
4758
|
+
}
|
|
4759
|
+
var MAX_REQUEST_ID = 999999999999999;
|
|
4760
|
+
var localRequestIds = /* @__PURE__ */ new Map();
|
|
4761
|
+
function requestIdKey(environment, issuer) {
|
|
4762
|
+
return `arca:v1:wsfex:request-id:${environment}:${issuer}`;
|
|
4763
|
+
}
|
|
4764
|
+
function readRequestIdMarker(json) {
|
|
4765
|
+
if (json === null) {
|
|
4766
|
+
return 0;
|
|
4767
|
+
}
|
|
4768
|
+
try {
|
|
4769
|
+
const marker = JSON.parse(json);
|
|
4770
|
+
if (marker?.v === 1 && Number.isSafeInteger(marker.id) && marker.id >= 0) {
|
|
4771
|
+
return marker.id;
|
|
4772
|
+
}
|
|
4773
|
+
} catch {
|
|
4774
|
+
}
|
|
4775
|
+
throw new ArcaConfigurationError(
|
|
4776
|
+
"Invalid WSFEX request id marker; preserve it for reconciliation."
|
|
4777
|
+
);
|
|
4778
|
+
}
|
|
4779
|
+
var localLocks = /* @__PURE__ */ new Map();
|
|
4780
|
+
async function withLocalLock(key, fn, signal) {
|
|
4781
|
+
const previous = localLocks.get(key) ?? Promise.resolve();
|
|
4782
|
+
let release = () => void 0;
|
|
4783
|
+
const current = new Promise((resolve) => {
|
|
4784
|
+
release = resolve;
|
|
4785
|
+
});
|
|
4786
|
+
const chained = previous.then(() => current);
|
|
4787
|
+
localLocks.set(key, chained);
|
|
4788
|
+
chained.then(() => {
|
|
4789
|
+
if (localLocks.get(key) === chained) {
|
|
4790
|
+
localLocks.delete(key);
|
|
4791
|
+
}
|
|
4792
|
+
});
|
|
4793
|
+
try {
|
|
4794
|
+
await waitForTurn(previous, signal);
|
|
4795
|
+
} catch (error) {
|
|
4796
|
+
release();
|
|
4797
|
+
throw error;
|
|
4798
|
+
}
|
|
4799
|
+
try {
|
|
4800
|
+
return await fn();
|
|
4801
|
+
} finally {
|
|
4802
|
+
release();
|
|
4803
|
+
}
|
|
4804
|
+
}
|
|
4805
|
+
function waitForTurn(previous, signal) {
|
|
4806
|
+
throwIfAborted(signal);
|
|
4807
|
+
let timer;
|
|
4808
|
+
let onAbort;
|
|
4809
|
+
const stopped = new Promise((_, reject) => {
|
|
4810
|
+
timer = setTimeout(
|
|
4811
|
+
() => reject(
|
|
4812
|
+
new ArcaLockTimeoutError(
|
|
4813
|
+
"ARCA store lock stayed held; no work was attempted.",
|
|
4814
|
+
{ reason: "held" }
|
|
4815
|
+
)
|
|
4816
|
+
),
|
|
4817
|
+
MAX_WAIT_MS
|
|
4818
|
+
);
|
|
4819
|
+
timer.unref?.();
|
|
4820
|
+
onAbort = () => {
|
|
4821
|
+
try {
|
|
4822
|
+
throwIfAborted(signal);
|
|
4823
|
+
} catch (error) {
|
|
4824
|
+
reject(error);
|
|
4825
|
+
}
|
|
4826
|
+
};
|
|
4827
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4828
|
+
});
|
|
4829
|
+
return Promise.race([previous, stopped]).finally(() => {
|
|
4830
|
+
clearTimeout(timer);
|
|
4831
|
+
if (onAbort) {
|
|
4832
|
+
signal?.removeEventListener("abort", onAbort);
|
|
4833
|
+
}
|
|
4834
|
+
});
|
|
4835
|
+
}
|
|
4836
|
+
function readExportRecord(json) {
|
|
4837
|
+
let record;
|
|
4838
|
+
try {
|
|
4839
|
+
record = JSON.parse(json);
|
|
4840
|
+
} catch (cause) {
|
|
4841
|
+
throw new ArcaConfigurationError(
|
|
4842
|
+
"Invalid ARCA reservation record; preserve it for reconciliation.",
|
|
4843
|
+
{ cause }
|
|
4844
|
+
);
|
|
4845
|
+
}
|
|
4846
|
+
if (record?.service !== "wsfex") {
|
|
4847
|
+
throw new ArcaInputError(
|
|
4848
|
+
"The idempotency key was already used with different input or operation.",
|
|
4849
|
+
{
|
|
4850
|
+
code: "ARCA_INPUT_IDEMPOTENCY_MISMATCH",
|
|
4851
|
+
field: "options.idempotencyKey"
|
|
4852
|
+
}
|
|
4853
|
+
);
|
|
4854
|
+
}
|
|
4855
|
+
if (record.v !== 2 || !["issue", "creditNote", "debitNote"].includes(record.operation) || typeof record.inputHash !== "string" || !record.sent || !Number.isSafeInteger(record.number) || !Number.isSafeInteger(record.requestId) || record.sent.salesPoint !== record.salesPoint || record.sent.voucherType !== record.voucherType) {
|
|
4856
|
+
throw new ArcaConfigurationError(
|
|
4857
|
+
"Invalid ARCA reservation record; preserve it for reconciliation."
|
|
4858
|
+
);
|
|
4859
|
+
}
|
|
4860
|
+
return record;
|
|
4861
|
+
}
|
|
4862
|
+
function exportHeader(data) {
|
|
4863
|
+
const header = {
|
|
4864
|
+
exportType: data.exportType,
|
|
4865
|
+
destination: data.destination,
|
|
4866
|
+
receiverName: data.receiverName,
|
|
4867
|
+
receiverAddress: data.receiverAddress,
|
|
4868
|
+
currencyId: data.currencyId,
|
|
4869
|
+
language: data.language
|
|
4870
|
+
};
|
|
4871
|
+
assign(header, "receiverTaxId", data.receiverTaxId);
|
|
4872
|
+
assign(header, "receiverCountryTaxId", data.receiverCountryTaxId);
|
|
4873
|
+
assign(header, "exchangeRate", data.exchangeRate);
|
|
4874
|
+
assign(
|
|
4875
|
+
header,
|
|
4876
|
+
"paidInForeignCurrency",
|
|
4877
|
+
data.sameCurrencyForeignCancellation === void 0 ? void 0 : data.sameCurrencyForeignCancellation === "S"
|
|
4878
|
+
);
|
|
4879
|
+
assign(header, "incoterms", data.incoterms);
|
|
4880
|
+
assign(header, "incotermsDetail", data.incotermsDetail);
|
|
4881
|
+
assign(
|
|
4882
|
+
header,
|
|
4883
|
+
"permitExists",
|
|
4884
|
+
data.permitExists === void 0 ? void 0 : data.permitExists === "S"
|
|
4885
|
+
);
|
|
4886
|
+
assign(header, "paymentTerms", data.paymentTerms);
|
|
4887
|
+
assign(header, "paymentDate", isoDate(data.paymentDate));
|
|
4888
|
+
return header;
|
|
4889
|
+
}
|
|
4890
|
+
function exportSummary(voucher) {
|
|
4891
|
+
const summary = {
|
|
4892
|
+
voucherClass: "E",
|
|
4893
|
+
number: voucher.number
|
|
4894
|
+
};
|
|
4895
|
+
assign(summary, "salesPoint", voucher.salesPoint);
|
|
4896
|
+
assign(summary, "voucherType", voucher.voucherType);
|
|
4897
|
+
assign(summary, "requestId", voucher.id);
|
|
4898
|
+
assign(summary, "date", isoDate(voucher.voucherDate));
|
|
4899
|
+
assign(summary, "exportType", voucher.exportType);
|
|
4900
|
+
assign(summary, "destination", voucher.destination);
|
|
4901
|
+
assign(summary, "receiverName", voucher.receiverName);
|
|
4902
|
+
assign(summary, "receiverAddress", voucher.receiverAddress);
|
|
4903
|
+
assign(summary, "receiverTaxId", voucher.receiverTaxId);
|
|
4904
|
+
assign(summary, "receiverCountryTaxId", voucher.receiverCountryTaxId);
|
|
4905
|
+
assign(summary, "currencyId", voucher.currencyId);
|
|
4906
|
+
assign(summary, "exchangeRate", voucher.exchangeRate);
|
|
4907
|
+
assign(
|
|
4908
|
+
summary,
|
|
4909
|
+
"totalAmount",
|
|
4910
|
+
voucher.totalAmount === void 0 ? void 0 : Math.round(voucher.totalAmount * 100)
|
|
4911
|
+
);
|
|
4912
|
+
assign(summary, "result", voucher.result);
|
|
4913
|
+
assign(summary, "cae", voucher.cae);
|
|
4914
|
+
assign(summary, "caeExpiry", isoDate(voucher.caeExpiry));
|
|
4915
|
+
return summary;
|
|
4916
|
+
}
|
|
4917
|
+
function exportQr(taxId, attempted, data, date, cae) {
|
|
4918
|
+
try {
|
|
4919
|
+
const qrPayload = arcaQrPayload({
|
|
4920
|
+
taxId,
|
|
4921
|
+
...attempted,
|
|
4922
|
+
date,
|
|
4923
|
+
total: Math.round(data.totalAmount * 100),
|
|
4924
|
+
currency: data.currencyId,
|
|
4925
|
+
...data.exchangeRate === void 0 ? {} : { exchangeRate: data.exchangeRate },
|
|
4926
|
+
cae
|
|
4927
|
+
});
|
|
4928
|
+
return { qr: qrUrlForPayload(qrPayload), qrPayload };
|
|
4929
|
+
} catch {
|
|
4930
|
+
return {};
|
|
4931
|
+
}
|
|
4932
|
+
}
|
|
4933
|
+
function originalTotal(original) {
|
|
4934
|
+
return Math.round(original.totalAmount * 100);
|
|
4935
|
+
}
|
|
4936
|
+
function deriveReceiver2(to) {
|
|
4937
|
+
assertIssueObject(to, "to");
|
|
4938
|
+
assertIssueKeys(
|
|
4939
|
+
to,
|
|
4940
|
+
["country", "name", "address", "taxId", "countryTaxId"],
|
|
4941
|
+
"to"
|
|
4942
|
+
);
|
|
4943
|
+
const receiver = to;
|
|
4944
|
+
if (!(Number.isSafeInteger(receiver.country) && receiver.country >= 1 && receiver.country <= 999)) {
|
|
4945
|
+
invalid3("to.country", "an ARCA destination country code");
|
|
4946
|
+
}
|
|
4947
|
+
if (receiver.taxId === void 0 && receiver.countryTaxId === void 0) {
|
|
4948
|
+
throw new ArcaInputError("to.taxId or to.countryTaxId is required.", {
|
|
4949
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
4950
|
+
field: "to.taxId",
|
|
4951
|
+
expected: "the receiver's tax ID abroad, ARCA's country CUIT, or both"
|
|
4952
|
+
});
|
|
4953
|
+
}
|
|
4954
|
+
if (receiver.countryTaxId !== void 0 && !(typeof receiver.countryTaxId === "string" && /^\d{11}$/.test(receiver.countryTaxId))) {
|
|
4955
|
+
invalid3("to.countryTaxId", "an 11-digit string");
|
|
4956
|
+
}
|
|
4957
|
+
return {
|
|
4958
|
+
destination: receiver.country,
|
|
4959
|
+
receiverName: text(receiver.name, "to.name", 200),
|
|
4960
|
+
...receiver.countryTaxId === void 0 ? {} : { receiverCountryTaxId: receiver.countryTaxId },
|
|
4961
|
+
receiverAddress: text(receiver.address, "to.address", 300),
|
|
4962
|
+
...optionalText(receiver.taxId, "receiverTaxId", 50, "to.taxId")
|
|
4963
|
+
};
|
|
4964
|
+
}
|
|
4965
|
+
function deriveExport(value) {
|
|
4966
|
+
assertIssueObject(value, "export");
|
|
4967
|
+
const exported = value;
|
|
4968
|
+
if (exported.kind === "goods") {
|
|
4969
|
+
assertIssueKeys(
|
|
4970
|
+
exported,
|
|
4971
|
+
["kind", "incoterms", "incotermsDetail", "permits"],
|
|
4972
|
+
"export"
|
|
4973
|
+
);
|
|
4974
|
+
if (typeof exported.incoterms !== "string" || !/^[A-Z]{3}$/.test(exported.incoterms)) {
|
|
4975
|
+
invalid3("export.incoterms", "a three-letter Incoterm such as FOB");
|
|
4976
|
+
}
|
|
4977
|
+
let permits;
|
|
4978
|
+
if (exported.permits !== void 0) {
|
|
4979
|
+
if (!Array.isArray(exported.permits) || exported.permits.length === 0) {
|
|
4980
|
+
invalid3("export.permits", "a non-empty list, or absent while pending");
|
|
4981
|
+
}
|
|
4982
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4983
|
+
permits = exported.permits.map((entry, index) => {
|
|
4984
|
+
const field = `export.permits[${index}]`;
|
|
4985
|
+
const permit = entry;
|
|
4986
|
+
assertIssueObject(permit, field);
|
|
4987
|
+
assertIssueKeys(permit, ["id", "destination"], field);
|
|
4988
|
+
if (typeof permit.id !== "string" || !/^\d{5}[A-Z0-9]{4}\d{6}[A-Z]$/.test(permit.id)) {
|
|
4989
|
+
invalid3(`${field}.id`, "a shipping permit such as 09052EC01006154G");
|
|
4990
|
+
}
|
|
4991
|
+
if (!(typeof permit.destination === "number" && Number.isSafeInteger(permit.destination) && permit.destination >= 1 && permit.destination <= 999)) {
|
|
4992
|
+
invalid3(`${field}.destination`, "an ARCA destination country code");
|
|
4993
|
+
}
|
|
4994
|
+
const pair = `${permit.id}:${permit.destination}`;
|
|
4995
|
+
if (seen.has(pair)) {
|
|
4996
|
+
invalid3(field, "a permit and destination listed once");
|
|
4997
|
+
}
|
|
4998
|
+
seen.add(pair);
|
|
4999
|
+
return {
|
|
5000
|
+
id: permit.id,
|
|
5001
|
+
destination: permit.destination
|
|
5002
|
+
};
|
|
5003
|
+
});
|
|
5004
|
+
}
|
|
5005
|
+
return {
|
|
5006
|
+
exportType: 1,
|
|
5007
|
+
permitExists: permits === void 0 ? "N" : "S",
|
|
5008
|
+
...permits === void 0 ? {} : { permits },
|
|
5009
|
+
incoterms: exported.incoterms,
|
|
5010
|
+
...optionalText(
|
|
5011
|
+
exported.incotermsDetail,
|
|
5012
|
+
"incotermsDetail",
|
|
5013
|
+
20,
|
|
5014
|
+
"export.incotermsDetail"
|
|
5015
|
+
)
|
|
5016
|
+
};
|
|
5017
|
+
}
|
|
5018
|
+
if (exported.kind === "services" || exported.kind === "other") {
|
|
5019
|
+
assertIssueKeys(exported, ["kind", "paymentDate"], "export");
|
|
5020
|
+
if (exported.paymentDate === void 0) {
|
|
5021
|
+
throw new ArcaInputError("export.paymentDate is required.", {
|
|
5022
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
5023
|
+
field: "export.paymentDate",
|
|
5024
|
+
expected: "the date the receiver pays"
|
|
5025
|
+
});
|
|
5026
|
+
}
|
|
5027
|
+
return {
|
|
5028
|
+
exportType: EXPORT_KINDS[exported.kind],
|
|
5029
|
+
paymentDate: normalizeWsfeDateInput(
|
|
5030
|
+
exported.paymentDate,
|
|
5031
|
+
"export.paymentDate"
|
|
5032
|
+
)
|
|
5033
|
+
};
|
|
5034
|
+
}
|
|
5035
|
+
return invalid3("export.kind", "goods, services or other");
|
|
5036
|
+
}
|
|
5037
|
+
function deriveCurrency2(currency, rate) {
|
|
5038
|
+
const value = currency ?? "ARS";
|
|
5039
|
+
let currencyId;
|
|
5040
|
+
if (typeof value === "object" && value !== null) {
|
|
5041
|
+
assertIssueKeys(value, ["id"], "currency");
|
|
5042
|
+
if (typeof value.id !== "string" || !/^[A-Z0-9]{3}$/.test(value.id)) {
|
|
5043
|
+
invalid3("currency.id", "a three-character ARCA currency code");
|
|
5044
|
+
}
|
|
5045
|
+
currencyId = value.id;
|
|
5046
|
+
} else if (value === "ARS" || value === "USD") {
|
|
5047
|
+
currencyId = ARCA_CURRENCY_IDS[value];
|
|
5048
|
+
} else {
|
|
5049
|
+
return invalid3("currency", "ARS, USD or { id }");
|
|
5050
|
+
}
|
|
5051
|
+
if (rate !== void 0 && typeof rate !== "string") {
|
|
5052
|
+
invalid3("exchangeRate", "a decimal string");
|
|
5053
|
+
}
|
|
5054
|
+
if (currencyId === ARCA_CURRENCY_IDS.ARS) {
|
|
5055
|
+
const exchangeRate2 = serializeArcaExchangeRate(rate ?? "1", "exchangeRate");
|
|
5056
|
+
if (Number(exchangeRate2) !== 1) {
|
|
5057
|
+
invalid3("exchangeRate", "1 for pesos");
|
|
5058
|
+
}
|
|
5059
|
+
return { currencyId, exchangeRate: exchangeRate2 };
|
|
5060
|
+
}
|
|
5061
|
+
if (rate === void 0) {
|
|
5062
|
+
throw new ArcaInputError("exchangeRate is required for this currency.", {
|
|
5063
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
5064
|
+
field: "exchangeRate",
|
|
5065
|
+
expected: "ARCA's rate, from wsfex.getExchangeRate()"
|
|
5066
|
+
});
|
|
5067
|
+
}
|
|
5068
|
+
return {
|
|
5069
|
+
currencyId,
|
|
5070
|
+
exchangeRate: serializeArcaExchangeRate(rate, "exchangeRate")
|
|
5071
|
+
};
|
|
5072
|
+
}
|
|
5073
|
+
function deriveLanguage(language) {
|
|
5074
|
+
if (language === void 0) {
|
|
5075
|
+
return 1;
|
|
5076
|
+
}
|
|
5077
|
+
if (typeof language !== "string" || !Object.hasOwn(LANGUAGES, language)) {
|
|
5078
|
+
return invalid3("language", "es, en or pt");
|
|
5079
|
+
}
|
|
5080
|
+
return LANGUAGES[language];
|
|
5081
|
+
}
|
|
5082
|
+
var SCALE_6 = 1000000n;
|
|
5083
|
+
function deriveItems(value, path) {
|
|
5084
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > 9999) {
|
|
5085
|
+
invalid3(path, "a list of 1 to 9999 items");
|
|
5086
|
+
}
|
|
5087
|
+
let total = 0n;
|
|
5088
|
+
const items = value.map((item, index) => {
|
|
5089
|
+
const line = deriveItem(item, `${path}[${index}]`);
|
|
5090
|
+
total += BigInt(item.amount);
|
|
5091
|
+
return line;
|
|
5092
|
+
});
|
|
5093
|
+
if (total < 0n) {
|
|
5094
|
+
invalid3(path, "lines whose total is zero or positive");
|
|
5095
|
+
}
|
|
5096
|
+
return { items, total: Number(total) };
|
|
5097
|
+
}
|
|
5098
|
+
function deriveItem(item, field) {
|
|
5099
|
+
assertIssueObject(item, field);
|
|
5100
|
+
assertIssueKeys(
|
|
5101
|
+
item,
|
|
5102
|
+
[
|
|
5103
|
+
"description",
|
|
5104
|
+
"unit",
|
|
5105
|
+
"quantity",
|
|
5106
|
+
"unitPrice",
|
|
5107
|
+
"discount",
|
|
5108
|
+
"code",
|
|
5109
|
+
"amount"
|
|
5110
|
+
],
|
|
5111
|
+
field
|
|
5112
|
+
);
|
|
5113
|
+
if (!(Number.isSafeInteger(item.unit) && item.unit >= 0 && item.unit <= 99)) {
|
|
5114
|
+
invalid3(`${field}.unit`, "an ARCA unit of measure");
|
|
5115
|
+
}
|
|
5116
|
+
assertItemAmount(item, field);
|
|
5117
|
+
const line = {
|
|
5118
|
+
...optionalText(item.code, "code", 50, `${field}.code`),
|
|
5119
|
+
description: text(item.description, `${field}.description`, 4e3),
|
|
5120
|
+
unit: item.unit,
|
|
5121
|
+
amount: item.amount / 100
|
|
5122
|
+
};
|
|
5123
|
+
if (!UNPRICED_UNITS.includes(item.unit)) {
|
|
5124
|
+
return { ...line, ...pricedLine(item, field) };
|
|
5125
|
+
}
|
|
5126
|
+
for (const key of ["quantity", "unitPrice", "discount"]) {
|
|
5127
|
+
if (item[key] !== void 0 && Number(item[key]) !== 0) {
|
|
5128
|
+
invalid3(`${field}.${key}`, `absent for unit ${item.unit}`);
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
return line;
|
|
5132
|
+
}
|
|
5133
|
+
function assertItemAmount(item, field) {
|
|
5134
|
+
if (!Number.isSafeInteger(item.amount)) {
|
|
5135
|
+
invalid3(`${field}.amount`, "an integer amount in minor units");
|
|
5136
|
+
}
|
|
5137
|
+
if (item.unit === 99 && item.amount >= 0) {
|
|
5138
|
+
invalid3(`${field}.amount`, "negative for a discount line");
|
|
5139
|
+
}
|
|
5140
|
+
if (item.unit !== 97 && item.unit !== 99 && item.amount < 0) {
|
|
5141
|
+
invalid3(`${field}.amount`, "zero or positive for this unit");
|
|
5142
|
+
}
|
|
5143
|
+
if (Math.abs(item.amount) > 999999999999999) {
|
|
5144
|
+
invalid3(`${field}.amount`, "at most 13 integer digits");
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5147
|
+
function pricedLine(item, field) {
|
|
5148
|
+
const quantity = scaledQuantity(item.quantity, `${field}.quantity`);
|
|
5149
|
+
const price = scaledPrice(item.unitPrice, `${field}.unitPrice`);
|
|
5150
|
+
const discount = item.discount ?? 0;
|
|
5151
|
+
if (!(Number.isSafeInteger(discount) && discount >= 0)) {
|
|
5152
|
+
invalid3(`${field}.discount`, "a non-negative amount in minor units");
|
|
5153
|
+
}
|
|
5154
|
+
const exact = quantity * price - BigInt(discount) * 10000000000n;
|
|
5155
|
+
if (exact < 0n) {
|
|
5156
|
+
invalid3(`${field}.discount`, "at most the line's quantity \xD7 unitPrice");
|
|
5157
|
+
}
|
|
5158
|
+
if (!withinLineTolerance(exact, BigInt(item.amount))) {
|
|
5159
|
+
throw new ArcaInputError(
|
|
5160
|
+
`${field}.amount must equal quantity \xD7 unitPrice less discount.`,
|
|
5161
|
+
{
|
|
5162
|
+
code: "ARCA_INPUT_AMOUNT_MISMATCH",
|
|
5163
|
+
field: `${field}.amount`,
|
|
5164
|
+
expected: String(roundHalfEvenRatio(exact, 10000000000n))
|
|
5165
|
+
}
|
|
5166
|
+
);
|
|
5167
|
+
}
|
|
5168
|
+
return {
|
|
5169
|
+
quantity: item.quantity,
|
|
5170
|
+
unitPrice: item.unitPrice,
|
|
5171
|
+
...discount > 0 ? { discount: discount / 100 } : {}
|
|
5172
|
+
};
|
|
5173
|
+
}
|
|
5174
|
+
function withinLineTolerance(exact, amountMinorUnits) {
|
|
5175
|
+
const computed = roundHalfEvenRatio(exact, 10000000n);
|
|
5176
|
+
const amount = amountMinorUnits * 1000n;
|
|
5177
|
+
const error = computed > amount ? computed - amount : amount - computed;
|
|
5178
|
+
if (error <= 1000n) {
|
|
5179
|
+
return true;
|
|
2810
5180
|
}
|
|
2811
|
-
const
|
|
2812
|
-
|
|
2813
|
-
|
|
5181
|
+
const base = computed < amount ? computed : amount;
|
|
5182
|
+
return base > 0n && error * 10000n <= base;
|
|
5183
|
+
}
|
|
5184
|
+
function scaledQuantity(value, field) {
|
|
5185
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
5186
|
+
return invalid3(field, "a positive number");
|
|
2814
5187
|
}
|
|
2815
|
-
|
|
5188
|
+
const fixed = value.toFixed(6);
|
|
5189
|
+
if (Number(fixed) !== value || fixed.split(".")[0].length > 12) {
|
|
5190
|
+
invalid3(field, "at most 12 integer digits and 6 decimals");
|
|
5191
|
+
}
|
|
5192
|
+
const [whole, fraction = ""] = fixed.split(".");
|
|
5193
|
+
return BigInt(whole) * SCALE_6 + BigInt(fraction.padEnd(6, "0"));
|
|
2816
5194
|
}
|
|
2817
|
-
function
|
|
2818
|
-
|
|
5195
|
+
function scaledPrice(value, field) {
|
|
5196
|
+
if (typeof value !== "string" || !/^\d{1,12}(\.\d{1,6})?$/.test(value)) {
|
|
5197
|
+
return invalid3(field, "a decimal string with at most 6 decimals");
|
|
5198
|
+
}
|
|
5199
|
+
const [whole, fraction = ""] = value.split(".");
|
|
5200
|
+
return BigInt(whole) * SCALE_6 + BigInt(fraction.padEnd(6, "0"));
|
|
2819
5201
|
}
|
|
2820
|
-
function
|
|
2821
|
-
|
|
5202
|
+
function reviewedTotal(total, computed) {
|
|
5203
|
+
if (total === void 0) {
|
|
5204
|
+
return computed;
|
|
5205
|
+
}
|
|
5206
|
+
if (!Number.isSafeInteger(total) || total < 0) {
|
|
5207
|
+
invalid3("total", "a non-negative integer amount in minor units");
|
|
5208
|
+
}
|
|
5209
|
+
if (total !== computed) {
|
|
5210
|
+
throw new ArcaInputError("total must equal the sum of items.", {
|
|
5211
|
+
code: "ARCA_INPUT_AMOUNT_MISMATCH",
|
|
5212
|
+
field: "total",
|
|
5213
|
+
expected: String(computed)
|
|
5214
|
+
});
|
|
5215
|
+
}
|
|
5216
|
+
return computed;
|
|
2822
5217
|
}
|
|
2823
|
-
function
|
|
2824
|
-
|
|
2825
|
-
|
|
5218
|
+
function assertSalesPoint2(value, field) {
|
|
5219
|
+
if (!(typeof value === "number" && Number.isSafeInteger(value) && value >= 1 && value <= 99998)) {
|
|
5220
|
+
invalid3(field, "an integer from 1 through 99998");
|
|
5221
|
+
}
|
|
5222
|
+
}
|
|
5223
|
+
function text(value, field, max) {
|
|
5224
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
5225
|
+
throw new ArcaInputError(`${field} is required.`, {
|
|
5226
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
5227
|
+
field,
|
|
5228
|
+
expected: `a non-empty string of at most ${max} characters`
|
|
5229
|
+
});
|
|
5230
|
+
}
|
|
5231
|
+
if (value.length > max) {
|
|
5232
|
+
invalid3(field, `at most ${max} characters`);
|
|
5233
|
+
}
|
|
5234
|
+
return value;
|
|
5235
|
+
}
|
|
5236
|
+
function optionalText(value, key, max, field = key) {
|
|
5237
|
+
if (value === void 0) {
|
|
5238
|
+
return {};
|
|
5239
|
+
}
|
|
5240
|
+
return { [key]: text(value, field, max) };
|
|
5241
|
+
}
|
|
5242
|
+
function isoDate(value) {
|
|
5243
|
+
return value === void 0 ? void 0 : toIsoDate(value) ?? value;
|
|
5244
|
+
}
|
|
5245
|
+
function assign(target, key, value) {
|
|
5246
|
+
if (value !== void 0) {
|
|
5247
|
+
target[key] = value;
|
|
5248
|
+
}
|
|
5249
|
+
}
|
|
5250
|
+
function invalid3(field, expected) {
|
|
5251
|
+
throw new ArcaInputError(`${field} must be ${expected}.`, {
|
|
5252
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
5253
|
+
field,
|
|
5254
|
+
expected
|
|
5255
|
+
});
|
|
2826
5256
|
}
|
|
2827
5257
|
|
|
2828
5258
|
// src/services/issuance-wsmtxca.ts
|
|
@@ -2838,7 +5268,7 @@ var iso = (value) => {
|
|
|
2838
5268
|
};
|
|
2839
5269
|
function assertWsmtxcaVoucherType(voucherType) {
|
|
2840
5270
|
if (voucherFamily(voucherType).voucherClass === "C") {
|
|
2841
|
-
|
|
5271
|
+
invalid4(
|
|
2842
5272
|
"voucherType",
|
|
2843
5273
|
"WSMTXCA authorizes class A and B vouchers only; issue class C through WSFE"
|
|
2844
5274
|
);
|
|
@@ -2859,7 +5289,7 @@ function wsmtxcaRequest(data, number) {
|
|
|
2859
5289
|
(source) => source !== void 0
|
|
2860
5290
|
).length;
|
|
2861
5291
|
if (!lines?.length || sources !== 1) {
|
|
2862
|
-
|
|
5292
|
+
invalid4("items", "WSMTXCA requires items with line detail");
|
|
2863
5293
|
}
|
|
2864
5294
|
const items = lines.map((line) => ({
|
|
2865
5295
|
unidadesMtx: line.matrixUnits,
|
|
@@ -2886,7 +5316,7 @@ function wsmtxcaRequest(data, number) {
|
|
|
2886
5316
|
}
|
|
2887
5317
|
const tributes = data.taxes?.length ? data.taxes : void 0;
|
|
2888
5318
|
if (tributes === void 0 && normalizeArcaAmountToMinorUnits(data.taxAmount, "taxes") !== 0n) {
|
|
2889
|
-
|
|
5319
|
+
invalid4("taxes", "Tribute amount requires tribute details");
|
|
2890
5320
|
}
|
|
2891
5321
|
return {
|
|
2892
5322
|
comprobanteCAERequest: {
|
|
@@ -2955,7 +5385,7 @@ function wsmtxcaRequest(data, number) {
|
|
|
2955
5385
|
}
|
|
2956
5386
|
};
|
|
2957
5387
|
}
|
|
2958
|
-
function
|
|
5388
|
+
function invalid4(field, message) {
|
|
2959
5389
|
throw new ArcaInputError(message, {
|
|
2960
5390
|
code: "ARCA_INPUT_INVALID_VALUE",
|
|
2961
5391
|
field
|
|
@@ -3186,7 +5616,7 @@ function wsmtxcaAdditionalData(fields) {
|
|
|
3186
5616
|
}
|
|
3187
5617
|
|
|
3188
5618
|
// src/services/wsfe-credit-note.ts
|
|
3189
|
-
function
|
|
5619
|
+
function invalid5(reason) {
|
|
3190
5620
|
throw new ArcaInputError(`issueCreditNote cannot proceed: ${reason}.`, {
|
|
3191
5621
|
code: "ARCA_INPUT_INVALID_VALUE"
|
|
3192
5622
|
});
|
|
@@ -3206,7 +5636,7 @@ function originalReceiverDocument(original) {
|
|
|
3206
5636
|
}
|
|
3207
5637
|
function required(value, field) {
|
|
3208
5638
|
if (value === void 0 || value === null) {
|
|
3209
|
-
|
|
5639
|
+
invalid5(`original is missing ${field}`);
|
|
3210
5640
|
}
|
|
3211
5641
|
return value;
|
|
3212
5642
|
}
|
|
@@ -3235,7 +5665,7 @@ function deriveWsfeFullCreditNote(original, input, now = /* @__PURE__ */ new Dat
|
|
|
3235
5665
|
}
|
|
3236
5666
|
function deriveWsfePartialCreditNote(originals, input, now = /* @__PURE__ */ new Date(), kind = "creditNote") {
|
|
3237
5667
|
if (input.items === void 0 && input.amounts === void 0) {
|
|
3238
|
-
|
|
5668
|
+
invalid5("items is required to credit chosen lines");
|
|
3239
5669
|
}
|
|
3240
5670
|
const requestedTotal = input.total === void 0 ? void 0 : assertArcaMinorUnits(input.total, "total");
|
|
3241
5671
|
const { note, header } = prepareCreditNote(originals, input, now, kind);
|
|
@@ -3248,7 +5678,7 @@ function deriveWsfePartialCreditNote(originals, input, now = /* @__PURE__ */ new
|
|
|
3248
5678
|
input.amounts,
|
|
3249
5679
|
input.taxes
|
|
3250
5680
|
) : calculateWsfeAmounts(lineSource);
|
|
3251
|
-
const
|
|
5681
|
+
const originalTotal2 = originals.reduce(
|
|
3252
5682
|
(sum, original) => sum + normalizeArcaAmountToMinorUnits(
|
|
3253
5683
|
required(original.totalAmount, "totalAmount"),
|
|
3254
5684
|
"totalAmount"
|
|
@@ -3256,8 +5686,8 @@ function deriveWsfePartialCreditNote(originals, input, now = /* @__PURE__ */ new
|
|
|
3256
5686
|
0n
|
|
3257
5687
|
);
|
|
3258
5688
|
const ceiling = originals.length === 1 ? "the original" : "the sum of the originals";
|
|
3259
|
-
if (kind === "creditNote" && (requestedTotal ?? BigInt(amounts.sentTotal)) >
|
|
3260
|
-
|
|
5689
|
+
if (kind === "creditNote" && (requestedTotal ?? BigInt(amounts.sentTotal)) > originalTotal2) {
|
|
5690
|
+
invalid5(
|
|
3261
5691
|
`the note total is greater than ${ceiling}; the SDK does not track earlier notes against an original`
|
|
3262
5692
|
);
|
|
3263
5693
|
}
|
|
@@ -3280,8 +5710,8 @@ function deriveWsfePartialCreditNote(originals, input, now = /* @__PURE__ */ new
|
|
|
3280
5710
|
)
|
|
3281
5711
|
);
|
|
3282
5712
|
const sentTotal = input.amounts && requestedTotal !== void 0 ? Number(requestedTotal) : total;
|
|
3283
|
-
if (kind === "creditNote" && BigInt(sentTotal) >
|
|
3284
|
-
|
|
5713
|
+
if (kind === "creditNote" && BigInt(sentTotal) > originalTotal2) {
|
|
5714
|
+
invalid5(`the note total is greater than ${ceiling}`);
|
|
3285
5715
|
}
|
|
3286
5716
|
data.totalAmount = minor(sentTotal, "total");
|
|
3287
5717
|
amounts.computedTotal += total - amounts.sentTotal;
|
|
@@ -3300,7 +5730,7 @@ function prepareCreditNote(originals, input, now, kind) {
|
|
|
3300
5730
|
);
|
|
3301
5731
|
const [first, ...rest] = derived;
|
|
3302
5732
|
if (first === void 0) {
|
|
3303
|
-
|
|
5733
|
+
invalid5("for must name at least one original");
|
|
3304
5734
|
}
|
|
3305
5735
|
for (const other of rest) {
|
|
3306
5736
|
assertInheritedHeader(first.header, other.header);
|
|
@@ -3323,7 +5753,7 @@ function assertInheritedHeader(first, other) {
|
|
|
3323
5753
|
const left = first[field];
|
|
3324
5754
|
const right = other[field];
|
|
3325
5755
|
if (JSON.stringify(left) !== JSON.stringify(right)) {
|
|
3326
|
-
|
|
5756
|
+
invalid5(
|
|
3327
5757
|
`the originals disagree on ${field}, which the note inherits from them`
|
|
3328
5758
|
);
|
|
3329
5759
|
}
|
|
@@ -3334,14 +5764,14 @@ function resolveReceiverCondition(original, input) {
|
|
|
3334
5764
|
const reported = original.receiverVatConditionId;
|
|
3335
5765
|
if (reported === void 0) {
|
|
3336
5766
|
if (supplied === void 0) {
|
|
3337
|
-
|
|
5767
|
+
invalid5(
|
|
3338
5768
|
"original is missing receiverVatConditionId; pass to.condition with the receiver's condition"
|
|
3339
5769
|
);
|
|
3340
5770
|
}
|
|
3341
5771
|
return supplied;
|
|
3342
5772
|
}
|
|
3343
5773
|
if (supplied !== void 0 && supplied !== reported) {
|
|
3344
|
-
|
|
5774
|
+
invalid5(
|
|
3345
5775
|
`to.condition (${supplied}) does not match the original's receiver condition (${reported})`
|
|
3346
5776
|
);
|
|
3347
5777
|
}
|
|
@@ -3351,14 +5781,14 @@ function prepareOneCreditNote(original, input, now, kind) {
|
|
|
3351
5781
|
assertOriginalExtensions(original);
|
|
3352
5782
|
const family = voucherFamily(original.voucherType ?? 0);
|
|
3353
5783
|
if (family.types[2] === original.voucherType) {
|
|
3354
|
-
|
|
5784
|
+
invalid5("original must be an invoice or debit note");
|
|
3355
5785
|
}
|
|
3356
5786
|
const note = {
|
|
3357
5787
|
voucherClass: family.voucherClass,
|
|
3358
5788
|
voucherType: family.types[kind === "creditNote" ? 2 : 1]
|
|
3359
5789
|
};
|
|
3360
5790
|
if (!(["A", "O"].includes(original.result ?? "") && original.cae?.trim() && original.caeExpiry?.trim())) {
|
|
3361
|
-
|
|
5791
|
+
invalid5("original is not authorized");
|
|
3362
5792
|
}
|
|
3363
5793
|
const voucherDate = normalizeWsfeDateInput(
|
|
3364
5794
|
input.date ?? buenosAiresDate(now),
|
|
@@ -3369,7 +5799,7 @@ function prepareOneCreditNote(original, input, now, kind) {
|
|
|
3369
5799
|
"original.voucherDate"
|
|
3370
5800
|
);
|
|
3371
5801
|
if (originalDate > voucherDate && originalDate.slice(0, 6) !== voucherDate.slice(0, 6)) {
|
|
3372
|
-
|
|
5802
|
+
invalid5(
|
|
3373
5803
|
"original date is later than the note and outside its month (10210)"
|
|
3374
5804
|
);
|
|
3375
5805
|
}
|
|
@@ -3446,7 +5876,7 @@ function assertNoteReceiver(to) {
|
|
|
3446
5876
|
try {
|
|
3447
5877
|
receiverConditionId(to.condition, "to.condition");
|
|
3448
5878
|
} catch {
|
|
3449
|
-
|
|
5879
|
+
invalid5("to.condition must be a supported receiver condition");
|
|
3450
5880
|
}
|
|
3451
5881
|
}
|
|
3452
5882
|
function assertCreditNoteInput(input) {
|
|
@@ -3479,7 +5909,7 @@ function assertCreditNoteInput(input) {
|
|
|
3479
5909
|
...input.to === void 0 ? {} : { to: { condition: input.to.condition } }
|
|
3480
5910
|
};
|
|
3481
5911
|
if (input.items !== void 0 && input.amounts !== void 0) {
|
|
3482
|
-
|
|
5912
|
+
invalid5("use items or amounts, never both");
|
|
3483
5913
|
}
|
|
3484
5914
|
if ((input.items === void 0 && input.amounts === void 0) === (input.all === void 0)) {
|
|
3485
5915
|
throw new ArcaInputError(
|
|
@@ -3616,7 +6046,7 @@ function assertOriginalExtensions(original) {
|
|
|
3616
6046
|
["associatedPeriod", "PeriodoAsoc"]
|
|
3617
6047
|
]) {
|
|
3618
6048
|
if (original.raw[rawField] && original[field] === void 0) {
|
|
3619
|
-
|
|
6049
|
+
invalid5(`original ${field} could not be decoded`);
|
|
3620
6050
|
}
|
|
3621
6051
|
}
|
|
3622
6052
|
try {
|
|
@@ -3647,7 +6077,7 @@ function assertOriginalExtensions(original) {
|
|
|
3647
6077
|
);
|
|
3648
6078
|
}
|
|
3649
6079
|
} catch {
|
|
3650
|
-
|
|
6080
|
+
invalid5("original extension fields are incomplete or malformed");
|
|
3651
6081
|
}
|
|
3652
6082
|
}
|
|
3653
6083
|
function assertFullMode(input) {
|
|
@@ -3684,7 +6114,7 @@ function assertFullMode(input) {
|
|
|
3684
6114
|
}
|
|
3685
6115
|
|
|
3686
6116
|
// src/services/vouchers.ts
|
|
3687
|
-
function createVouchersService(wsfe, context, wsmtxca) {
|
|
6117
|
+
function createVouchersService(wsfe, context, wsmtxca, wsfex) {
|
|
3688
6118
|
const select = (options = {}) => {
|
|
3689
6119
|
validateOptions(options);
|
|
3690
6120
|
if (options.service !== "wsmtxca") {
|
|
@@ -3695,63 +6125,192 @@ function createVouchersService(wsfe, context, wsmtxca) {
|
|
|
3695
6125
|
}
|
|
3696
6126
|
return createWsmtxcaIssuanceService(wsmtxca);
|
|
3697
6127
|
};
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
)
|
|
3707
|
-
|
|
6128
|
+
const exportIssuance = wsfex && context ? createExportIssuance(wsfex, context) : void 0;
|
|
6129
|
+
const exporting = () => {
|
|
6130
|
+
if (!exportIssuance) {
|
|
6131
|
+
throw new ArcaConfigurationError("WSFEX service is not configured");
|
|
6132
|
+
}
|
|
6133
|
+
return exportIssuance;
|
|
6134
|
+
};
|
|
6135
|
+
const exportOptions = (options) => {
|
|
6136
|
+
const cloned = cloneOptions(options ?? {});
|
|
6137
|
+
validateOptions(cloned);
|
|
6138
|
+
validateKeyStore(cloned, context);
|
|
6139
|
+
return cloned;
|
|
6140
|
+
};
|
|
6141
|
+
async function prepareExportNote(input, options, kind) {
|
|
6142
|
+
assertIssueObject(input, "input");
|
|
6143
|
+
assertIssueObject(input.for, "for");
|
|
6144
|
+
assertIssueKeys(input.for, ["salesPoint", "voucherType", "number"], "for");
|
|
6145
|
+
for (const [field, max] of [
|
|
6146
|
+
["salesPoint", 99998],
|
|
6147
|
+
["number", 99999999]
|
|
6148
|
+
]) {
|
|
6149
|
+
const value = input.for[field];
|
|
6150
|
+
if (!(Number.isSafeInteger(value) && value >= 1 && value <= max)) {
|
|
6151
|
+
throw new ArcaInputError(
|
|
6152
|
+
`for.${field} must be an integer from 1 through ${max}.`,
|
|
6153
|
+
{
|
|
6154
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
6155
|
+
field: `for.${field}`
|
|
6156
|
+
}
|
|
6157
|
+
);
|
|
6158
|
+
}
|
|
6159
|
+
}
|
|
6160
|
+
const original = await exporting().original(input.for, options);
|
|
6161
|
+
if (original === null) {
|
|
6162
|
+
throw new ArcaInputError("ARCA has no voucher at for.", {
|
|
6163
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
6164
|
+
field: "for",
|
|
6165
|
+
expected: "an authorized export voucher"
|
|
6166
|
+
});
|
|
6167
|
+
}
|
|
6168
|
+
return { original, prepared: deriveExportNote(original, input, kind) };
|
|
6169
|
+
}
|
|
6170
|
+
async function issueExportNote(input, inputOptions, kind) {
|
|
6171
|
+
const options = exportOptions(inputOptions);
|
|
6172
|
+
const snapshot = structuredClone(input);
|
|
6173
|
+
return await exporting().issue(
|
|
6174
|
+
async () => (await prepareExportNote(snapshot, options, kind)).prepared,
|
|
6175
|
+
kind,
|
|
6176
|
+
snapshot,
|
|
6177
|
+
options
|
|
6178
|
+
);
|
|
6179
|
+
}
|
|
6180
|
+
async function previewExportNote(input, inputOptions, kind) {
|
|
6181
|
+
const options = cloneOptions(inputOptions ?? {});
|
|
6182
|
+
assertIssueObject(options, "options");
|
|
6183
|
+
assertIssueKeys(
|
|
6184
|
+
options,
|
|
6185
|
+
["representedTaxId", "forceRefresh", "abortSignal"],
|
|
6186
|
+
"options",
|
|
6187
|
+
kind === "creditNote" ? "previewCreditNote()" : "previewDebitNote()"
|
|
6188
|
+
);
|
|
6189
|
+
validateOptions(options);
|
|
6190
|
+
const { original, prepared } = await prepareExportNote(
|
|
6191
|
+
structuredClone(input),
|
|
6192
|
+
options,
|
|
6193
|
+
kind
|
|
6194
|
+
);
|
|
6195
|
+
assertExportWindow(prepared.data);
|
|
6196
|
+
return exportPreview(prepared, [exportSummary(original)]);
|
|
6197
|
+
}
|
|
6198
|
+
async function readReservation(key, inputOptions) {
|
|
6199
|
+
const store = context?.store;
|
|
6200
|
+
if (exportIssuance === void 0 || store === void 0 || !context) {
|
|
6201
|
+
return;
|
|
6202
|
+
}
|
|
6203
|
+
const options = cloneOptions(inputOptions ?? {});
|
|
6204
|
+
assertIssueObject(options, "options");
|
|
6205
|
+
assertIssueKeys(
|
|
6206
|
+
options,
|
|
6207
|
+
["representedTaxId", "forceRefresh", "include", "abortSignal"],
|
|
6208
|
+
"options"
|
|
6209
|
+
);
|
|
6210
|
+
validateOptions(options);
|
|
6211
|
+
validateKeyStore({ ...options, idempotencyKey: key }, context);
|
|
6212
|
+
const json = await storeCall(
|
|
6213
|
+
() => store.get(attemptKey(context.environment, context.taxId, key))
|
|
6214
|
+
);
|
|
6215
|
+
return { json, options };
|
|
6216
|
+
}
|
|
6217
|
+
const service = {
|
|
6218
|
+
lookup: (async (voucher, options) => {
|
|
6219
|
+
if (!isExportVoucherType(voucher?.voucherType)) {
|
|
6220
|
+
return await lookupVoucher(select(options), voucher, options ?? {});
|
|
6221
|
+
}
|
|
6222
|
+
const checked = cloneOptions(options ?? {});
|
|
6223
|
+
assertLookup(voucher, checked);
|
|
6224
|
+
return await exporting().lookup(voucher, checked);
|
|
6225
|
+
}),
|
|
6226
|
+
lastAuthorized: (async (sequence, options) => {
|
|
6227
|
+
if (!isExportVoucherType(sequence?.voucherType)) {
|
|
6228
|
+
return await lastAuthorizedNumber(
|
|
6229
|
+
select(options),
|
|
6230
|
+
sequence,
|
|
6231
|
+
options ?? {}
|
|
6232
|
+
);
|
|
6233
|
+
}
|
|
6234
|
+
const checked = cloneOptions(options ?? {});
|
|
6235
|
+
assertSequence(sequence, checked);
|
|
6236
|
+
return await exporting().lastAuthorized(sequence, checked);
|
|
6237
|
+
}),
|
|
6238
|
+
recover: (async (key, options) => {
|
|
6239
|
+
const read = await readReservation(key, options);
|
|
6240
|
+
if (typeof read?.json === "string" && isExportRecord(read.json)) {
|
|
6241
|
+
return await exporting().recover(key, read.json, read.options);
|
|
6242
|
+
}
|
|
6243
|
+
return await recoverOperation(
|
|
6244
|
+
select,
|
|
6245
|
+
key,
|
|
6246
|
+
options === void 0 ? {} : options,
|
|
6247
|
+
context,
|
|
6248
|
+
read?.json
|
|
6249
|
+
);
|
|
6250
|
+
}),
|
|
6251
|
+
issueDebitNote: (async (input, options) => isExportNoteInput(input) ? issueExportNote(input, options, "debitNote") : issueCreditNote(
|
|
3708
6252
|
select(options),
|
|
3709
6253
|
input,
|
|
3710
6254
|
options ?? {},
|
|
3711
6255
|
context,
|
|
3712
6256
|
"debitNote",
|
|
3713
6257
|
select
|
|
3714
|
-
),
|
|
3715
|
-
previewCreditNote: async (input, options) => previewNote(
|
|
6258
|
+
)),
|
|
6259
|
+
previewCreditNote: (async (input, options) => isExportNoteInput(input) ? previewExportNote(input, options, "creditNote") : previewNote(
|
|
3716
6260
|
select(options),
|
|
3717
6261
|
input,
|
|
3718
6262
|
options ?? {},
|
|
3719
6263
|
context,
|
|
3720
6264
|
"creditNote"
|
|
3721
|
-
),
|
|
3722
|
-
previewDebitNote: async (input, options) => previewNote(
|
|
6265
|
+
)),
|
|
6266
|
+
previewDebitNote: (async (input, options) => isExportNoteInput(input) ? previewExportNote(input, options, "debitNote") : previewNote(
|
|
3723
6267
|
select(options),
|
|
3724
6268
|
input,
|
|
3725
6269
|
options ?? {},
|
|
3726
6270
|
context,
|
|
3727
6271
|
"debitNote"
|
|
3728
|
-
),
|
|
3729
|
-
issueCreditNote: async (input, options) =>
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
6272
|
+
)),
|
|
6273
|
+
issueCreditNote: (async (input, options) => isExportNoteInput(input) ? issueExportNote(input, options, "creditNote") : issueCreditNote(
|
|
6274
|
+
select(options),
|
|
6275
|
+
input,
|
|
6276
|
+
options === void 0 ? {} : options,
|
|
6277
|
+
context,
|
|
6278
|
+
"creditNote",
|
|
6279
|
+
select
|
|
6280
|
+
)),
|
|
6281
|
+
issue: (async (input, options) => {
|
|
6282
|
+
if (isExportInput(input)) {
|
|
6283
|
+
const checked = exportOptions(options);
|
|
6284
|
+
const snapshot = structuredClone(input);
|
|
6285
|
+
return await exporting().issue(
|
|
6286
|
+
() => deriveExportInvoice(snapshot),
|
|
6287
|
+
"issue",
|
|
6288
|
+
snapshot,
|
|
6289
|
+
checked
|
|
6290
|
+
);
|
|
6291
|
+
}
|
|
6292
|
+
return await issueInvoice(
|
|
3742
6293
|
select(options),
|
|
3743
6294
|
input,
|
|
3744
6295
|
options === void 0 ? {} : options,
|
|
3745
6296
|
context,
|
|
3746
6297
|
select
|
|
3747
6298
|
);
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
6299
|
+
}),
|
|
6300
|
+
preview: ((input, options) => {
|
|
6301
|
+
if (isExportInput(input)) {
|
|
6302
|
+
const checked = options ?? {};
|
|
6303
|
+
assertIssueObject(checked, "options");
|
|
6304
|
+
assertIssueKeys(checked, ["representedTaxId"], "options");
|
|
6305
|
+
validateOptions(checked);
|
|
6306
|
+
const prepared = deriveExportInvoice(input);
|
|
6307
|
+
assertExportWindow(prepared.data);
|
|
6308
|
+
return exportPreview(prepared);
|
|
6309
|
+
}
|
|
6310
|
+
return previewInvoice(input, options === void 0 ? {} : options);
|
|
6311
|
+
})
|
|
3754
6312
|
};
|
|
6313
|
+
return service;
|
|
3755
6314
|
}
|
|
3756
6315
|
function previewInvoice(input, options) {
|
|
3757
6316
|
assertIssueObject(options, "options");
|
|
@@ -3830,15 +6389,14 @@ async function issueInvoice(wsfe, input, inputOptions, context, select) {
|
|
|
3830
6389
|
const options = cloneOptions(inputOptions);
|
|
3831
6390
|
validateOptions(options);
|
|
3832
6391
|
validateKeyStore(options, context);
|
|
3833
|
-
const
|
|
6392
|
+
const snapshot = structuredClone(input);
|
|
3834
6393
|
return await runOperation(
|
|
3835
6394
|
wsfe,
|
|
3836
6395
|
"issue",
|
|
3837
|
-
|
|
3838
|
-
() =>
|
|
6396
|
+
snapshot,
|
|
6397
|
+
async () => prepareInvoice(snapshot, options),
|
|
3839
6398
|
options,
|
|
3840
6399
|
context,
|
|
3841
|
-
prepared.amounts,
|
|
3842
6400
|
select
|
|
3843
6401
|
);
|
|
3844
6402
|
}
|
|
@@ -3849,7 +6407,7 @@ function withIssuerName(input) {
|
|
|
3849
6407
|
const name = issuerConditionName(input.issuer);
|
|
3850
6408
|
return name === void 0 ? input : { ...input, issuer: name };
|
|
3851
6409
|
}
|
|
3852
|
-
async function runOperation(wsfe, operation, input, prepare, options, context,
|
|
6410
|
+
async function runOperation(wsfe, operation, input, prepare, options, context, select) {
|
|
3853
6411
|
const issuer = issuerTaxId(options, context);
|
|
3854
6412
|
if (options.idempotencyKey === void 0 || !context?.store) {
|
|
3855
6413
|
return runAuthorization(
|
|
@@ -3870,7 +6428,7 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
|
|
|
3870
6428
|
...options.service === "wsmtxca" ? { service: "wsmtxca" } : {},
|
|
3871
6429
|
...options.number === void 0 ? {} : { number: options.number }
|
|
3872
6430
|
});
|
|
3873
|
-
const
|
|
6431
|
+
const settled2 = settledKey(environment, taxId, idempotencyKey);
|
|
3874
6432
|
const existing = await storeCall(() => store.get(key));
|
|
3875
6433
|
if (existing !== null) {
|
|
3876
6434
|
return await replay(existing);
|
|
@@ -3884,27 +6442,32 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
|
|
|
3884
6442
|
if (!store.withLock) {
|
|
3885
6443
|
return await claim();
|
|
3886
6444
|
}
|
|
3887
|
-
return await store.withLock(
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
6445
|
+
return await store.withLock(
|
|
6446
|
+
sequence.lock,
|
|
6447
|
+
async () => {
|
|
6448
|
+
const prior = await storeCall(() => store.get(key));
|
|
6449
|
+
if (prior !== null) {
|
|
6450
|
+
return await replay(prior, true);
|
|
6451
|
+
}
|
|
6452
|
+
const barrier = await runSequenceBarrier({
|
|
6453
|
+
store,
|
|
6454
|
+
environment,
|
|
6455
|
+
taxId,
|
|
6456
|
+
sequence: sequence.marker,
|
|
6457
|
+
sequenceTaxId: sequence.taxId,
|
|
6458
|
+
select: select ?? (() => wsfe),
|
|
6459
|
+
options,
|
|
6460
|
+
supersededBy: idempotencyKey,
|
|
6461
|
+
readNext: () => nextNumber(wsfe, prepared.data, { ...options, number: void 0 })
|
|
6462
|
+
});
|
|
6463
|
+
return "blocked" in barrier ? {
|
|
6464
|
+
...barrier.blocked,
|
|
6465
|
+
...requestEvidence(prepared.data, void 0, options)
|
|
6466
|
+
} : await claim(barrier.reserved);
|
|
6467
|
+
},
|
|
6468
|
+
// The deadline also bounds the wait: giving up there reserved nothing.
|
|
6469
|
+
{ signal: options.abortSignal }
|
|
6470
|
+
);
|
|
3908
6471
|
function reservation(number) {
|
|
3909
6472
|
const service = options.service ?? "wsfe";
|
|
3910
6473
|
const versioned = service === "wsmtxca" || prepared.data.lines !== void 0;
|
|
@@ -3988,10 +6551,7 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
|
|
|
3988
6551
|
current,
|
|
3989
6552
|
runAuthorization(
|
|
3990
6553
|
wsfe,
|
|
3991
|
-
|
|
3992
|
-
...preparedFromRecord(current),
|
|
3993
|
-
...replayAmounts ? { amounts: replayAmounts } : {}
|
|
3994
|
-
},
|
|
6554
|
+
preparedFromRecord(current),
|
|
3995
6555
|
options,
|
|
3996
6556
|
issuer,
|
|
3997
6557
|
current.number,
|
|
@@ -4009,7 +6569,7 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
|
|
|
4009
6569
|
const uncoordinated = !store.withLock;
|
|
4010
6570
|
const outcome = await recordConflict(
|
|
4011
6571
|
store,
|
|
4012
|
-
|
|
6572
|
+
settled2,
|
|
4013
6573
|
await running,
|
|
4014
6574
|
!(presumed && uncoordinated)
|
|
4015
6575
|
);
|
|
@@ -4033,7 +6593,7 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
|
|
|
4033
6593
|
async function clearRejection(record) {
|
|
4034
6594
|
const checked = await settle2(
|
|
4035
6595
|
record,
|
|
4036
|
-
|
|
6596
|
+
consultReservation2(select ?? (() => wsfe), record, options, taxId, true),
|
|
4037
6597
|
{ presumed: true }
|
|
4038
6598
|
);
|
|
4039
6599
|
if (checked.kind !== "indeterminate" || checked.lookup.kind !== "not_found") {
|
|
@@ -4112,7 +6672,8 @@ async function consultKey({ store, environment, taxId, idempotencyKey }, stored,
|
|
|
4112
6672
|
() => store.get(attemptKey(environment, taxId, idempotencyKey))
|
|
4113
6673
|
);
|
|
4114
6674
|
return await consult(json === null ? stored : readRecord(json));
|
|
4115
|
-
}
|
|
6675
|
+
},
|
|
6676
|
+
{ signal: options.abortSignal }
|
|
4116
6677
|
);
|
|
4117
6678
|
}
|
|
4118
6679
|
async function recordConflict(store, key, outcome, durable = true) {
|
|
@@ -4148,11 +6709,11 @@ function readSettledRecord(json) {
|
|
|
4148
6709
|
);
|
|
4149
6710
|
}
|
|
4150
6711
|
}
|
|
4151
|
-
async function settledOutcome(select, reservation,
|
|
4152
|
-
if (
|
|
4153
|
-
return settledConflict(
|
|
6712
|
+
async function settledOutcome(select, reservation, settled2, options, taxId, settledFor) {
|
|
6713
|
+
if (settled2.kind === "conflict") {
|
|
6714
|
+
return settledConflict(settled2, reservation, options);
|
|
4154
6715
|
}
|
|
4155
|
-
const outcome = await
|
|
6716
|
+
const outcome = await consultReservation2(
|
|
4156
6717
|
select,
|
|
4157
6718
|
reservation,
|
|
4158
6719
|
options,
|
|
@@ -4160,14 +6721,14 @@ async function settledOutcome(select, reservation, settled, options, taxId, sett
|
|
|
4160
6721
|
true
|
|
4161
6722
|
);
|
|
4162
6723
|
const empty = outcome.kind === "indeterminate" && outcome.lookup.kind === "not_found";
|
|
4163
|
-
if (!empty && (outcome.kind !== "conflict" || await successionDisputed(
|
|
6724
|
+
if (!empty && (outcome.kind !== "conflict" || await successionDisputed(settled2, taxId, settledFor))) {
|
|
4164
6725
|
return outcome;
|
|
4165
6726
|
}
|
|
4166
6727
|
return withheld(
|
|
4167
6728
|
reservation,
|
|
4168
|
-
{ kind: "superseded", by:
|
|
6729
|
+
{ kind: "superseded", by: settled2.by, byTaxId: settled2.byTaxId ?? taxId },
|
|
4169
6730
|
options,
|
|
4170
|
-
|
|
6731
|
+
settled2.number
|
|
4171
6732
|
);
|
|
4172
6733
|
}
|
|
4173
6734
|
function withheld(reservation, lookup, options, number = reservation.number) {
|
|
@@ -4207,16 +6768,16 @@ async function successionDisputed(superseded, taxId, settledFor) {
|
|
|
4207
6768
|
}
|
|
4208
6769
|
return true;
|
|
4209
6770
|
}
|
|
4210
|
-
function settledConflict(
|
|
6771
|
+
function settledConflict(settled2, reservation, options) {
|
|
4211
6772
|
return {
|
|
4212
6773
|
kind: "conflict",
|
|
4213
6774
|
attempted: {
|
|
4214
6775
|
salesPoint: reservation.salesPoint,
|
|
4215
6776
|
voucherType: reservation.voucherType,
|
|
4216
|
-
number:
|
|
6777
|
+
number: settled2.number
|
|
4217
6778
|
},
|
|
4218
6779
|
attempt: replayEvidence(reservation.service),
|
|
4219
|
-
found:
|
|
6780
|
+
found: settled2.found,
|
|
4220
6781
|
reason: "This key already recorded another voucher at the reserved number. Reconcile before issuing under a new key.",
|
|
4221
6782
|
...reservationEvidence(reservation, options)
|
|
4222
6783
|
};
|
|
@@ -4238,6 +6799,15 @@ function validateKeyStore(options, context) {
|
|
|
4238
6799
|
}
|
|
4239
6800
|
}
|
|
4240
6801
|
function readRecord(json) {
|
|
6802
|
+
if (isExportRecord(json)) {
|
|
6803
|
+
throw new ArcaInputError(
|
|
6804
|
+
"The idempotency key was already used for an export voucher.",
|
|
6805
|
+
{
|
|
6806
|
+
code: "ARCA_INPUT_IDEMPOTENCY_MISMATCH",
|
|
6807
|
+
field: "options.idempotencyKey"
|
|
6808
|
+
}
|
|
6809
|
+
);
|
|
6810
|
+
}
|
|
4241
6811
|
try {
|
|
4242
6812
|
const record = JSON.parse(json);
|
|
4243
6813
|
if (!record || record.v !== 1 && record.v !== 2 || record.v === 2 && record.service === void 0 || !["issue", "creditNote", "debitNote"].includes(record.operation) || typeof record.inputHash !== "string" || !record.sent || record.rejectedAt !== void 0 && typeof record.rejectedAt !== "string" || !Number.isSafeInteger(record.number) || record.number < 1 || record.number > 99999999 || record.sent.salesPoint !== record.salesPoint || record.sent.voucherType !== record.voucherType) {
|
|
@@ -4258,6 +6828,13 @@ function readRecord(json) {
|
|
|
4258
6828
|
);
|
|
4259
6829
|
}
|
|
4260
6830
|
}
|
|
6831
|
+
function isExportRecord(json) {
|
|
6832
|
+
try {
|
|
6833
|
+
return JSON.parse(json)?.service === "wsfex";
|
|
6834
|
+
} catch {
|
|
6835
|
+
return false;
|
|
6836
|
+
}
|
|
6837
|
+
}
|
|
4261
6838
|
function preparedFromRecord(record) {
|
|
4262
6839
|
const sentTotal = Number(
|
|
4263
6840
|
normalizeArcaAmountToMinorUnits(record.sent.totalAmount, "totalAmount")
|
|
@@ -4291,7 +6868,7 @@ async function nextNumber(wsfe, data, options) {
|
|
|
4291
6868
|
return number;
|
|
4292
6869
|
}
|
|
4293
6870
|
async function runAuthorization(wsfe, { data, voucherClass, amounts }, options, taxId, reservedNumber, { lookupFirst = false, strangerAtNumber = false } = {}) {
|
|
4294
|
-
const
|
|
6871
|
+
const auth2 = {
|
|
4295
6872
|
representedTaxId: options.representedTaxId,
|
|
4296
6873
|
forceRefresh: options.forceRefresh,
|
|
4297
6874
|
...options.abortSignal === void 0 ? {} : { abortSignal: options.abortSignal }
|
|
@@ -4304,7 +6881,7 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
4304
6881
|
number
|
|
4305
6882
|
};
|
|
4306
6883
|
const includedRequest = requestEvidence(data, number, options);
|
|
4307
|
-
const voucher = (cae, caeExpiry, lookup) =>
|
|
6884
|
+
const voucher = (cae, caeExpiry, lookup) => issuedVoucher2(
|
|
4308
6885
|
{ attempted, voucherClass, data, amounts, taxId },
|
|
4309
6886
|
cae,
|
|
4310
6887
|
caeExpiry,
|
|
@@ -4312,7 +6889,7 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
4312
6889
|
);
|
|
4313
6890
|
const recovery = {
|
|
4314
6891
|
wsfe,
|
|
4315
|
-
auth,
|
|
6892
|
+
auth: auth2,
|
|
4316
6893
|
data,
|
|
4317
6894
|
attempted,
|
|
4318
6895
|
includeRawResponse,
|
|
@@ -4324,7 +6901,7 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
4324
6901
|
const attempt2 = replayEvidence(options.service);
|
|
4325
6902
|
let lookup;
|
|
4326
6903
|
try {
|
|
4327
|
-
lookup = await wsfe.lookupVoucher({ ...
|
|
6904
|
+
lookup = await wsfe.lookupVoucher({ ...auth2, ...attempted });
|
|
4328
6905
|
} catch (error) {
|
|
4329
6906
|
return {
|
|
4330
6907
|
kind: "indeterminate",
|
|
@@ -4339,13 +6916,13 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
4339
6916
|
}
|
|
4340
6917
|
}
|
|
4341
6918
|
let authorization = await wsfe.issue({
|
|
4342
|
-
...
|
|
6919
|
+
...auth2,
|
|
4343
6920
|
data,
|
|
4344
6921
|
voucherNumber: number
|
|
4345
6922
|
});
|
|
4346
|
-
if (authorization.kind === "indeterminate" && authorization.reason === "authentication_rejected" &&
|
|
6923
|
+
if (authorization.kind === "indeterminate" && authorization.reason === "authentication_rejected" && auth2.forceRefresh !== true) {
|
|
4347
6924
|
authorization = await wsfe.issue({
|
|
4348
|
-
...
|
|
6925
|
+
...auth2,
|
|
4349
6926
|
forceRefresh: true,
|
|
4350
6927
|
data,
|
|
4351
6928
|
voucherNumber: number
|
|
@@ -4378,7 +6955,7 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
4378
6955
|
return recoverInvoice({
|
|
4379
6956
|
wsfe,
|
|
4380
6957
|
service: options.service,
|
|
4381
|
-
auth,
|
|
6958
|
+
auth: auth2,
|
|
4382
6959
|
data,
|
|
4383
6960
|
attempted,
|
|
4384
6961
|
attempt,
|
|
@@ -4420,7 +6997,7 @@ function mayHoldNumber(issues, service = "wsfe") {
|
|
|
4420
6997
|
}
|
|
4421
6998
|
async function recoverInvoice({
|
|
4422
6999
|
wsfe,
|
|
4423
|
-
auth,
|
|
7000
|
+
auth: auth2,
|
|
4424
7001
|
data,
|
|
4425
7002
|
attempted,
|
|
4426
7003
|
attempt,
|
|
@@ -4431,7 +7008,7 @@ async function recoverInvoice({
|
|
|
4431
7008
|
service,
|
|
4432
7009
|
strangerAtNumber = false
|
|
4433
7010
|
}) {
|
|
4434
|
-
if (suppliedLookup === void 0 &&
|
|
7011
|
+
if (suppliedLookup === void 0 && auth2.abortSignal?.aborted) {
|
|
4435
7012
|
return {
|
|
4436
7013
|
kind: "indeterminate",
|
|
4437
7014
|
attempted,
|
|
@@ -4442,13 +7019,13 @@ async function recoverInvoice({
|
|
|
4442
7019
|
}
|
|
4443
7020
|
let lookup;
|
|
4444
7021
|
try {
|
|
4445
|
-
lookup = suppliedLookup ?? await wsfe.lookupVoucher({ ...
|
|
7022
|
+
lookup = suppliedLookup ?? await wsfe.lookupVoucher({ ...auth2, ...attempted });
|
|
4446
7023
|
} catch (error) {
|
|
4447
7024
|
return {
|
|
4448
7025
|
kind: "indeterminate",
|
|
4449
7026
|
attempted,
|
|
4450
7027
|
attempt,
|
|
4451
|
-
lookup:
|
|
7028
|
+
lookup: auth2.abortSignal?.aborted ? { kind: "aborted" } : { kind: "failed", error: toArcaSafeErrorMetadata(error) },
|
|
4452
7029
|
...includedRequest
|
|
4453
7030
|
};
|
|
4454
7031
|
}
|
|
@@ -4668,7 +7245,6 @@ async function issueCreditNote(wsfe, input, inputOptions, context, kind = "credi
|
|
|
4668
7245
|
() => prepareNote(wsfe, note, options, context, kind),
|
|
4669
7246
|
options,
|
|
4670
7247
|
context,
|
|
4671
|
-
void 0,
|
|
4672
7248
|
select
|
|
4673
7249
|
);
|
|
4674
7250
|
}
|
|
@@ -4785,8 +7361,7 @@ function assertBounds(value, bounds, field, method) {
|
|
|
4785
7361
|
}
|
|
4786
7362
|
}
|
|
4787
7363
|
}
|
|
4788
|
-
|
|
4789
|
-
const options = cloneOptions(inputOptions);
|
|
7364
|
+
function assertLookup(voucher, options) {
|
|
4790
7365
|
assertIssueKeys(
|
|
4791
7366
|
options,
|
|
4792
7367
|
["representedTaxId", "service", "forceRefresh", "abortSignal"],
|
|
@@ -4801,6 +7376,26 @@ async function lookupVoucher(wsfe, voucher, inputOptions) {
|
|
|
4801
7376
|
"lookup()"
|
|
4802
7377
|
);
|
|
4803
7378
|
assertBounds(voucher, LOOKUP_BOUNDS, "voucher", "lookup()");
|
|
7379
|
+
}
|
|
7380
|
+
function assertSequence(sequence, options) {
|
|
7381
|
+
assertIssueKeys(
|
|
7382
|
+
options,
|
|
7383
|
+
["representedTaxId", "service", "forceRefresh", "abortSignal"],
|
|
7384
|
+
"options",
|
|
7385
|
+
"lastAuthorized()"
|
|
7386
|
+
);
|
|
7387
|
+
assertIssueObject(sequence, "sequence");
|
|
7388
|
+
assertIssueKeys(
|
|
7389
|
+
sequence,
|
|
7390
|
+
["salesPoint", "voucherType"],
|
|
7391
|
+
"sequence",
|
|
7392
|
+
"lastAuthorized()"
|
|
7393
|
+
);
|
|
7394
|
+
assertBounds(sequence, SEQUENCE_BOUNDS, "sequence", "lastAuthorized()");
|
|
7395
|
+
}
|
|
7396
|
+
async function lookupVoucher(wsfe, voucher, inputOptions) {
|
|
7397
|
+
const options = cloneOptions(inputOptions);
|
|
7398
|
+
assertLookup(voucher, options);
|
|
4804
7399
|
const found = await wsfe.lookupVoucher({
|
|
4805
7400
|
representedTaxId: options.representedTaxId,
|
|
4806
7401
|
forceRefresh: options.forceRefresh,
|
|
@@ -4822,20 +7417,7 @@ async function lookupVoucher(wsfe, voucher, inputOptions) {
|
|
|
4822
7417
|
}
|
|
4823
7418
|
async function lastAuthorizedNumber(wsfe, sequence, inputOptions) {
|
|
4824
7419
|
const options = cloneOptions(inputOptions);
|
|
4825
|
-
|
|
4826
|
-
options,
|
|
4827
|
-
["representedTaxId", "service", "forceRefresh", "abortSignal"],
|
|
4828
|
-
"options",
|
|
4829
|
-
"lastAuthorized()"
|
|
4830
|
-
);
|
|
4831
|
-
assertIssueObject(sequence, "sequence");
|
|
4832
|
-
assertIssueKeys(
|
|
4833
|
-
sequence,
|
|
4834
|
-
["salesPoint", "voucherType"],
|
|
4835
|
-
"sequence",
|
|
4836
|
-
"lastAuthorized()"
|
|
4837
|
-
);
|
|
4838
|
-
assertBounds(sequence, SEQUENCE_BOUNDS, "sequence", "lastAuthorized()");
|
|
7420
|
+
assertSequence(sequence, options);
|
|
4839
7421
|
const next = await wsfe.getNextVoucherNumber({
|
|
4840
7422
|
representedTaxId: options.representedTaxId,
|
|
4841
7423
|
forceRefresh: options.forceRefresh,
|
|
@@ -4898,7 +7480,7 @@ function requestEvidence(data, number, options) {
|
|
|
4898
7480
|
request: options.service === "wsmtxca" ? wsmtxcaRequest(data, number) : data
|
|
4899
7481
|
} : {};
|
|
4900
7482
|
}
|
|
4901
|
-
async function recoverOperation(select, key, inputOptions, context) {
|
|
7483
|
+
async function recoverOperation(select, key, inputOptions, context, read) {
|
|
4902
7484
|
const options = cloneOptions(inputOptions);
|
|
4903
7485
|
assertIssueObject(options, "options");
|
|
4904
7486
|
assertIssueKeys(
|
|
@@ -4910,9 +7492,7 @@ async function recoverOperation(select, key, inputOptions, context) {
|
|
|
4910
7492
|
validateKeyStore({ ...options, idempotencyKey: key }, context);
|
|
4911
7493
|
const scope = keyScope(context, key);
|
|
4912
7494
|
const { store, environment, taxId } = scope;
|
|
4913
|
-
const json = await storeCall(
|
|
4914
|
-
() => store.get(attemptKey(environment, taxId, key))
|
|
4915
|
-
);
|
|
7495
|
+
const json = read === void 0 ? await storeCall(() => store.get(attemptKey(environment, taxId, key))) : read;
|
|
4916
7496
|
if (json === null) {
|
|
4917
7497
|
throw new ArcaInputError("No reservation exists for this idempotency key", {
|
|
4918
7498
|
code: "ARCA_INPUT_RESERVATION_NOT_FOUND",
|
|
@@ -4937,7 +7517,7 @@ async function recoverOperation(select, key, inputOptions, context) {
|
|
|
4937
7517
|
return await recordConflict(
|
|
4938
7518
|
store,
|
|
4939
7519
|
settledKey(environment, taxId, key),
|
|
4940
|
-
await
|
|
7520
|
+
await consultReservation2(select, current, options, taxId, rejected),
|
|
4941
7521
|
!rejected || store.withLock !== void 0
|
|
4942
7522
|
);
|
|
4943
7523
|
}
|
|
@@ -4954,7 +7534,7 @@ function keyScope(context, idempotencyKey) {
|
|
|
4954
7534
|
idempotencyKey
|
|
4955
7535
|
};
|
|
4956
7536
|
}
|
|
4957
|
-
function
|
|
7537
|
+
function consultReservation2(select, record, options, taxId, strangerAtNumber = false) {
|
|
4958
7538
|
const storedOptions = {
|
|
4959
7539
|
...options,
|
|
4960
7540
|
service: record.service ?? "wsfe",
|
|
@@ -4980,7 +7560,7 @@ function consultReservation(select, record, options, taxId, strangerAtNumber = f
|
|
|
4980
7560
|
record.number,
|
|
4981
7561
|
storedOptions
|
|
4982
7562
|
),
|
|
4983
|
-
voucher: (cae, caeExpiry, lookup) =>
|
|
7563
|
+
voucher: (cae, caeExpiry, lookup) => issuedVoucher2(
|
|
4984
7564
|
{
|
|
4985
7565
|
attempted,
|
|
4986
7566
|
voucherClass: prepared.voucherClass,
|
|
@@ -4997,7 +7577,7 @@ function consultReservation(select, record, options, taxId, strangerAtNumber = f
|
|
|
4997
7577
|
function issuerTaxId(options, context) {
|
|
4998
7578
|
return options.representedTaxId === void 0 ? context?.taxId : String(options.representedTaxId);
|
|
4999
7579
|
}
|
|
5000
|
-
function
|
|
7580
|
+
function issuedVoucher2({
|
|
5001
7581
|
attempted,
|
|
5002
7582
|
voucherClass,
|
|
5003
7583
|
data,
|
|
@@ -5093,8 +7673,8 @@ async function runSequenceBarrier({
|
|
|
5093
7673
|
return {};
|
|
5094
7674
|
}
|
|
5095
7675
|
const owner = claimed.issuerTaxId ?? taxId;
|
|
5096
|
-
const
|
|
5097
|
-
if (await storeCall(() => store.get(
|
|
7676
|
+
const settled2 = settledKey(environment, owner, claimed.key);
|
|
7677
|
+
if (await storeCall(() => store.get(settled2)) !== null) {
|
|
5098
7678
|
return {};
|
|
5099
7679
|
}
|
|
5100
7680
|
const reservation = await storeCall(
|
|
@@ -5116,8 +7696,8 @@ async function runSequenceBarrier({
|
|
|
5116
7696
|
};
|
|
5117
7697
|
const outcome = await recordConflict(
|
|
5118
7698
|
store,
|
|
5119
|
-
|
|
5120
|
-
await
|
|
7699
|
+
settled2,
|
|
7700
|
+
await consultReservation2(
|
|
5121
7701
|
select,
|
|
5122
7702
|
// This call's certificate may not be the one that claimed. Without a
|
|
5123
7703
|
// represented CUIT, it would read its own numbering instead of the one
|
|
@@ -5148,7 +7728,7 @@ async function runSequenceBarrier({
|
|
|
5148
7728
|
}
|
|
5149
7729
|
await storeCall(
|
|
5150
7730
|
() => store.add(
|
|
5151
|
-
|
|
7731
|
+
settled2,
|
|
5152
7732
|
JSON.stringify({
|
|
5153
7733
|
v: 1,
|
|
5154
7734
|
kind: "superseded",
|
|
@@ -5378,7 +7958,7 @@ function createArcaClient(config = {}) {
|
|
|
5378
7958
|
);
|
|
5379
7959
|
}
|
|
5380
7960
|
const logger = createArcaLogger(normalizedConfig.logger);
|
|
5381
|
-
const
|
|
7961
|
+
const auth2 = createWsaaAuthModule({ config: normalizedConfig, logger });
|
|
5382
7962
|
const soap = createSoapTransport({ config: normalizedConfig, logger });
|
|
5383
7963
|
const publicConfig = Object.freeze({
|
|
5384
7964
|
taxId: normalizedConfig.taxId,
|
|
@@ -5387,13 +7967,19 @@ function createArcaClient(config = {}) {
|
|
|
5387
7967
|
retries: normalizedConfig.retries,
|
|
5388
7968
|
retryDelay: normalizedConfig.retryDelay
|
|
5389
7969
|
});
|
|
5390
|
-
const wsfe = createWsfeService({ config: normalizedConfig, auth, soap });
|
|
7970
|
+
const wsfe = createWsfeService({ config: normalizedConfig, auth: auth2, soap });
|
|
5391
7971
|
const wsmtxca = createWsmtxcaService({
|
|
5392
7972
|
config: normalizedConfig,
|
|
5393
|
-
auth,
|
|
7973
|
+
auth: auth2,
|
|
5394
7974
|
soap
|
|
5395
7975
|
});
|
|
5396
|
-
const
|
|
7976
|
+
const wsfex = createWsfexService({ config: normalizedConfig, auth: auth2, soap });
|
|
7977
|
+
const vouchers = createVouchersService(
|
|
7978
|
+
wsfe,
|
|
7979
|
+
normalizedConfig,
|
|
7980
|
+
wsmtxca,
|
|
7981
|
+
wsfex
|
|
7982
|
+
);
|
|
5397
7983
|
return {
|
|
5398
7984
|
config: publicConfig,
|
|
5399
7985
|
issue: vouchers.issue,
|
|
@@ -5406,8 +7992,9 @@ function createArcaClient(config = {}) {
|
|
|
5406
7992
|
lookup: vouchers.lookup,
|
|
5407
7993
|
lastAuthorized: vouchers.lastAuthorized,
|
|
5408
7994
|
wsfe,
|
|
7995
|
+
wsfex,
|
|
5409
7996
|
wsmtxca,
|
|
5410
|
-
padron: createPadronService({ config: normalizedConfig, auth, soap })
|
|
7997
|
+
padron: createPadronService({ config: normalizedConfig, auth: auth2, soap })
|
|
5411
7998
|
};
|
|
5412
7999
|
}
|
|
5413
8000
|
|
|
@@ -5425,45 +8012,6 @@ import {
|
|
|
5425
8012
|
writeFile
|
|
5426
8013
|
} from "fs/promises";
|
|
5427
8014
|
import { join } from "path";
|
|
5428
|
-
|
|
5429
|
-
// src/store/lock.ts
|
|
5430
|
-
import { randomUUID } from "crypto";
|
|
5431
|
-
var ARCA_LEASE_MS = 6e4;
|
|
5432
|
-
var RENEW_MS = 2e4;
|
|
5433
|
-
var POLL_MS = 50;
|
|
5434
|
-
var MAX_WAIT_MS = 2 * ARCA_LEASE_MS;
|
|
5435
|
-
async function withLease(key, driver, fn) {
|
|
5436
|
-
const owner = randomUUID();
|
|
5437
|
-
const deadline = Date.now() + MAX_WAIT_MS;
|
|
5438
|
-
let held = await driver.acquire(owner);
|
|
5439
|
-
while (!held) {
|
|
5440
|
-
if (Date.now() >= deadline) {
|
|
5441
|
-
throw new ArcaConfigurationError(
|
|
5442
|
-
`ARCA store lock ${key} stayed held; no work was attempted.`
|
|
5443
|
-
);
|
|
5444
|
-
}
|
|
5445
|
-
await delay2(POLL_MS + Math.floor(Math.random() * POLL_MS));
|
|
5446
|
-
held = await driver.acquire(owner);
|
|
5447
|
-
}
|
|
5448
|
-
const renewal = setInterval(() => {
|
|
5449
|
-
driver.renew(owner).catch(() => void 0);
|
|
5450
|
-
}, RENEW_MS);
|
|
5451
|
-
renewal.unref?.();
|
|
5452
|
-
try {
|
|
5453
|
-
return await fn();
|
|
5454
|
-
} finally {
|
|
5455
|
-
clearInterval(renewal);
|
|
5456
|
-
await driver.release(owner).catch(() => void 0);
|
|
5457
|
-
}
|
|
5458
|
-
}
|
|
5459
|
-
function delay2(ms) {
|
|
5460
|
-
return new Promise((resolve) => {
|
|
5461
|
-
const timer = setTimeout(resolve, ms);
|
|
5462
|
-
timer.unref?.();
|
|
5463
|
-
});
|
|
5464
|
-
}
|
|
5465
|
-
|
|
5466
|
-
// src/store/file.ts
|
|
5467
8015
|
function createFileStore(directory) {
|
|
5468
8016
|
const path = (key) => join(directory, createHash3("sha256").update(key).digest("hex"));
|
|
5469
8017
|
const ensure = () => mkdir(directory, { recursive: true, mode: 448 });
|
|
@@ -5509,10 +8057,10 @@ function createFileStore(directory) {
|
|
|
5509
8057
|
}
|
|
5510
8058
|
}
|
|
5511
8059
|
}),
|
|
5512
|
-
withLock: (key, fn) => (
|
|
8060
|
+
withLock: (key, fn, options) => (
|
|
5513
8061
|
// A lock directory: mkdir is the atomic claim on every POSIX filesystem
|
|
5514
8062
|
// and on Windows, and the holder file inside carries the lease.
|
|
5515
|
-
withLease(
|
|
8063
|
+
withLease(fileLease(`${path(key)}.lock`, ensure), fn, options)
|
|
5516
8064
|
)
|
|
5517
8065
|
};
|
|
5518
8066
|
}
|
|
@@ -5683,17 +8231,20 @@ export {
|
|
|
5683
8231
|
discoverArcaClientConfig,
|
|
5684
8232
|
storeCall,
|
|
5685
8233
|
describeVoucherType,
|
|
8234
|
+
ARCA_LEASE_MS,
|
|
8235
|
+
withLease,
|
|
8236
|
+
throwIfAborted,
|
|
5686
8237
|
ARCA_QR_URL,
|
|
5687
8238
|
arcaQrUrl,
|
|
5688
8239
|
arcaQrPayload,
|
|
5689
8240
|
voucherDateWindow,
|
|
8241
|
+
createWsfexService,
|
|
5690
8242
|
matchWsfeVoucherIdentity,
|
|
8243
|
+
abortable,
|
|
5691
8244
|
createMemoryWsaaSessionStore,
|
|
5692
8245
|
createWsaaAuthModule,
|
|
5693
8246
|
createWsaaStoreAdapter,
|
|
5694
8247
|
createArcaClient,
|
|
5695
|
-
ARCA_LEASE_MS,
|
|
5696
|
-
withLease,
|
|
5697
8248
|
createFileStore
|
|
5698
8249
|
};
|
|
5699
|
-
//# sourceMappingURL=chunk-
|
|
8250
|
+
//# sourceMappingURL=chunk-R4MOVPYT.mjs.map
|