facturas 0.12.4 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -35
- package/dist/{chunk-YYDSBHMO.mjs → chunk-BPBTQDCO.mjs} +16 -173
- package/dist/chunk-BPBTQDCO.mjs.map +1 -0
- package/dist/{chunk-7V5S3ISG.mjs → chunk-KPWAQMR5.mjs} +766 -190
- package/dist/chunk-KPWAQMR5.mjs.map +1 -0
- package/dist/{chunk-GX5ERXT6.mjs → chunk-LQMNKQ2V.mjs} +1 -1
- package/dist/chunk-LQMNKQ2V.mjs.map +1 -0
- package/dist/{chunk-3EJINDBP.mjs → chunk-WN62NMUJ.mjs} +13 -13
- package/dist/chunk-WN62NMUJ.mjs.map +1 -0
- package/dist/cli.d.ts +5 -5
- package/dist/cli.mjs +4 -4
- package/dist/{client-tzfxAy7w.d.ts → client-bblLZeUb.d.ts} +83 -46
- package/dist/constants.d.ts +1 -1
- package/dist/constants.mjs +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/{fiscal-evidence-DXl_U1uz.d.ts → fiscal-evidence-DaCyGd5o.d.ts} +2 -2
- package/dist/{index-CT1ffEAL.d.ts → index-DAC3edtw.d.ts} +1 -1
- package/dist/index.d.ts +6 -7
- package/dist/index.mjs +9 -15
- package/dist/index.mjs.map +1 -1
- package/dist/padron.d.ts +2 -2
- package/dist/{types-SdBQAflw.d.ts → types-Su7QwloQ.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/wsfe.d.ts +255 -31
- package/dist/wsfe.mjs +1 -8
- package/dist/wsmtxca.d.ts +6 -6
- package/dist/wsmtxca.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-3EJINDBP.mjs.map +0 -1
- package/dist/chunk-7V5S3ISG.mjs.map +0 -1
- package/dist/chunk-GX5ERXT6.mjs.map +0 -1
- package/dist/chunk-YYDSBHMO.mjs.map +0 -1
- package/dist/chunk-ZZDL6I56.mjs +0 -169
- package/dist/chunk-ZZDL6I56.mjs.map +0 -1
- package/dist/decimal-CgWMOiAC.d.ts +0 -272
|
@@ -1,29 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ARCA_CURRENCY_IDS,
|
|
3
|
+
ARCA_DOCUMENT_TYPES,
|
|
4
|
+
ARCA_FINAL_CONSUMER_IDENTIFICATION_THRESHOLD_MINOR_UNITS,
|
|
5
|
+
ARCA_INVOICE_CLASS_BY_ISSUER,
|
|
6
|
+
ARCA_ISSUER_CONDITION_IDS,
|
|
7
|
+
ARCA_RECEIVER_CONDITION_IDS,
|
|
8
|
+
ARCA_VAT_RATES,
|
|
9
|
+
ARCA_VOUCHER_TYPES
|
|
10
|
+
} from "./chunk-LQMNKQ2V.mjs";
|
|
1
11
|
import {
|
|
2
12
|
createPadronService
|
|
3
13
|
} from "./chunk-WVKVJTFG.mjs";
|
|
4
14
|
import {
|
|
5
15
|
arcaMinorUnitsToNumber,
|
|
6
16
|
assertArcaMinorUnits,
|
|
7
|
-
calculateWsfeAmounts,
|
|
8
17
|
createWsfeService,
|
|
9
18
|
isWithinArcaTolerance,
|
|
10
19
|
normalizeArcaAmountToMinorUnits,
|
|
11
20
|
normalizeWsfeDateInput,
|
|
12
21
|
normalizeWsfeVoucherInput,
|
|
22
|
+
roundHalfEvenRatio,
|
|
13
23
|
serializeArcaExchangeRate
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import {
|
|
16
|
-
ARCA_CURRENCY_IDS,
|
|
17
|
-
ARCA_DOCUMENT_TYPES,
|
|
18
|
-
ARCA_FINAL_CONSUMER_IDENTIFICATION_THRESHOLD_MINOR_UNITS,
|
|
19
|
-
ARCA_INVOICE_CLASS_BY_ISSUER,
|
|
20
|
-
ARCA_ISSUER_CONDITION_IDS,
|
|
21
|
-
ARCA_RECEIVER_CONDITION_IDS,
|
|
22
|
-
ARCA_VOUCHER_TYPES
|
|
23
|
-
} from "./chunk-GX5ERXT6.mjs";
|
|
24
|
+
} from "./chunk-BPBTQDCO.mjs";
|
|
24
25
|
import {
|
|
25
26
|
createWsmtxcaService
|
|
26
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-WN62NMUJ.mjs";
|
|
27
28
|
import {
|
|
28
29
|
ArcaConfigurationError,
|
|
29
30
|
ArcaError,
|
|
@@ -1190,7 +1191,7 @@ function createWsaaAuthModule(options) {
|
|
|
1190
1191
|
}
|
|
1191
1192
|
return {
|
|
1192
1193
|
login(service, authOptions = {}) {
|
|
1193
|
-
return abortable(runLogin(service, authOptions), authOptions.
|
|
1194
|
+
return abortable(runLogin(service, authOptions), authOptions.abortSignal);
|
|
1194
1195
|
}
|
|
1195
1196
|
};
|
|
1196
1197
|
function runLogin(service, authOptions) {
|
|
@@ -1698,6 +1699,17 @@ function validateIssuanceFields(fields) {
|
|
|
1698
1699
|
if (fields.concept !== void 0 && !["products", "services", "products_and_services"].includes(fields.concept)) {
|
|
1699
1700
|
bad("concept");
|
|
1700
1701
|
}
|
|
1702
|
+
if (fields.fce !== void 0) {
|
|
1703
|
+
objectKeys(fields.fce, "fce", [
|
|
1704
|
+
"cbu",
|
|
1705
|
+
"alias",
|
|
1706
|
+
"transfer",
|
|
1707
|
+
"annulment",
|
|
1708
|
+
"reference"
|
|
1709
|
+
]);
|
|
1710
|
+
validateFceOptions(fields.fce);
|
|
1711
|
+
}
|
|
1712
|
+
normalizedFceAnnulment(fields);
|
|
1701
1713
|
}
|
|
1702
1714
|
function positiveId(value, field) {
|
|
1703
1715
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) {
|
|
@@ -1772,6 +1784,7 @@ function applyFceFields(data, fce) {
|
|
|
1772
1784
|
bad("fce");
|
|
1773
1785
|
}
|
|
1774
1786
|
validateFceOptions(fce);
|
|
1787
|
+
normalizedFceAnnulment({ fce, optionalFields: data.optionalFields });
|
|
1775
1788
|
const extra = [
|
|
1776
1789
|
...fce.cbu === void 0 ? [] : [{ id: "2101", value: fce.cbu }],
|
|
1777
1790
|
...fce.alias === void 0 ? [] : [{ id: "2102", value: fce.alias }],
|
|
@@ -1785,6 +1798,26 @@ function applyFceFields(data, fce) {
|
|
|
1785
1798
|
}
|
|
1786
1799
|
data.optionalFields = options;
|
|
1787
1800
|
}
|
|
1801
|
+
function normalizedFceAnnulment(fields) {
|
|
1802
|
+
const encoded = (fields.optionalFields ?? []).filter(
|
|
1803
|
+
(field) => field.id === "22"
|
|
1804
|
+
);
|
|
1805
|
+
if (encoded.length > 1) {
|
|
1806
|
+
bad("optionalFields");
|
|
1807
|
+
}
|
|
1808
|
+
const encodedValue = encoded[0]?.value;
|
|
1809
|
+
if (encodedValue !== void 0 && !["S", "N"].includes(encodedValue)) {
|
|
1810
|
+
bad("fce.annulment");
|
|
1811
|
+
}
|
|
1812
|
+
const direct = fields.fce?.annulment;
|
|
1813
|
+
if (direct !== void 0 && encodedValue !== void 0) {
|
|
1814
|
+
throw new ArcaInputError(
|
|
1815
|
+
direct === (encodedValue === "S") ? "FCE annulment is duplicated in fce and optionalFields." : "FCE annulment conflicts between fce and optionalFields.",
|
|
1816
|
+
{ code: "ARCA_INPUT_INVALID_VALUE", field: "fce.annulment" }
|
|
1817
|
+
);
|
|
1818
|
+
}
|
|
1819
|
+
return direct ?? (encodedValue === void 0 ? void 0 : encodedValue === "S");
|
|
1820
|
+
}
|
|
1788
1821
|
function validateFceHeader(data) {
|
|
1789
1822
|
const family = voucherFamily(data.voucherType);
|
|
1790
1823
|
if (family.family === "fce") {
|
|
@@ -1833,53 +1866,36 @@ var iso = (value) => {
|
|
|
1833
1866
|
return `${date.slice(0, 4)}-${date.slice(4, 6)}-${date.slice(6, 8)}`;
|
|
1834
1867
|
};
|
|
1835
1868
|
function wsmtxcaRequest(data, number) {
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
const
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
unidadesMtx: item.matrixUnits,
|
|
1862
|
-
codigoMtx: item.matrixCode,
|
|
1863
|
-
codigo: item.code,
|
|
1864
|
-
descripcion: item.description,
|
|
1865
|
-
cantidad: item.quantity,
|
|
1866
|
-
codigoUnidadMedida: item.unit,
|
|
1867
|
-
precioUnitario: item.unitPrice,
|
|
1868
|
-
importeBonificacion: minor(item.discount ?? 0, "details.discount"),
|
|
1869
|
-
codigoCondicionIVA: item.vatCondition,
|
|
1870
|
-
...item.vatAmount === void 0 ? {} : { importeIVA: minor(item.vatAmount, "details.vatAmount") },
|
|
1871
|
-
importeItem: minor(item.amount, "details.amount")
|
|
1872
|
-
};
|
|
1873
|
-
});
|
|
1874
|
-
const itemTotal = data.details.reduce(
|
|
1875
|
-
(sum, item) => sum + BigInt(item.amount),
|
|
1876
|
-
0n
|
|
1877
|
-
);
|
|
1869
|
+
const legacy = data.details !== void 0;
|
|
1870
|
+
const authorized = data.authorizedLines !== void 0;
|
|
1871
|
+
const lines = data.lines ?? data.authorizedLines ?? data.details;
|
|
1872
|
+
const sources = [data.lines, data.authorizedLines, data.details].filter(
|
|
1873
|
+
(source) => source !== void 0
|
|
1874
|
+
).length;
|
|
1875
|
+
if (!lines?.length || sources !== 1) {
|
|
1876
|
+
invalid("items", "WSMTXCA requires items with line detail");
|
|
1877
|
+
}
|
|
1878
|
+
const items = lines.map((line) => ({
|
|
1879
|
+
unidadesMtx: line.matrixUnits,
|
|
1880
|
+
codigoMtx: line.matrixCode,
|
|
1881
|
+
codigo: line.code,
|
|
1882
|
+
descripcion: line.description,
|
|
1883
|
+
cantidad: line.quantity,
|
|
1884
|
+
codigoUnidadMedida: line.unit,
|
|
1885
|
+
precioUnitario: line.unitPrice,
|
|
1886
|
+
// Old durable records omitted a zero discount. Rebuild the exact request
|
|
1887
|
+
// those releases sent without rewriting the stored fiscal evidence.
|
|
1888
|
+
importeBonificacion: minor(line.discount ?? 0, "items.discount"),
|
|
1889
|
+
codigoCondicionIVA: line.vatCondition,
|
|
1890
|
+
...line.vatAmount === void 0 ? {} : { importeIVA: minor(line.vatAmount, "items.vatAmount") },
|
|
1891
|
+
importeItem: minor(line.amount, "items.amount")
|
|
1892
|
+
}));
|
|
1893
|
+
const itemTotal = lines.reduce((sum, line) => sum + BigInt(line.amount), 0n);
|
|
1878
1894
|
const expected = normalizeArcaAmountToMinorUnits(data.totalAmount, "total") - normalizeArcaAmountToMinorUnits(data.taxAmount, "taxes");
|
|
1879
|
-
if (!isWithinArcaTolerance(itemTotal, expected, 1)) {
|
|
1880
|
-
|
|
1881
|
-
"
|
|
1882
|
-
"
|
|
1895
|
+
if (legacy || authorized ? !isWithinArcaTolerance(itemTotal, expected, 1) : itemTotal !== expected) {
|
|
1896
|
+
throw new ArcaError(
|
|
1897
|
+
"The derived WSMTXCA items do not sum to the voucher header excluding tributes. This is an SDK invariant failure.",
|
|
1898
|
+
"ARCA_ISSUE_INVARIANT"
|
|
1883
1899
|
);
|
|
1884
1900
|
}
|
|
1885
1901
|
const tributes = data.taxes?.length ? data.taxes : void 0;
|
|
@@ -1974,7 +1990,7 @@ function rows(value, key) {
|
|
|
1974
1990
|
function wsmtxcaHeader(found) {
|
|
1975
1991
|
const raw = found.raw;
|
|
1976
1992
|
const items = rows(raw.arrayItems, "item");
|
|
1977
|
-
const
|
|
1993
|
+
const lines = items?.map((i) => ({
|
|
1978
1994
|
description: String(i.descripcion ?? ""),
|
|
1979
1995
|
quantity: Number(i.cantidad),
|
|
1980
1996
|
unit: Number(i.codigoUnidadMedida),
|
|
@@ -2004,12 +2020,12 @@ function wsmtxcaHeader(found) {
|
|
|
2004
2020
|
serviceStartDate: raw.fechaServicioDesde === void 0 ? void 0 : String(raw.fechaServicioDesde),
|
|
2005
2021
|
serviceEndDate: raw.fechaServicioHasta === void 0 ? void 0 : String(raw.fechaServicioHasta),
|
|
2006
2022
|
paymentDueDate: raw.fechaVencimientoPago === void 0 ? void 0 : String(raw.fechaVencimientoPago),
|
|
2007
|
-
|
|
2023
|
+
lines,
|
|
2008
2024
|
vatRates: rows(raw.arraySubtotalesIVA, "subtotalIVA")?.map((v) => ({
|
|
2009
2025
|
id: Number(v.codigo),
|
|
2010
2026
|
amount: Number(v.importe),
|
|
2011
2027
|
baseAmount: Number(
|
|
2012
|
-
(
|
|
2028
|
+
(lines ?? []).filter((i) => i.vatCondition === Number(v.codigo)).reduce((sum, i) => sum + BigInt(i.amount), 0n) - normalizeArcaAmountToMinorUnits(Number(v.importe), "vat")
|
|
2013
2029
|
) / 100
|
|
2014
2030
|
})),
|
|
2015
2031
|
taxes: rows(raw.arrayOtrosTributos, "otroTributo")?.map((t) => ({
|
|
@@ -2150,7 +2166,7 @@ function createWsmtxcaIssuanceService(wsmtxca) {
|
|
|
2150
2166
|
issue: (input) => wsmtxca.issue({
|
|
2151
2167
|
representedTaxId: input.representedTaxId,
|
|
2152
2168
|
forceRefresh: input.forceRefresh,
|
|
2153
|
-
|
|
2169
|
+
abortSignal: input.abortSignal,
|
|
2154
2170
|
data: wsmtxcaRequest(input.data, input.voucherNumber)
|
|
2155
2171
|
}),
|
|
2156
2172
|
lookupVoucher: async (input) => {
|
|
@@ -2175,6 +2191,385 @@ function wsmtxcaAdditionalData(fields) {
|
|
|
2175
2191
|
};
|
|
2176
2192
|
}
|
|
2177
2193
|
|
|
2194
|
+
// src/services/wsfe-amounts.ts
|
|
2195
|
+
var RATES = {
|
|
2196
|
+
0: { id: ARCA_VAT_RATES.IVA_0, basisPoints: 0n },
|
|
2197
|
+
2.5: { id: ARCA_VAT_RATES.IVA_2_5, basisPoints: 250n },
|
|
2198
|
+
5: { id: ARCA_VAT_RATES.IVA_5, basisPoints: 500n },
|
|
2199
|
+
10.5: { id: ARCA_VAT_RATES.IVA_10_5, basisPoints: 1050n },
|
|
2200
|
+
21: { id: ARCA_VAT_RATES.IVA_21, basisPoints: 2100n },
|
|
2201
|
+
27: { id: ARCA_VAT_RATES.IVA_27, basisPoints: 2700n }
|
|
2202
|
+
};
|
|
2203
|
+
function calculateWsfeAmounts(input) {
|
|
2204
|
+
if (!Array.isArray(input.items) || input.items.length === 0) {
|
|
2205
|
+
invalidItem("items", "a non-empty array of items");
|
|
2206
|
+
}
|
|
2207
|
+
const isVat = input.voucherClass === "A" || input.voucherClass === "B";
|
|
2208
|
+
if (!(isVat || input.voucherClass === "C")) {
|
|
2209
|
+
invalidItem("voucherClass", "A, B or C");
|
|
2210
|
+
}
|
|
2211
|
+
const totals = collectItems(input.items, isVat);
|
|
2212
|
+
let net = totals.net;
|
|
2213
|
+
let vat = 0n;
|
|
2214
|
+
const { exempt, untaxed, groups } = totals;
|
|
2215
|
+
const vatRates = [];
|
|
2216
|
+
for (const [rate, group] of groups) {
|
|
2217
|
+
const { id, basisPoints } = RATES[rate];
|
|
2218
|
+
const netFromGross = roundHalfEvenRatio(
|
|
2219
|
+
group.gross * 10000n,
|
|
2220
|
+
10000n + basisPoints
|
|
2221
|
+
);
|
|
2222
|
+
const base = group.net + netFromGross;
|
|
2223
|
+
const tax = roundHalfEvenRatio(group.net * basisPoints, 10000n) + group.gross - netFromGross;
|
|
2224
|
+
if (base === 0n) {
|
|
2225
|
+
continue;
|
|
2226
|
+
}
|
|
2227
|
+
net += base;
|
|
2228
|
+
vat += tax;
|
|
2229
|
+
vatRates.push({
|
|
2230
|
+
id,
|
|
2231
|
+
baseAmount: arcaMinorUnitsToNumber(base, "netAmount"),
|
|
2232
|
+
amount: arcaMinorUnitsToNumber(tax, "vatAmount")
|
|
2233
|
+
});
|
|
2234
|
+
}
|
|
2235
|
+
const computed = net + vat + exempt + untaxed;
|
|
2236
|
+
arcaMinorUnitsToNumber(computed, "totalAmount");
|
|
2237
|
+
const sent = input.total === void 0 ? computed : assertArcaMinorUnits(input.total, "total");
|
|
2238
|
+
const adjustment = sent - computed;
|
|
2239
|
+
const allowance = BigInt(vatRates.length);
|
|
2240
|
+
if (adjustment < -allowance || adjustment > allowance || vat + adjustment < 0n) {
|
|
2241
|
+
throw new ArcaInputError(
|
|
2242
|
+
"total does not match the computed amount within the VAT allowance.",
|
|
2243
|
+
{
|
|
2244
|
+
code: "ARCA_INPUT_AMOUNT_MISMATCH",
|
|
2245
|
+
field: "total",
|
|
2246
|
+
expected: `${computed} minor units (at most ${allowance} minor units of VAT adjustment, with non-negative VAT)`
|
|
2247
|
+
}
|
|
2248
|
+
);
|
|
2249
|
+
}
|
|
2250
|
+
return {
|
|
2251
|
+
data: {
|
|
2252
|
+
totalAmount: arcaMinorUnitsToNumber(sent, "totalAmount"),
|
|
2253
|
+
netAmount: arcaMinorUnitsToNumber(net, "netAmount"),
|
|
2254
|
+
vatAmount: arcaMinorUnitsToNumber(vat + adjustment, "vatAmount"),
|
|
2255
|
+
nonTaxableAmount: arcaMinorUnitsToNumber(untaxed, "nonTaxableAmount"),
|
|
2256
|
+
exemptAmount: arcaMinorUnitsToNumber(exempt, "exemptAmount"),
|
|
2257
|
+
taxAmount: 0,
|
|
2258
|
+
...isVat ? { vatRates } : {}
|
|
2259
|
+
},
|
|
2260
|
+
amounts: {
|
|
2261
|
+
computedTotal: Number(computed),
|
|
2262
|
+
sentTotal: Number(sent),
|
|
2263
|
+
vatAdjustment: Number(adjustment)
|
|
2264
|
+
}
|
|
2265
|
+
};
|
|
2266
|
+
}
|
|
2267
|
+
function collectItems(items, isVat) {
|
|
2268
|
+
let net = 0n;
|
|
2269
|
+
let exempt = 0n;
|
|
2270
|
+
let untaxed = 0n;
|
|
2271
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2272
|
+
for (const [index, item] of items.entries()) {
|
|
2273
|
+
const path = `items[${index}]`;
|
|
2274
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) {
|
|
2275
|
+
invalidItem(path, "an item object");
|
|
2276
|
+
}
|
|
2277
|
+
if (!isVat) {
|
|
2278
|
+
net += classCAmount(item, path);
|
|
2279
|
+
continue;
|
|
2280
|
+
}
|
|
2281
|
+
const { amount, field, rate } = vatItemAmount(item, path);
|
|
2282
|
+
if (rate === "exempt") {
|
|
2283
|
+
exempt += amount;
|
|
2284
|
+
continue;
|
|
2285
|
+
}
|
|
2286
|
+
if (rate === "untaxed") {
|
|
2287
|
+
untaxed += amount;
|
|
2288
|
+
continue;
|
|
2289
|
+
}
|
|
2290
|
+
const group = groups.get(rate) ?? { net: 0n, gross: 0n };
|
|
2291
|
+
group[field] += amount;
|
|
2292
|
+
groups.set(rate, group);
|
|
2293
|
+
}
|
|
2294
|
+
return { net, exempt, untaxed, groups };
|
|
2295
|
+
}
|
|
2296
|
+
function classCAmount(item, path) {
|
|
2297
|
+
if ("vat" in item || "net" in item || "gross" in item) {
|
|
2298
|
+
invalidItem("items", "amount items for a class C voucher");
|
|
2299
|
+
}
|
|
2300
|
+
return assertArcaMinorUnits(item.amount, `${path}.amount`);
|
|
2301
|
+
}
|
|
2302
|
+
function vatItemAmount(item, path) {
|
|
2303
|
+
if ("amount" in item || "net" in item === "gross" in item) {
|
|
2304
|
+
invalidItem(
|
|
2305
|
+
"items",
|
|
2306
|
+
"exactly one of net or gross, and vat, for a class A or B voucher"
|
|
2307
|
+
);
|
|
2308
|
+
}
|
|
2309
|
+
const field = "net" in item ? "net" : "gross";
|
|
2310
|
+
const amount = assertArcaMinorUnits(
|
|
2311
|
+
item[field],
|
|
2312
|
+
`${path}.${field}`
|
|
2313
|
+
);
|
|
2314
|
+
const rate = item.vat;
|
|
2315
|
+
if (rate !== "exempt" && rate !== "untaxed" && (typeof rate !== "number" || !Object.hasOwn(RATES, rate))) {
|
|
2316
|
+
invalidItem(`${path}.vat`, "0, 2.5, 5, 10.5, 21, 27, exempt, or untaxed");
|
|
2317
|
+
}
|
|
2318
|
+
return { amount, field, rate };
|
|
2319
|
+
}
|
|
2320
|
+
function invalidItem(field, expected) {
|
|
2321
|
+
throw new ArcaInputError(`${field} must be ${expected}.`, {
|
|
2322
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
2323
|
+
field,
|
|
2324
|
+
expected
|
|
2325
|
+
});
|
|
2326
|
+
}
|
|
2327
|
+
var UNTAXED_CONDITION = 1;
|
|
2328
|
+
var EXEMPT_CONDITION = 2;
|
|
2329
|
+
function deriveWsmtxcaSettlement(input, vatAdjustment) {
|
|
2330
|
+
if (!Array.isArray(input.items) || input.items.length === 0) {
|
|
2331
|
+
invalidItem("items", "a non-empty array of items");
|
|
2332
|
+
}
|
|
2333
|
+
const isVat = input.voucherClass === "A" || input.voucherClass === "B";
|
|
2334
|
+
const drafts = [];
|
|
2335
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2336
|
+
for (const [index, item] of input.items.entries()) {
|
|
2337
|
+
const path = `items[${index}]`;
|
|
2338
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) {
|
|
2339
|
+
invalidItem(path, "an item object");
|
|
2340
|
+
}
|
|
2341
|
+
drafts.push(lineDraft(item, path, isVat, groups));
|
|
2342
|
+
}
|
|
2343
|
+
reconcileGroups(drafts, groups);
|
|
2344
|
+
absorbAdjustment(drafts, BigInt(vatAdjustment));
|
|
2345
|
+
const lines = drafts.map(({ line, vat, amount }) => ({
|
|
2346
|
+
...line,
|
|
2347
|
+
amount: Number(amount),
|
|
2348
|
+
...input.voucherClass === "A" ? { vatAmount: Number(vat) } : {}
|
|
2349
|
+
}));
|
|
2350
|
+
const vatByCondition = /* @__PURE__ */ new Map();
|
|
2351
|
+
for (const draft of drafts) {
|
|
2352
|
+
if (draft.rate === void 0) {
|
|
2353
|
+
continue;
|
|
2354
|
+
}
|
|
2355
|
+
const condition = RATES[draft.rate].id;
|
|
2356
|
+
vatByCondition.set(
|
|
2357
|
+
condition,
|
|
2358
|
+
(vatByCondition.get(condition) ?? 0) + Number(draft.vat)
|
|
2359
|
+
);
|
|
2360
|
+
}
|
|
2361
|
+
return { lines, vatByCondition };
|
|
2362
|
+
}
|
|
2363
|
+
function lineDraft(item, path, isVat, groups) {
|
|
2364
|
+
const line = assertItemLine(item, path);
|
|
2365
|
+
if (!isVat) {
|
|
2366
|
+
const amount2 = classCAmount(item, path);
|
|
2367
|
+
return {
|
|
2368
|
+
line: { ...line, vatCondition: RATES[0].id, amount: 0 },
|
|
2369
|
+
vat: 0n,
|
|
2370
|
+
amount: amount2,
|
|
2371
|
+
amountFormula: { numerator: amount2, denominator: 1n }
|
|
2372
|
+
};
|
|
2373
|
+
}
|
|
2374
|
+
const { amount, field, rate } = vatItemAmount(item, path);
|
|
2375
|
+
if (rate === "exempt" || rate === "untaxed") {
|
|
2376
|
+
return {
|
|
2377
|
+
line: {
|
|
2378
|
+
...line,
|
|
2379
|
+
vatCondition: rate === "exempt" ? EXEMPT_CONDITION : UNTAXED_CONDITION,
|
|
2380
|
+
amount: 0
|
|
2381
|
+
},
|
|
2382
|
+
vat: 0n,
|
|
2383
|
+
amount,
|
|
2384
|
+
amountFormula: { numerator: amount, denominator: 1n }
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2387
|
+
const { id, basisPoints } = RATES[rate];
|
|
2388
|
+
const group = groups.get(rate) ?? { net: 0n, gross: 0n };
|
|
2389
|
+
group[field] += amount;
|
|
2390
|
+
groups.set(rate, group);
|
|
2391
|
+
const vat = field === "gross" ? amount - roundHalfEvenRatio(amount * 10000n, 10000n + basisPoints) : roundHalfEvenRatio(amount * basisPoints, 10000n);
|
|
2392
|
+
return {
|
|
2393
|
+
line: { ...line, vatCondition: id, amount: 0 },
|
|
2394
|
+
vat,
|
|
2395
|
+
amount: field === "gross" ? amount : amount + vat,
|
|
2396
|
+
rate,
|
|
2397
|
+
field,
|
|
2398
|
+
vatFormula: field === "gross" ? {
|
|
2399
|
+
numerator: amount * basisPoints,
|
|
2400
|
+
denominator: 10000n + basisPoints
|
|
2401
|
+
} : { numerator: amount * basisPoints, denominator: 10000n },
|
|
2402
|
+
amountFormula: field === "gross" ? { numerator: amount, denominator: 1n } : {
|
|
2403
|
+
numerator: amount * (10000n + basisPoints),
|
|
2404
|
+
denominator: 10000n
|
|
2405
|
+
}
|
|
2406
|
+
};
|
|
2407
|
+
}
|
|
2408
|
+
function reconcileGroups(drafts, groups) {
|
|
2409
|
+
for (const [rate, group] of groups) {
|
|
2410
|
+
const { basisPoints } = RATES[rate];
|
|
2411
|
+
const netFromGross = roundHalfEvenRatio(
|
|
2412
|
+
group.gross * 10000n,
|
|
2413
|
+
10000n + basisPoints
|
|
2414
|
+
);
|
|
2415
|
+
settle(
|
|
2416
|
+
drafts,
|
|
2417
|
+
(draft) => draft.rate === rate && draft.field === "net",
|
|
2418
|
+
roundHalfEvenRatio(group.net * basisPoints, 10000n) - sumVat(drafts, (d) => d.rate === rate && d.field === "net"),
|
|
2419
|
+
true
|
|
2420
|
+
);
|
|
2421
|
+
settle(
|
|
2422
|
+
drafts,
|
|
2423
|
+
(draft) => draft.rate === rate && draft.field === "gross",
|
|
2424
|
+
group.gross - netFromGross - sumVat(drafts, (d) => d.rate === rate && d.field === "gross"),
|
|
2425
|
+
false
|
|
2426
|
+
);
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
function sumVat(drafts, of) {
|
|
2430
|
+
return drafts.reduce((sum, draft) => of(draft) ? sum + draft.vat : sum, 0n);
|
|
2431
|
+
}
|
|
2432
|
+
function settle(drafts, of, amount, movesItem) {
|
|
2433
|
+
let remaining = amount;
|
|
2434
|
+
const targets = drafts.filter(of);
|
|
2435
|
+
let cursor = 0;
|
|
2436
|
+
while (remaining !== 0n) {
|
|
2437
|
+
const step = remaining > 0n ? 1n : -1n;
|
|
2438
|
+
let target;
|
|
2439
|
+
for (let offset = 0; offset < targets.length; offset++) {
|
|
2440
|
+
const index = (cursor + offset) % targets.length;
|
|
2441
|
+
const candidate = targets[index];
|
|
2442
|
+
if (candidate && canMove(candidate, step, movesItem)) {
|
|
2443
|
+
target = candidate;
|
|
2444
|
+
cursor = (index + 1) % targets.length;
|
|
2445
|
+
break;
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
if (target === void 0) {
|
|
2449
|
+
throw new ArcaError(
|
|
2450
|
+
"The derived WSMTXCA VAT does not fit its lines. This is an SDK invariant failure.",
|
|
2451
|
+
"ARCA_ISSUE_INVARIANT"
|
|
2452
|
+
);
|
|
2453
|
+
}
|
|
2454
|
+
applyMove(target, step, movesItem);
|
|
2455
|
+
remaining -= step;
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
function canMove(draft, step, movesItem) {
|
|
2459
|
+
const vat = draft.vat + step;
|
|
2460
|
+
if (vat < 0n || draft.rate === 0 && vat !== 0n) {
|
|
2461
|
+
return false;
|
|
2462
|
+
}
|
|
2463
|
+
if (draft.vatFormula !== void 0 && !withinAbsoluteCent(vat, draft.vatFormula)) {
|
|
2464
|
+
return false;
|
|
2465
|
+
}
|
|
2466
|
+
return !movesItem || withinAbsoluteCent(draft.amount + step, draft.amountFormula);
|
|
2467
|
+
}
|
|
2468
|
+
function withinAbsoluteCent(value, formula) {
|
|
2469
|
+
const difference = value * formula.denominator - formula.numerator;
|
|
2470
|
+
return (difference < 0n ? -difference : difference) <= formula.denominator;
|
|
2471
|
+
}
|
|
2472
|
+
function applyMove(draft, step, movesItem) {
|
|
2473
|
+
draft.vat += step;
|
|
2474
|
+
if (movesItem) {
|
|
2475
|
+
draft.amount += step;
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
function absorbAdjustment(drafts, adjustment) {
|
|
2479
|
+
if (adjustment === 0n) {
|
|
2480
|
+
return;
|
|
2481
|
+
}
|
|
2482
|
+
const step = adjustment > 0n ? 1n : -1n;
|
|
2483
|
+
let remaining = adjustment;
|
|
2484
|
+
const rates = [
|
|
2485
|
+
...new Set(
|
|
2486
|
+
drafts.map((draft) => draft.rate).filter(
|
|
2487
|
+
(rate) => rate !== void 0 && rate > 0
|
|
2488
|
+
)
|
|
2489
|
+
)
|
|
2490
|
+
];
|
|
2491
|
+
for (const rate of rates) {
|
|
2492
|
+
const target = drafts.find(
|
|
2493
|
+
(draft) => draft.rate === rate && canMove(draft, step, true)
|
|
2494
|
+
);
|
|
2495
|
+
if (target === void 0) {
|
|
2496
|
+
continue;
|
|
2497
|
+
}
|
|
2498
|
+
applyMove(target, step, true);
|
|
2499
|
+
remaining -= step;
|
|
2500
|
+
if (remaining === 0n) {
|
|
2501
|
+
return;
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
throw new ArcaInputError(
|
|
2505
|
+
"total cannot be reconciled with WSMTXCA item formulas within ARCA's tolerance.",
|
|
2506
|
+
{
|
|
2507
|
+
code: "ARCA_INPUT_AMOUNT_MISMATCH",
|
|
2508
|
+
field: "total"
|
|
2509
|
+
}
|
|
2510
|
+
);
|
|
2511
|
+
}
|
|
2512
|
+
var ITEM_KEYS = [
|
|
2513
|
+
"net",
|
|
2514
|
+
"gross",
|
|
2515
|
+
"amount",
|
|
2516
|
+
"vat",
|
|
2517
|
+
"description",
|
|
2518
|
+
"quantity",
|
|
2519
|
+
"unit",
|
|
2520
|
+
"unitPrice",
|
|
2521
|
+
"discount",
|
|
2522
|
+
"code",
|
|
2523
|
+
"matrixCode",
|
|
2524
|
+
"matrixUnits"
|
|
2525
|
+
];
|
|
2526
|
+
function assertItemLine(item, path) {
|
|
2527
|
+
assertRequiredLineFields(item, path);
|
|
2528
|
+
for (const key of ["code", "matrixCode"]) {
|
|
2529
|
+
if (item[key] !== void 0 && typeof item[key] !== "string") {
|
|
2530
|
+
invalidItem(`${path}.${key}`, "a string");
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
if (item.matrixUnits !== void 0 && !Number.isFinite(item.matrixUnits)) {
|
|
2534
|
+
invalidItem(`${path}.matrixUnits`, "a number");
|
|
2535
|
+
}
|
|
2536
|
+
return {
|
|
2537
|
+
...item.matrixUnits === void 0 ? {} : { matrixUnits: item.matrixUnits },
|
|
2538
|
+
...item.matrixCode === void 0 ? {} : { matrixCode: item.matrixCode },
|
|
2539
|
+
...item.code === void 0 ? {} : { code: item.code },
|
|
2540
|
+
description: item.description,
|
|
2541
|
+
quantity: item.quantity,
|
|
2542
|
+
unit: item.unit,
|
|
2543
|
+
unitPrice: item.unitPrice,
|
|
2544
|
+
discount: Number(
|
|
2545
|
+
assertArcaMinorUnits(item.discount ?? 0, `${path}.discount`)
|
|
2546
|
+
)
|
|
2547
|
+
};
|
|
2548
|
+
}
|
|
2549
|
+
function assertRequiredLineFields(item, path) {
|
|
2550
|
+
for (const key of Object.keys(item)) {
|
|
2551
|
+
if (!ITEM_KEYS.includes(key)) {
|
|
2552
|
+
invalidItem(`${path}.${key}`, "a supported item field");
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
const { description, quantity, unit, unitPrice } = item;
|
|
2556
|
+
if (typeof description !== "string" || description.trim() === "") {
|
|
2557
|
+
invalidItem(`${path}.description`, "a non-empty description");
|
|
2558
|
+
}
|
|
2559
|
+
if (!Number.isFinite(quantity) || quantity <= 0) {
|
|
2560
|
+
invalidItem(`${path}.quantity`, "a positive quantity");
|
|
2561
|
+
}
|
|
2562
|
+
if (!Number.isInteger(unit) || unit < 0) {
|
|
2563
|
+
invalidItem(`${path}.unit`, "an ARCA unit of measure code");
|
|
2564
|
+
}
|
|
2565
|
+
if (typeof unitPrice !== "string" || !/^\d+(\.\d{1,6})?$/.test(unitPrice)) {
|
|
2566
|
+
invalidItem(
|
|
2567
|
+
`${path}.unitPrice`,
|
|
2568
|
+
"a major-unit decimal string with at most six decimals"
|
|
2569
|
+
);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2178
2573
|
// src/services/wsfe-derive.ts
|
|
2179
2574
|
var INVOICE_TYPES = {
|
|
2180
2575
|
A: ARCA_VOUCHER_TYPES.FACTURA_A,
|
|
@@ -2188,7 +2583,6 @@ function deriveWsfeInvoice(input, now = /* @__PURE__ */ new Date()) {
|
|
|
2188
2583
|
[
|
|
2189
2584
|
...ISSUANCE_KEYS,
|
|
2190
2585
|
"family",
|
|
2191
|
-
"details",
|
|
2192
2586
|
"issuer",
|
|
2193
2587
|
"items",
|
|
2194
2588
|
"salesPoint",
|
|
@@ -2209,11 +2603,12 @@ function deriveWsfeInvoice(input, now = /* @__PURE__ */ new Date()) {
|
|
|
2209
2603
|
assertSalesPoint(input.salesPoint);
|
|
2210
2604
|
const receiver = deriveReceiver(input.to);
|
|
2211
2605
|
const voucherClass = resolveInvoiceClass(input.issuer, input.to.condition);
|
|
2212
|
-
const
|
|
2606
|
+
const lineSource = input.amounts ? void 0 : {
|
|
2213
2607
|
voucherClass,
|
|
2214
2608
|
items: input.items,
|
|
2215
2609
|
total: input.total === void 0 ? void 0 : input.total - tributeTotal(input.taxes ?? [])
|
|
2216
|
-
}
|
|
2610
|
+
};
|
|
2611
|
+
const { data: amountsData, amounts } = lineSource === void 0 ? reviewedInvoiceAmounts(input.amounts, input.taxes) : calculateWsfeAmounts(lineSource);
|
|
2217
2612
|
const currency = deriveCurrency(input);
|
|
2218
2613
|
const voucherDate = normalizeWsfeDateInput(
|
|
2219
2614
|
input.date === void 0 ? buenosAiresDate(now) : input.date,
|
|
@@ -2258,12 +2653,17 @@ function deriveWsfeInvoice(input, now = /* @__PURE__ */ new Date()) {
|
|
|
2258
2653
|
throw cause;
|
|
2259
2654
|
}
|
|
2260
2655
|
throw new ArcaError(
|
|
2261
|
-
"The derived invoice failed
|
|
2656
|
+
"The derived invoice failed WSFE validation. This is an SDK invariant failure.",
|
|
2262
2657
|
"ARCA_ISSUE_INVARIANT",
|
|
2263
2658
|
{ cause }
|
|
2264
2659
|
);
|
|
2265
2660
|
}
|
|
2266
|
-
return {
|
|
2661
|
+
return {
|
|
2662
|
+
data,
|
|
2663
|
+
voucherClass,
|
|
2664
|
+
amounts,
|
|
2665
|
+
...lineSource === void 0 ? {} : { lineSource }
|
|
2666
|
+
};
|
|
2267
2667
|
}
|
|
2268
2668
|
function reviewedInvoiceAmounts(amounts, taxes) {
|
|
2269
2669
|
const taxTotal = taxes === void 0 ? 0 : tributeTotal(taxes);
|
|
@@ -2438,7 +2838,7 @@ function assertIssueKeys(value, keys, prefix, method = "issue()") {
|
|
|
2438
2838
|
throw new ArcaInputError(`${field} is not supported by ${method}.`, {
|
|
2439
2839
|
code: "ARCA_INPUT_RESERVED_FIELD",
|
|
2440
2840
|
field,
|
|
2441
|
-
expected: "a
|
|
2841
|
+
expected: "a field the facade supports"
|
|
2442
2842
|
});
|
|
2443
2843
|
}
|
|
2444
2844
|
}
|
|
@@ -2492,10 +2892,9 @@ function deriveNumericReceiver(to) {
|
|
|
2492
2892
|
|
|
2493
2893
|
// src/services/wsfe-credit-note.ts
|
|
2494
2894
|
function invalid3(reason) {
|
|
2495
|
-
throw new ArcaInputError(
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
);
|
|
2895
|
+
throw new ArcaInputError(`issueCreditNote cannot proceed: ${reason}.`, {
|
|
2896
|
+
code: "ARCA_INPUT_INVALID_VALUE"
|
|
2897
|
+
});
|
|
2499
2898
|
}
|
|
2500
2899
|
function required(value, field) {
|
|
2501
2900
|
if (value === void 0 || value === null) {
|
|
@@ -2504,7 +2903,7 @@ function required(value, field) {
|
|
|
2504
2903
|
return value;
|
|
2505
2904
|
}
|
|
2506
2905
|
function deriveWsfeFullCreditNote(original, input, now = /* @__PURE__ */ new Date(), kind = "creditNote") {
|
|
2507
|
-
const { note, header } = prepareCreditNote(original, input, now, kind);
|
|
2906
|
+
const { note, header } = prepareCreditNote([original], input, now, kind);
|
|
2508
2907
|
const data = {
|
|
2509
2908
|
...header,
|
|
2510
2909
|
...original.taxes ? { taxes: structuredClone(original.taxes) } : {},
|
|
@@ -2526,24 +2925,32 @@ function deriveWsfeFullCreditNote(original, input, now = /* @__PURE__ */ new Dat
|
|
|
2526
2925
|
amounts: { computedTotal: total, sentTotal: total, vatAdjustment: 0 }
|
|
2527
2926
|
};
|
|
2528
2927
|
}
|
|
2529
|
-
function deriveWsfePartialCreditNote(
|
|
2928
|
+
function deriveWsfePartialCreditNote(originals, input, now = /* @__PURE__ */ new Date(), kind = "creditNote") {
|
|
2530
2929
|
if (input.items === void 0 && input.amounts === void 0) {
|
|
2531
2930
|
invalid3("items is required to credit chosen lines");
|
|
2532
2931
|
}
|
|
2533
2932
|
const requestedTotal = input.total === void 0 ? void 0 : assertArcaMinorUnits(input.total, "total");
|
|
2534
|
-
const { note, header } = prepareCreditNote(
|
|
2535
|
-
const
|
|
2933
|
+
const { note, header } = prepareCreditNote(originals, input, now, kind);
|
|
2934
|
+
const lineSource = input.amounts ? void 0 : {
|
|
2536
2935
|
voucherClass: note.voucherClass,
|
|
2537
2936
|
items: input.items,
|
|
2538
2937
|
total: input.total === void 0 ? void 0 : input.total - tributeTotal(input.taxes ?? [])
|
|
2539
|
-
}
|
|
2540
|
-
const
|
|
2541
|
-
|
|
2542
|
-
|
|
2938
|
+
};
|
|
2939
|
+
const { data: amountsData, amounts } = lineSource === void 0 ? reviewedInvoiceAmounts(
|
|
2940
|
+
input.amounts,
|
|
2941
|
+
input.taxes
|
|
2942
|
+
) : calculateWsfeAmounts(lineSource);
|
|
2943
|
+
const originalTotal = originals.reduce(
|
|
2944
|
+
(sum, original) => sum + normalizeArcaAmountToMinorUnits(
|
|
2945
|
+
required(original.totalAmount, "totalAmount"),
|
|
2946
|
+
"totalAmount"
|
|
2947
|
+
),
|
|
2948
|
+
0n
|
|
2543
2949
|
);
|
|
2950
|
+
const ceiling = originals.length === 1 ? "the original" : "the sum of the originals";
|
|
2544
2951
|
if (kind === "creditNote" && (requestedTotal ?? BigInt(amounts.sentTotal)) > originalTotal) {
|
|
2545
2952
|
invalid3(
|
|
2546
|
-
|
|
2953
|
+
`the note total is greater than ${ceiling}; the SDK does not track earlier notes against an original`
|
|
2547
2954
|
);
|
|
2548
2955
|
}
|
|
2549
2956
|
const data = { ...header, ...amountsData };
|
|
@@ -2566,15 +2973,55 @@ function deriveWsfePartialCreditNote(original, input, now = /* @__PURE__ */ new
|
|
|
2566
2973
|
);
|
|
2567
2974
|
const sentTotal = input.amounts && requestedTotal !== void 0 ? Number(requestedTotal) : total;
|
|
2568
2975
|
if (kind === "creditNote" && BigInt(sentTotal) > originalTotal) {
|
|
2569
|
-
invalid3(
|
|
2976
|
+
invalid3(`the note total is greater than ${ceiling}`);
|
|
2570
2977
|
}
|
|
2571
2978
|
data.totalAmount = minor(sentTotal, "total");
|
|
2572
2979
|
amounts.computedTotal += total - amounts.sentTotal;
|
|
2573
2980
|
amounts.sentTotal = sentTotal;
|
|
2574
2981
|
normalizeWsfeVoucherInput(data);
|
|
2575
|
-
return {
|
|
2982
|
+
return {
|
|
2983
|
+
data,
|
|
2984
|
+
voucherClass: note.voucherClass,
|
|
2985
|
+
amounts,
|
|
2986
|
+
...lineSource === void 0 ? {} : { lineSource }
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2989
|
+
function prepareCreditNote(originals, input, now, kind) {
|
|
2990
|
+
const derived = originals.map(
|
|
2991
|
+
(original) => prepareOneCreditNote(original, input, now, kind)
|
|
2992
|
+
);
|
|
2993
|
+
const [first, ...rest] = derived;
|
|
2994
|
+
if (first === void 0) {
|
|
2995
|
+
invalid3("for must name at least one original");
|
|
2996
|
+
}
|
|
2997
|
+
for (const other of rest) {
|
|
2998
|
+
assertInheritedHeader(first.header, other.header);
|
|
2999
|
+
}
|
|
3000
|
+
return {
|
|
3001
|
+
note: first.note,
|
|
3002
|
+
header: {
|
|
3003
|
+
...first.header,
|
|
3004
|
+
associatedVouchers: derived.flatMap(
|
|
3005
|
+
(one) => one.header.associatedVouchers ?? []
|
|
3006
|
+
)
|
|
3007
|
+
}
|
|
3008
|
+
};
|
|
3009
|
+
}
|
|
3010
|
+
function assertInheritedHeader(first, other) {
|
|
3011
|
+
for (const field of /* @__PURE__ */ new Set([...Object.keys(first), ...Object.keys(other)])) {
|
|
3012
|
+
if (field === "associatedVouchers") {
|
|
3013
|
+
continue;
|
|
3014
|
+
}
|
|
3015
|
+
const left = first[field];
|
|
3016
|
+
const right = other[field];
|
|
3017
|
+
if (JSON.stringify(left) !== JSON.stringify(right)) {
|
|
3018
|
+
invalid3(
|
|
3019
|
+
`the originals disagree on ${field}, which the note inherits from them`
|
|
3020
|
+
);
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
2576
3023
|
}
|
|
2577
|
-
function
|
|
3024
|
+
function prepareOneCreditNote(original, input, now, kind) {
|
|
2578
3025
|
assertOriginalExtensions(original);
|
|
2579
3026
|
const family = voucherFamily(original.voucherType ?? 0);
|
|
2580
3027
|
if (family.types[2] === original.voucherType) {
|
|
@@ -2660,7 +3107,6 @@ var CREDIT_NOTE_KEYS = [
|
|
|
2660
3107
|
"taxes",
|
|
2661
3108
|
"amounts",
|
|
2662
3109
|
"optionalFields",
|
|
2663
|
-
"details",
|
|
2664
3110
|
"fce"
|
|
2665
3111
|
];
|
|
2666
3112
|
var TARGET_BOUNDS = [
|
|
@@ -2673,7 +3119,7 @@ function assertCreditNoteInput(input) {
|
|
|
2673
3119
|
validateIssuanceFields(input);
|
|
2674
3120
|
if ("associatedPeriod" in input) {
|
|
2675
3121
|
throw new ArcaInputError(
|
|
2676
|
-
"issueCreditNote
|
|
3122
|
+
"issueCreditNote adjusts either the originals named in for or a period, never both; drop one of them.",
|
|
2677
3123
|
{
|
|
2678
3124
|
code: "ARCA_INPUT_RESERVED_FIELD",
|
|
2679
3125
|
field: "associatedPeriod",
|
|
@@ -2682,7 +3128,7 @@ function assertCreditNoteInput(input) {
|
|
|
2682
3128
|
);
|
|
2683
3129
|
}
|
|
2684
3130
|
assertIssueKeys(input, CREDIT_NOTE_KEYS, "input", "issueCreditNote()");
|
|
2685
|
-
const target =
|
|
3131
|
+
const target = assertCreditNoteTargets(input.for);
|
|
2686
3132
|
if (input.salesPoint !== void 0) {
|
|
2687
3133
|
assertCreditNoteBound(input.salesPoint, 99999, "salesPoint");
|
|
2688
3134
|
}
|
|
@@ -2690,7 +3136,6 @@ function assertCreditNoteInput(input) {
|
|
|
2690
3136
|
const common = {
|
|
2691
3137
|
...input.fce === void 0 ? {} : { fce: structuredClone(input.fce) },
|
|
2692
3138
|
...input.taxes === void 0 ? {} : { taxes: structuredClone(input.taxes) },
|
|
2693
|
-
...input.details === void 0 ? {} : { details: structuredClone(input.details) },
|
|
2694
3139
|
...input.optionalFields === void 0 ? {} : { optionalFields: structuredClone(input.optionalFields) },
|
|
2695
3140
|
for: target,
|
|
2696
3141
|
...input.salesPoint === void 0 ? {} : { salesPoint: input.salesPoint },
|
|
@@ -2727,13 +3172,47 @@ function assertCreditNoteInput(input) {
|
|
|
2727
3172
|
...input.total === void 0 ? {} : { total: input.total }
|
|
2728
3173
|
};
|
|
2729
3174
|
}
|
|
2730
|
-
function
|
|
3175
|
+
function assertCreditNoteTargets(value) {
|
|
3176
|
+
if (Array.isArray(value)) {
|
|
3177
|
+
if (value.length === 0) {
|
|
3178
|
+
throw new ArcaInputError(
|
|
3179
|
+
"issueCreditNote requires for to name at least one authorized original.",
|
|
3180
|
+
{
|
|
3181
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
3182
|
+
field: "input.for",
|
|
3183
|
+
expected: "a non-empty array of { salesPoint, voucherType, number }"
|
|
3184
|
+
}
|
|
3185
|
+
);
|
|
3186
|
+
}
|
|
3187
|
+
const targets = value.map(
|
|
3188
|
+
(target, index) => assertCreditNoteTarget(target, `for[${index}]`)
|
|
3189
|
+
);
|
|
3190
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3191
|
+
for (const [index, target] of targets.entries()) {
|
|
3192
|
+
const key = `${target.salesPoint}:${target.voucherType}:${target.number}`;
|
|
3193
|
+
if (seen.has(key)) {
|
|
3194
|
+
throw new ArcaInputError(
|
|
3195
|
+
"issueCreditNote requires each original in for to be unique.",
|
|
3196
|
+
{
|
|
3197
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
3198
|
+
field: `input.for[${index}]`,
|
|
3199
|
+
expected: "unique voucher coordinates"
|
|
3200
|
+
}
|
|
3201
|
+
);
|
|
3202
|
+
}
|
|
3203
|
+
seen.add(key);
|
|
3204
|
+
}
|
|
3205
|
+
return targets;
|
|
3206
|
+
}
|
|
3207
|
+
return assertCreditNoteTarget(value, "for");
|
|
3208
|
+
}
|
|
3209
|
+
function assertCreditNoteTarget(value, path) {
|
|
2731
3210
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2732
3211
|
throw new ArcaInputError(
|
|
2733
3212
|
"issueCreditNote requires for: the coordinates of the authorized invoice the note corrects.",
|
|
2734
3213
|
{
|
|
2735
3214
|
code: "ARCA_INPUT_MISSING_FIELD",
|
|
2736
|
-
field:
|
|
3215
|
+
field: `input.${path}`,
|
|
2737
3216
|
expected: "{ salesPoint, voucherType, number }"
|
|
2738
3217
|
}
|
|
2739
3218
|
);
|
|
@@ -2741,20 +3220,20 @@ function assertCreditNoteTarget(value) {
|
|
|
2741
3220
|
assertIssueKeys(
|
|
2742
3221
|
value,
|
|
2743
3222
|
["salesPoint", "voucherType", "number"],
|
|
2744
|
-
|
|
3223
|
+
`input.${path}`,
|
|
2745
3224
|
"issueCreditNote()"
|
|
2746
3225
|
);
|
|
2747
3226
|
for (const [field, max] of TARGET_BOUNDS) {
|
|
2748
|
-
assertCreditNoteBound(value[field], max,
|
|
3227
|
+
assertCreditNoteBound(value[field], max, `${path}.${field}`);
|
|
2749
3228
|
}
|
|
2750
3229
|
if (![1, 2, 6, 7, 11, 12, 51, 52, 201, 202, 206, 207, 211, 212].includes(
|
|
2751
3230
|
value.voucherType
|
|
2752
3231
|
)) {
|
|
2753
3232
|
throw new ArcaInputError(
|
|
2754
|
-
"issueCreditNote requires an authorized invoice or debit note in a supported family in for.voucherType.
|
|
3233
|
+
"issueCreditNote requires an authorized invoice or debit note in a supported family in for.voucherType.",
|
|
2755
3234
|
{
|
|
2756
3235
|
code: "ARCA_INPUT_INVALID_VALUE",
|
|
2757
|
-
field:
|
|
3236
|
+
field: `input.${path}.voucherType`,
|
|
2758
3237
|
expected: "1, 6 or 11"
|
|
2759
3238
|
}
|
|
2760
3239
|
);
|
|
@@ -2765,6 +3244,9 @@ function assertCreditNoteTarget(value) {
|
|
|
2765
3244
|
number: value.number
|
|
2766
3245
|
};
|
|
2767
3246
|
}
|
|
3247
|
+
function creditNoteTargets(input) {
|
|
3248
|
+
return Array.isArray(input.for) ? input.for : [input.for];
|
|
3249
|
+
}
|
|
2768
3250
|
function assertCreditNoteBound(value, max, path) {
|
|
2769
3251
|
if (!Number.isSafeInteger(value) || value < 1 || value > max) {
|
|
2770
3252
|
throw new ArcaInputError(
|
|
@@ -2852,6 +3334,16 @@ function assertFullMode(input) {
|
|
|
2852
3334
|
}
|
|
2853
3335
|
);
|
|
2854
3336
|
}
|
|
3337
|
+
if (Array.isArray(input.for) && input.for.length > 1) {
|
|
3338
|
+
throw new ArcaInputError(
|
|
3339
|
+
"issueCreditNote takes all: true against one original; a full note of several originals has no single total. Pass items or amounts instead.",
|
|
3340
|
+
{
|
|
3341
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
3342
|
+
field: "input.all",
|
|
3343
|
+
expected: "one original in for when all is true"
|
|
3344
|
+
}
|
|
3345
|
+
);
|
|
3346
|
+
}
|
|
2855
3347
|
}
|
|
2856
3348
|
|
|
2857
3349
|
// src/services/vouchers.ts
|
|
@@ -2938,21 +3430,37 @@ function previewInvoice(input, options) {
|
|
|
2938
3430
|
}
|
|
2939
3431
|
function prepareInvoice(input, options) {
|
|
2940
3432
|
const prepared = deriveWsfeInvoice(input);
|
|
2941
|
-
|
|
2942
|
-
prepared.data.details = structuredClone(input.details);
|
|
2943
|
-
}
|
|
3433
|
+
attachLines(prepared, options);
|
|
2944
3434
|
validatePrepared(prepared, options);
|
|
2945
3435
|
return prepared;
|
|
2946
3436
|
}
|
|
3437
|
+
function attachLines(prepared, options) {
|
|
3438
|
+
if (options.service !== "wsmtxca") {
|
|
3439
|
+
return;
|
|
3440
|
+
}
|
|
3441
|
+
if (prepared.lineSource === void 0) {
|
|
3442
|
+
throw new ArcaInputError(
|
|
3443
|
+
"WSMTXCA needs items with line detail; a reviewed amounts breakdown has no lines.",
|
|
3444
|
+
{ code: "ARCA_INPUT_INVALID_VALUE", field: "amounts" }
|
|
3445
|
+
);
|
|
3446
|
+
}
|
|
3447
|
+
const settlement = deriveWsmtxcaSettlement(
|
|
3448
|
+
prepared.lineSource,
|
|
3449
|
+
prepared.amounts.vatAdjustment
|
|
3450
|
+
);
|
|
3451
|
+
prepared.data.lines = settlement.lines;
|
|
3452
|
+
prepared.data.vatRates = prepared.data.vatRates?.map((rate) => ({
|
|
3453
|
+
...rate,
|
|
3454
|
+
amount: arcaMinorUnitsToNumber(
|
|
3455
|
+
BigInt(settlement.vatByCondition.get(rate.id) ?? 0),
|
|
3456
|
+
"vatAmount"
|
|
3457
|
+
)
|
|
3458
|
+
}));
|
|
3459
|
+
}
|
|
2947
3460
|
function validatePrepared(prepared, options) {
|
|
2948
3461
|
validateFiscalHeader(prepared.data);
|
|
2949
3462
|
if (options.service === "wsmtxca") {
|
|
2950
3463
|
wsmtxcaRequest(prepared.data);
|
|
2951
|
-
} else if (prepared.data.details !== void 0) {
|
|
2952
|
-
throw new ArcaInputError("Detailed items require service: wsmtxca", {
|
|
2953
|
-
code: "ARCA_INPUT_INVALID_VALUE",
|
|
2954
|
-
field: "details"
|
|
2955
|
-
});
|
|
2956
3464
|
}
|
|
2957
3465
|
}
|
|
2958
3466
|
function toPreview(prepared, options) {
|
|
@@ -3038,12 +3546,15 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
|
|
|
3038
3546
|
supersededBy: idempotencyKey,
|
|
3039
3547
|
readNext: () => nextNumber(wsfe, prepared.data, options)
|
|
3040
3548
|
});
|
|
3041
|
-
return "blocked" in barrier ?
|
|
3549
|
+
return "blocked" in barrier ? {
|
|
3550
|
+
...barrier.blocked,
|
|
3551
|
+
...requestEvidence(prepared.data, void 0, options)
|
|
3552
|
+
} : await claim(barrier.reserved);
|
|
3042
3553
|
}
|
|
3043
3554
|
);
|
|
3044
3555
|
function reservation(number) {
|
|
3045
3556
|
const service = options.service ?? "wsfe";
|
|
3046
|
-
const versioned = service === "wsmtxca" || prepared.data.
|
|
3557
|
+
const versioned = service === "wsmtxca" || prepared.data.lines !== void 0;
|
|
3047
3558
|
return {
|
|
3048
3559
|
v: versioned ? 2 : 1,
|
|
3049
3560
|
operation,
|
|
@@ -3075,7 +3586,7 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
|
|
|
3075
3586
|
await storeCall(() => store.set(sequenceRecord, JSON.stringify(claimed)));
|
|
3076
3587
|
}
|
|
3077
3588
|
if (await storeCall(() => store.add(key, JSON.stringify(record)))) {
|
|
3078
|
-
const outcome = await
|
|
3589
|
+
const outcome = await settle2(
|
|
3079
3590
|
runAuthorization(wsfe, prepared, options, number)
|
|
3080
3591
|
);
|
|
3081
3592
|
if (coordinated && outcome.kind !== "indeterminate") {
|
|
@@ -3118,7 +3629,7 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
|
|
|
3118
3629
|
(other) => storeCall(() => store.get(settledKey(environment, taxId, other)))
|
|
3119
3630
|
);
|
|
3120
3631
|
}
|
|
3121
|
-
return await
|
|
3632
|
+
return await settle2(
|
|
3122
3633
|
runAuthorization(
|
|
3123
3634
|
wsfe,
|
|
3124
3635
|
{
|
|
@@ -3144,7 +3655,7 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
|
|
|
3144
3655
|
consult
|
|
3145
3656
|
);
|
|
3146
3657
|
}
|
|
3147
|
-
async function
|
|
3658
|
+
async function settle2(running) {
|
|
3148
3659
|
return await recordConflict(store, settled, await running);
|
|
3149
3660
|
}
|
|
3150
3661
|
}
|
|
@@ -3157,7 +3668,7 @@ async function recordConflict(store, key, outcome) {
|
|
|
3157
3668
|
kind: "conflict",
|
|
3158
3669
|
number: outcome.attempted.number,
|
|
3159
3670
|
found: Object.fromEntries(
|
|
3160
|
-
Object.entries(outcome.found).filter(([field]) => field !== "
|
|
3671
|
+
Object.entries(outcome.found).filter(([field]) => field !== "rawResponse")
|
|
3161
3672
|
),
|
|
3162
3673
|
settledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3163
3674
|
};
|
|
@@ -3180,7 +3691,7 @@ function readSettledRecord(json) {
|
|
|
3180
3691
|
}
|
|
3181
3692
|
async function settledOutcome(select, reservation, settled, options, settledFor) {
|
|
3182
3693
|
if (settled.kind === "conflict") {
|
|
3183
|
-
return settledConflict(settled, reservation);
|
|
3694
|
+
return settledConflict(settled, reservation, options);
|
|
3184
3695
|
}
|
|
3185
3696
|
const outcome = await consultReservation(select, reservation, options, true);
|
|
3186
3697
|
const empty = outcome.kind === "indeterminate" && outcome.lookup.kind === "not_found";
|
|
@@ -3195,7 +3706,12 @@ async function settledOutcome(select, reservation, settled, options, settledFor)
|
|
|
3195
3706
|
number: settled.number
|
|
3196
3707
|
},
|
|
3197
3708
|
attempt: replayEvidence(reservation.service),
|
|
3198
|
-
lookup: { kind: "superseded", by: settled.by }
|
|
3709
|
+
lookup: { kind: "superseded", by: settled.by },
|
|
3710
|
+
...requestEvidence(
|
|
3711
|
+
preparedFromRecord(reservation).data,
|
|
3712
|
+
reservation.number,
|
|
3713
|
+
{ ...options, service: reservation.service ?? "wsfe" }
|
|
3714
|
+
)
|
|
3199
3715
|
};
|
|
3200
3716
|
}
|
|
3201
3717
|
async function successionDisputed(key, settledFor) {
|
|
@@ -3213,7 +3729,7 @@ async function successionDisputed(key, settledFor) {
|
|
|
3213
3729
|
}
|
|
3214
3730
|
return true;
|
|
3215
3731
|
}
|
|
3216
|
-
function settledConflict(settled, reservation) {
|
|
3732
|
+
function settledConflict(settled, reservation, options) {
|
|
3217
3733
|
return {
|
|
3218
3734
|
kind: "conflict",
|
|
3219
3735
|
attempted: {
|
|
@@ -3223,7 +3739,12 @@ function settledConflict(settled, reservation) {
|
|
|
3223
3739
|
},
|
|
3224
3740
|
attempt: replayEvidence(reservation.service),
|
|
3225
3741
|
found: settled.found,
|
|
3226
|
-
reason: "This key already recorded another voucher at the reserved number. Reconcile before issuing under a new key."
|
|
3742
|
+
reason: "This key already recorded another voucher at the reserved number. Reconcile before issuing under a new key.",
|
|
3743
|
+
...requestEvidence(
|
|
3744
|
+
preparedFromRecord(reservation).data,
|
|
3745
|
+
reservation.number,
|
|
3746
|
+
{ ...options, service: reservation.service ?? "wsfe" }
|
|
3747
|
+
)
|
|
3227
3748
|
};
|
|
3228
3749
|
}
|
|
3229
3750
|
function validateKeyStore(options, context) {
|
|
@@ -3280,7 +3801,7 @@ async function nextNumber(wsfe, data, options) {
|
|
|
3280
3801
|
const number = await wsfe.getNextVoucherNumber({
|
|
3281
3802
|
representedTaxId: options.representedTaxId,
|
|
3282
3803
|
forceRefresh: options.forceRefresh,
|
|
3283
|
-
...options.
|
|
3804
|
+
...options.abortSignal === void 0 ? {} : { abortSignal: options.abortSignal },
|
|
3284
3805
|
salesPoint: data.salesPoint,
|
|
3285
3806
|
voucherType: data.voucherType
|
|
3286
3807
|
});
|
|
@@ -3299,17 +3820,16 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3299
3820
|
const auth = {
|
|
3300
3821
|
representedTaxId: options.representedTaxId,
|
|
3301
3822
|
forceRefresh: options.forceRefresh,
|
|
3302
|
-
...options.
|
|
3823
|
+
...options.abortSignal === void 0 ? {} : { abortSignal: options.abortSignal }
|
|
3303
3824
|
};
|
|
3304
|
-
const
|
|
3305
|
-
const includeExact = options.include?.exactInput === true;
|
|
3825
|
+
const includeRawResponse = options.include?.rawResponse === true;
|
|
3306
3826
|
const number = reservedNumber ?? await nextNumber(wsfe, data, options);
|
|
3307
3827
|
const attempted = {
|
|
3308
3828
|
salesPoint: data.salesPoint,
|
|
3309
3829
|
voucherType: data.voucherType,
|
|
3310
3830
|
number
|
|
3311
3831
|
};
|
|
3312
|
-
const
|
|
3832
|
+
const includedRequest = requestEvidence(data, number, options);
|
|
3313
3833
|
const voucher = (cae, caeExpiry) => ({
|
|
3314
3834
|
...attempted,
|
|
3315
3835
|
voucherClass,
|
|
@@ -3323,8 +3843,8 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3323
3843
|
auth,
|
|
3324
3844
|
data,
|
|
3325
3845
|
attempted,
|
|
3326
|
-
|
|
3327
|
-
|
|
3846
|
+
includeRawResponse,
|
|
3847
|
+
includedRequest,
|
|
3328
3848
|
voucher,
|
|
3329
3849
|
service: options.service
|
|
3330
3850
|
};
|
|
@@ -3338,7 +3858,8 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3338
3858
|
kind: "indeterminate",
|
|
3339
3859
|
attempted,
|
|
3340
3860
|
attempt: attempt2,
|
|
3341
|
-
lookup: options.
|
|
3861
|
+
lookup: options.abortSignal?.aborted ? { kind: "aborted" } : { kind: "failed", error: toArcaSafeErrorMetadata(error) },
|
|
3862
|
+
...includedRequest
|
|
3342
3863
|
};
|
|
3343
3864
|
}
|
|
3344
3865
|
if (lookup.kind === "found") {
|
|
@@ -3355,8 +3876,8 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3355
3876
|
kind: "authorized",
|
|
3356
3877
|
recoveredByMatch: false,
|
|
3357
3878
|
voucher: voucher(authorization.cae, authorization.caeExpiry),
|
|
3358
|
-
authorization: projectEvidence(authorization,
|
|
3359
|
-
...
|
|
3879
|
+
authorization: projectEvidence(authorization, includeRawResponse),
|
|
3880
|
+
...includedRequest
|
|
3360
3881
|
};
|
|
3361
3882
|
}
|
|
3362
3883
|
if (authorization.kind === "rejected") {
|
|
@@ -3364,7 +3885,7 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3364
3885
|
authorization,
|
|
3365
3886
|
recovery,
|
|
3366
3887
|
options,
|
|
3367
|
-
|
|
3888
|
+
includeRawResponse,
|
|
3368
3889
|
replay
|
|
3369
3890
|
);
|
|
3370
3891
|
}
|
|
@@ -3373,7 +3894,7 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3373
3894
|
kind: "indeterminate",
|
|
3374
3895
|
reason: authorization.voucherNumber === number ? "incomplete_response" : "contradictory_response"
|
|
3375
3896
|
};
|
|
3376
|
-
const attempt = projectEvidence(uncertain,
|
|
3897
|
+
const attempt = projectEvidence(uncertain, includeRawResponse);
|
|
3377
3898
|
return recoverInvoice({
|
|
3378
3899
|
wsfe,
|
|
3379
3900
|
service: options.service,
|
|
@@ -3381,12 +3902,12 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3381
3902
|
data,
|
|
3382
3903
|
attempted,
|
|
3383
3904
|
attempt,
|
|
3384
|
-
|
|
3385
|
-
|
|
3905
|
+
includeRawResponse,
|
|
3906
|
+
includedRequest,
|
|
3386
3907
|
voucher
|
|
3387
3908
|
});
|
|
3388
3909
|
}
|
|
3389
|
-
async function resolveRejection(authorization, recovery, options,
|
|
3910
|
+
async function resolveRejection(authorization, recovery, options, includeRawResponse, replay) {
|
|
3390
3911
|
const issues = [...authorization.errors, ...authorization.observations];
|
|
3391
3912
|
const consult = options.idempotencyKey !== void 0 && (options.service === "wsmtxca" || issues.some((issue) => issue.code === "10016"));
|
|
3392
3913
|
if (consult) {
|
|
@@ -3398,7 +3919,7 @@ async function resolveRejection(authorization, recovery, options, includeRaw, re
|
|
|
3398
3919
|
kind: "indeterminate",
|
|
3399
3920
|
reason: "contradictory_response"
|
|
3400
3921
|
},
|
|
3401
|
-
|
|
3922
|
+
includeRawResponse
|
|
3402
3923
|
),
|
|
3403
3924
|
strangerAtNumber: !replay
|
|
3404
3925
|
});
|
|
@@ -3410,7 +3931,8 @@ async function resolveRejection(authorization, recovery, options, includeRaw, re
|
|
|
3410
3931
|
kind: "rejected",
|
|
3411
3932
|
attempted: recovery.attempted,
|
|
3412
3933
|
issues: issues.map(projectIssue),
|
|
3413
|
-
authorization: projectEvidence(authorization,
|
|
3934
|
+
authorization: projectEvidence(authorization, includeRawResponse),
|
|
3935
|
+
...recovery.includedRequest
|
|
3414
3936
|
};
|
|
3415
3937
|
}
|
|
3416
3938
|
async function recoverInvoice({
|
|
@@ -3419,19 +3941,20 @@ async function recoverInvoice({
|
|
|
3419
3941
|
data,
|
|
3420
3942
|
attempted,
|
|
3421
3943
|
attempt,
|
|
3422
|
-
|
|
3423
|
-
|
|
3944
|
+
includeRawResponse,
|
|
3945
|
+
includedRequest,
|
|
3424
3946
|
voucher,
|
|
3425
3947
|
lookup: suppliedLookup,
|
|
3426
3948
|
service,
|
|
3427
3949
|
strangerAtNumber = false
|
|
3428
3950
|
}) {
|
|
3429
|
-
if (suppliedLookup === void 0 && auth.
|
|
3951
|
+
if (suppliedLookup === void 0 && auth.abortSignal?.aborted) {
|
|
3430
3952
|
return {
|
|
3431
3953
|
kind: "indeterminate",
|
|
3432
3954
|
attempted,
|
|
3433
3955
|
attempt,
|
|
3434
|
-
lookup: { kind: "aborted" }
|
|
3956
|
+
lookup: { kind: "aborted" },
|
|
3957
|
+
...includedRequest
|
|
3435
3958
|
};
|
|
3436
3959
|
}
|
|
3437
3960
|
let lookup;
|
|
@@ -3442,16 +3965,18 @@ async function recoverInvoice({
|
|
|
3442
3965
|
kind: "indeterminate",
|
|
3443
3966
|
attempted,
|
|
3444
3967
|
attempt,
|
|
3445
|
-
lookup: auth.
|
|
3968
|
+
lookup: auth.abortSignal?.aborted ? { kind: "aborted" } : { kind: "failed", error: toArcaSafeErrorMetadata(error) },
|
|
3969
|
+
...includedRequest
|
|
3446
3970
|
};
|
|
3447
3971
|
}
|
|
3448
|
-
const
|
|
3972
|
+
const rawResponse = includeRawResponse ? { rawResponse: lookup.raw } : {};
|
|
3449
3973
|
if (lookup.kind === "not_found") {
|
|
3450
3974
|
return {
|
|
3451
3975
|
kind: "indeterminate",
|
|
3452
3976
|
attempted,
|
|
3453
3977
|
attempt,
|
|
3454
|
-
lookup: { kind: "not_found", ...
|
|
3978
|
+
lookup: { kind: "not_found", ...rawResponse },
|
|
3979
|
+
...includedRequest
|
|
3455
3980
|
};
|
|
3456
3981
|
}
|
|
3457
3982
|
if (strangerAtNumber) {
|
|
@@ -3459,8 +3984,9 @@ async function recoverInvoice({
|
|
|
3459
3984
|
kind: "conflict",
|
|
3460
3985
|
attempted,
|
|
3461
3986
|
attempt,
|
|
3462
|
-
found: { ...toVoucherSummary(lookup.voucher), ...
|
|
3463
|
-
reason: "ARCA refused the number this call reserved and another voucher occupies it"
|
|
3987
|
+
found: { ...toVoucherSummary(lookup.voucher), ...rawResponse },
|
|
3988
|
+
reason: "ARCA refused the number this call reserved and another voucher occupies it",
|
|
3989
|
+
...includedRequest
|
|
3464
3990
|
};
|
|
3465
3991
|
}
|
|
3466
3992
|
const detailsMatch = service === "wsmtxca" ? matchWsmtxcaDetails(data, attempted.number, lookup.voucher.raw) : void 0;
|
|
@@ -3475,15 +4001,21 @@ async function recoverInvoice({
|
|
|
3475
4001
|
kind: "conflict",
|
|
3476
4002
|
attempted,
|
|
3477
4003
|
attempt,
|
|
3478
|
-
found: { ...toVoucherSummary(lookup.voucher), ...
|
|
3479
|
-
reason: `${matched.reason}. Configure a store and pass idempotencyKey for retries
|
|
4004
|
+
found: { ...toVoucherSummary(lookup.voucher), ...rawResponse },
|
|
4005
|
+
reason: `${matched.reason}. Configure a store and pass idempotencyKey for retries.`,
|
|
4006
|
+
...includedRequest
|
|
3480
4007
|
};
|
|
3481
4008
|
}
|
|
3482
4009
|
return {
|
|
3483
4010
|
kind: "indeterminate",
|
|
3484
4011
|
attempted,
|
|
3485
4012
|
attempt,
|
|
3486
|
-
lookup: {
|
|
4013
|
+
lookup: {
|
|
4014
|
+
kind: "incomplete",
|
|
4015
|
+
reason: matched.reason,
|
|
4016
|
+
...rawResponse
|
|
4017
|
+
},
|
|
4018
|
+
...includedRequest
|
|
3487
4019
|
};
|
|
3488
4020
|
}
|
|
3489
4021
|
return {
|
|
@@ -3494,8 +4026,8 @@ async function recoverInvoice({
|
|
|
3494
4026
|
lookup.voucher.caeExpiry
|
|
3495
4027
|
),
|
|
3496
4028
|
attempt,
|
|
3497
|
-
lookup: { ...toVoucherSummary(lookup.voucher), ...
|
|
3498
|
-
...
|
|
4029
|
+
lookup: { ...toVoucherSummary(lookup.voucher), ...rawResponse },
|
|
4030
|
+
...includedRequest
|
|
3499
4031
|
};
|
|
3500
4032
|
}
|
|
3501
4033
|
function projectIssue(issue) {
|
|
@@ -3509,7 +4041,7 @@ function projectIssue(issue) {
|
|
|
3509
4041
|
...issue.resultLevel === void 0 ? {} : { resultLevel: issue.resultLevel }
|
|
3510
4042
|
};
|
|
3511
4043
|
}
|
|
3512
|
-
function projectEvidence(evidence,
|
|
4044
|
+
function projectEvidence(evidence, includeRawResponse) {
|
|
3513
4045
|
const base = {
|
|
3514
4046
|
kind: evidence.kind,
|
|
3515
4047
|
service: evidence.service,
|
|
@@ -3521,7 +4053,7 @@ function projectEvidence(evidence, includeRaw) {
|
|
|
3521
4053
|
},
|
|
3522
4054
|
errors: evidence.errors.map(projectIssue),
|
|
3523
4055
|
observations: evidence.observations.map(projectIssue),
|
|
3524
|
-
...
|
|
4056
|
+
...includeRawResponse && evidence.raw !== void 0 ? { rawResponse: evidence.raw } : {}
|
|
3525
4057
|
};
|
|
3526
4058
|
const projected = { ...base };
|
|
3527
4059
|
for (const field of [
|
|
@@ -3548,10 +4080,10 @@ function projectEvidence(evidence, includeRaw) {
|
|
|
3548
4080
|
}
|
|
3549
4081
|
function cloneOptions(options) {
|
|
3550
4082
|
assertIssueObject(options, "options");
|
|
3551
|
-
const {
|
|
4083
|
+
const { abortSignal, ...rest } = options;
|
|
3552
4084
|
return {
|
|
3553
4085
|
...structuredClone(rest),
|
|
3554
|
-
...
|
|
4086
|
+
...abortSignal === void 0 ? {} : { abortSignal }
|
|
3555
4087
|
};
|
|
3556
4088
|
}
|
|
3557
4089
|
function validateOptions(options) {
|
|
@@ -3565,14 +4097,14 @@ function validateOptions(options) {
|
|
|
3565
4097
|
"idempotencyKey",
|
|
3566
4098
|
"service",
|
|
3567
4099
|
"number",
|
|
3568
|
-
"
|
|
4100
|
+
"abortSignal"
|
|
3569
4101
|
],
|
|
3570
4102
|
"options"
|
|
3571
4103
|
);
|
|
3572
|
-
if (options.
|
|
3573
|
-
throw new ArcaInputError("options.
|
|
4104
|
+
if (options.abortSignal !== void 0 && (typeof options.abortSignal !== "object" || options.abortSignal === null || typeof options.abortSignal.aborted !== "boolean" || typeof options.abortSignal.addEventListener !== "function")) {
|
|
4105
|
+
throw new ArcaInputError("options.abortSignal must be an AbortSignal.", {
|
|
3574
4106
|
code: "ARCA_INPUT_INVALID_VALUE",
|
|
3575
|
-
field: "options.
|
|
4107
|
+
field: "options.abortSignal"
|
|
3576
4108
|
});
|
|
3577
4109
|
}
|
|
3578
4110
|
if (options.service !== void 0 && options.service !== "wsfe" && options.service !== "wsmtxca") {
|
|
@@ -3603,8 +4135,12 @@ function validateOptions(options) {
|
|
|
3603
4135
|
}
|
|
3604
4136
|
if (options.include !== void 0) {
|
|
3605
4137
|
assertIssueObject(options.include, "options.include");
|
|
3606
|
-
assertIssueKeys(
|
|
3607
|
-
|
|
4138
|
+
assertIssueKeys(
|
|
4139
|
+
options.include,
|
|
4140
|
+
["request", "rawResponse"],
|
|
4141
|
+
"options.include"
|
|
4142
|
+
);
|
|
4143
|
+
for (const field of ["request", "rawResponse"]) {
|
|
3608
4144
|
if (options.include[field] !== void 0 && typeof options.include[field] !== "boolean") {
|
|
3609
4145
|
throw new ArcaInputError(
|
|
3610
4146
|
`options.include.${field} must be a boolean.`,
|
|
@@ -3652,16 +4188,16 @@ async function issueCreditNote(wsfe, input, inputOptions, context, kind = "credi
|
|
|
3652
4188
|
);
|
|
3653
4189
|
}
|
|
3654
4190
|
async function previewNote(wsfe, input, inputOptions, context, kind) {
|
|
3655
|
-
const options =
|
|
4191
|
+
const options = cloneOptions(inputOptions);
|
|
3656
4192
|
assertIssueKeys(
|
|
3657
4193
|
options,
|
|
3658
|
-
["representedTaxId", "service", "forceRefresh"],
|
|
4194
|
+
["representedTaxId", "service", "forceRefresh", "abortSignal"],
|
|
3659
4195
|
"options"
|
|
3660
4196
|
);
|
|
3661
4197
|
const prepared = await prepareNote(wsfe, input, options, context, kind);
|
|
3662
4198
|
return {
|
|
3663
4199
|
...toPreview(prepared, options),
|
|
3664
|
-
...prepared.
|
|
4200
|
+
...prepared.originals === void 0 ? {} : { originals: prepared.originals }
|
|
3665
4201
|
};
|
|
3666
4202
|
}
|
|
3667
4203
|
async function prepareNote(wsfe, input, options, context, kind) {
|
|
@@ -3676,10 +4212,72 @@ async function prepareNote(wsfe, input, options, context, kind) {
|
|
|
3676
4212
|
code: "ARCA_INPUT_INVALID_VALUE"
|
|
3677
4213
|
});
|
|
3678
4214
|
}
|
|
3679
|
-
const
|
|
4215
|
+
const targets = creditNoteTargets(note);
|
|
4216
|
+
validateFceAssociations(note, targets, options.service ?? "wsfe");
|
|
4217
|
+
const originals = [];
|
|
4218
|
+
for (const target of targets) {
|
|
4219
|
+
originals.push(await lookupOriginal(wsfe, target, options));
|
|
4220
|
+
}
|
|
4221
|
+
const [firstOriginal] = originals;
|
|
4222
|
+
const prepared = note.all === true ? deriveWsfeFullCreditNote(firstOriginal, note) : deriveWsfePartialCreditNote(originals, note, /* @__PURE__ */ new Date(), kind);
|
|
4223
|
+
if (note.all === true) {
|
|
4224
|
+
const mirrored = firstOriginal.lines;
|
|
4225
|
+
if (mirrored !== void 0) {
|
|
4226
|
+
prepared.data.authorizedLines = structuredClone(mirrored);
|
|
4227
|
+
}
|
|
4228
|
+
} else {
|
|
4229
|
+
attachLines(prepared, options);
|
|
4230
|
+
}
|
|
4231
|
+
if (voucherFamily(prepared.data.voucherType).family === "fce") {
|
|
4232
|
+
const taxId = options.representedTaxId ?? context?.taxId;
|
|
4233
|
+
if (!taxId) {
|
|
4234
|
+
throw new ArcaInputError("FCE association requires the issuer tax ID", {
|
|
4235
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
4236
|
+
field: "representedTaxId"
|
|
4237
|
+
});
|
|
4238
|
+
}
|
|
4239
|
+
for (const associated of prepared.data.associatedVouchers ?? []) {
|
|
4240
|
+
associated.taxId = String(taxId);
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
validatePrepared(prepared, options);
|
|
4244
|
+
return { ...prepared, originals: originals.map(toVoucherSummary) };
|
|
4245
|
+
}
|
|
4246
|
+
function validateFceAssociations(note, targets, service) {
|
|
4247
|
+
const fceTargets = targets.filter(
|
|
4248
|
+
(target2) => voucherFamily(target2.voucherType).family === "fce"
|
|
4249
|
+
);
|
|
4250
|
+
if (fceTargets.length === 0) {
|
|
4251
|
+
return;
|
|
4252
|
+
}
|
|
4253
|
+
const nonAnnulment = normalizedFceAnnulment(note) === false;
|
|
4254
|
+
if ((service === "wsmtxca" || nonAnnulment) && targets.length !== 1) {
|
|
4255
|
+
throw new ArcaInputError(
|
|
4256
|
+
service === "wsmtxca" ? "WSMTXCA FCE notes require exactly one associated original." : "Non-annulment FCE notes require exactly one associated invoice.",
|
|
4257
|
+
{
|
|
4258
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
4259
|
+
field: "input.for",
|
|
4260
|
+
expected: "one FCE original"
|
|
4261
|
+
}
|
|
4262
|
+
);
|
|
4263
|
+
}
|
|
4264
|
+
const [target] = targets;
|
|
4265
|
+
if (nonAnnulment && target !== void 0 && voucherFamily(target.voucherType).types[0] !== target.voucherType) {
|
|
4266
|
+
throw new ArcaInputError(
|
|
4267
|
+
"Non-annulment FCE notes must be associated to an FCE invoice.",
|
|
4268
|
+
{
|
|
4269
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
4270
|
+
field: "input.for.voucherType",
|
|
4271
|
+
expected: "an FCE invoice type"
|
|
4272
|
+
}
|
|
4273
|
+
);
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
async function lookupOriginal(wsfe, target, options) {
|
|
3680
4277
|
const original = await wsfe.lookupVoucher({
|
|
3681
4278
|
representedTaxId: options.representedTaxId,
|
|
3682
4279
|
forceRefresh: options.forceRefresh,
|
|
4280
|
+
...options.abortSignal === void 0 ? {} : { abortSignal: options.abortSignal },
|
|
3683
4281
|
...target
|
|
3684
4282
|
});
|
|
3685
4283
|
if (original.kind !== "found") {
|
|
@@ -3694,28 +4292,7 @@ async function prepareNote(wsfe, input, options, context, kind) {
|
|
|
3694
4292
|
{ code: "ARCA_INPUT_INVALID_VALUE", field: "input.for" }
|
|
3695
4293
|
);
|
|
3696
4294
|
}
|
|
3697
|
-
|
|
3698
|
-
if (note.details !== void 0) {
|
|
3699
|
-
prepared.data.details = structuredClone(note.details);
|
|
3700
|
-
} else if (note.all && "details" in original.voucher) {
|
|
3701
|
-
prepared.data.details = structuredClone(
|
|
3702
|
-
original.voucher.details
|
|
3703
|
-
);
|
|
3704
|
-
}
|
|
3705
|
-
if (voucherFamily(prepared.data.voucherType).family === "fce") {
|
|
3706
|
-
const taxId = options.representedTaxId ?? context?.taxId;
|
|
3707
|
-
if (!taxId) {
|
|
3708
|
-
throw new ArcaInputError("FCE association requires the issuer tax ID", {
|
|
3709
|
-
code: "ARCA_INPUT_MISSING_FIELD",
|
|
3710
|
-
field: "representedTaxId"
|
|
3711
|
-
});
|
|
3712
|
-
}
|
|
3713
|
-
for (const associated of prepared.data.associatedVouchers ?? []) {
|
|
3714
|
-
associated.taxId = String(taxId);
|
|
3715
|
-
}
|
|
3716
|
-
}
|
|
3717
|
-
validatePrepared(prepared, options);
|
|
3718
|
-
return { ...prepared, original: toVoucherSummary(original.voucher) };
|
|
4295
|
+
return original.voucher;
|
|
3719
4296
|
}
|
|
3720
4297
|
function preparePeriodNote(input, options, kind) {
|
|
3721
4298
|
const { associatedPeriod, ...invoice } = input;
|
|
@@ -3744,9 +4321,9 @@ function preparePeriodNote(input, options, kind) {
|
|
|
3744
4321
|
validatePrepared(prepared, options);
|
|
3745
4322
|
return prepared;
|
|
3746
4323
|
}
|
|
3747
|
-
function
|
|
3748
|
-
return include ? {
|
|
3749
|
-
|
|
4324
|
+
function requestEvidence(data, number, options) {
|
|
4325
|
+
return options.include?.request === true ? {
|
|
4326
|
+
request: options.service === "wsmtxca" ? wsmtxcaRequest(data, number) : data
|
|
3750
4327
|
} : {};
|
|
3751
4328
|
}
|
|
3752
4329
|
async function recoverOperation(select, key, inputOptions, context) {
|
|
@@ -3754,7 +4331,7 @@ async function recoverOperation(select, key, inputOptions, context) {
|
|
|
3754
4331
|
assertIssueObject(options, "options");
|
|
3755
4332
|
assertIssueKeys(
|
|
3756
4333
|
options,
|
|
3757
|
-
["representedTaxId", "forceRefresh", "include", "
|
|
4334
|
+
["representedTaxId", "forceRefresh", "include", "abortSignal"],
|
|
3758
4335
|
"options"
|
|
3759
4336
|
);
|
|
3760
4337
|
validateOptions(options);
|
|
@@ -3831,12 +4408,11 @@ function consultReservation(select, record, options, strangerAtNumber = false) {
|
|
|
3831
4408
|
strangerAtNumber,
|
|
3832
4409
|
attempted,
|
|
3833
4410
|
attempt: replayEvidence(storedOptions.service),
|
|
3834
|
-
|
|
3835
|
-
|
|
4411
|
+
includeRawResponse: options.include?.rawResponse === true,
|
|
4412
|
+
includedRequest: requestEvidence(
|
|
3836
4413
|
prepared.data,
|
|
3837
4414
|
record.number,
|
|
3838
|
-
storedOptions
|
|
3839
|
-
options.include?.exactInput === true
|
|
4415
|
+
storedOptions
|
|
3840
4416
|
),
|
|
3841
4417
|
voucher: (cae, caeExpiry) => ({
|
|
3842
4418
|
...attempted,
|
|
@@ -3891,7 +4467,7 @@ async function runSequenceBarrier({
|
|
|
3891
4467
|
settled,
|
|
3892
4468
|
await consultReservation(select, record, {
|
|
3893
4469
|
forceRefresh: options.forceRefresh,
|
|
3894
|
-
...options.
|
|
4470
|
+
...options.abortSignal === void 0 ? {} : { abortSignal: options.abortSignal }
|
|
3895
4471
|
})
|
|
3896
4472
|
);
|
|
3897
4473
|
if (outcome.kind === "authorized" || outcome.kind === "conflict") {
|
|
@@ -4367,4 +4943,4 @@ export {
|
|
|
4367
4943
|
withLease,
|
|
4368
4944
|
createFileStore
|
|
4369
4945
|
};
|
|
4370
|
-
//# sourceMappingURL=chunk-
|
|
4946
|
+
//# sourceMappingURL=chunk-KPWAQMR5.mjs.map
|