facturas 0.9.0 → 0.10.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/dist/index.mjs CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  normalizeWsfeDateInput,
11
11
  normalizeWsfeVoucherInput,
12
12
  serializeArcaExchangeRate
13
- } from "./chunk-RCM4NAAG.mjs";
13
+ } from "./chunk-QHHLHSBW.mjs";
14
14
  import {
15
15
  ARCA_CURRENCY_IDS,
16
16
  ARCA_DOCUMENT_TYPES,
@@ -22,8 +22,8 @@ import {
22
22
  } from "./chunk-76WU5BVI.mjs";
23
23
  import {
24
24
  createWsmtxcaService
25
- } from "./chunk-XNBD5NVL.mjs";
26
- import "./chunk-4QVFXT35.mjs";
25
+ } from "./chunk-ALH5IPSY.mjs";
26
+ import "./chunk-UDX6HLAU.mjs";
27
27
  import {
28
28
  ArcaAuthenticationError,
29
29
  ArcaConfigurationError,
@@ -379,18 +379,15 @@ function deriveWsfeInvoice(input, now = /* @__PURE__ */ new Date()) {
379
379
  ],
380
380
  "input"
381
381
  );
382
- if (typeof input.issuer !== "string" || !Object.hasOwn(ARCA_ISSUER_CONDITION_IDS, input.issuer)) {
383
- invalid(
384
- "issuer",
385
- "responsable_inscripto, monotributo, exento, or no_alcanzado"
386
- );
387
- }
388
- if (!Number.isSafeInteger(input.salesPoint) || input.salesPoint < 1 || input.salesPoint > 99999) {
389
- invalid("salesPoint", "an integer from 1 through 99999");
390
- }
382
+ assertIssuerCondition(input.issuer);
383
+ assertSalesPoint(input.salesPoint);
391
384
  const receiver = deriveReceiver(input.to);
392
- const voucherClass = ARCA_INVOICE_CLASS_BY_ISSUER[input.issuer][input.to.condition];
393
- const { data: amountsData, amounts } = calculateWsfeAmounts(input);
385
+ const voucherClass = resolveInvoiceClass(input.issuer, input.to.condition);
386
+ const { data: amountsData, amounts } = calculateWsfeAmounts({
387
+ voucherClass,
388
+ items: input.items,
389
+ total: input.total
390
+ });
394
391
  const currency = deriveCurrency(input);
395
392
  if (input.to.condition === "consumidor_final" && receiver.documentType === ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL) {
396
393
  const [whole, fraction = ""] = currency.exchangeRate.split(".");
@@ -430,6 +427,22 @@ function deriveWsfeInvoice(input, now = /* @__PURE__ */ new Date()) {
430
427
  }
431
428
  return { data, voucherClass, amounts };
432
429
  }
430
+ function assertIssuerCondition(issuer) {
431
+ if (typeof issuer !== "string" || !Object.hasOwn(ARCA_ISSUER_CONDITION_IDS, issuer)) {
432
+ invalid(
433
+ "issuer",
434
+ "responsable_inscripto, monotributo, exento, or no_alcanzado"
435
+ );
436
+ }
437
+ }
438
+ function assertSalesPoint(salesPoint) {
439
+ if (!Number.isSafeInteger(salesPoint) || salesPoint < 1 || salesPoint > 99999) {
440
+ invalid("salesPoint", "an integer from 1 through 99999");
441
+ }
442
+ }
443
+ function resolveInvoiceClass(issuer, condition) {
444
+ return ARCA_INVOICE_CLASS_BY_ISSUER[issuer][condition];
445
+ }
433
446
  function deriveReceiver(to) {
434
447
  assertIssueObject(to, "to");
435
448
  assertIssueKeys(to, ["condition", "cuit", "dni"], "to");
@@ -544,11 +557,11 @@ function assertIssueObject(value, field) {
544
557
  invalid(field, "an object");
545
558
  }
546
559
  }
547
- function assertIssueKeys(value, keys, prefix) {
560
+ function assertIssueKeys(value, keys, prefix, method = "issue()") {
548
561
  for (const key of Object.keys(value)) {
549
562
  if (!keys.includes(key)) {
550
563
  const field = prefix === "input" ? key : `${prefix}.${key}`;
551
- throw new ArcaInputError(`${field} is not supported by issue().`, {
564
+ throw new ArcaInputError(`${field} is not supported by ${method}.`, {
552
565
  code: "ARCA_INPUT_RESERVED_FIELD",
553
566
  field,
554
567
  expected: "a supported facade field; use the exact API for other fiscal fields"
@@ -564,7 +577,7 @@ function invalid(field, expected) {
564
577
  });
565
578
  }
566
579
 
567
- // src/services/wsfe-cancel.ts
580
+ // src/services/wsfe-credit-note.ts
568
581
  var NOTES = {
569
582
  1: { voucherType: 3, voucherClass: "A" },
570
583
  6: { voucherType: 8, voucherClass: "B" },
@@ -572,7 +585,7 @@ var NOTES = {
572
585
  };
573
586
  function invalid2(reason) {
574
587
  throw new ArcaInputError(
575
- `Cannot cancel: ${reason}. Use wsfe.issue() for exact control.`,
588
+ `issueCreditNote cannot proceed: ${reason}. Use wsfe.issue() for exact control.`,
576
589
  { code: "ARCA_INPUT_INVALID_VALUE" }
577
590
  );
578
591
  }
@@ -582,7 +595,52 @@ function required(value, field) {
582
595
  }
583
596
  return value;
584
597
  }
585
- function deriveWsfeCancellation(original, date, now = /* @__PURE__ */ new Date()) {
598
+ function deriveWsfeFullCreditNote(original, input, now = /* @__PURE__ */ new Date()) {
599
+ const { note, header } = prepareCreditNote(original, input, now);
600
+ const data = {
601
+ ...header,
602
+ totalAmount: required(original.totalAmount, "totalAmount"),
603
+ netAmount: required(original.netAmount, "netAmount"),
604
+ vatAmount: required(original.vatAmount, "vatAmount"),
605
+ exemptAmount: required(original.exemptAmount, "exemptAmount"),
606
+ nonTaxableAmount: required(original.nonTaxableAmount, "nonTaxableAmount"),
607
+ taxAmount: required(original.taxAmount, "taxAmount"),
608
+ ...original.vatRates === void 0 ? {} : { vatRates: original.vatRates.map((rate) => ({ ...rate })) }
609
+ };
610
+ normalizeWsfeVoucherInput(data);
611
+ const total = Number(
612
+ normalizeArcaAmountToMinorUnits(data.totalAmount, "totalAmount")
613
+ );
614
+ return {
615
+ data,
616
+ voucherClass: note.voucherClass,
617
+ amounts: { computedTotal: total, sentTotal: total, vatAdjustment: 0 }
618
+ };
619
+ }
620
+ function deriveWsfePartialCreditNote(original, input, now = /* @__PURE__ */ new Date()) {
621
+ if (input.items === void 0) {
622
+ invalid2("items is required to credit chosen lines");
623
+ }
624
+ const { note, header } = prepareCreditNote(original, input, now);
625
+ const { data: amountsData, amounts } = calculateWsfeAmounts({
626
+ voucherClass: note.voucherClass,
627
+ items: input.items,
628
+ total: input.total
629
+ });
630
+ const originalTotal = normalizeArcaAmountToMinorUnits(
631
+ required(original.totalAmount, "totalAmount"),
632
+ "totalAmount"
633
+ );
634
+ if (BigInt(amounts.sentTotal) > originalTotal) {
635
+ invalid2(
636
+ "the note total is greater than the original; the SDK does not track earlier notes against an original"
637
+ );
638
+ }
639
+ const data = { ...header, ...amountsData };
640
+ normalizeWsfeVoucherInput(data);
641
+ return { data, voucherClass: note.voucherClass, amounts };
642
+ }
643
+ function prepareCreditNote(original, input, now) {
586
644
  const note = NOTES[original.voucherType ?? 0];
587
645
  if (!note) {
588
646
  invalid2("original must be an invoice of type 1, 6 or 11");
@@ -595,7 +653,7 @@ function deriveWsfeCancellation(original, date, now = /* @__PURE__ */ new Date()
595
653
  invalid2("unsupported currency; only ARS and USD are supported");
596
654
  }
597
655
  const voucherDate = normalizeWsfeDateInput(
598
- date ?? buenosAiresDate(now),
656
+ input.date ?? buenosAiresDate(now),
599
657
  "date"
600
658
  );
601
659
  const originalDate = normalizeWsfeDateInput(
@@ -607,8 +665,8 @@ function deriveWsfeCancellation(original, date, now = /* @__PURE__ */ new Date()
607
665
  "original date is later than the note and outside its month (10210)"
608
666
  );
609
667
  }
610
- const data = {
611
- salesPoint: required(original.salesPoint, "salesPoint"),
668
+ const header = {
669
+ salesPoint: input.salesPoint ?? required(original.salesPoint, "salesPoint"),
612
670
  voucherType: note.voucherType,
613
671
  concept: required(original.concept, "concept"),
614
672
  documentType: required(original.documentType, "documentType"),
@@ -620,13 +678,6 @@ function deriveWsfeCancellation(original, date, now = /* @__PURE__ */ new Date()
620
678
  currencyId: original.currencyId,
621
679
  exchangeRate: required(original.exchangeRate, "exchangeRate"),
622
680
  voucherDate,
623
- totalAmount: required(original.totalAmount, "totalAmount"),
624
- netAmount: required(original.netAmount, "netAmount"),
625
- vatAmount: required(original.vatAmount, "vatAmount"),
626
- exemptAmount: required(original.exemptAmount, "exemptAmount"),
627
- nonTaxableAmount: required(original.nonTaxableAmount, "nonTaxableAmount"),
628
- taxAmount: required(original.taxAmount, "taxAmount"),
629
- ...original.vatRates === void 0 ? {} : { vatRates: original.vatRates.map((rate) => ({ ...rate })) },
630
681
  associatedVouchers: [
631
682
  {
632
683
  type: required(original.voucherType, "voucherType"),
@@ -636,16 +687,8 @@ function deriveWsfeCancellation(original, date, now = /* @__PURE__ */ new Date()
636
687
  }
637
688
  ]
638
689
  };
639
- copyServiceDates(original, data);
640
- normalizeWsfeVoucherInput(data);
641
- const total = Number(
642
- normalizeArcaAmountToMinorUnits(data.totalAmount, "totalAmount")
643
- );
644
- return {
645
- data,
646
- voucherClass: note.voucherClass,
647
- amounts: { computedTotal: total, sentTotal: total, vatAdjustment: 0 }
648
- };
690
+ copyServiceDates(original, header);
691
+ return { note, header };
649
692
  }
650
693
  function rejectExtensions(original) {
651
694
  for (const [field, rawField] of [
@@ -668,15 +711,15 @@ function rejectExtensions(original) {
668
711
  function present(value) {
669
712
  return value !== void 0 && value !== null && value !== "" && (!Array.isArray(value) || value.length > 0);
670
713
  }
671
- function copyServiceDates(original, data) {
672
- if (data.concept !== 2 && data.concept !== 3) {
714
+ function copyServiceDates(original, header) {
715
+ if (header.concept !== 2 && header.concept !== 3) {
673
716
  return;
674
717
  }
675
- data.serviceStartDate = required(
718
+ header.serviceStartDate = required(
676
719
  original.serviceStartDate,
677
720
  "serviceStartDate"
678
721
  );
679
- data.serviceEndDate = required(
722
+ header.serviceEndDate = required(
680
723
  original.serviceEndDate,
681
724
  "serviceEndDate"
682
725
  );
@@ -684,7 +727,134 @@ function copyServiceDates(original, data) {
684
727
  required(original.paymentDueDate, "paymentDueDate"),
685
728
  "original.paymentDueDate"
686
729
  );
687
- data.paymentDueDate = due < data.voucherDate ? data.voucherDate : due;
730
+ header.paymentDueDate = due < header.voucherDate ? header.voucherDate : due;
731
+ }
732
+ var CREDIT_NOTE_KEYS = ["for", "salesPoint", "date", "items", "total", "all"];
733
+ var TARGET_BOUNDS = [
734
+ ["salesPoint", 99999],
735
+ ["voucherType", 999],
736
+ ["number", 99999999]
737
+ ];
738
+ function assertCreditNoteInput(input) {
739
+ assertIssueObject(input, "input");
740
+ if ("associatedPeriod" in input) {
741
+ throw new ArcaInputError(
742
+ "issueCreditNote does not support associatedPeriod; a note against a period is exact-layer work. Use wsfe.issue() for exact control.",
743
+ {
744
+ code: "ARCA_INPUT_RESERVED_FIELD",
745
+ field: "associatedPeriod",
746
+ expected: "an associated invoice through for"
747
+ }
748
+ );
749
+ }
750
+ assertIssueKeys(input, CREDIT_NOTE_KEYS, "input", "issueCreditNote()");
751
+ const target = assertCreditNoteTarget(input.for);
752
+ if (input.salesPoint !== void 0) {
753
+ assertCreditNoteBound(input.salesPoint, 99999, "salesPoint");
754
+ }
755
+ const date = input.date === void 0 ? void 0 : normalizeWsfeDateInput(input.date, "date");
756
+ const common = {
757
+ for: target,
758
+ ...input.salesPoint === void 0 ? {} : { salesPoint: input.salesPoint },
759
+ ...date === void 0 ? {} : { date }
760
+ };
761
+ if (input.items === void 0 === (input.all === void 0)) {
762
+ throw new ArcaInputError(
763
+ "issueCreditNote needs exactly one mode: items for the credited lines, or all: true for the whole original.",
764
+ {
765
+ code: "ARCA_INPUT_INVALID_VALUE",
766
+ field: input.items === void 0 ? "input.items" : "input.all",
767
+ expected: "either items or all: true, never both and never neither"
768
+ }
769
+ );
770
+ }
771
+ if (input.all !== void 0) {
772
+ if (input.all !== true) {
773
+ throw new ArcaInputError(
774
+ "issueCreditNote accepts only all: true; pass items to credit chosen lines.",
775
+ {
776
+ code: "ARCA_INPUT_INVALID_VALUE",
777
+ field: "input.all",
778
+ expected: "the literal true"
779
+ }
780
+ );
781
+ }
782
+ if (input.total !== void 0) {
783
+ throw new ArcaInputError(
784
+ "issueCreditNote takes total only with items; all: true credits the original's own total.",
785
+ {
786
+ code: "ARCA_INPUT_INVALID_VALUE",
787
+ field: "input.total",
788
+ expected: "no total when all is true"
789
+ }
790
+ );
791
+ }
792
+ return { ...common, all: true };
793
+ }
794
+ return {
795
+ ...common,
796
+ items: copyCreditNoteItems(input.items),
797
+ ...input.total === void 0 ? {} : { total: input.total }
798
+ };
799
+ }
800
+ function assertCreditNoteTarget(value) {
801
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
802
+ throw new ArcaInputError(
803
+ "issueCreditNote requires for: the coordinates of the authorized invoice the note corrects.",
804
+ {
805
+ code: "ARCA_INPUT_MISSING_FIELD",
806
+ field: "input.for",
807
+ expected: "{ salesPoint, voucherType, number }"
808
+ }
809
+ );
810
+ }
811
+ assertIssueKeys(
812
+ value,
813
+ ["salesPoint", "voucherType", "number"],
814
+ "input.for",
815
+ "issueCreditNote()"
816
+ );
817
+ for (const [field, max] of TARGET_BOUNDS) {
818
+ assertCreditNoteBound(value[field], max, `for.${field}`);
819
+ }
820
+ if (![1, 6, 11].includes(value.voucherType)) {
821
+ throw new ArcaInputError(
822
+ "issueCreditNote requires an authorized invoice of type 1, 6 or 11 in for.voucherType. Use wsfe.issue() for exact control.",
823
+ {
824
+ code: "ARCA_INPUT_INVALID_VALUE",
825
+ field: "input.for.voucherType",
826
+ expected: "1, 6 or 11"
827
+ }
828
+ );
829
+ }
830
+ return {
831
+ salesPoint: value.salesPoint,
832
+ voucherType: value.voucherType,
833
+ number: value.number
834
+ };
835
+ }
836
+ function assertCreditNoteBound(value, max, path) {
837
+ if (!Number.isSafeInteger(value) || value < 1 || value > max) {
838
+ throw new ArcaInputError(
839
+ `issueCreditNote requires input.${path} to be an integer from 1 through ${max}.`,
840
+ { code: "ARCA_INPUT_INVALID_VALUE", field: `input.${path}` }
841
+ );
842
+ }
843
+ }
844
+ function copyCreditNoteItems(items) {
845
+ if (!Array.isArray(items)) {
846
+ throw new ArcaInputError(
847
+ "issueCreditNote requires items to be a non-empty array of items.",
848
+ {
849
+ code: "ARCA_INPUT_INVALID_VALUE",
850
+ field: "input.items",
851
+ expected: "a non-empty array of items"
852
+ }
853
+ );
854
+ }
855
+ return items.map(
856
+ (item) => item === null || typeof item !== "object" ? item : { ...item }
857
+ );
688
858
  }
689
859
 
690
860
  // src/services/wsfe-identity.ts
@@ -932,8 +1102,13 @@ function associationIdentity(vouchers) {
932
1102
  // src/services/vouchers.ts
933
1103
  function createVouchersService(wsfe, context) {
934
1104
  return {
935
- cancel: async (target, options) => {
936
- const result = await cancelInvoice(wsfe, target, options ?? {}, context);
1105
+ issueCreditNote: async (input, options) => {
1106
+ const result = await issueCreditNote(
1107
+ wsfe,
1108
+ input,
1109
+ options === void 0 ? {} : options,
1110
+ context
1111
+ );
937
1112
  return result;
938
1113
  },
939
1114
  issue: async (input, options) => {
@@ -944,7 +1119,27 @@ function createVouchersService(wsfe, context) {
944
1119
  context
945
1120
  );
946
1121
  return result;
947
- }
1122
+ },
1123
+ preview: (input, options) => previewInvoice(input, options === void 0 ? {} : options)
1124
+ };
1125
+ }
1126
+ function previewInvoice(input, options) {
1127
+ assertIssueObject(options, "options");
1128
+ assertIssueKeys(options, ["representedTaxId"], "options");
1129
+ if (options.representedTaxId !== void 0) {
1130
+ issueDocumentNumber(
1131
+ options.representedTaxId,
1132
+ "options.representedTaxId",
1133
+ 11,
1134
+ 11
1135
+ );
1136
+ }
1137
+ const { data, voucherClass, amounts } = deriveWsfeInvoice(input);
1138
+ return {
1139
+ voucherClass,
1140
+ voucherType: data.voucherType,
1141
+ amounts,
1142
+ request: data
948
1143
  };
949
1144
  }
950
1145
  async function issueInvoice(wsfe, input, options, context) {
@@ -1042,7 +1237,7 @@ function validateKeyStore(options, context) {
1042
1237
  function readRecord(json) {
1043
1238
  try {
1044
1239
  const record = JSON.parse(json);
1045
- if (!record || record.v !== 1 || !["issue", "cancel"].includes(record.operation) || typeof record.inputHash !== "string" || !record.sent || !Number.isSafeInteger(record.number) || record.number < 1 || record.number > 99999999 || record.sent.salesPoint !== record.salesPoint || record.sent.voucherType !== record.voucherType) {
1240
+ if (!record || record.v !== 1 || !["issue", "creditNote"].includes(record.operation) || typeof record.inputHash !== "string" || !record.sent || !Number.isSafeInteger(record.number) || record.number < 1 || record.number > 99999999 || record.sent.salesPoint !== record.salesPoint || record.sent.voucherType !== record.voucherType) {
1046
1241
  throw new Error("Invalid reservation structure");
1047
1242
  }
1048
1243
  normalizeWsfeVoucherInput(record.sent);
@@ -1339,59 +1534,41 @@ function replayEvidence() {
1339
1534
  observations: []
1340
1535
  };
1341
1536
  }
1342
- async function cancelInvoice(wsfe, target, options, context) {
1343
- assertIssueObject(options, "options");
1344
- const { date, ...issueOptions } = options;
1345
- validateOptions(issueOptions);
1346
- validateKeyStore(issueOptions, context);
1347
- assertIssueObject(target, "target");
1348
- for (const [field, max] of [
1349
- ["salesPoint", 99999],
1350
- ["voucherType", 999],
1351
- ["number", 99999999]
1352
- ]) {
1353
- if (!Number.isSafeInteger(target[field]) || target[field] < 1 || target[field] > max) {
1354
- throw new ArcaInputError(`Invalid cancel target ${field}.`, {
1355
- code: "ARCA_INPUT_INVALID_VALUE",
1356
- field
1357
- });
1358
- }
1359
- }
1360
- if (![1, 6, 11].includes(target.voucherType)) {
1361
- throw new ArcaInputError(
1362
- "cancel requires an invoice of type 1, 6 or 11; use wsfe.issue() for exact control.",
1363
- { code: "ARCA_INPUT_INVALID_VALUE" }
1364
- );
1365
- }
1366
- if (date !== void 0) {
1367
- normalizeWsfeDateInput(date, "date");
1368
- }
1537
+ async function issueCreditNote(wsfe, input, options, context) {
1538
+ validateOptions(options);
1539
+ validateKeyStore(options, context);
1540
+ const note = assertCreditNoteInput(input);
1541
+ const target = note.for;
1369
1542
  return await runOperation(
1370
1543
  wsfe,
1371
- "cancel",
1372
- { target, date },
1544
+ "creditNote",
1545
+ note,
1373
1546
  async () => {
1374
1547
  const original = await wsfe.lookupVoucher({
1375
- representedTaxId: issueOptions.representedTaxId,
1376
- forceRefresh: issueOptions.forceRefresh,
1548
+ representedTaxId: options.representedTaxId,
1549
+ forceRefresh: options.forceRefresh,
1377
1550
  salesPoint: target.salesPoint,
1378
1551
  voucherType: target.voucherType,
1379
1552
  number: target.number
1380
1553
  });
1381
1554
  if (original.kind === "not_found") {
1382
- throw new ArcaInputError("original voucher not found", {
1383
- code: "ARCA_INPUT_INVALID_VALUE"
1384
- });
1555
+ throw new ArcaInputError(
1556
+ "issueCreditNote failed: original voucher not found.",
1557
+ {
1558
+ code: "ARCA_INPUT_INVALID_VALUE",
1559
+ field: "input.for"
1560
+ }
1561
+ );
1385
1562
  }
1386
1563
  if (original.voucher.salesPoint !== target.salesPoint || original.voucher.voucherType !== target.voucherType || original.voucher.voucherNumber !== target.number) {
1387
1564
  throw new ArcaInputError(
1388
- "Original lookup coordinates do not match the cancel target.",
1389
- { code: "ARCA_INPUT_INVALID_VALUE" }
1565
+ "issueCreditNote failed: the original lookup coordinates do not match input.for.",
1566
+ { code: "ARCA_INPUT_INVALID_VALUE", field: "input.for" }
1390
1567
  );
1391
1568
  }
1392
- return deriveWsfeCancellation(original.voucher, date);
1569
+ return note.all === true ? deriveWsfeFullCreditNote(original.voucher, note) : deriveWsfePartialCreditNote(original.voucher, note);
1393
1570
  },
1394
- issueOptions,
1571
+ options,
1395
1572
  context
1396
1573
  );
1397
1574
  }
@@ -2278,7 +2455,8 @@ function createArcaClient(config = {}) {
2278
2455
  return {
2279
2456
  config: publicConfig,
2280
2457
  issue: vouchers.issue,
2281
- cancel: vouchers.cancel,
2458
+ preview: vouchers.preview,
2459
+ issueCreditNote: vouchers.issueCreditNote,
2282
2460
  wsfe,
2283
2461
  wsmtxca: createWsmtxcaService({ config: normalizedConfig, auth, soap }),
2284
2462
  padron: createPadronService({ config: normalizedConfig, auth, soap })