facturas 0.7.0 → 0.8.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 +16 -4
- package/dist/chunk-76WU5BVI.mjs +109 -0
- package/dist/chunk-76WU5BVI.mjs.map +1 -0
- package/dist/{chunk-MBWOFO67.mjs → chunk-HUT3PFKF.mjs} +14 -2
- package/dist/{chunk-MBWOFO67.mjs.map → chunk-HUT3PFKF.mjs.map} +1 -1
- package/dist/{chunk-EDY3PNKJ.mjs → chunk-NUV5RZPZ.mjs} +2 -2
- package/dist/{chunk-IOKZX6CA.mjs → chunk-PPYGVNFA.mjs} +2 -2
- package/dist/{chunk-C55KOV5N.mjs → chunk-TOVSOJ3G.mjs} +239 -46
- package/dist/chunk-TOVSOJ3G.mjs.map +1 -0
- package/dist/{chunk-PKE4Z4GE.mjs → chunk-ZX4OOCML.mjs} +6 -5
- package/dist/chunk-ZX4OOCML.mjs.map +1 -0
- package/dist/constants.d.ts +49 -1
- package/dist/constants.mjs +9 -1
- package/dist/{fiscal-evidence-BQuBbJ3h.d.ts → errors-B0uouRzR.d.ts} +82 -74
- package/dist/errors.d.ts +1 -1
- package/dist/errors.mjs +5 -3
- package/dist/index.d.ts +199 -5
- package/dist/index.mjs +712 -10
- package/dist/index.mjs.map +1 -1
- package/dist/padron.mjs +2 -2
- package/dist/wsfe-CG_erq7V.d.ts +314 -0
- package/dist/wsfe.d.ts +4 -309
- package/dist/wsfe.mjs +4 -4
- package/dist/wsmtxca.d.ts +1 -1
- package/dist/wsmtxca.mjs +3 -3
- package/package.json +1 -1
- package/dist/chunk-C55KOV5N.mjs.map +0 -1
- package/dist/chunk-PKE4Z4GE.mjs.map +0 -1
- package/dist/chunk-VVY2LZIZ.mjs +0 -61
- package/dist/chunk-VVY2LZIZ.mjs.map +0 -1
- /package/dist/{chunk-EDY3PNKJ.mjs.map → chunk-NUV5RZPZ.mjs.map} +0 -0
- /package/dist/{chunk-IOKZX6CA.mjs.map → chunk-PPYGVNFA.mjs.map} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createPadronService
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NUV5RZPZ.mjs";
|
|
4
4
|
import {
|
|
5
5
|
buildFacturaB,
|
|
6
6
|
buildFacturaC,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
calculateWsfeAmounts,
|
|
8
|
+
createWsfeService,
|
|
9
|
+
normalizeArcaAmountToMinorUnits,
|
|
10
|
+
normalizeWsfeDateInput,
|
|
11
|
+
normalizeWsfeVoucherInput,
|
|
12
|
+
serializeArcaExchangeRate
|
|
13
|
+
} from "./chunk-TOVSOJ3G.mjs";
|
|
14
|
+
import {
|
|
15
|
+
ARCA_CURRENCY_IDS,
|
|
16
|
+
ARCA_DOCUMENT_TYPES,
|
|
17
|
+
ARCA_FINAL_CONSUMER_IDENTIFICATION_THRESHOLD_MINOR_UNITS,
|
|
18
|
+
ARCA_INVOICE_CLASS_BY_ISSUER,
|
|
19
|
+
ARCA_ISSUER_CONDITION_IDS,
|
|
20
|
+
ARCA_RECEIVER_CONDITION_IDS,
|
|
21
|
+
ARCA_VOUCHER_TYPES
|
|
22
|
+
} from "./chunk-76WU5BVI.mjs";
|
|
10
23
|
import {
|
|
11
24
|
createWsmtxcaService
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
25
|
+
} from "./chunk-ZX4OOCML.mjs";
|
|
26
|
+
import "./chunk-PPYGVNFA.mjs";
|
|
14
27
|
import {
|
|
15
28
|
ArcaAuthenticationError,
|
|
16
29
|
ArcaConfigurationError,
|
|
@@ -22,8 +35,9 @@ import {
|
|
|
22
35
|
ArcaTransportError,
|
|
23
36
|
createResponseBodyDiagnostic,
|
|
24
37
|
createSafeErrorDiagnostic,
|
|
25
|
-
isArcaAuthenticationError
|
|
26
|
-
|
|
38
|
+
isArcaAuthenticationError,
|
|
39
|
+
toArcaSafeErrorMetadata
|
|
40
|
+
} from "./chunk-HUT3PFKF.mjs";
|
|
27
41
|
|
|
28
42
|
// src/config.ts
|
|
29
43
|
var ARCA_ENVIRONMENTS = ["production", "test"];
|
|
@@ -287,6 +301,686 @@ function defaultArcaLog(level, message, ...args) {
|
|
|
287
301
|
method(message, ...args);
|
|
288
302
|
}
|
|
289
303
|
|
|
304
|
+
// src/services/wsfe-derive.ts
|
|
305
|
+
var INVOICE_TYPES = {
|
|
306
|
+
A: ARCA_VOUCHER_TYPES.FACTURA_A,
|
|
307
|
+
B: ARCA_VOUCHER_TYPES.FACTURA_B,
|
|
308
|
+
C: ARCA_VOUCHER_TYPES.FACTURA_C
|
|
309
|
+
};
|
|
310
|
+
function deriveWsfeInvoice(input, now = /* @__PURE__ */ new Date()) {
|
|
311
|
+
assertIssueObject(input, "input");
|
|
312
|
+
assertIssueKeys(
|
|
313
|
+
input,
|
|
314
|
+
[
|
|
315
|
+
"issuer",
|
|
316
|
+
"items",
|
|
317
|
+
"salesPoint",
|
|
318
|
+
"to",
|
|
319
|
+
"total",
|
|
320
|
+
"date",
|
|
321
|
+
"currency",
|
|
322
|
+
"exchangeRate",
|
|
323
|
+
"service"
|
|
324
|
+
],
|
|
325
|
+
"input"
|
|
326
|
+
);
|
|
327
|
+
if (typeof input.issuer !== "string" || !Object.hasOwn(ARCA_ISSUER_CONDITION_IDS, input.issuer)) {
|
|
328
|
+
invalid(
|
|
329
|
+
"issuer",
|
|
330
|
+
"responsable_inscripto, monotributo, exento, or no_alcanzado"
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
if (!Number.isSafeInteger(input.salesPoint) || input.salesPoint < 1 || input.salesPoint > 99999) {
|
|
334
|
+
invalid("salesPoint", "an integer from 1 through 99999");
|
|
335
|
+
}
|
|
336
|
+
const receiver = deriveReceiver(input.to);
|
|
337
|
+
const voucherClass = ARCA_INVOICE_CLASS_BY_ISSUER[input.issuer][input.to.condition];
|
|
338
|
+
const { data: amountsData, amounts } = calculateWsfeAmounts(input);
|
|
339
|
+
const currency = deriveCurrency(input);
|
|
340
|
+
if (input.to.condition === "consumidor_final" && receiver.documentType === ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL) {
|
|
341
|
+
const [whole, fraction = ""] = currency.exchangeRate.split(".");
|
|
342
|
+
const rate = BigInt(whole) * 1000000n + BigInt(fraction.padEnd(6, "0"));
|
|
343
|
+
if (BigInt(amounts.sentTotal) * rate >= ARCA_FINAL_CONSUMER_IDENTIFICATION_THRESHOLD_MINOR_UNITS * 1000000n) {
|
|
344
|
+
throw new ArcaInputError(
|
|
345
|
+
"The final consumer must be identified for this amount.",
|
|
346
|
+
{
|
|
347
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
348
|
+
field: "to",
|
|
349
|
+
expected: "cuit or dni for operations at or above ARS 10,000,000 (RG 5866)"
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const voucherDate = normalizeWsfeDateInput(
|
|
355
|
+
input.date === void 0 ? buenosAiresDate(now) : input.date,
|
|
356
|
+
"date"
|
|
357
|
+
);
|
|
358
|
+
const data = {
|
|
359
|
+
salesPoint: input.salesPoint,
|
|
360
|
+
voucherType: INVOICE_TYPES[voucherClass],
|
|
361
|
+
voucherDate,
|
|
362
|
+
...receiver,
|
|
363
|
+
...currency,
|
|
364
|
+
...amountsData,
|
|
365
|
+
...deriveService(input.service, voucherDate)
|
|
366
|
+
};
|
|
367
|
+
try {
|
|
368
|
+
normalizeWsfeVoucherInput(data);
|
|
369
|
+
} catch (cause) {
|
|
370
|
+
throw new ArcaError(
|
|
371
|
+
"The derived invoice failed exact WSFE validation. This is an SDK invariant failure.",
|
|
372
|
+
"ARCA_ISSUE_INVARIANT",
|
|
373
|
+
{ cause }
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
return { data, voucherClass, amounts };
|
|
377
|
+
}
|
|
378
|
+
function deriveReceiver(to) {
|
|
379
|
+
assertIssueObject(to, "to");
|
|
380
|
+
assertIssueKeys(to, ["condition", "cuit", "dni"], "to");
|
|
381
|
+
if (typeof to.condition !== "string" || !Object.hasOwn(ARCA_RECEIVER_CONDITION_IDS, to.condition)) {
|
|
382
|
+
invalid("to.condition", "one of the five supported receiver conditions");
|
|
383
|
+
}
|
|
384
|
+
if (to.cuit !== void 0 && to.dni !== void 0) {
|
|
385
|
+
invalid("to", "either cuit or dni, never both");
|
|
386
|
+
}
|
|
387
|
+
if (to.condition !== "consumidor_final" && to.cuit === void 0) {
|
|
388
|
+
throw new ArcaInputError(
|
|
389
|
+
"to.cuit is required for this receiver (WSFE 10063 for class A).",
|
|
390
|
+
{
|
|
391
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
392
|
+
field: "to.cuit",
|
|
393
|
+
expected: "an 11-digit CUIT"
|
|
394
|
+
}
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
const documentType = to.cuit === void 0 ? to.dni === void 0 ? ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL : ARCA_DOCUMENT_TYPES.DNI : ARCA_DOCUMENT_TYPES.CUIT;
|
|
398
|
+
const documentNumber = to.cuit === void 0 ? to.dni === void 0 ? 0 : issueDocumentNumber(to.dni, "to.dni", 1, 11) : issueDocumentNumber(to.cuit, "to.cuit", 11, 11);
|
|
399
|
+
return {
|
|
400
|
+
documentType,
|
|
401
|
+
documentNumber,
|
|
402
|
+
receiverVatConditionId: ARCA_RECEIVER_CONDITION_IDS[to.condition]
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
function issueDocumentNumber(value, field, min, max) {
|
|
406
|
+
if (typeof value !== "number" && typeof value !== "string") {
|
|
407
|
+
invalid(field, `a positive document number with ${min} to ${max} digits`);
|
|
408
|
+
}
|
|
409
|
+
const text = String(value);
|
|
410
|
+
if (!/^\d+$/.test(text) || text.length < min || text.length > max || !Number.isSafeInteger(Number(text)) || Number(text) <= 0) {
|
|
411
|
+
invalid(field, `a positive document number with ${min} to ${max} digits`);
|
|
412
|
+
}
|
|
413
|
+
return Number(text);
|
|
414
|
+
}
|
|
415
|
+
function deriveCurrency(input) {
|
|
416
|
+
const currency = input.currency === void 0 ? "ARS" : input.currency;
|
|
417
|
+
if (currency !== "ARS" && currency !== "USD") {
|
|
418
|
+
invalid("currency", "ARS or USD");
|
|
419
|
+
}
|
|
420
|
+
if (input.exchangeRate !== void 0 && typeof input.exchangeRate !== "string") {
|
|
421
|
+
invalid("exchangeRate", "a decimal string");
|
|
422
|
+
}
|
|
423
|
+
if (currency === "USD" && input.exchangeRate === void 0) {
|
|
424
|
+
throw new ArcaInputError("exchangeRate is required for USD.", {
|
|
425
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
426
|
+
field: "exchangeRate",
|
|
427
|
+
expected: "a positive decimal string"
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
const exchangeRate = serializeArcaExchangeRate(
|
|
431
|
+
input.exchangeRate ?? "1",
|
|
432
|
+
"exchangeRate"
|
|
433
|
+
);
|
|
434
|
+
if (currency === "ARS" && exchangeRate !== "1") {
|
|
435
|
+
throw new ArcaInputError("exchangeRate must be 1 for ARS.", {
|
|
436
|
+
code: "ARCA_INPUT_INVALID_EXCHANGE_RATE",
|
|
437
|
+
field: "exchangeRate",
|
|
438
|
+
expected: "1 for ARS"
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
return { currencyId: ARCA_CURRENCY_IDS[currency], exchangeRate };
|
|
442
|
+
}
|
|
443
|
+
function deriveService(service, date) {
|
|
444
|
+
if (service === void 0) {
|
|
445
|
+
return { concept: 1 };
|
|
446
|
+
}
|
|
447
|
+
assertIssueObject(service, "service");
|
|
448
|
+
assertIssueKeys(service, ["from", "to", "dueDate"], "service");
|
|
449
|
+
for (const field of ["from", "to", "dueDate"]) {
|
|
450
|
+
if (service[field] === void 0) {
|
|
451
|
+
throw new ArcaInputError(`service.${field} is required.`, {
|
|
452
|
+
code: "ARCA_INPUT_MISSING_FIELD",
|
|
453
|
+
field: `service.${field}`,
|
|
454
|
+
expected: "a calendar date"
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const serviceStartDate = normalizeWsfeDateInput(
|
|
459
|
+
service.from,
|
|
460
|
+
"service.from"
|
|
461
|
+
);
|
|
462
|
+
const serviceEndDate = normalizeWsfeDateInput(
|
|
463
|
+
service.to,
|
|
464
|
+
"service.to"
|
|
465
|
+
);
|
|
466
|
+
const paymentDueDate = normalizeWsfeDateInput(
|
|
467
|
+
service.dueDate,
|
|
468
|
+
"service.dueDate"
|
|
469
|
+
);
|
|
470
|
+
if (serviceEndDate < serviceStartDate) {
|
|
471
|
+
invalid("service.to", "a date on or after service.from");
|
|
472
|
+
}
|
|
473
|
+
if (paymentDueDate < date) {
|
|
474
|
+
invalid("service.dueDate", "a date on or after date");
|
|
475
|
+
}
|
|
476
|
+
return { concept: 2, serviceStartDate, serviceEndDate, paymentDueDate };
|
|
477
|
+
}
|
|
478
|
+
function buenosAiresDate(now) {
|
|
479
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
480
|
+
timeZone: "America/Argentina/Buenos_Aires",
|
|
481
|
+
year: "numeric",
|
|
482
|
+
month: "2-digit",
|
|
483
|
+
day: "2-digit"
|
|
484
|
+
}).formatToParts(now);
|
|
485
|
+
return ["year", "month", "day"].map((part) => parts.find((entry) => entry.type === part)?.value).join("");
|
|
486
|
+
}
|
|
487
|
+
function assertIssueObject(value, field) {
|
|
488
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
489
|
+
invalid(field, "an object");
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function assertIssueKeys(value, keys, prefix) {
|
|
493
|
+
for (const key of Object.keys(value)) {
|
|
494
|
+
if (!keys.includes(key)) {
|
|
495
|
+
const field = prefix === "input" ? key : `${prefix}.${key}`;
|
|
496
|
+
throw new ArcaInputError(
|
|
497
|
+
`${field} is not supported by vouchers.issue().`,
|
|
498
|
+
{
|
|
499
|
+
code: "ARCA_INPUT_RESERVED_FIELD",
|
|
500
|
+
field,
|
|
501
|
+
expected: "a supported facade field; use the exact API for other fiscal fields"
|
|
502
|
+
}
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
function invalid(field, expected) {
|
|
508
|
+
throw new ArcaInputError(`${field} must be ${expected}.`, {
|
|
509
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
510
|
+
field,
|
|
511
|
+
expected
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// src/services/wsfe-identity.ts
|
|
516
|
+
function matchWsfeVoucherIdentity(sent, number, found) {
|
|
517
|
+
let missing;
|
|
518
|
+
const compare = (field, expected, actual, normalize) => {
|
|
519
|
+
if (actual === void 0 || actual === null || expected === void 0) {
|
|
520
|
+
missing ??= field;
|
|
521
|
+
return void 0;
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
const left = normalize ? normalize(expected) : expected;
|
|
525
|
+
const right = normalize ? normalize(actual) : actual;
|
|
526
|
+
if (left !== right) {
|
|
527
|
+
return {
|
|
528
|
+
matches: false,
|
|
529
|
+
evidence: "conflict",
|
|
530
|
+
reason: `${field} differs from the sent input`
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
} catch {
|
|
534
|
+
missing ??= field;
|
|
535
|
+
}
|
|
536
|
+
return void 0;
|
|
537
|
+
};
|
|
538
|
+
const checks = [
|
|
539
|
+
["voucherType", sent.voucherType, found.voucherType],
|
|
540
|
+
["salesPoint", sent.salesPoint, found.salesPoint],
|
|
541
|
+
["number", number, found.voucherNumber, normalizeVoucherNumber],
|
|
542
|
+
[
|
|
543
|
+
"date",
|
|
544
|
+
sent.voucherDate,
|
|
545
|
+
found.voucherDate,
|
|
546
|
+
(value) => normalizeWsfeDateInput(value, "date")
|
|
547
|
+
],
|
|
548
|
+
["concept", sent.concept, found.concept],
|
|
549
|
+
["documentType", sent.documentType, found.documentType],
|
|
550
|
+
[
|
|
551
|
+
"documentNumber",
|
|
552
|
+
sent.documentNumber,
|
|
553
|
+
found.documentNumber,
|
|
554
|
+
normalizeDocument
|
|
555
|
+
],
|
|
556
|
+
[
|
|
557
|
+
"receiverVatConditionId",
|
|
558
|
+
sent.receiverVatConditionId,
|
|
559
|
+
found.receiverVatConditionId
|
|
560
|
+
],
|
|
561
|
+
["currencyId", sent.currencyId, found.currencyId],
|
|
562
|
+
[
|
|
563
|
+
"exchangeRate",
|
|
564
|
+
sent.exchangeRate ?? (sent.currencyId === "PES" ? 1 : void 0),
|
|
565
|
+
found.exchangeRate,
|
|
566
|
+
(value) => serializeArcaExchangeRate(value, "exchangeRate")
|
|
567
|
+
]
|
|
568
|
+
];
|
|
569
|
+
for (const field of [
|
|
570
|
+
"totalAmount",
|
|
571
|
+
"netAmount",
|
|
572
|
+
"vatAmount",
|
|
573
|
+
"exemptAmount",
|
|
574
|
+
"nonTaxableAmount",
|
|
575
|
+
"taxAmount"
|
|
576
|
+
]) {
|
|
577
|
+
checks.push([
|
|
578
|
+
field,
|
|
579
|
+
sent[field],
|
|
580
|
+
found[field],
|
|
581
|
+
(value) => normalizeArcaAmountToMinorUnits(value, field)
|
|
582
|
+
]);
|
|
583
|
+
}
|
|
584
|
+
if (sent.concept === 2) {
|
|
585
|
+
for (const field of [
|
|
586
|
+
"serviceStartDate",
|
|
587
|
+
"serviceEndDate",
|
|
588
|
+
"paymentDueDate"
|
|
589
|
+
]) {
|
|
590
|
+
checks.push([
|
|
591
|
+
field,
|
|
592
|
+
sent[field],
|
|
593
|
+
found[field],
|
|
594
|
+
(value) => normalizeWsfeDateInput(value, field)
|
|
595
|
+
]);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
for (const check of checks) {
|
|
599
|
+
const result = compare(...check);
|
|
600
|
+
if (result) {
|
|
601
|
+
return result;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
const rates = compareVatRates(sent.vatRates ?? [], found.vatRates);
|
|
605
|
+
if (!rates.matches) {
|
|
606
|
+
if (rates.evidence === "conflict") {
|
|
607
|
+
return rates;
|
|
608
|
+
}
|
|
609
|
+
missing ??= rates.reason;
|
|
610
|
+
}
|
|
611
|
+
missing ??= incompleteAuthorization(sent, found);
|
|
612
|
+
return missing ? {
|
|
613
|
+
matches: false,
|
|
614
|
+
evidence: "incomplete",
|
|
615
|
+
reason: `Cannot verify ${missing}`
|
|
616
|
+
} : { matches: true };
|
|
617
|
+
}
|
|
618
|
+
function incompleteAuthorization(sent, found) {
|
|
619
|
+
let missing;
|
|
620
|
+
if (sent.concept !== 1 && sent.concept !== 2) {
|
|
621
|
+
missing ??= "unsupported concept";
|
|
622
|
+
}
|
|
623
|
+
for (const field of [
|
|
624
|
+
"taxes",
|
|
625
|
+
"associatedVouchers",
|
|
626
|
+
"associatedPeriod",
|
|
627
|
+
"optionalFields",
|
|
628
|
+
"buyers",
|
|
629
|
+
"activities",
|
|
630
|
+
"sameCurrencyForeignCancellation"
|
|
631
|
+
]) {
|
|
632
|
+
const value = sent[field];
|
|
633
|
+
if (value !== void 0 && (!Array.isArray(value) || value.length > 0)) {
|
|
634
|
+
missing ??= `unsupported exact field ${field}`;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
if (!(found.result === "A" || found.result === "O")) {
|
|
638
|
+
missing ??= "authorized result";
|
|
639
|
+
}
|
|
640
|
+
if (!found.cae?.trim()) {
|
|
641
|
+
missing ??= "cae";
|
|
642
|
+
}
|
|
643
|
+
if (!found.caeExpiry?.trim()) {
|
|
644
|
+
missing ??= "caeExpiry";
|
|
645
|
+
}
|
|
646
|
+
return missing;
|
|
647
|
+
}
|
|
648
|
+
function normalizeVoucherNumber(value) {
|
|
649
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 99999999) {
|
|
650
|
+
throw new Error("Invalid voucher number");
|
|
651
|
+
}
|
|
652
|
+
return value;
|
|
653
|
+
}
|
|
654
|
+
function normalizeDocument(value) {
|
|
655
|
+
const text = String(value);
|
|
656
|
+
if (!/^\d+$/.test(text)) {
|
|
657
|
+
throw new Error("Invalid document number");
|
|
658
|
+
}
|
|
659
|
+
return BigInt(text);
|
|
660
|
+
}
|
|
661
|
+
function compareVatRates(expected, actual) {
|
|
662
|
+
if (actual === void 0) {
|
|
663
|
+
return expected.length === 0 ? { matches: true } : { matches: false, evidence: "incomplete", reason: "vatRates" };
|
|
664
|
+
}
|
|
665
|
+
if (actual.length !== expected.length || new Set(actual.map((rate) => rate.id)).size !== actual.length) {
|
|
666
|
+
return {
|
|
667
|
+
matches: false,
|
|
668
|
+
evidence: "conflict",
|
|
669
|
+
reason: "vatRates ids differ from the sent input"
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
for (const rate of expected) {
|
|
673
|
+
const found = actual.find((item) => item.id === rate.id);
|
|
674
|
+
if (!found) {
|
|
675
|
+
return {
|
|
676
|
+
matches: false,
|
|
677
|
+
evidence: "conflict",
|
|
678
|
+
reason: `vatRates id ${rate.id} differs from the sent input`
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
for (const field of ["baseAmount", "amount"]) {
|
|
682
|
+
try {
|
|
683
|
+
if (normalizeArcaAmountToMinorUnits(rate[field], field) !== normalizeArcaAmountToMinorUnits(found[field], field)) {
|
|
684
|
+
return {
|
|
685
|
+
matches: false,
|
|
686
|
+
evidence: "conflict",
|
|
687
|
+
reason: `vatRates[${rate.id}].${field} differs from the sent input`
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
} catch {
|
|
691
|
+
return {
|
|
692
|
+
matches: false,
|
|
693
|
+
evidence: "incomplete",
|
|
694
|
+
reason: `vatRates[${rate.id}].${field}`
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return { matches: true };
|
|
700
|
+
}
|
|
701
|
+
function toVoucherSummary(found) {
|
|
702
|
+
const summary = { number: found.voucherNumber };
|
|
703
|
+
for (const field of [
|
|
704
|
+
"salesPoint",
|
|
705
|
+
"voucherType",
|
|
706
|
+
"concept",
|
|
707
|
+
"documentType",
|
|
708
|
+
"documentNumber",
|
|
709
|
+
"receiverVatConditionId",
|
|
710
|
+
"currencyId",
|
|
711
|
+
"exchangeRate",
|
|
712
|
+
"totalAmount",
|
|
713
|
+
"netAmount",
|
|
714
|
+
"vatAmount",
|
|
715
|
+
"exemptAmount",
|
|
716
|
+
"nonTaxableAmount",
|
|
717
|
+
"taxAmount",
|
|
718
|
+
"serviceStartDate",
|
|
719
|
+
"serviceEndDate",
|
|
720
|
+
"paymentDueDate",
|
|
721
|
+
"result",
|
|
722
|
+
"cae",
|
|
723
|
+
"caeExpiry"
|
|
724
|
+
]) {
|
|
725
|
+
if (found[field] !== void 0) {
|
|
726
|
+
Object.assign(summary, { [field]: found[field] });
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
if (found.voucherDate !== void 0) {
|
|
730
|
+
summary.date = found.voucherDate;
|
|
731
|
+
}
|
|
732
|
+
if (found.vatRates !== void 0) {
|
|
733
|
+
summary.vatRates = found.vatRates.map(({ id, baseAmount, amount }) => ({
|
|
734
|
+
id,
|
|
735
|
+
baseAmount,
|
|
736
|
+
amount
|
|
737
|
+
}));
|
|
738
|
+
}
|
|
739
|
+
return summary;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// src/services/vouchers.ts
|
|
743
|
+
var SERIALIZATION_NOTICE = "Serialize calls per (representedTaxId, salesPoint, voucherType). The SDK does not coordinate writers; uncoordinated calls collide on 10016. Servers and queues must persist attempts and use wsfe.authorizeVoucherOutcome().";
|
|
744
|
+
function createVouchersService(wsfe) {
|
|
745
|
+
return {
|
|
746
|
+
issue: async (input, options) => {
|
|
747
|
+
const result = await issueInvoice(
|
|
748
|
+
wsfe,
|
|
749
|
+
input,
|
|
750
|
+
options === void 0 ? {} : options
|
|
751
|
+
);
|
|
752
|
+
return result;
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
async function issueInvoice(wsfe, input, options) {
|
|
757
|
+
validateOptions(options);
|
|
758
|
+
const { data, voucherClass, amounts } = deriveWsfeInvoice(input);
|
|
759
|
+
const auth = {
|
|
760
|
+
representedTaxId: options.representedTaxId,
|
|
761
|
+
forceRefresh: options.forceRefresh
|
|
762
|
+
};
|
|
763
|
+
const includeRaw = options.include?.raw === true;
|
|
764
|
+
const includeExact = options.include?.exactInput === true;
|
|
765
|
+
const number = await wsfe.getNextVoucherNumber({
|
|
766
|
+
...auth,
|
|
767
|
+
salesPoint: data.salesPoint,
|
|
768
|
+
voucherType: data.voucherType
|
|
769
|
+
});
|
|
770
|
+
if (!Number.isSafeInteger(number) || number < 1 || number > 99999999) {
|
|
771
|
+
throw new ArcaServiceError(
|
|
772
|
+
"WSFE returned an invalid next voucher number.",
|
|
773
|
+
{
|
|
774
|
+
service: "wsfe",
|
|
775
|
+
operation: "FECompUltimoAutorizado"
|
|
776
|
+
}
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
const attempted = {
|
|
780
|
+
salesPoint: data.salesPoint,
|
|
781
|
+
voucherType: data.voucherType,
|
|
782
|
+
number
|
|
783
|
+
};
|
|
784
|
+
const exact = includeExact ? { sent: data } : {};
|
|
785
|
+
const authorization = await wsfe.authorizeVoucherOutcome({
|
|
786
|
+
...auth,
|
|
787
|
+
data,
|
|
788
|
+
voucherNumber: number
|
|
789
|
+
});
|
|
790
|
+
const voucher = (cae, caeExpiry) => ({
|
|
791
|
+
...attempted,
|
|
792
|
+
voucherClass,
|
|
793
|
+
date: data.voucherDate,
|
|
794
|
+
cae,
|
|
795
|
+
caeExpiry,
|
|
796
|
+
amounts
|
|
797
|
+
});
|
|
798
|
+
if (authorization.kind === "authorized" && authorization.caeExpiry && authorization.voucherNumber === number) {
|
|
799
|
+
return {
|
|
800
|
+
kind: "authorized",
|
|
801
|
+
recoveredByMatch: false,
|
|
802
|
+
voucher: voucher(authorization.cae, authorization.caeExpiry),
|
|
803
|
+
authorization: projectEvidence(authorization, includeRaw),
|
|
804
|
+
...exact
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
if (authorization.kind === "rejected") {
|
|
808
|
+
return {
|
|
809
|
+
kind: "rejected",
|
|
810
|
+
attempted,
|
|
811
|
+
issues: [...authorization.errors, ...authorization.observations].map(
|
|
812
|
+
projectIssue
|
|
813
|
+
),
|
|
814
|
+
authorization: projectEvidence(authorization, includeRaw)
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
const uncertain = authorization.kind === "indeterminate" ? authorization : {
|
|
818
|
+
...authorization,
|
|
819
|
+
kind: "indeterminate",
|
|
820
|
+
reason: authorization.voucherNumber === number ? "incomplete_response" : "contradictory_response"
|
|
821
|
+
};
|
|
822
|
+
const attempt = projectEvidence(uncertain, includeRaw);
|
|
823
|
+
return recoverInvoice({
|
|
824
|
+
wsfe,
|
|
825
|
+
auth,
|
|
826
|
+
data,
|
|
827
|
+
attempted,
|
|
828
|
+
attempt,
|
|
829
|
+
includeRaw,
|
|
830
|
+
exact,
|
|
831
|
+
voucher
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
async function recoverInvoice({
|
|
835
|
+
wsfe,
|
|
836
|
+
auth,
|
|
837
|
+
data,
|
|
838
|
+
attempted,
|
|
839
|
+
attempt,
|
|
840
|
+
includeRaw,
|
|
841
|
+
exact,
|
|
842
|
+
voucher
|
|
843
|
+
}) {
|
|
844
|
+
let lookup;
|
|
845
|
+
try {
|
|
846
|
+
lookup = await wsfe.lookupVoucher({ ...auth, ...attempted });
|
|
847
|
+
} catch (error) {
|
|
848
|
+
return {
|
|
849
|
+
kind: "indeterminate",
|
|
850
|
+
attempted,
|
|
851
|
+
attempt,
|
|
852
|
+
lookup: { kind: "failed", error: toArcaSafeErrorMetadata(error) }
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
const raw = includeRaw ? { raw: lookup.raw } : {};
|
|
856
|
+
if (lookup.kind === "not_found") {
|
|
857
|
+
return {
|
|
858
|
+
kind: "indeterminate",
|
|
859
|
+
attempted,
|
|
860
|
+
attempt,
|
|
861
|
+
lookup: { kind: "not_found", ...raw }
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
const matched = matchWsfeVoucherIdentity(
|
|
865
|
+
data,
|
|
866
|
+
attempted.number,
|
|
867
|
+
lookup.voucher
|
|
868
|
+
);
|
|
869
|
+
if (!matched.matches) {
|
|
870
|
+
if (matched.evidence === "conflict") {
|
|
871
|
+
return {
|
|
872
|
+
kind: "conflict",
|
|
873
|
+
attempted,
|
|
874
|
+
attempt,
|
|
875
|
+
found: { ...toVoucherSummary(lookup.voucher), ...raw },
|
|
876
|
+
reason: `${matched.reason}. ${SERIALIZATION_NOTICE}`
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
return {
|
|
880
|
+
kind: "indeterminate",
|
|
881
|
+
attempted,
|
|
882
|
+
attempt,
|
|
883
|
+
lookup: { kind: "incomplete", reason: matched.reason, ...raw }
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
return {
|
|
887
|
+
kind: "authorized",
|
|
888
|
+
recoveredByMatch: true,
|
|
889
|
+
voucher: voucher(
|
|
890
|
+
lookup.voucher.cae,
|
|
891
|
+
lookup.voucher.caeExpiry
|
|
892
|
+
),
|
|
893
|
+
attempt,
|
|
894
|
+
lookup: { ...toVoucherSummary(lookup.voucher), ...raw },
|
|
895
|
+
...exact
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
function projectIssue(issue) {
|
|
899
|
+
return {
|
|
900
|
+
service: issue.service,
|
|
901
|
+
operation: issue.operation,
|
|
902
|
+
source: issue.source,
|
|
903
|
+
category: issue.category,
|
|
904
|
+
message: issue.message,
|
|
905
|
+
...issue.code === void 0 ? {} : { code: issue.code },
|
|
906
|
+
...issue.resultLevel === void 0 ? {} : { resultLevel: issue.resultLevel }
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
function projectEvidence(evidence, includeRaw) {
|
|
910
|
+
const base = {
|
|
911
|
+
kind: evidence.kind,
|
|
912
|
+
service: evidence.service,
|
|
913
|
+
operation: evidence.operation,
|
|
914
|
+
results: {
|
|
915
|
+
...evidence.results.header === void 0 ? {} : { header: evidence.results.header },
|
|
916
|
+
...evidence.results.detail === void 0 ? {} : { detail: evidence.results.detail },
|
|
917
|
+
...evidence.results.operation === void 0 ? {} : { operation: evidence.results.operation }
|
|
918
|
+
},
|
|
919
|
+
errors: evidence.errors.map(projectIssue),
|
|
920
|
+
observations: evidence.observations.map(projectIssue),
|
|
921
|
+
...includeRaw && evidence.raw !== void 0 ? { raw: evidence.raw } : {}
|
|
922
|
+
};
|
|
923
|
+
const projected = { ...base };
|
|
924
|
+
for (const field of [
|
|
925
|
+
"result",
|
|
926
|
+
"resultLevel",
|
|
927
|
+
"cae",
|
|
928
|
+
"caeExpiry",
|
|
929
|
+
"voucherNumber",
|
|
930
|
+
"reason"
|
|
931
|
+
]) {
|
|
932
|
+
if (field in evidence && evidence[field] !== void 0) {
|
|
933
|
+
projected[field] = evidence[field];
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
if (evidence.kind === "indeterminate" && evidence.authentication) {
|
|
937
|
+
const { code, reason, providerCode } = evidence.authentication;
|
|
938
|
+
projected.authentication = {
|
|
939
|
+
code,
|
|
940
|
+
reason,
|
|
941
|
+
...providerCode === void 0 ? {} : { providerCode }
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
return projected;
|
|
945
|
+
}
|
|
946
|
+
function validateOptions(options) {
|
|
947
|
+
assertIssueObject(options, "options");
|
|
948
|
+
assertIssueKeys(
|
|
949
|
+
options,
|
|
950
|
+
["representedTaxId", "forceRefresh", "include"],
|
|
951
|
+
"options"
|
|
952
|
+
);
|
|
953
|
+
if (options.representedTaxId !== void 0) {
|
|
954
|
+
issueDocumentNumber(
|
|
955
|
+
options.representedTaxId,
|
|
956
|
+
"options.representedTaxId",
|
|
957
|
+
11,
|
|
958
|
+
11
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
if (options.forceRefresh !== void 0 && typeof options.forceRefresh !== "boolean") {
|
|
962
|
+
throw new ArcaInputError("options.forceRefresh must be a boolean.", {
|
|
963
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
964
|
+
field: "options.forceRefresh"
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
if (options.include !== void 0) {
|
|
968
|
+
assertIssueObject(options.include, "options.include");
|
|
969
|
+
assertIssueKeys(options.include, ["raw", "exactInput"], "options.include");
|
|
970
|
+
for (const field of ["raw", "exactInput"]) {
|
|
971
|
+
if (options.include[field] !== void 0 && typeof options.include[field] !== "boolean") {
|
|
972
|
+
throw new ArcaInputError(
|
|
973
|
+
`options.include.${field} must be a boolean.`,
|
|
974
|
+
{
|
|
975
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
976
|
+
field: `options.include.${field}`
|
|
977
|
+
}
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
290
984
|
// src/internal/http.ts
|
|
291
985
|
import https from "https";
|
|
292
986
|
var defaultAgent = new https.Agent({
|
|
@@ -1134,9 +1828,11 @@ function createArcaClient(config) {
|
|
|
1134
1828
|
retries: normalizedConfig.retries,
|
|
1135
1829
|
retryDelay: normalizedConfig.retryDelay
|
|
1136
1830
|
});
|
|
1831
|
+
const wsfe = createWsfeService({ config: normalizedConfig, auth, soap });
|
|
1137
1832
|
return {
|
|
1138
1833
|
config: publicConfig,
|
|
1139
|
-
wsfe
|
|
1834
|
+
wsfe,
|
|
1835
|
+
vouchers: createVouchersService(wsfe),
|
|
1140
1836
|
wsmtxca: createWsmtxcaService({ config: normalizedConfig, auth, soap }),
|
|
1141
1837
|
padron: createPadronService({ config: normalizedConfig, auth, soap })
|
|
1142
1838
|
};
|
|
@@ -1144,6 +1840,10 @@ function createArcaClient(config) {
|
|
|
1144
1840
|
export {
|
|
1145
1841
|
ARCA_ENVIRONMENTS,
|
|
1146
1842
|
ARCA_ENV_VARIABLES,
|
|
1843
|
+
ARCA_FINAL_CONSUMER_IDENTIFICATION_THRESHOLD_MINOR_UNITS,
|
|
1844
|
+
ARCA_INVOICE_CLASS_BY_ISSUER,
|
|
1845
|
+
ARCA_ISSUER_CONDITION_IDS,
|
|
1846
|
+
ARCA_RECEIVER_CONDITION_IDS,
|
|
1147
1847
|
ArcaAuthenticationError,
|
|
1148
1848
|
ArcaConfigurationError,
|
|
1149
1849
|
ArcaError,
|
|
@@ -1162,6 +1862,8 @@ export {
|
|
|
1162
1862
|
createWsfeService,
|
|
1163
1863
|
createWsmtxcaService,
|
|
1164
1864
|
isArcaAuthenticationError,
|
|
1165
|
-
|
|
1865
|
+
matchWsfeVoucherIdentity,
|
|
1866
|
+
resolveArcaEnvironment,
|
|
1867
|
+
toArcaSafeErrorMetadata
|
|
1166
1868
|
};
|
|
1167
1869
|
//# sourceMappingURL=index.mjs.map
|