facturas 0.6.0 → 0.7.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 +9 -2
- package/dist/chunk-C55KOV5N.mjs +1586 -0
- package/dist/chunk-C55KOV5N.mjs.map +1 -0
- package/dist/chunk-EDY3PNKJ.mjs +106 -0
- package/dist/chunk-EDY3PNKJ.mjs.map +1 -0
- package/dist/chunk-IOKZX6CA.mjs +195 -0
- package/dist/chunk-IOKZX6CA.mjs.map +1 -0
- package/dist/chunk-MBWOFO67.mjs +215 -0
- package/dist/chunk-MBWOFO67.mjs.map +1 -0
- package/dist/chunk-PKE4Z4GE.mjs +702 -0
- package/dist/chunk-PKE4Z4GE.mjs.map +1 -0
- package/dist/chunk-VVY2LZIZ.mjs +61 -0
- package/dist/chunk-VVY2LZIZ.mjs.map +1 -0
- package/dist/constants.d.ts +32 -2
- package/dist/constants.mjs +14 -29
- package/dist/constants.mjs.map +1 -1
- package/dist/errors.d.ts +2 -92
- package/dist/errors.mjs +14 -92
- package/dist/errors.mjs.map +1 -1
- package/dist/fiscal-evidence-BQuBbJ3h.d.ts +189 -0
- package/dist/{index-3fdgJ3c2.d.ts → index-CVpygFb0.d.ts} +1 -1
- package/dist/index.d.ts +15 -8
- package/dist/index.mjs +269 -1878
- package/dist/index.mjs.map +1 -1
- package/dist/padron.d.ts +2 -2
- package/dist/padron.mjs +4 -117
- package/dist/padron.mjs.map +1 -1
- package/dist/{types-Cup-oevW.d.ts → types-BXq0da71.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/wsfe.d.ts +48 -7
- package/dist/wsfe.mjs +10 -1007
- package/dist/wsfe.mjs.map +1 -1
- package/dist/wsmtxca.d.ts +3 -3
- package/dist/wsmtxca.mjs +5 -641
- package/dist/wsmtxca.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/fiscal-evidence-DbbFH74x.d.ts +0 -69
|
@@ -0,0 +1,1586 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ARCA_CURRENCY_IDS,
|
|
3
|
+
ARCA_VAT_RATES,
|
|
4
|
+
ARCA_VOUCHER_TYPES
|
|
5
|
+
} from "./chunk-VVY2LZIZ.mjs";
|
|
6
|
+
import {
|
|
7
|
+
classifyArcaAuthenticationError,
|
|
8
|
+
classifyArcaAuthenticationIssues,
|
|
9
|
+
createArcaAuthenticationErrorFromEvidence,
|
|
10
|
+
createArcaAuthenticationEvidence,
|
|
11
|
+
executeWithAuthenticationRecovery
|
|
12
|
+
} from "./chunk-IOKZX6CA.mjs";
|
|
13
|
+
import {
|
|
14
|
+
ArcaInputError,
|
|
15
|
+
ArcaInvalidSoapResponseError,
|
|
16
|
+
ArcaServiceError,
|
|
17
|
+
ArcaSoapFaultError,
|
|
18
|
+
ArcaTransportError
|
|
19
|
+
} from "./chunk-MBWOFO67.mjs";
|
|
20
|
+
|
|
21
|
+
// src/internal/decimal.ts
|
|
22
|
+
var AMOUNT_SCALE = 100;
|
|
23
|
+
var EXCHANGE_RATE_SCALE = 1e6;
|
|
24
|
+
var EXCHANGE_RATE_SCALE_BIGINT = 1000000n;
|
|
25
|
+
var PERCENTAGE_SCALE = 100;
|
|
26
|
+
var MAX_ARCA_AMOUNT_MINOR_UNITS = 999999999999999n;
|
|
27
|
+
var MAX_ARCA_EXCHANGE_RATE_SCALED = 9999999999n;
|
|
28
|
+
var MAX_ARCA_PERCENTAGE_HUNDREDTHS = 99999n;
|
|
29
|
+
var VAT_RATE_BASIS_POINTS = {
|
|
30
|
+
0: 0n,
|
|
31
|
+
2.5: 250n,
|
|
32
|
+
5: 500n,
|
|
33
|
+
10.5: 1050n,
|
|
34
|
+
21: 2100n,
|
|
35
|
+
27: 2700n
|
|
36
|
+
};
|
|
37
|
+
function normalizeArcaAmountToMinorUnits(value, field) {
|
|
38
|
+
return normalizeScaledNumber({
|
|
39
|
+
value,
|
|
40
|
+
field,
|
|
41
|
+
scale: AMOUNT_SCALE,
|
|
42
|
+
maximum: MAX_ARCA_AMOUNT_MINOR_UNITS,
|
|
43
|
+
expected: "a finite non-negative amount with at most 2 decimal places"
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function serializeArcaAmount(value, field) {
|
|
47
|
+
return formatScaledInteger(normalizeArcaAmountToMinorUnits(value, field), 2);
|
|
48
|
+
}
|
|
49
|
+
function serializeArcaPercentage(value, field) {
|
|
50
|
+
const scaled = normalizeScaledNumber({
|
|
51
|
+
value,
|
|
52
|
+
field,
|
|
53
|
+
scale: PERCENTAGE_SCALE,
|
|
54
|
+
maximum: MAX_ARCA_PERCENTAGE_HUNDREDTHS,
|
|
55
|
+
expected: "a finite non-negative percentage with at most 2 decimal places"
|
|
56
|
+
});
|
|
57
|
+
return formatScaledInteger(scaled, 2);
|
|
58
|
+
}
|
|
59
|
+
function serializeArcaExchangeRate(value, field) {
|
|
60
|
+
const scaled = typeof value === "number" ? normalizeExchangeRateNumber(value, field) : normalizeExchangeRateString(value, field);
|
|
61
|
+
if (scaled <= 0n || scaled > MAX_ARCA_EXCHANGE_RATE_SCALED) {
|
|
62
|
+
throwInvalidExchangeRate(field);
|
|
63
|
+
}
|
|
64
|
+
return formatScaledInteger(scaled, 6, true);
|
|
65
|
+
}
|
|
66
|
+
function assertArcaMinorUnits(value, field) {
|
|
67
|
+
if (!(Number.isSafeInteger(value) && value >= 0)) {
|
|
68
|
+
throw new ArcaInputError(
|
|
69
|
+
`${field} must be a non-negative safe integer in currency minor units.`,
|
|
70
|
+
{
|
|
71
|
+
code: "ARCA_INPUT_INVALID_AMOUNT",
|
|
72
|
+
field,
|
|
73
|
+
expected: "a non-negative safe integer in currency minor units"
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
const minorUnits = BigInt(value);
|
|
78
|
+
if (minorUnits > MAX_ARCA_AMOUNT_MINOR_UNITS) {
|
|
79
|
+
throw new ArcaInputError(`${field} exceeds the WSFE amount limit.`, {
|
|
80
|
+
code: "ARCA_INPUT_INVALID_AMOUNT",
|
|
81
|
+
field,
|
|
82
|
+
expected: "at most 13 integer digits and 2 decimal places"
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return minorUnits;
|
|
86
|
+
}
|
|
87
|
+
function arcaMinorUnitsToNumber(minorUnits, field) {
|
|
88
|
+
if (minorUnits < 0n || minorUnits > MAX_ARCA_AMOUNT_MINOR_UNITS) {
|
|
89
|
+
throw new ArcaInputError(`${field} exceeds the WSFE amount limit.`, {
|
|
90
|
+
code: "ARCA_INPUT_INVALID_AMOUNT",
|
|
91
|
+
field,
|
|
92
|
+
expected: "at most 13 integer digits and 2 decimal places"
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return Number(formatScaledInteger(minorUnits, 2));
|
|
96
|
+
}
|
|
97
|
+
function calculateVatMinorUnits(taxableMinorUnits, vatRate, field) {
|
|
98
|
+
const basisPoints = VAT_RATE_BASIS_POINTS[vatRate];
|
|
99
|
+
if (basisPoints === void 0) {
|
|
100
|
+
throw new ArcaInputError(`${field} is not a supported VAT rate.`, {
|
|
101
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
102
|
+
field,
|
|
103
|
+
expected: "one of 0, 2.5, 5, 10.5, 21, or 27"
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return (taxableMinorUnits * basisPoints + 5000n) / 10000n;
|
|
107
|
+
}
|
|
108
|
+
function isWithinArcaTolerance(actualMinorUnits, expectedMinorUnits, absoluteCentAllowance = 1) {
|
|
109
|
+
const difference = absoluteBigInt(actualMinorUnits - expectedMinorUnits);
|
|
110
|
+
if (difference <= BigInt(Math.max(1, absoluteCentAllowance))) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
const comparisonBase = absoluteBigInt(expectedMinorUnits);
|
|
114
|
+
return comparisonBase > 0n && difference * 10000n <= comparisonBase;
|
|
115
|
+
}
|
|
116
|
+
function normalizeScaledNumber({
|
|
117
|
+
value,
|
|
118
|
+
field,
|
|
119
|
+
scale,
|
|
120
|
+
maximum,
|
|
121
|
+
expected
|
|
122
|
+
}) {
|
|
123
|
+
if (!(Number.isFinite(value) && value >= 0)) {
|
|
124
|
+
throw new ArcaInputError(`${field} must be ${expected}.`, {
|
|
125
|
+
code: "ARCA_INPUT_INVALID_AMOUNT",
|
|
126
|
+
field,
|
|
127
|
+
expected
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
const scaled = value * scale;
|
|
131
|
+
const nearestInteger = Math.round(scaled);
|
|
132
|
+
const representationTolerance = Math.max(
|
|
133
|
+
1e-9,
|
|
134
|
+
Math.abs(scaled) * Number.EPSILON * 4
|
|
135
|
+
);
|
|
136
|
+
if (Math.abs(scaled - nearestInteger) > representationTolerance) {
|
|
137
|
+
throw new ArcaInputError(
|
|
138
|
+
`${field} has more precision than its ARCA field allows.`,
|
|
139
|
+
{
|
|
140
|
+
code: "ARCA_INPUT_AMOUNT_PRECISION",
|
|
141
|
+
field,
|
|
142
|
+
expected
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
if (!Number.isSafeInteger(nearestInteger)) {
|
|
147
|
+
throw new ArcaInputError(`${field} exceeds the safely supported range.`, {
|
|
148
|
+
code: "ARCA_INPUT_INVALID_AMOUNT",
|
|
149
|
+
field,
|
|
150
|
+
expected
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
const normalized = BigInt(nearestInteger);
|
|
154
|
+
if (normalized > maximum) {
|
|
155
|
+
throw new ArcaInputError(`${field} exceeds the ARCA field limit.`, {
|
|
156
|
+
code: "ARCA_INPUT_INVALID_AMOUNT",
|
|
157
|
+
field,
|
|
158
|
+
expected
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return normalized;
|
|
162
|
+
}
|
|
163
|
+
function normalizeExchangeRateNumber(value, field) {
|
|
164
|
+
if (!(Number.isFinite(value) && value > 0)) {
|
|
165
|
+
throwInvalidExchangeRate(field);
|
|
166
|
+
}
|
|
167
|
+
const scaled = value * EXCHANGE_RATE_SCALE;
|
|
168
|
+
const nearestInteger = Math.round(scaled);
|
|
169
|
+
const representationTolerance = Math.max(
|
|
170
|
+
1e-9,
|
|
171
|
+
Math.abs(scaled) * Number.EPSILON * 4
|
|
172
|
+
);
|
|
173
|
+
if (Math.abs(scaled - nearestInteger) > representationTolerance || !Number.isSafeInteger(nearestInteger)) {
|
|
174
|
+
throwInvalidExchangeRate(field);
|
|
175
|
+
}
|
|
176
|
+
return BigInt(nearestInteger);
|
|
177
|
+
}
|
|
178
|
+
function normalizeExchangeRateString(value, field) {
|
|
179
|
+
const match = value.match(/^(0|[1-9]\d{0,3})(?:\.(\d{1,6}))?$/);
|
|
180
|
+
if (!match) {
|
|
181
|
+
throwInvalidExchangeRate(field);
|
|
182
|
+
}
|
|
183
|
+
const [, integerPart, fractionPart = ""] = match;
|
|
184
|
+
return BigInt(integerPart) * EXCHANGE_RATE_SCALE_BIGINT + BigInt(fractionPart.padEnd(6, "0"));
|
|
185
|
+
}
|
|
186
|
+
function throwInvalidExchangeRate(field) {
|
|
187
|
+
throw new ArcaInputError(
|
|
188
|
+
`${field} must be a positive decimal with at most 4 integer and 6 fractional digits.`,
|
|
189
|
+
{
|
|
190
|
+
code: "ARCA_INPUT_INVALID_EXCHANGE_RATE",
|
|
191
|
+
field,
|
|
192
|
+
expected: "a positive decimal with up to 4 integer and 6 fractional digits"
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
function formatScaledInteger(value, fractionDigits, trimTrailingZeros = false) {
|
|
197
|
+
const scale = 10n ** BigInt(fractionDigits);
|
|
198
|
+
const integerPart = value / scale;
|
|
199
|
+
const fractionPart = (value % scale).toString().padStart(fractionDigits, "0");
|
|
200
|
+
if (trimTrailingZeros) {
|
|
201
|
+
const trimmedFraction = fractionPart.replace(/0+$/, "");
|
|
202
|
+
return trimmedFraction.length === 0 ? integerPart.toString() : `${integerPart}.${trimmedFraction}`;
|
|
203
|
+
}
|
|
204
|
+
return `${integerPart}.${fractionPart}`;
|
|
205
|
+
}
|
|
206
|
+
function absoluteBigInt(value) {
|
|
207
|
+
return value < 0n ? -value : value;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/services/wsfe.ts
|
|
211
|
+
function createWsfeService(options) {
|
|
212
|
+
async function executeWsfeAuthenticatedRawOperation(operation, input, body = {}, retries) {
|
|
213
|
+
const auth = await options.auth.login("wsfe", {
|
|
214
|
+
representedTaxId: input.representedTaxId,
|
|
215
|
+
forceRefresh: input.forceRefresh
|
|
216
|
+
});
|
|
217
|
+
const response = await options.soap.execute({
|
|
218
|
+
service: "wsfe",
|
|
219
|
+
operation,
|
|
220
|
+
...retries === void 0 ? {} : { retries },
|
|
221
|
+
body: {
|
|
222
|
+
Auth: createWsfeAuth(
|
|
223
|
+
input.representedTaxId ?? options.config.taxId,
|
|
224
|
+
auth.token,
|
|
225
|
+
auth.sign
|
|
226
|
+
),
|
|
227
|
+
...body
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
return unwrapWsfeOperationEnvelope(operation, response.result);
|
|
231
|
+
}
|
|
232
|
+
function executeWsfeAuthenticatedOperation(operation, input, body = {}) {
|
|
233
|
+
return executeWithAuthenticationRecovery({
|
|
234
|
+
service: "wsfe",
|
|
235
|
+
operation,
|
|
236
|
+
forceRefresh: input.forceRefresh,
|
|
237
|
+
async execute(forceRefresh) {
|
|
238
|
+
const result = await executeWsfeAuthenticatedRawOperation(
|
|
239
|
+
operation,
|
|
240
|
+
{ representedTaxId: input.representedTaxId, forceRefresh },
|
|
241
|
+
body
|
|
242
|
+
);
|
|
243
|
+
throwForWsfeOperationErrors(operation, result);
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
async function executeWsfeOperation(operation, body = {}) {
|
|
249
|
+
const response = await options.soap.execute({
|
|
250
|
+
service: "wsfe",
|
|
251
|
+
operation,
|
|
252
|
+
body
|
|
253
|
+
});
|
|
254
|
+
const result = unwrapWsfeOperationEnvelope(operation, response.result);
|
|
255
|
+
throwForWsfeOperationErrors(operation, result);
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
async function getNextVoucherNumber({
|
|
259
|
+
representedTaxId,
|
|
260
|
+
salesPoint,
|
|
261
|
+
voucherType,
|
|
262
|
+
forceRefresh
|
|
263
|
+
}) {
|
|
264
|
+
const result = await executeWsfeAuthenticatedOperation(
|
|
265
|
+
"FECompUltimoAutorizado",
|
|
266
|
+
{
|
|
267
|
+
representedTaxId,
|
|
268
|
+
forceRefresh
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
PtoVta: salesPoint,
|
|
272
|
+
CbteTipo: voucherType
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
return Number(result.CbteNro ?? 0) + 1;
|
|
276
|
+
}
|
|
277
|
+
async function getWsfeCatalog(operation, resultKey, input) {
|
|
278
|
+
const result = await executeWsfeAuthenticatedOperation(operation, {
|
|
279
|
+
representedTaxId: input.representedTaxId,
|
|
280
|
+
forceRefresh: input.forceRefresh
|
|
281
|
+
});
|
|
282
|
+
return getWsfeResultEntries(result, resultKey).map(mapWsfeCatalogEntry);
|
|
283
|
+
}
|
|
284
|
+
function authorizeVoucher({
|
|
285
|
+
representedTaxId,
|
|
286
|
+
data,
|
|
287
|
+
voucherNumber,
|
|
288
|
+
forceRefresh
|
|
289
|
+
}) {
|
|
290
|
+
const normalizedInput = normalizeWsfeVoucherInput(data);
|
|
291
|
+
return authorizeNormalizedVoucher({
|
|
292
|
+
representedTaxId,
|
|
293
|
+
data: normalizedInput,
|
|
294
|
+
voucherNumber,
|
|
295
|
+
forceRefresh
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
function authorizeVoucherOutcome({
|
|
299
|
+
representedTaxId,
|
|
300
|
+
data,
|
|
301
|
+
voucherNumber,
|
|
302
|
+
forceRefresh
|
|
303
|
+
}) {
|
|
304
|
+
const normalizedInput = normalizeWsfeVoucherInput(data);
|
|
305
|
+
return executeWsfeAuthorization({
|
|
306
|
+
representedTaxId,
|
|
307
|
+
data: normalizedInput,
|
|
308
|
+
voucherNumber,
|
|
309
|
+
forceRefresh
|
|
310
|
+
}).then(({ outcome }) => outcome);
|
|
311
|
+
}
|
|
312
|
+
function authorizeNormalizedVoucher({
|
|
313
|
+
representedTaxId,
|
|
314
|
+
data: normalizedInput,
|
|
315
|
+
voucherNumber,
|
|
316
|
+
forceRefresh,
|
|
317
|
+
allowAuthenticationRecovery
|
|
318
|
+
}) {
|
|
319
|
+
return executeWithAuthenticationRecovery({
|
|
320
|
+
service: "wsfe",
|
|
321
|
+
operation: "FECAESolicitar",
|
|
322
|
+
forceRefresh,
|
|
323
|
+
allowRetry: allowAuthenticationRecovery,
|
|
324
|
+
execute: (attemptForceRefresh) => authorizeNormalizedVoucherOnce({
|
|
325
|
+
representedTaxId,
|
|
326
|
+
data: normalizedInput,
|
|
327
|
+
voucherNumber,
|
|
328
|
+
forceRefresh: attemptForceRefresh
|
|
329
|
+
})
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
async function authorizeNormalizedVoucherOnce({
|
|
333
|
+
representedTaxId,
|
|
334
|
+
data: normalizedInput,
|
|
335
|
+
voucherNumber,
|
|
336
|
+
forceRefresh
|
|
337
|
+
}) {
|
|
338
|
+
const execution = await executeWsfeAuthorization({
|
|
339
|
+
representedTaxId,
|
|
340
|
+
data: normalizedInput,
|
|
341
|
+
voucherNumber,
|
|
342
|
+
forceRefresh
|
|
343
|
+
});
|
|
344
|
+
if (execution.error) {
|
|
345
|
+
throw execution.error;
|
|
346
|
+
}
|
|
347
|
+
if (execution.outcome.kind !== "authorized") {
|
|
348
|
+
throw createWsfeOutcomeError(execution.outcome);
|
|
349
|
+
}
|
|
350
|
+
const { cae, caeExpiry, raw } = execution.outcome;
|
|
351
|
+
if (!(caeExpiry && raw)) {
|
|
352
|
+
throw new ArcaServiceError("WSFE did not return CAE authorization data", {
|
|
353
|
+
service: "wsfe",
|
|
354
|
+
operation: "FECAESolicitar",
|
|
355
|
+
result: execution.outcome.result,
|
|
356
|
+
resultLevel: execution.outcome.resultLevel,
|
|
357
|
+
results: execution.outcome.results,
|
|
358
|
+
cae,
|
|
359
|
+
issues: [
|
|
360
|
+
...execution.outcome.errors,
|
|
361
|
+
...execution.outcome.observations
|
|
362
|
+
]
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
cae,
|
|
367
|
+
caeExpiry,
|
|
368
|
+
voucherNumber,
|
|
369
|
+
raw
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
async function executeWsfeAuthorization({
|
|
373
|
+
representedTaxId,
|
|
374
|
+
data: normalizedInput,
|
|
375
|
+
voucherNumber,
|
|
376
|
+
forceRefresh
|
|
377
|
+
}) {
|
|
378
|
+
const requestData = mapWsfeVoucherInput(normalizedInput, voucherNumber);
|
|
379
|
+
try {
|
|
380
|
+
const result = await executeWsfeAuthenticatedRawOperation(
|
|
381
|
+
"FECAESolicitar",
|
|
382
|
+
{ representedTaxId, forceRefresh },
|
|
383
|
+
{
|
|
384
|
+
FeCAEReq: {
|
|
385
|
+
FeCabReq: {
|
|
386
|
+
CantReg: 1,
|
|
387
|
+
PtoVta: normalizedInput.salesPoint,
|
|
388
|
+
CbteTipo: normalizedInput.voucherType
|
|
389
|
+
},
|
|
390
|
+
FeDetReq: {
|
|
391
|
+
FECAEDetRequest: requestData
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
0
|
|
396
|
+
);
|
|
397
|
+
return {
|
|
398
|
+
outcome: classifyWsfeAuthorization(result, voucherNumber)
|
|
399
|
+
};
|
|
400
|
+
} catch (error) {
|
|
401
|
+
return {
|
|
402
|
+
outcome: createWsfeIndeterminateOutcome(error),
|
|
403
|
+
error
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function lookupVoucher({
|
|
408
|
+
representedTaxId,
|
|
409
|
+
number,
|
|
410
|
+
salesPoint,
|
|
411
|
+
voucherType,
|
|
412
|
+
forceRefresh
|
|
413
|
+
}) {
|
|
414
|
+
return executeWithAuthenticationRecovery({
|
|
415
|
+
service: "wsfe",
|
|
416
|
+
operation: "FECompConsultar",
|
|
417
|
+
forceRefresh,
|
|
418
|
+
execute: (attemptForceRefresh) => lookupVoucherOnce({
|
|
419
|
+
representedTaxId,
|
|
420
|
+
number,
|
|
421
|
+
salesPoint,
|
|
422
|
+
voucherType,
|
|
423
|
+
forceRefresh: attemptForceRefresh
|
|
424
|
+
})
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
async function lookupVoucherOnce({
|
|
428
|
+
representedTaxId,
|
|
429
|
+
number,
|
|
430
|
+
salesPoint,
|
|
431
|
+
voucherType,
|
|
432
|
+
forceRefresh
|
|
433
|
+
}) {
|
|
434
|
+
const operation = "FECompConsultar";
|
|
435
|
+
const result = await executeWsfeAuthenticatedRawOperation(
|
|
436
|
+
operation,
|
|
437
|
+
{ representedTaxId, forceRefresh },
|
|
438
|
+
{
|
|
439
|
+
FeCompConsReq: {
|
|
440
|
+
CbteNro: number,
|
|
441
|
+
PtoVta: salesPoint,
|
|
442
|
+
CbteTipo: voucherType
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
);
|
|
446
|
+
const errors = extractWsfeGlobalIssues(result, operation);
|
|
447
|
+
if (errors.length > 0 && errors.every((issue) => issue.code === "602")) {
|
|
448
|
+
return {
|
|
449
|
+
kind: "not_found",
|
|
450
|
+
service: "wsfe",
|
|
451
|
+
operation,
|
|
452
|
+
errors,
|
|
453
|
+
observations: [],
|
|
454
|
+
raw: result
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
if (errors.length > 0) {
|
|
458
|
+
throw createWsfeServiceError(operation, errors);
|
|
459
|
+
}
|
|
460
|
+
const raw = toWsfeRecord(result.ResultGet);
|
|
461
|
+
if (!raw) {
|
|
462
|
+
throw new ArcaServiceError("WSFE did not return the consulted voucher", {
|
|
463
|
+
service: "wsfe",
|
|
464
|
+
operation
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
return {
|
|
468
|
+
kind: "found",
|
|
469
|
+
service: "wsfe",
|
|
470
|
+
operation,
|
|
471
|
+
voucher: mapWsfeVoucherInfo(raw),
|
|
472
|
+
observations: [],
|
|
473
|
+
raw: result
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
return {
|
|
477
|
+
authorizeVoucherOutcome,
|
|
478
|
+
authorizeVoucher,
|
|
479
|
+
async createNextVoucher({ representedTaxId, data, forceRefresh }) {
|
|
480
|
+
const normalizedInput = normalizeWsfeVoucherInput(data);
|
|
481
|
+
const voucherNumber = await getNextVoucherNumber({
|
|
482
|
+
representedTaxId,
|
|
483
|
+
salesPoint: normalizedInput.salesPoint,
|
|
484
|
+
voucherType: normalizedInput.voucherType,
|
|
485
|
+
forceRefresh
|
|
486
|
+
});
|
|
487
|
+
return authorizeNormalizedVoucher({
|
|
488
|
+
representedTaxId,
|
|
489
|
+
data: normalizedInput,
|
|
490
|
+
voucherNumber,
|
|
491
|
+
allowAuthenticationRecovery: forceRefresh !== true
|
|
492
|
+
});
|
|
493
|
+
},
|
|
494
|
+
getNextVoucherNumber,
|
|
495
|
+
getLastVoucher(input) {
|
|
496
|
+
return getNextVoucherNumber(input);
|
|
497
|
+
},
|
|
498
|
+
async getSalesPoints({ representedTaxId, forceRefresh }) {
|
|
499
|
+
const result = await executeWsfeAuthenticatedOperation(
|
|
500
|
+
"FEParamGetPtosVenta",
|
|
501
|
+
{
|
|
502
|
+
representedTaxId,
|
|
503
|
+
forceRefresh
|
|
504
|
+
}
|
|
505
|
+
);
|
|
506
|
+
const rawPoints = result.ResultGet?.PtoVenta;
|
|
507
|
+
if (!rawPoints) {
|
|
508
|
+
return [];
|
|
509
|
+
}
|
|
510
|
+
const entries = Array.isArray(rawPoints) ? rawPoints : [rawPoints];
|
|
511
|
+
return entries.map(mapWsfeSalesPoint);
|
|
512
|
+
},
|
|
513
|
+
getVoucherTypes(input) {
|
|
514
|
+
return getWsfeCatalog("FEParamGetTiposCbte", "CbteTipo", input);
|
|
515
|
+
},
|
|
516
|
+
getDocumentTypes(input) {
|
|
517
|
+
return getWsfeCatalog("FEParamGetTiposDoc", "DocTipo", input);
|
|
518
|
+
},
|
|
519
|
+
getConceptTypes(input) {
|
|
520
|
+
return getWsfeCatalog("FEParamGetTiposConcepto", "ConceptoTipo", input);
|
|
521
|
+
},
|
|
522
|
+
async getCurrencyTypes({ representedTaxId, forceRefresh }) {
|
|
523
|
+
const result = await executeWsfeAuthenticatedOperation(
|
|
524
|
+
"FEParamGetTiposMonedas",
|
|
525
|
+
{
|
|
526
|
+
representedTaxId,
|
|
527
|
+
forceRefresh
|
|
528
|
+
}
|
|
529
|
+
);
|
|
530
|
+
return getWsfeResultEntries(result, "Moneda").map(mapWsfeCurrencyType);
|
|
531
|
+
},
|
|
532
|
+
getVatRates(input) {
|
|
533
|
+
return getWsfeCatalog("FEParamGetTiposIva", "IvaTipo", input);
|
|
534
|
+
},
|
|
535
|
+
getTaxTypes(input) {
|
|
536
|
+
return getWsfeCatalog("FEParamGetTiposTributos", "TributoTipo", input);
|
|
537
|
+
},
|
|
538
|
+
getOptionalTypes(input) {
|
|
539
|
+
return getWsfeCatalog("FEParamGetTiposOpcional", "OpcionalTipo", input);
|
|
540
|
+
},
|
|
541
|
+
async getActivities({ representedTaxId, forceRefresh }) {
|
|
542
|
+
const result = await executeWsfeAuthenticatedOperation(
|
|
543
|
+
"FEParamGetActividades",
|
|
544
|
+
{
|
|
545
|
+
representedTaxId,
|
|
546
|
+
forceRefresh
|
|
547
|
+
}
|
|
548
|
+
);
|
|
549
|
+
return getWsfeResultEntries(result, "ActividadesTipo").map(
|
|
550
|
+
mapWsfeActivityType
|
|
551
|
+
);
|
|
552
|
+
},
|
|
553
|
+
async getReceiverVatConditions({
|
|
554
|
+
representedTaxId,
|
|
555
|
+
voucherClass,
|
|
556
|
+
forceRefresh
|
|
557
|
+
}) {
|
|
558
|
+
const result = await executeWsfeAuthenticatedOperation(
|
|
559
|
+
"FEParamGetCondicionIvaReceptor",
|
|
560
|
+
{
|
|
561
|
+
representedTaxId,
|
|
562
|
+
forceRefresh
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
...voucherClass === void 0 ? {} : { ClaseCmp: voucherClass }
|
|
566
|
+
}
|
|
567
|
+
);
|
|
568
|
+
return getWsfeResultEntries(result, "CondicionIvaReceptor").map(
|
|
569
|
+
mapWsfeReceiverVatCondition
|
|
570
|
+
);
|
|
571
|
+
},
|
|
572
|
+
async getServerStatus() {
|
|
573
|
+
const result = await executeWsfeOperation("FEDummy");
|
|
574
|
+
return mapWsfeServerStatus(result);
|
|
575
|
+
},
|
|
576
|
+
async getQuotation({ currencyId, representedTaxId, forceRefresh }) {
|
|
577
|
+
const result = await executeWsfeAuthenticatedOperation(
|
|
578
|
+
"FEParamGetCotizacion",
|
|
579
|
+
{
|
|
580
|
+
representedTaxId,
|
|
581
|
+
forceRefresh
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
MonId: currencyId
|
|
585
|
+
}
|
|
586
|
+
);
|
|
587
|
+
const raw = result.ResultGet ?? {};
|
|
588
|
+
return mapWsfeQuotation(raw);
|
|
589
|
+
},
|
|
590
|
+
async getVoucherInfo(input) {
|
|
591
|
+
const lookup = await lookupVoucher(input);
|
|
592
|
+
return lookup.kind === "found" ? lookup.voucher : null;
|
|
593
|
+
},
|
|
594
|
+
lookupVoucher
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
function mapWsfeVoucherInput(input, voucherNumber) {
|
|
598
|
+
const data = {
|
|
599
|
+
Concepto: input.concept,
|
|
600
|
+
DocTipo: input.documentType,
|
|
601
|
+
DocNro: input.documentNumber,
|
|
602
|
+
CbteDesde: voucherNumber,
|
|
603
|
+
CbteHasta: voucherNumber,
|
|
604
|
+
CbteFch: input.voucherDate,
|
|
605
|
+
ImpTotal: input.totalAmount,
|
|
606
|
+
ImpTotConc: input.nonTaxableAmount,
|
|
607
|
+
ImpNeto: input.netAmount,
|
|
608
|
+
ImpOpEx: input.exemptAmount,
|
|
609
|
+
ImpTrib: input.taxAmount,
|
|
610
|
+
ImpIVA: input.vatAmount,
|
|
611
|
+
MonId: input.currencyId,
|
|
612
|
+
CondicionIVAReceptorId: input.receiverVatConditionId,
|
|
613
|
+
PtoVta: input.salesPoint,
|
|
614
|
+
CbteTipo: input.voucherType
|
|
615
|
+
};
|
|
616
|
+
if (input.exchangeRate !== void 0) {
|
|
617
|
+
data.MonCotiz = input.exchangeRate;
|
|
618
|
+
}
|
|
619
|
+
if (input.currencyId !== "PES" && input.sameCurrencyForeignCancellation !== void 0) {
|
|
620
|
+
data.CanMisMonExt = input.sameCurrencyForeignCancellation;
|
|
621
|
+
}
|
|
622
|
+
if (input.serviceStartDate !== void 0) {
|
|
623
|
+
data.FchServDesde = input.serviceStartDate;
|
|
624
|
+
}
|
|
625
|
+
if (input.serviceEndDate !== void 0) {
|
|
626
|
+
data.FchServHasta = input.serviceEndDate;
|
|
627
|
+
}
|
|
628
|
+
if (input.paymentDueDate !== void 0) {
|
|
629
|
+
data.FchVtoPago = input.paymentDueDate;
|
|
630
|
+
}
|
|
631
|
+
if (input.associatedVouchers) {
|
|
632
|
+
data.CbtesAsoc = {
|
|
633
|
+
CbteAsoc: input.associatedVouchers.map((v) => ({
|
|
634
|
+
Tipo: v.type,
|
|
635
|
+
PtoVta: v.salesPoint,
|
|
636
|
+
Nro: v.number,
|
|
637
|
+
...v.taxId === void 0 ? {} : { Cuit: v.taxId },
|
|
638
|
+
...v.voucherDate === void 0 ? {} : { CbteFch: v.voucherDate }
|
|
639
|
+
}))
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
if (input.associatedPeriod) {
|
|
643
|
+
data.PeriodoAsoc = {
|
|
644
|
+
FchDesde: input.associatedPeriod.startDate,
|
|
645
|
+
FchHasta: input.associatedPeriod.endDate
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
if (input.taxes) {
|
|
649
|
+
data.Tributos = {
|
|
650
|
+
Tributo: input.taxes.map((t) => ({
|
|
651
|
+
Id: t.id,
|
|
652
|
+
...t.description === void 0 ? {} : { Desc: t.description },
|
|
653
|
+
BaseImp: t.baseAmount,
|
|
654
|
+
Alic: t.rate,
|
|
655
|
+
Importe: t.amount
|
|
656
|
+
}))
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
if (input.vatRates) {
|
|
660
|
+
data.Iva = {
|
|
661
|
+
AlicIva: input.vatRates.map((v) => ({
|
|
662
|
+
Id: v.id,
|
|
663
|
+
BaseImp: v.baseAmount,
|
|
664
|
+
Importe: v.amount
|
|
665
|
+
}))
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
if (input.optionalFields) {
|
|
669
|
+
data.Opcionales = {
|
|
670
|
+
Opcional: input.optionalFields.map((o) => ({
|
|
671
|
+
Id: o.id,
|
|
672
|
+
Valor: o.value
|
|
673
|
+
}))
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
if (input.buyers) {
|
|
677
|
+
data.Compradores = {
|
|
678
|
+
Comprador: input.buyers.map((b) => ({
|
|
679
|
+
DocTipo: b.documentType,
|
|
680
|
+
DocNro: b.documentNumber,
|
|
681
|
+
Porcentaje: b.percentage
|
|
682
|
+
}))
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
if (input.activities) {
|
|
686
|
+
data.Actividades = {
|
|
687
|
+
Actividad: input.activities.map((a) => ({
|
|
688
|
+
Id: a.id
|
|
689
|
+
}))
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
return data;
|
|
693
|
+
}
|
|
694
|
+
function normalizeWsfeVoucherInput(input) {
|
|
695
|
+
if (input.receiverVatConditionId === void 0) {
|
|
696
|
+
throw new ArcaInputError("receiverVatConditionId is required.", {
|
|
697
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
698
|
+
field: "receiverVatConditionId",
|
|
699
|
+
expected: "a receiver VAT condition accepted for the voucher class"
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
const {
|
|
703
|
+
voucherDate,
|
|
704
|
+
exchangeRate,
|
|
705
|
+
serviceStartDate,
|
|
706
|
+
serviceEndDate,
|
|
707
|
+
paymentDueDate,
|
|
708
|
+
associatedVouchers,
|
|
709
|
+
associatedPeriod,
|
|
710
|
+
taxes,
|
|
711
|
+
vatRates,
|
|
712
|
+
...rest
|
|
713
|
+
} = input;
|
|
714
|
+
const normalizedExchangeRate = normalizeWsfeExchangeRate(input, exchangeRate);
|
|
715
|
+
const normalizedAmounts = normalizeAndValidateWsfeAmounts(input);
|
|
716
|
+
return {
|
|
717
|
+
...rest,
|
|
718
|
+
...normalizedAmounts,
|
|
719
|
+
voucherDate: normalizeWsfeDateInput(voucherDate, "voucherDate"),
|
|
720
|
+
...normalizedExchangeRate === void 0 ? {} : { exchangeRate: normalizedExchangeRate },
|
|
721
|
+
...serviceStartDate === void 0 ? {} : {
|
|
722
|
+
serviceStartDate: normalizeWsfeDateInput(
|
|
723
|
+
serviceStartDate,
|
|
724
|
+
"serviceStartDate"
|
|
725
|
+
)
|
|
726
|
+
},
|
|
727
|
+
...serviceEndDate === void 0 ? {} : {
|
|
728
|
+
serviceEndDate: normalizeWsfeDateInput(
|
|
729
|
+
serviceEndDate,
|
|
730
|
+
"serviceEndDate"
|
|
731
|
+
)
|
|
732
|
+
},
|
|
733
|
+
...paymentDueDate === void 0 ? {} : {
|
|
734
|
+
paymentDueDate: normalizeWsfeDateInput(
|
|
735
|
+
paymentDueDate,
|
|
736
|
+
"paymentDueDate"
|
|
737
|
+
)
|
|
738
|
+
},
|
|
739
|
+
...associatedVouchers === void 0 ? {} : {
|
|
740
|
+
associatedVouchers: associatedVouchers.map((voucher, index) => {
|
|
741
|
+
const { voucherDate: associatedVoucherDate, ...associatedRest } = voucher;
|
|
742
|
+
return {
|
|
743
|
+
...associatedRest,
|
|
744
|
+
...associatedVoucherDate === void 0 ? {} : {
|
|
745
|
+
voucherDate: normalizeWsfeDateInput(
|
|
746
|
+
associatedVoucherDate,
|
|
747
|
+
`associatedVouchers[${index}].voucherDate`
|
|
748
|
+
)
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
})
|
|
752
|
+
},
|
|
753
|
+
...associatedPeriod === void 0 ? {} : {
|
|
754
|
+
associatedPeriod: {
|
|
755
|
+
startDate: normalizeWsfeDateInput(
|
|
756
|
+
associatedPeriod.startDate,
|
|
757
|
+
"associatedPeriod.startDate"
|
|
758
|
+
),
|
|
759
|
+
endDate: normalizeWsfeDateInput(
|
|
760
|
+
associatedPeriod.endDate,
|
|
761
|
+
"associatedPeriod.endDate"
|
|
762
|
+
)
|
|
763
|
+
}
|
|
764
|
+
},
|
|
765
|
+
...taxes === void 0 ? {} : {
|
|
766
|
+
taxes: taxes.map((tax, index) => ({
|
|
767
|
+
...tax,
|
|
768
|
+
baseAmount: serializeArcaAmount(
|
|
769
|
+
tax.baseAmount,
|
|
770
|
+
`taxes[${index}].baseAmount`
|
|
771
|
+
),
|
|
772
|
+
rate: serializeArcaPercentage(tax.rate, `taxes[${index}].rate`),
|
|
773
|
+
amount: serializeArcaAmount(tax.amount, `taxes[${index}].amount`)
|
|
774
|
+
}))
|
|
775
|
+
},
|
|
776
|
+
...vatRates === void 0 ? {} : {
|
|
777
|
+
vatRates: vatRates.map((vatRate, index) => ({
|
|
778
|
+
...vatRate,
|
|
779
|
+
baseAmount: serializeArcaAmount(
|
|
780
|
+
vatRate.baseAmount,
|
|
781
|
+
`vatRates[${index}].baseAmount`
|
|
782
|
+
),
|
|
783
|
+
amount: serializeArcaAmount(
|
|
784
|
+
vatRate.amount,
|
|
785
|
+
`vatRates[${index}].amount`
|
|
786
|
+
)
|
|
787
|
+
}))
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
function normalizeAndValidateWsfeAmounts(input) {
|
|
792
|
+
const totalAmount = normalizeArcaAmountToMinorUnits(
|
|
793
|
+
input.totalAmount,
|
|
794
|
+
"totalAmount"
|
|
795
|
+
);
|
|
796
|
+
const nonTaxableAmount = normalizeArcaAmountToMinorUnits(
|
|
797
|
+
input.nonTaxableAmount,
|
|
798
|
+
"nonTaxableAmount"
|
|
799
|
+
);
|
|
800
|
+
const netAmount = normalizeArcaAmountToMinorUnits(
|
|
801
|
+
input.netAmount,
|
|
802
|
+
"netAmount"
|
|
803
|
+
);
|
|
804
|
+
const exemptAmount = normalizeArcaAmountToMinorUnits(
|
|
805
|
+
input.exemptAmount,
|
|
806
|
+
"exemptAmount"
|
|
807
|
+
);
|
|
808
|
+
const taxAmount = normalizeArcaAmountToMinorUnits(
|
|
809
|
+
input.taxAmount,
|
|
810
|
+
"taxAmount"
|
|
811
|
+
);
|
|
812
|
+
const vatAmount = normalizeArcaAmountToMinorUnits(
|
|
813
|
+
input.vatAmount,
|
|
814
|
+
"vatAmount"
|
|
815
|
+
);
|
|
816
|
+
const decomposedTotal = nonTaxableAmount + netAmount + exemptAmount + taxAmount + vatAmount;
|
|
817
|
+
assertWsfeAmountMatch(
|
|
818
|
+
totalAmount,
|
|
819
|
+
decomposedTotal,
|
|
820
|
+
"totalAmount",
|
|
821
|
+
"the sum of nonTaxableAmount, netAmount, exemptAmount, taxAmount, and vatAmount"
|
|
822
|
+
);
|
|
823
|
+
const vatRates = input.vatRates ?? [];
|
|
824
|
+
if (vatAmount > 0n && vatRates.length === 0) {
|
|
825
|
+
throw new ArcaInputError(
|
|
826
|
+
"vatRates is required when vatAmount is greater than zero.",
|
|
827
|
+
{
|
|
828
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
829
|
+
field: "vatRates",
|
|
830
|
+
expected: "VAT detail whose amounts reconcile with vatAmount"
|
|
831
|
+
}
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
if (vatRates.length > 0) {
|
|
835
|
+
const normalizedVatRates = vatRates.map((vatRate, index) => ({
|
|
836
|
+
baseAmount: normalizeArcaAmountToMinorUnits(
|
|
837
|
+
vatRate.baseAmount,
|
|
838
|
+
`vatRates[${index}].baseAmount`
|
|
839
|
+
),
|
|
840
|
+
amount: normalizeArcaAmountToMinorUnits(
|
|
841
|
+
vatRate.amount,
|
|
842
|
+
`vatRates[${index}].amount`
|
|
843
|
+
)
|
|
844
|
+
}));
|
|
845
|
+
const vatRateAmountSum = normalizedVatRates.reduce(
|
|
846
|
+
(sum, vatRate) => sum + vatRate.amount,
|
|
847
|
+
0n
|
|
848
|
+
);
|
|
849
|
+
const vatRateBaseSum = normalizedVatRates.reduce(
|
|
850
|
+
(sum, vatRate) => sum + vatRate.baseAmount,
|
|
851
|
+
0n
|
|
852
|
+
);
|
|
853
|
+
assertWsfeAmountMatch(
|
|
854
|
+
vatAmount,
|
|
855
|
+
vatRateAmountSum,
|
|
856
|
+
"vatAmount",
|
|
857
|
+
"the sum of vatRates[].amount",
|
|
858
|
+
vatRates.length
|
|
859
|
+
);
|
|
860
|
+
if (requiresWsfeVatBaseReconciliation(input.voucherType)) {
|
|
861
|
+
assertWsfeAmountMatch(
|
|
862
|
+
netAmount,
|
|
863
|
+
vatRateBaseSum,
|
|
864
|
+
"netAmount",
|
|
865
|
+
"the sum of vatRates[].baseAmount",
|
|
866
|
+
vatRates.length
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
const taxes = input.taxes ?? [];
|
|
871
|
+
if (taxAmount > 0n && taxes.length === 0) {
|
|
872
|
+
throw new ArcaInputError(
|
|
873
|
+
"taxes is required when taxAmount is greater than zero.",
|
|
874
|
+
{
|
|
875
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
876
|
+
field: "taxes",
|
|
877
|
+
expected: "tax detail whose amounts reconcile with taxAmount"
|
|
878
|
+
}
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
if (taxes.length > 0) {
|
|
882
|
+
const taxAmountSum = taxes.reduce((sum, tax, index) => {
|
|
883
|
+
normalizeArcaAmountToMinorUnits(
|
|
884
|
+
tax.baseAmount,
|
|
885
|
+
`taxes[${index}].baseAmount`
|
|
886
|
+
);
|
|
887
|
+
serializeArcaPercentage(tax.rate, `taxes[${index}].rate`);
|
|
888
|
+
return sum + normalizeArcaAmountToMinorUnits(tax.amount, `taxes[${index}].amount`);
|
|
889
|
+
}, 0n);
|
|
890
|
+
assertWsfeAmountMatch(
|
|
891
|
+
taxAmount,
|
|
892
|
+
taxAmountSum,
|
|
893
|
+
"taxAmount",
|
|
894
|
+
"the sum of taxes[].amount",
|
|
895
|
+
taxes.length
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
return {
|
|
899
|
+
totalAmount: serializeArcaAmount(input.totalAmount, "totalAmount"),
|
|
900
|
+
nonTaxableAmount: serializeArcaAmount(
|
|
901
|
+
input.nonTaxableAmount,
|
|
902
|
+
"nonTaxableAmount"
|
|
903
|
+
),
|
|
904
|
+
netAmount: serializeArcaAmount(input.netAmount, "netAmount"),
|
|
905
|
+
exemptAmount: serializeArcaAmount(input.exemptAmount, "exemptAmount"),
|
|
906
|
+
taxAmount: serializeArcaAmount(input.taxAmount, "taxAmount"),
|
|
907
|
+
vatAmount: serializeArcaAmount(input.vatAmount, "vatAmount")
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
function requiresWsfeVatBaseReconciliation(voucherType) {
|
|
911
|
+
return ![2, 3, 7, 8, 11, 12, 13, 15, 52, 53].includes(voucherType);
|
|
912
|
+
}
|
|
913
|
+
function assertWsfeAmountMatch(actual, expectedAmount, field, expectedDescription, absoluteCentAllowance = 1) {
|
|
914
|
+
if (!isWithinArcaTolerance(actual, expectedAmount, absoluteCentAllowance)) {
|
|
915
|
+
throw new ArcaInputError(
|
|
916
|
+
`${field} does not reconcile within ARCA's documented tolerance.`,
|
|
917
|
+
{
|
|
918
|
+
code: "ARCA_INPUT_AMOUNT_MISMATCH",
|
|
919
|
+
field,
|
|
920
|
+
expected: `within ARCA tolerance of ${expectedDescription}`
|
|
921
|
+
}
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
function normalizeWsfeExchangeRate(input, exchangeRate) {
|
|
926
|
+
if (input.currencyId === "PES") {
|
|
927
|
+
if (exchangeRate !== void 0 && serializeArcaExchangeRate(exchangeRate, "exchangeRate") !== "1") {
|
|
928
|
+
throw new ArcaInputError(
|
|
929
|
+
"exchangeRate must be 1 when currencyId is PES.",
|
|
930
|
+
{
|
|
931
|
+
code: "ARCA_INPUT_INVALID_EXCHANGE_RATE",
|
|
932
|
+
field: "exchangeRate",
|
|
933
|
+
expected: "1 when currencyId is PES"
|
|
934
|
+
}
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
return "1";
|
|
938
|
+
}
|
|
939
|
+
if (exchangeRate === void 0) {
|
|
940
|
+
if (input.sameCurrencyForeignCancellation === "S") {
|
|
941
|
+
return void 0;
|
|
942
|
+
}
|
|
943
|
+
throw new ArcaInputError(
|
|
944
|
+
"exchangeRate is required unless sameCurrencyForeignCancellation is S for a foreign-currency voucher.",
|
|
945
|
+
{
|
|
946
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
947
|
+
field: "exchangeRate",
|
|
948
|
+
expected: "a positive exchange rate unless sameCurrencyForeignCancellation is S"
|
|
949
|
+
}
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
return serializeArcaExchangeRate(exchangeRate, "exchangeRate");
|
|
953
|
+
}
|
|
954
|
+
function normalizeWsfeDateInput(value, fieldName) {
|
|
955
|
+
if (typeof value !== "string") {
|
|
956
|
+
throw new ArcaInputError(
|
|
957
|
+
`Invalid WSFE ${fieldName}: expected a YYYY-MM-DD or YYYYMMDD string`,
|
|
958
|
+
{
|
|
959
|
+
code: "ARCA_INPUT_INVALID_DATE",
|
|
960
|
+
field: fieldName,
|
|
961
|
+
expected: "a YYYY-MM-DD or YYYYMMDD calendar date string"
|
|
962
|
+
}
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
const normalizedValue = value.trim();
|
|
966
|
+
const afipMatch = normalizedValue.match(/^(\d{4})(\d{2})(\d{2})$/);
|
|
967
|
+
if (afipMatch) {
|
|
968
|
+
const [, year, month, day] = afipMatch;
|
|
969
|
+
assertValidCalendarDate(year, month, day, fieldName);
|
|
970
|
+
return normalizedValue;
|
|
971
|
+
}
|
|
972
|
+
const isoMatch = normalizedValue.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
973
|
+
if (isoMatch) {
|
|
974
|
+
const [, year, month, day] = isoMatch;
|
|
975
|
+
assertValidCalendarDate(year, month, day, fieldName);
|
|
976
|
+
return `${year}${month}${day}`;
|
|
977
|
+
}
|
|
978
|
+
throw new ArcaInputError(
|
|
979
|
+
`Invalid WSFE ${fieldName}: expected a YYYY-MM-DD or YYYYMMDD string`,
|
|
980
|
+
{
|
|
981
|
+
code: "ARCA_INPUT_INVALID_DATE",
|
|
982
|
+
field: fieldName,
|
|
983
|
+
expected: "a YYYY-MM-DD or YYYYMMDD calendar date string"
|
|
984
|
+
}
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
function assertValidCalendarDate(yearInput, monthInput, dayInput, fieldName) {
|
|
988
|
+
const year = Number(yearInput);
|
|
989
|
+
const month = Number(monthInput);
|
|
990
|
+
const day = Number(dayInput);
|
|
991
|
+
const candidate = new Date(Date.UTC(year, month - 1, day));
|
|
992
|
+
if (candidate.getUTCFullYear() !== year || candidate.getUTCMonth() !== month - 1 || candidate.getUTCDate() !== day) {
|
|
993
|
+
throw new ArcaInputError(
|
|
994
|
+
`Invalid WSFE ${fieldName}: received a non-existent calendar date`,
|
|
995
|
+
{
|
|
996
|
+
code: "ARCA_INPUT_INVALID_DATE",
|
|
997
|
+
field: fieldName,
|
|
998
|
+
expected: "an existing calendar date"
|
|
999
|
+
}
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
function mapWsfeSalesPoint(raw) {
|
|
1004
|
+
const record = raw;
|
|
1005
|
+
return {
|
|
1006
|
+
number: Number(record.Nro ?? 0),
|
|
1007
|
+
...record.EmisionTipo === void 0 ? {} : { emissionType: String(record.EmisionTipo) },
|
|
1008
|
+
...record.Bloqueado === void 0 ? {} : { blocked: String(record.Bloqueado) },
|
|
1009
|
+
...record.FchBaja === void 0 ? {} : { deletedSince: String(record.FchBaja) }
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
function mapWsfeCatalogEntry(raw) {
|
|
1013
|
+
const record = raw;
|
|
1014
|
+
return {
|
|
1015
|
+
id: Number(record.Id ?? 0),
|
|
1016
|
+
description: String(record.Desc ?? "")
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
function mapWsfeActivityType(raw) {
|
|
1020
|
+
const record = raw;
|
|
1021
|
+
return {
|
|
1022
|
+
id: Number(record.Id ?? 0),
|
|
1023
|
+
description: String(record.Desc ?? ""),
|
|
1024
|
+
order: Number(record.Orden ?? 0)
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function mapWsfeReceiverVatCondition(raw) {
|
|
1028
|
+
const record = raw;
|
|
1029
|
+
return {
|
|
1030
|
+
id: Number(record.Id ?? 0),
|
|
1031
|
+
description: String(record.Desc ?? ""),
|
|
1032
|
+
voucherClass: String(record.Cmp_Clase ?? "")
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
function mapWsfeCurrencyType(raw) {
|
|
1036
|
+
const record = raw;
|
|
1037
|
+
return {
|
|
1038
|
+
id: String(record.Id ?? ""),
|
|
1039
|
+
description: String(record.Desc ?? ""),
|
|
1040
|
+
validFrom: String(record.FchDesde ?? ""),
|
|
1041
|
+
validTo: String(record.FchHasta ?? "")
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
function mapWsfeServerStatus(raw) {
|
|
1045
|
+
return {
|
|
1046
|
+
appServer: String(raw.AppServer ?? ""),
|
|
1047
|
+
dbServer: String(raw.DbServer ?? ""),
|
|
1048
|
+
authServer: String(raw.AuthServer ?? "")
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
function mapWsfeQuotation(raw) {
|
|
1052
|
+
return {
|
|
1053
|
+
currencyId: String(raw.MonId ?? ""),
|
|
1054
|
+
rate: Number(raw.MonCotiz ?? 0),
|
|
1055
|
+
date: String(raw.FchCotiz ?? "")
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
function mapWsfeVoucherInfo(raw) {
|
|
1059
|
+
const voucher = {
|
|
1060
|
+
voucherNumber: Number(raw.CbteDesde ?? raw.CbteHasta ?? 0),
|
|
1061
|
+
raw
|
|
1062
|
+
};
|
|
1063
|
+
assignWsfeValue(voucher, "voucherDate", normalizeWsfeString(raw.CbteFch));
|
|
1064
|
+
assignWsfeValue(voucher, "salesPoint", normalizeWsfeNumber(raw.PtoVta));
|
|
1065
|
+
assignWsfeValue(voucher, "voucherType", normalizeWsfeNumber(raw.CbteTipo));
|
|
1066
|
+
assignWsfeValue(voucher, "concept", normalizeWsfeNumber(raw.Concepto));
|
|
1067
|
+
assignWsfeValue(voucher, "documentType", normalizeWsfeNumber(raw.DocTipo));
|
|
1068
|
+
assignWsfeValue(voucher, "documentNumber", normalizeWsfeString(raw.DocNro));
|
|
1069
|
+
assignWsfeValue(
|
|
1070
|
+
voucher,
|
|
1071
|
+
"receiverVatConditionId",
|
|
1072
|
+
normalizeWsfeNumber(raw.CondicionIVAReceptorId)
|
|
1073
|
+
);
|
|
1074
|
+
assignWsfeValue(voucher, "totalAmount", normalizeWsfeNumber(raw.ImpTotal));
|
|
1075
|
+
assignWsfeValue(
|
|
1076
|
+
voucher,
|
|
1077
|
+
"nonTaxableAmount",
|
|
1078
|
+
normalizeWsfeNumber(raw.ImpTotConc)
|
|
1079
|
+
);
|
|
1080
|
+
assignWsfeValue(voucher, "netAmount", normalizeWsfeNumber(raw.ImpNeto));
|
|
1081
|
+
assignWsfeValue(voucher, "exemptAmount", normalizeWsfeNumber(raw.ImpOpEx));
|
|
1082
|
+
assignWsfeValue(voucher, "taxAmount", normalizeWsfeNumber(raw.ImpTrib));
|
|
1083
|
+
assignWsfeValue(voucher, "vatAmount", normalizeWsfeNumber(raw.ImpIVA));
|
|
1084
|
+
assignWsfeValue(voucher, "currencyId", normalizeWsfeString(raw.MonId));
|
|
1085
|
+
assignWsfeValue(voucher, "exchangeRate", normalizeWsfeNumber(raw.MonCotiz));
|
|
1086
|
+
assignWsfeValue(voucher, "result", normalizeWsfeString(raw.Resultado));
|
|
1087
|
+
assignWsfeValue(
|
|
1088
|
+
voucher,
|
|
1089
|
+
"cae",
|
|
1090
|
+
normalizeWsfeString(raw.CodAutorizacion ?? raw.CAE)
|
|
1091
|
+
);
|
|
1092
|
+
assignWsfeValue(
|
|
1093
|
+
voucher,
|
|
1094
|
+
"caeExpiry",
|
|
1095
|
+
normalizeWsfeString(raw.FchVto ?? raw.CAEFchVto)
|
|
1096
|
+
);
|
|
1097
|
+
return voucher;
|
|
1098
|
+
}
|
|
1099
|
+
function createWsfeAuth(representedTaxId, token, sign) {
|
|
1100
|
+
return {
|
|
1101
|
+
Token: token,
|
|
1102
|
+
Sign: sign,
|
|
1103
|
+
Cuit: Number.parseInt(String(representedTaxId), 10)
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
function unwrapWsfeOperationEnvelope(operation, response) {
|
|
1107
|
+
const operationResponse = response[`${operation}Response`];
|
|
1108
|
+
const result = operationResponse?.[`${operation}Result`] ?? response[`${operation}Result`] ?? response;
|
|
1109
|
+
return result;
|
|
1110
|
+
}
|
|
1111
|
+
function throwForWsfeOperationErrors(operation, result) {
|
|
1112
|
+
const errors = extractWsfeGlobalIssues(result, operation);
|
|
1113
|
+
if (errors.length > 0) {
|
|
1114
|
+
throw createWsfeServiceError(operation, errors);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
function normalizeWsfeDetailResponse(result) {
|
|
1118
|
+
const detailResponse = result.FeDetResp;
|
|
1119
|
+
const rawDetail = detailResponse?.FECAEDetResponse;
|
|
1120
|
+
if (Array.isArray(rawDetail)) {
|
|
1121
|
+
return rawDetail[0] ?? {};
|
|
1122
|
+
}
|
|
1123
|
+
return rawDetail ?? {};
|
|
1124
|
+
}
|
|
1125
|
+
function classifyWsfeAuthorization(result, voucherNumber) {
|
|
1126
|
+
const operation = "FECAESolicitar";
|
|
1127
|
+
const header = toWsfeRecord(result.FeCabResp) ?? {};
|
|
1128
|
+
const detail = normalizeWsfeDetailResponse(result);
|
|
1129
|
+
const headerResult = normalizeWsfeResult(header.Resultado);
|
|
1130
|
+
const detailResult = normalizeWsfeResult(detail.Resultado);
|
|
1131
|
+
const resultCode = detailResult ?? headerResult;
|
|
1132
|
+
const resultLevel = getWsfeResultLevel(headerResult, detailResult);
|
|
1133
|
+
const cae = normalizeWsfeString(detail.CAE);
|
|
1134
|
+
const caeExpiry = normalizeWsfeString(detail.CAEFchVto);
|
|
1135
|
+
const errors = extractWsfeGlobalIssues(result, operation, "header");
|
|
1136
|
+
const observations = extractWsfeObservations(
|
|
1137
|
+
detail,
|
|
1138
|
+
detailResult === "R" ? "business" : "observation"
|
|
1139
|
+
);
|
|
1140
|
+
const hasInfrastructureError = errors.some(
|
|
1141
|
+
(issue) => issue.category === "infrastructure"
|
|
1142
|
+
);
|
|
1143
|
+
const base = {
|
|
1144
|
+
service: "wsfe",
|
|
1145
|
+
operation,
|
|
1146
|
+
results: createWsfeResults(headerResult, detailResult),
|
|
1147
|
+
errors,
|
|
1148
|
+
observations,
|
|
1149
|
+
raw: result
|
|
1150
|
+
};
|
|
1151
|
+
const context = {
|
|
1152
|
+
base,
|
|
1153
|
+
headerResult,
|
|
1154
|
+
detailResult,
|
|
1155
|
+
resultCode,
|
|
1156
|
+
resultLevel,
|
|
1157
|
+
cae,
|
|
1158
|
+
caeExpiry
|
|
1159
|
+
};
|
|
1160
|
+
if (hasContradictoryWsfeResults(context)) {
|
|
1161
|
+
return createWsfeStructuredIndeterminate(context, "contradictory_response");
|
|
1162
|
+
}
|
|
1163
|
+
const authenticationError = classifyArcaAuthenticationIssues(errors, {
|
|
1164
|
+
service: "wsfe",
|
|
1165
|
+
operation
|
|
1166
|
+
});
|
|
1167
|
+
if (authenticationError && detailResult === void 0 && headerResult !== "A" && headerResult !== "O" && !cae) {
|
|
1168
|
+
return {
|
|
1169
|
+
...createWsfeStructuredIndeterminate(context, "authentication_rejected"),
|
|
1170
|
+
authentication: createArcaAuthenticationEvidence(authenticationError)
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
if (hasInfrastructureError) {
|
|
1174
|
+
return createWsfeStructuredIndeterminate(context, "incomplete_response");
|
|
1175
|
+
}
|
|
1176
|
+
if (isAuthorizedWsfeContext(context)) {
|
|
1177
|
+
return {
|
|
1178
|
+
...base,
|
|
1179
|
+
kind: "authorized",
|
|
1180
|
+
result: "A",
|
|
1181
|
+
resultLevel: "detail",
|
|
1182
|
+
cae: context.cae,
|
|
1183
|
+
caeExpiry: context.caeExpiry,
|
|
1184
|
+
voucherNumber
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
if (isRejectedWsfeDetailContext(context)) {
|
|
1188
|
+
return {
|
|
1189
|
+
...base,
|
|
1190
|
+
kind: "rejected",
|
|
1191
|
+
result: "R",
|
|
1192
|
+
resultLevel: "detail"
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
if (isRejectedWsfeHeaderContext(context)) {
|
|
1196
|
+
return {
|
|
1197
|
+
...base,
|
|
1198
|
+
kind: "rejected",
|
|
1199
|
+
result: "R",
|
|
1200
|
+
resultLevel: "header"
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
return createWsfeStructuredIndeterminate(
|
|
1204
|
+
context,
|
|
1205
|
+
hasWsfeCaeContradiction(context) ? "contradictory_response" : "incomplete_response"
|
|
1206
|
+
);
|
|
1207
|
+
}
|
|
1208
|
+
function getWsfeResultLevel(headerResult, detailResult) {
|
|
1209
|
+
if (detailResult) {
|
|
1210
|
+
return "detail";
|
|
1211
|
+
}
|
|
1212
|
+
return headerResult ? "header" : void 0;
|
|
1213
|
+
}
|
|
1214
|
+
function hasContradictoryWsfeResults(context) {
|
|
1215
|
+
return Boolean(
|
|
1216
|
+
context.headerResult && context.detailResult && context.headerResult !== context.detailResult
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
function isAuthorizedWsfeContext(context) {
|
|
1220
|
+
return Boolean(
|
|
1221
|
+
context.detailResult === "A" && context.headerResult !== "R" && context.base.errors.length === 0 && context.cae && context.caeExpiry
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
function isRejectedWsfeDetailContext(context) {
|
|
1225
|
+
return context.detailResult === "R" && context.headerResult !== "A" && !context.cae;
|
|
1226
|
+
}
|
|
1227
|
+
function isRejectedWsfeHeaderContext(context) {
|
|
1228
|
+
return context.headerResult === "R" && context.detailResult === void 0 && !context.cae && context.base.errors.length > 0 && context.base.errors.every((issue) => issue.category === "business");
|
|
1229
|
+
}
|
|
1230
|
+
function hasWsfeCaeContradiction(context) {
|
|
1231
|
+
return (context.resultCode === "A" || context.resultCode === "R") && Boolean(context.cae);
|
|
1232
|
+
}
|
|
1233
|
+
function createWsfeStructuredIndeterminate(context, reason) {
|
|
1234
|
+
const outcome = {
|
|
1235
|
+
...context.base,
|
|
1236
|
+
kind: "indeterminate",
|
|
1237
|
+
reason
|
|
1238
|
+
};
|
|
1239
|
+
assignWsfeValue(outcome, "result", context.resultCode);
|
|
1240
|
+
assignWsfeValue(outcome, "resultLevel", context.resultLevel);
|
|
1241
|
+
assignWsfeValue(outcome, "cae", context.cae);
|
|
1242
|
+
assignWsfeValue(outcome, "caeExpiry", context.caeExpiry);
|
|
1243
|
+
return outcome;
|
|
1244
|
+
}
|
|
1245
|
+
function createWsfeResults(headerResult, detailResult) {
|
|
1246
|
+
const results = {};
|
|
1247
|
+
assignWsfeValue(results, "header", headerResult);
|
|
1248
|
+
assignWsfeValue(results, "detail", detailResult);
|
|
1249
|
+
return results;
|
|
1250
|
+
}
|
|
1251
|
+
function createWsfeIndeterminateOutcome(error) {
|
|
1252
|
+
const authenticationError = classifyArcaAuthenticationError(error, {
|
|
1253
|
+
service: "wsfe",
|
|
1254
|
+
operation: "FECAESolicitar"
|
|
1255
|
+
});
|
|
1256
|
+
return {
|
|
1257
|
+
kind: "indeterminate",
|
|
1258
|
+
service: "wsfe",
|
|
1259
|
+
operation: "FECAESolicitar",
|
|
1260
|
+
results: {},
|
|
1261
|
+
reason: authenticationError ? "authentication_rejected" : getArcaIndeterminateReason(error),
|
|
1262
|
+
...authenticationError ? {
|
|
1263
|
+
authentication: createArcaAuthenticationEvidence(authenticationError)
|
|
1264
|
+
} : {},
|
|
1265
|
+
errors: [],
|
|
1266
|
+
observations: []
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
function getArcaIndeterminateReason(error) {
|
|
1270
|
+
if (error instanceof ArcaTransportError) {
|
|
1271
|
+
return "transport_error";
|
|
1272
|
+
}
|
|
1273
|
+
if (error instanceof ArcaSoapFaultError) {
|
|
1274
|
+
return "soap_fault";
|
|
1275
|
+
}
|
|
1276
|
+
if (error instanceof ArcaInvalidSoapResponseError) {
|
|
1277
|
+
return "invalid_response";
|
|
1278
|
+
}
|
|
1279
|
+
return "unexpected_error";
|
|
1280
|
+
}
|
|
1281
|
+
function createWsfeOutcomeError(outcome) {
|
|
1282
|
+
if (outcome.kind === "indeterminate" && outcome.authentication) {
|
|
1283
|
+
return createArcaAuthenticationErrorFromEvidence(outcome.authentication, {
|
|
1284
|
+
service: "wsfe",
|
|
1285
|
+
operation: outcome.operation
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
const issues = [...outcome.errors, ...outcome.observations];
|
|
1289
|
+
const firstIssue = issues[0];
|
|
1290
|
+
const message = firstIssue ? formatWsfeIssue(firstIssue) : outcome.kind === "rejected" ? "WSFE rejected the voucher authorization" : outcome.result === "A" ? "WSFE did not return CAE authorization data" : "WSFE did not return conclusive voucher authorization data";
|
|
1291
|
+
return new ArcaServiceError(message, {
|
|
1292
|
+
service: "wsfe",
|
|
1293
|
+
operation: outcome.operation,
|
|
1294
|
+
...firstIssue?.code === void 0 ? {} : { serviceCode: firstIssue.code },
|
|
1295
|
+
...outcome.result === void 0 ? {} : { result: outcome.result },
|
|
1296
|
+
...outcome.resultLevel === void 0 ? {} : { resultLevel: outcome.resultLevel },
|
|
1297
|
+
results: outcome.results,
|
|
1298
|
+
...outcome.kind === "indeterminate" && outcome.cae ? { cae: outcome.cae } : {},
|
|
1299
|
+
issues
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
function createWsfeServiceError(operation, issues) {
|
|
1303
|
+
const authenticationError = classifyArcaAuthenticationIssues(issues, {
|
|
1304
|
+
service: "wsfe",
|
|
1305
|
+
operation
|
|
1306
|
+
});
|
|
1307
|
+
if (authenticationError) {
|
|
1308
|
+
return authenticationError;
|
|
1309
|
+
}
|
|
1310
|
+
const firstIssue = issues[0];
|
|
1311
|
+
return new ArcaServiceError(
|
|
1312
|
+
firstIssue ? formatWsfeIssue(firstIssue) : "WSFE returned a service error",
|
|
1313
|
+
{
|
|
1314
|
+
service: "wsfe",
|
|
1315
|
+
operation,
|
|
1316
|
+
...firstIssue?.code === void 0 ? {} : { serviceCode: firstIssue.code },
|
|
1317
|
+
issues
|
|
1318
|
+
}
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
function extractWsfeGlobalIssues(result, operation, resultLevel) {
|
|
1322
|
+
const errorsContainer = toWsfeRecord(result.Errors);
|
|
1323
|
+
return normalizeWsfeIssueEntries(errorsContainer?.Err).map((entry) => ({
|
|
1324
|
+
service: "wsfe",
|
|
1325
|
+
operation,
|
|
1326
|
+
source: "error",
|
|
1327
|
+
category: operation === "FECAESolicitar" && WSFE_AUTHORIZATION_INFRASTRUCTURE_CODES.has(entry.code ?? "") ? "infrastructure" : operation === "FECAESolicitar" ? "business" : "unknown",
|
|
1328
|
+
...entry.code === void 0 ? {} : { code: entry.code },
|
|
1329
|
+
message: entry.message,
|
|
1330
|
+
...resultLevel === void 0 ? {} : { resultLevel }
|
|
1331
|
+
}));
|
|
1332
|
+
}
|
|
1333
|
+
function extractWsfeObservations(detail, category) {
|
|
1334
|
+
const observationsContainer = toWsfeRecord(detail.Observaciones);
|
|
1335
|
+
return normalizeWsfeIssueEntries(observationsContainer?.Obs).map((entry) => ({
|
|
1336
|
+
service: "wsfe",
|
|
1337
|
+
operation: "FECAESolicitar",
|
|
1338
|
+
source: "observation",
|
|
1339
|
+
category,
|
|
1340
|
+
...entry.code === void 0 ? {} : { code: entry.code },
|
|
1341
|
+
message: entry.message,
|
|
1342
|
+
resultLevel: "detail"
|
|
1343
|
+
}));
|
|
1344
|
+
}
|
|
1345
|
+
function normalizeWsfeIssueEntries(rawErrors) {
|
|
1346
|
+
const entries = Array.isArray(rawErrors) ? rawErrors : rawErrors ? [rawErrors] : [];
|
|
1347
|
+
return entries.map((entry) => entry).map((entry) => {
|
|
1348
|
+
const code = entry.Code ?? entry.code;
|
|
1349
|
+
const message = entry.Msg ?? entry.msg ?? "Unknown WSFE error";
|
|
1350
|
+
return {
|
|
1351
|
+
...code === void 0 ? {} : { code: String(code) },
|
|
1352
|
+
message: String(message)
|
|
1353
|
+
};
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
function formatWsfeIssue(issue) {
|
|
1357
|
+
return issue.code ? `(${issue.code}) ${issue.message}` : issue.message;
|
|
1358
|
+
}
|
|
1359
|
+
function normalizeWsfeResult(value) {
|
|
1360
|
+
if (typeof value !== "string") {
|
|
1361
|
+
return void 0;
|
|
1362
|
+
}
|
|
1363
|
+
const normalized = value.trim().toUpperCase();
|
|
1364
|
+
return normalized || void 0;
|
|
1365
|
+
}
|
|
1366
|
+
function normalizeWsfeString(value) {
|
|
1367
|
+
if (value === void 0 || value === null) {
|
|
1368
|
+
return void 0;
|
|
1369
|
+
}
|
|
1370
|
+
const normalized = String(value).trim();
|
|
1371
|
+
return normalized || void 0;
|
|
1372
|
+
}
|
|
1373
|
+
function normalizeWsfeNumber(value) {
|
|
1374
|
+
if (value === void 0 || value === null || value === "") {
|
|
1375
|
+
return void 0;
|
|
1376
|
+
}
|
|
1377
|
+
const normalized = Number(value);
|
|
1378
|
+
return Number.isFinite(normalized) ? normalized : void 0;
|
|
1379
|
+
}
|
|
1380
|
+
function assignWsfeValue(target, key, value) {
|
|
1381
|
+
if (value !== void 0) {
|
|
1382
|
+
target[key] = value;
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
function toWsfeRecord(value) {
|
|
1386
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1387
|
+
}
|
|
1388
|
+
var WSFE_AUTHORIZATION_INFRASTRUCTURE_CODES = /* @__PURE__ */ new Set([
|
|
1389
|
+
"500",
|
|
1390
|
+
"501",
|
|
1391
|
+
"502",
|
|
1392
|
+
"600",
|
|
1393
|
+
"601"
|
|
1394
|
+
]);
|
|
1395
|
+
function getWsfeResultEntries(result, key) {
|
|
1396
|
+
const rawEntries = result.ResultGet?.[key];
|
|
1397
|
+
if (!rawEntries) {
|
|
1398
|
+
return [];
|
|
1399
|
+
}
|
|
1400
|
+
return (Array.isArray(rawEntries) ? rawEntries : [rawEntries]).map(
|
|
1401
|
+
(entry) => entry
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// src/services/wsfe-builders.ts
|
|
1406
|
+
var VAT_RATE_IDS = {
|
|
1407
|
+
0: ARCA_VAT_RATES.IVA_0,
|
|
1408
|
+
2.5: ARCA_VAT_RATES.IVA_2_5,
|
|
1409
|
+
5: ARCA_VAT_RATES.IVA_5,
|
|
1410
|
+
10.5: ARCA_VAT_RATES.IVA_10_5,
|
|
1411
|
+
21: ARCA_VAT_RATES.IVA_21,
|
|
1412
|
+
27: ARCA_VAT_RATES.IVA_27
|
|
1413
|
+
};
|
|
1414
|
+
function buildFacturaB(input) {
|
|
1415
|
+
const taxableMinorUnits = assertArcaMinorUnits(
|
|
1416
|
+
input.taxableAmount,
|
|
1417
|
+
"taxableAmount"
|
|
1418
|
+
);
|
|
1419
|
+
if (taxableMinorUnits === 0n) {
|
|
1420
|
+
throw new ArcaInputError(
|
|
1421
|
+
"taxableAmount must be greater than zero for Factura B.",
|
|
1422
|
+
{
|
|
1423
|
+
code: "ARCA_INPUT_INVALID_AMOUNT",
|
|
1424
|
+
field: "taxableAmount",
|
|
1425
|
+
expected: "a positive safe integer in currency minor units"
|
|
1426
|
+
}
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1429
|
+
const vatMinorUnits = calculateVatMinorUnits(
|
|
1430
|
+
taxableMinorUnits,
|
|
1431
|
+
input.vatRate,
|
|
1432
|
+
"vatRate"
|
|
1433
|
+
);
|
|
1434
|
+
const totalMinorUnits = taxableMinorUnits + vatMinorUnits;
|
|
1435
|
+
const vatRateId = VAT_RATE_IDS[input.vatRate];
|
|
1436
|
+
if (vatRateId === void 0) {
|
|
1437
|
+
throw new ArcaInputError("vatRate is not a supported VAT rate.", {
|
|
1438
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
1439
|
+
field: "vatRate",
|
|
1440
|
+
expected: "one of 0, 2.5, 5, 10.5, 21, or 27"
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
if (input.vatRate !== 0 && vatMinorUnits === 0n) {
|
|
1444
|
+
throw new ArcaInputError(
|
|
1445
|
+
"taxableAmount is too small to produce VAT at the selected positive vatRate.",
|
|
1446
|
+
{
|
|
1447
|
+
code: "ARCA_INPUT_INVALID_AMOUNT",
|
|
1448
|
+
field: "taxableAmount",
|
|
1449
|
+
expected: "an amount that rounds to at least one currency minor unit of VAT for a positive vatRate"
|
|
1450
|
+
}
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
const vatRates = [
|
|
1454
|
+
Object.freeze({
|
|
1455
|
+
id: vatRateId,
|
|
1456
|
+
baseAmount: arcaMinorUnitsToNumber(taxableMinorUnits, "taxableAmount"),
|
|
1457
|
+
amount: arcaMinorUnitsToNumber(vatMinorUnits, "vatAmount")
|
|
1458
|
+
})
|
|
1459
|
+
];
|
|
1460
|
+
Object.freeze(vatRates);
|
|
1461
|
+
return Object.freeze({
|
|
1462
|
+
...buildCommonExactInput(input),
|
|
1463
|
+
voucherType: ARCA_VOUCHER_TYPES.FACTURA_B,
|
|
1464
|
+
totalAmount: arcaMinorUnitsToNumber(totalMinorUnits, "totalAmount"),
|
|
1465
|
+
nonTaxableAmount: 0,
|
|
1466
|
+
netAmount: arcaMinorUnitsToNumber(taxableMinorUnits, "taxableAmount"),
|
|
1467
|
+
exemptAmount: 0,
|
|
1468
|
+
taxAmount: 0,
|
|
1469
|
+
vatAmount: arcaMinorUnitsToNumber(vatMinorUnits, "vatAmount"),
|
|
1470
|
+
vatRates
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
function buildFacturaC(input) {
|
|
1474
|
+
const amountMinorUnits = assertArcaMinorUnits(input.amount, "amount");
|
|
1475
|
+
const amount = arcaMinorUnitsToNumber(amountMinorUnits, "amount");
|
|
1476
|
+
return Object.freeze({
|
|
1477
|
+
...buildCommonExactInput(input),
|
|
1478
|
+
voucherType: ARCA_VOUCHER_TYPES.FACTURA_C,
|
|
1479
|
+
totalAmount: amount,
|
|
1480
|
+
nonTaxableAmount: 0,
|
|
1481
|
+
// ARCA defines ImpNeto as the subtotal for class C vouchers.
|
|
1482
|
+
netAmount: amount,
|
|
1483
|
+
exemptAmount: 0,
|
|
1484
|
+
taxAmount: 0,
|
|
1485
|
+
vatAmount: 0
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
function buildCommonExactInput(input) {
|
|
1489
|
+
return {
|
|
1490
|
+
salesPoint: input.salesPoint,
|
|
1491
|
+
concept: input.concept,
|
|
1492
|
+
documentType: input.documentType,
|
|
1493
|
+
documentNumber: input.documentNumber,
|
|
1494
|
+
receiverVatConditionId: input.receiverVatConditionId,
|
|
1495
|
+
voucherDate: input.voucherDate,
|
|
1496
|
+
...normalizeBuilderCurrency(input),
|
|
1497
|
+
...input.serviceStartDate === void 0 ? {} : { serviceStartDate: input.serviceStartDate },
|
|
1498
|
+
...input.serviceEndDate === void 0 ? {} : { serviceEndDate: input.serviceEndDate },
|
|
1499
|
+
...input.paymentDueDate === void 0 ? {} : { paymentDueDate: input.paymentDueDate }
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
function normalizeBuilderCurrency(input) {
|
|
1503
|
+
const unsafeInput = input;
|
|
1504
|
+
const currency = unsafeInput.currency ?? "ARS";
|
|
1505
|
+
if (unsafeInput.sameCurrencyForeignCancellation !== void 0 && typeof unsafeInput.sameCurrencyForeignCancellation !== "boolean") {
|
|
1506
|
+
throw new ArcaInputError(
|
|
1507
|
+
"sameCurrencyForeignCancellation must be a boolean when provided.",
|
|
1508
|
+
{
|
|
1509
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
1510
|
+
field: "sameCurrencyForeignCancellation",
|
|
1511
|
+
expected: "true, false, or omitted"
|
|
1512
|
+
}
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
if (currency === "ARS") {
|
|
1516
|
+
if (unsafeInput.exchangeRate !== void 0) {
|
|
1517
|
+
throw new ArcaInputError(
|
|
1518
|
+
"exchangeRate must be omitted when currency is ARS.",
|
|
1519
|
+
{
|
|
1520
|
+
code: "ARCA_INPUT_INVALID_EXCHANGE_RATE",
|
|
1521
|
+
field: "exchangeRate",
|
|
1522
|
+
expected: "omitted when currency is ARS"
|
|
1523
|
+
}
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
if (unsafeInput.sameCurrencyForeignCancellation !== void 0) {
|
|
1527
|
+
throw new ArcaInputError(
|
|
1528
|
+
"sameCurrencyForeignCancellation applies only when currency is USD.",
|
|
1529
|
+
{
|
|
1530
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
1531
|
+
field: "sameCurrencyForeignCancellation",
|
|
1532
|
+
expected: "omitted when currency is ARS"
|
|
1533
|
+
}
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
return {
|
|
1537
|
+
currencyId: ARCA_CURRENCY_IDS.ARS,
|
|
1538
|
+
exchangeRate: "1"
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
if (currency !== "USD") {
|
|
1542
|
+
throw new ArcaInputError("currency is not supported by this builder.", {
|
|
1543
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
1544
|
+
field: "currency",
|
|
1545
|
+
expected: "ARS or USD"
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
if (unsafeInput.sameCurrencyForeignCancellation === true) {
|
|
1549
|
+
if (unsafeInput.exchangeRate !== void 0) {
|
|
1550
|
+
throw new ArcaInputError(
|
|
1551
|
+
"exchangeRate must be omitted for same-currency foreign cancellation.",
|
|
1552
|
+
{
|
|
1553
|
+
code: "ARCA_INPUT_INVALID_EXCHANGE_RATE",
|
|
1554
|
+
field: "exchangeRate",
|
|
1555
|
+
expected: "omitted when sameCurrencyForeignCancellation is true"
|
|
1556
|
+
}
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
return {
|
|
1560
|
+
currencyId: ARCA_CURRENCY_IDS.USD,
|
|
1561
|
+
sameCurrencyForeignCancellation: "S"
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
if (typeof unsafeInput.exchangeRate !== "string") {
|
|
1565
|
+
throw new ArcaInputError("exchangeRate is required for USD invoices.", {
|
|
1566
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
1567
|
+
field: "exchangeRate",
|
|
1568
|
+
expected: "a decimal string unless sameCurrencyForeignCancellation is true"
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
return {
|
|
1572
|
+
currencyId: ARCA_CURRENCY_IDS.USD,
|
|
1573
|
+
exchangeRate: serializeArcaExchangeRate(
|
|
1574
|
+
unsafeInput.exchangeRate,
|
|
1575
|
+
"exchangeRate"
|
|
1576
|
+
),
|
|
1577
|
+
...unsafeInput.sameCurrencyForeignCancellation === false ? { sameCurrencyForeignCancellation: "N" } : {}
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
export {
|
|
1582
|
+
createWsfeService,
|
|
1583
|
+
buildFacturaB,
|
|
1584
|
+
buildFacturaC
|
|
1585
|
+
};
|
|
1586
|
+
//# sourceMappingURL=chunk-C55KOV5N.mjs.map
|