@taxora/sdk 1.8.0 → 1.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.js CHANGED
@@ -440,6 +440,356 @@ var AuthEndpoint = class {
440
440
  }
441
441
  };
442
442
 
443
+ // src/dto/EInvoicingQuota.ts
444
+ function asString(value, fallback = "") {
445
+ return typeof value === "string" ? value : fallback;
446
+ }
447
+ function asNullableString(value) {
448
+ return typeof value === "string" ? value : null;
449
+ }
450
+ function asInt(value) {
451
+ return typeof value === "number" ? Math.trunc(value) : Number(value ?? 0) || 0;
452
+ }
453
+ function asRecord(value) {
454
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
455
+ }
456
+ var EInvoicingQuotaPeriod = class _EInvoicingQuotaPeriod {
457
+ constructor(period, used, included, freeUsed, freeRemaining, billableUnits, amount, monthlyFee, total, state, byCountry, billedAt, manualReference, monthlyFeeReference) {
458
+ this.period = period;
459
+ this.used = used;
460
+ this.included = included;
461
+ this.freeUsed = freeUsed;
462
+ this.freeRemaining = freeRemaining;
463
+ this.billableUnits = billableUnits;
464
+ this.amount = amount;
465
+ this.monthlyFee = monthlyFee;
466
+ this.total = total;
467
+ this.state = state;
468
+ this.byCountry = byCountry;
469
+ this.billedAt = billedAt;
470
+ this.manualReference = manualReference;
471
+ this.monthlyFeeReference = monthlyFeeReference;
472
+ }
473
+ period;
474
+ used;
475
+ included;
476
+ freeUsed;
477
+ freeRemaining;
478
+ billableUnits;
479
+ amount;
480
+ monthlyFee;
481
+ total;
482
+ state;
483
+ byCountry;
484
+ billedAt;
485
+ manualReference;
486
+ monthlyFeeReference;
487
+ static fromArray(data) {
488
+ const byCountry = {};
489
+ if (Array.isArray(data["by_country"])) {
490
+ for (const row of data["by_country"]) {
491
+ const entry = asRecord(row);
492
+ if (entry && typeof entry["country"] === "string") {
493
+ byCountry[entry["country"]] = asInt(entry["used"]);
494
+ }
495
+ }
496
+ }
497
+ return new _EInvoicingQuotaPeriod(
498
+ asString(data["period"]),
499
+ asInt(data["used"]),
500
+ asInt(data["included"]),
501
+ asInt(data["free_used"]),
502
+ asInt(data["free_remaining"]),
503
+ asInt(data["billable_units"]),
504
+ asString(data["amount"], "0.00"),
505
+ asNullableString(data["monthly_fee"]),
506
+ asString(data["total"], "0.00"),
507
+ asString(data["state"], "open"),
508
+ byCountry,
509
+ asNullableString(data["billed_at"]),
510
+ asNullableString(data["manual_reference"]),
511
+ asNullableString(data["monthly_fee_reference"])
512
+ );
513
+ }
514
+ };
515
+ var EInvoicingQuota = class _EInvoicingQuota {
516
+ constructor(billing, plan, currency, included, tiers, countries, monthlyFee, current, history) {
517
+ this.billing = billing;
518
+ this.plan = plan;
519
+ this.currency = currency;
520
+ this.included = included;
521
+ this.tiers = tiers;
522
+ this.countries = countries;
523
+ this.monthlyFee = monthlyFee;
524
+ this.current = current;
525
+ this.history = history;
526
+ }
527
+ billing;
528
+ plan;
529
+ currency;
530
+ included;
531
+ tiers;
532
+ countries;
533
+ monthlyFee;
534
+ current;
535
+ history;
536
+ static fromArray(data) {
537
+ const tiers = [];
538
+ if (Array.isArray(data["tiers"])) {
539
+ for (const row of data["tiers"]) {
540
+ const tier = asRecord(row);
541
+ if (tier) {
542
+ tiers.push({
543
+ upTo: tier["up_to"] === null || tier["up_to"] === void 0 ? null : asInt(tier["up_to"]),
544
+ unitPrice: asString(tier["unit_price"], "0")
545
+ });
546
+ }
547
+ }
548
+ }
549
+ const history = [];
550
+ if (Array.isArray(data["history"])) {
551
+ for (const row of data["history"]) {
552
+ const period = asRecord(row);
553
+ if (period) history.push(EInvoicingQuotaPeriod.fromArray(period));
554
+ }
555
+ }
556
+ return new _EInvoicingQuota(
557
+ asString(data["billing"], "invoice"),
558
+ asNullableString(data["plan"]),
559
+ asString(data["currency"], "EUR"),
560
+ asInt(data["included"]),
561
+ tiers,
562
+ Array.isArray(data["countries"]) ? data["countries"].map(String) : [],
563
+ asNullableString(data["monthly_fee"]),
564
+ EInvoicingQuotaPeriod.fromArray(asRecord(data["current"]) ?? {}),
565
+ history
566
+ );
567
+ }
568
+ };
569
+
570
+ // src/dto/UsageSummary.ts
571
+ function asRecord2(value) {
572
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
573
+ }
574
+ function asRecordList(value) {
575
+ return Array.isArray(value) ? value.filter((row) => asRecord2(row) !== null) : [];
576
+ }
577
+ function asString2(value, fallback = "") {
578
+ return typeof value === "string" ? value : fallback;
579
+ }
580
+ function asNullableString2(value) {
581
+ return typeof value === "string" ? value : null;
582
+ }
583
+ function asNumber(value) {
584
+ return typeof value === "number" ? value : Number(value ?? 0) || 0;
585
+ }
586
+ function asNullableNumber(value) {
587
+ if (value === null || value === void 0 || value === "") return null;
588
+ const num = Number(value);
589
+ return Number.isFinite(num) ? num : null;
590
+ }
591
+ var UsageSummary = class _UsageSummary {
592
+ constructor(id, companyId, feature, country, regime, billingPeriod, eventCount, billableAmount, state, stateLabel, periodClosedAt, billingTriggeredAt, billedAt, stripeInvoiceId, stripeSubscriptionItemId, billingNotes, events, createdAt, updatedAt) {
593
+ this.id = id;
594
+ this.companyId = companyId;
595
+ this.feature = feature;
596
+ this.country = country;
597
+ this.regime = regime;
598
+ this.billingPeriod = billingPeriod;
599
+ this.eventCount = eventCount;
600
+ this.billableAmount = billableAmount;
601
+ this.state = state;
602
+ this.stateLabel = stateLabel;
603
+ this.periodClosedAt = periodClosedAt;
604
+ this.billingTriggeredAt = billingTriggeredAt;
605
+ this.billedAt = billedAt;
606
+ this.stripeInvoiceId = stripeInvoiceId;
607
+ this.stripeSubscriptionItemId = stripeSubscriptionItemId;
608
+ this.billingNotes = billingNotes;
609
+ this.events = events;
610
+ this.createdAt = createdAt;
611
+ this.updatedAt = updatedAt;
612
+ }
613
+ id;
614
+ companyId;
615
+ feature;
616
+ country;
617
+ regime;
618
+ billingPeriod;
619
+ eventCount;
620
+ billableAmount;
621
+ state;
622
+ stateLabel;
623
+ periodClosedAt;
624
+ billingTriggeredAt;
625
+ billedAt;
626
+ stripeInvoiceId;
627
+ stripeSubscriptionItemId;
628
+ billingNotes;
629
+ events;
630
+ createdAt;
631
+ updatedAt;
632
+ static fromArray(data) {
633
+ const amount = data["billable_amount"];
634
+ return new _UsageSummary(
635
+ asNumber(data["id"]),
636
+ asNumber(data["company_id"]),
637
+ asString2(data["feature"]),
638
+ asNullableString2(data["country"]),
639
+ asNullableString2(data["regime"]),
640
+ asString2(data["billing_period"]),
641
+ asNumber(data["event_count"]),
642
+ typeof amount === "string" ? amount : typeof amount === "number" ? String(amount) : null,
643
+ asNullableString2(data["state"]),
644
+ asNullableString2(data["state_label"]),
645
+ asNullableString2(data["period_closed_at"]),
646
+ asNullableString2(data["billing_triggered_at"]),
647
+ asNullableString2(data["billed_at"]),
648
+ asNullableString2(data["stripe_invoice_id"]),
649
+ asNullableString2(data["stripe_subscription_item_id"]),
650
+ asNullableString2(data["billing_notes"]),
651
+ asRecordList(data["events"]).map((event) => ({
652
+ id: asNumber(event["id"]),
653
+ usageSummaryId: asNullableNumber(event["usage_summary_id"]),
654
+ feature: asString2(event["feature"]),
655
+ country: asNullableString2(event["country"]),
656
+ regime: asNullableString2(event["regime"]),
657
+ billingPeriod: asString2(event["billing_period"]),
658
+ resourceType: asNullableString2(event["resource_type"]),
659
+ resourceId: asNullableNumber(event["resource_id"]),
660
+ stripeUsageRecordId: asNullableString2(event["stripe_usage_record_id"]),
661
+ stripeReportedAt: asNullableString2(event["stripe_reported_at"]),
662
+ createdAt: asNullableString2(event["created_at"])
663
+ })),
664
+ asNullableString2(data["created_at"]),
665
+ asNullableString2(data["updated_at"])
666
+ );
667
+ }
668
+ };
669
+ var UsageSummaryPage = class _UsageSummaryPage {
670
+ constructor(rows, currentPage, perPage, total, lastPage) {
671
+ this.rows = rows;
672
+ this.currentPage = currentPage;
673
+ this.perPage = perPage;
674
+ this.total = total;
675
+ this.lastPage = lastPage;
676
+ }
677
+ rows;
678
+ currentPage;
679
+ perPage;
680
+ total;
681
+ lastPage;
682
+ static fromResponse(payload) {
683
+ const obj = asRecord2(payload) ?? {};
684
+ const rows = asRecordList(obj["data"]).map(UsageSummary.fromArray);
685
+ const meta = asRecord2(obj["meta"]) ?? {};
686
+ const num = (value, fallback) => typeof value === "number" ? value : fallback;
687
+ return new _UsageSummaryPage(
688
+ rows,
689
+ num(meta["current_page"], 1),
690
+ num(meta["per_page"], rows.length),
691
+ num(meta["total"], rows.length),
692
+ num(meta["last_page"], 1)
693
+ );
694
+ }
695
+ [Symbol.iterator]() {
696
+ return this.rows[Symbol.iterator]();
697
+ }
698
+ get length() {
699
+ return this.rows.length;
700
+ }
701
+ };
702
+
703
+ // src/endpoints/BillingEndpoint.ts
704
+ var PERIOD_REGEX = /^\d{4}-(0[1-9]|1[0-2])$/;
705
+ var BillingEndpoint = class {
706
+ constructor(baseUrl, apiKey, tokenStorage, httpClient, retryPolicy = new RetryPolicy()) {
707
+ this.baseUrl = baseUrl;
708
+ this.apiKey = apiKey;
709
+ this.tokenStorage = tokenStorage;
710
+ this.httpClient = httpClient;
711
+ this.retryPolicy = retryPolicy;
712
+ }
713
+ baseUrl;
714
+ apiKey;
715
+ tokenStorage;
716
+ httpClient;
717
+ retryPolicy;
718
+ /** Paginated usage summaries, newest billing period first. `perPage` is clamped to 1-100 server-side. */
719
+ async listUsageSummaries(page = 1, perPage = 25) {
720
+ const params = new URLSearchParams();
721
+ params.set("page", String(page));
722
+ params.set("per_page", String(perPage));
723
+ const data = await this.getJson(`${this.baseUrl}/billing/usage-summaries?${params.toString()}`);
724
+ return UsageSummaryPage.fromResponse(data);
725
+ }
726
+ /**
727
+ * The usage summaries of one billing period (YYYY-MM), one per feature,
728
+ * each with its usage events. A period without usage yields a 404 (HttpException).
729
+ */
730
+ async getUsageSummaries(period) {
731
+ if (!PERIOD_REGEX.test(period)) {
732
+ throw new TaxoraException(`Invalid billing period "${period}". Expected YYYY-MM, e.g. "2026-09".`);
733
+ }
734
+ const data = await this.getJson(`${this.baseUrl}/billing/usage-summaries/${period}`);
735
+ return Array.isArray(data) ? data.filter((row) => typeof row === "object" && row !== null).map(UsageSummary.fromArray) : [];
736
+ }
737
+ /**
738
+ * The e-invoicing allowance: documents included per month, used, still free and
739
+ * what is due for the documents beyond it — one pool over all e-invoicing
740
+ * countries, current month first, then up to `months - 1` earlier months (1-24).
741
+ * Resolves to null when the company has no e-invoicing.
742
+ */
743
+ async getEInvoicingQuota(months = 6) {
744
+ if (!Number.isInteger(months) || months < 1 || months > 24) {
745
+ throw new TaxoraException("months must be an integer between 1 and 24.");
746
+ }
747
+ const data = await this.getJson(`${this.baseUrl}/billing/einvoicing-quota?months=${months}`);
748
+ if (typeof data !== "object" || data === null || Array.isArray(data) || !("billing" in data)) {
749
+ return null;
750
+ }
751
+ return EInvoicingQuota.fromArray(data);
752
+ }
753
+ buildHeaders() {
754
+ const headers = {
755
+ "Content-Type": "application/json",
756
+ "x-api-key": this.apiKey
757
+ };
758
+ const token = this.tokenStorage.get();
759
+ if (token) {
760
+ headers["Authorization"] = `Bearer ${token.accessToken}`;
761
+ }
762
+ return headers;
763
+ }
764
+ /** GET + envelope unwrap (`{success, data}` → data, which may be an object or an array). */
765
+ async getJson(url) {
766
+ return withRetries(this.retryPolicy, "billing request", async () => {
767
+ const response = await this.httpClient.request("GET", url, { headers: this.buildHeaders() });
768
+ const responseText = await response.text();
769
+ if (!response.ok) {
770
+ throw new HttpException(
771
+ describeApiError(responseText, response.status),
772
+ response.status,
773
+ responseText,
774
+ {},
775
+ response.headers.get("retry-after")
776
+ );
777
+ }
778
+ let parsed;
779
+ try {
780
+ parsed = JSON.parse(responseText);
781
+ } catch {
782
+ throw new HttpException("Invalid JSON response", response.status, responseText);
783
+ }
784
+ if (typeof parsed !== "object" || parsed === null) {
785
+ throw new HttpException("Unexpected response format", response.status, responseText);
786
+ }
787
+ const obj = parsed;
788
+ return obj["data"] !== void 0 && obj["data"] !== null ? obj["data"] : obj;
789
+ });
790
+ }
791
+ };
792
+
443
793
  // src/dto/CompanyResource.ts
444
794
  function normalizeCompanyResource(data) {
445
795
  const apiRateLimit = typeof data["api_rate_limit"] === "number" ? data["api_rate_limit"] : void 0;
@@ -505,6 +855,67 @@ var CompanyEndpoint = class {
505
855
  }
506
856
  };
507
857
 
858
+ // src/enums/ComplianceDeliveryAction.ts
859
+ var ComplianceDeliveryAction = {
860
+ /** The buyer can receive e-invoices: create the transaction, Taxora sends it. */
861
+ EINVOICE: "einvoice",
862
+ /** The buyer is not on the network: send your regular invoice yourself (a transaction would end as not_required and is not billed). */
863
+ REGULAR_INVOICE: "regular_invoice",
864
+ /** No definite directory answer yet: create the transaction anyway, Taxora asks again before sending. */
865
+ EINVOICE_PENDING: "einvoice_pending"
866
+ };
867
+
868
+ // src/enums/PeppolLookupStatus.ts
869
+ var PeppolLookupStatus = {
870
+ REACHABLE: "reachable",
871
+ NOT_REACHABLE: "not_reachable",
872
+ PENDING: "pending",
873
+ UNKNOWN: "unknown"
874
+ };
875
+ var VALUES = Object.values(PeppolLookupStatus);
876
+ function toPeppolLookupStatus(value) {
877
+ if (typeof value === "string") {
878
+ const normalized = value.trim().toLowerCase();
879
+ if (VALUES.includes(normalized)) {
880
+ return normalized;
881
+ }
882
+ }
883
+ return PeppolLookupStatus.UNKNOWN;
884
+ }
885
+ var DESCRIPTIONS = {
886
+ [PeppolLookupStatus.REACHABLE]: "The participant is registered on Peppol and can receive e-invoices.",
887
+ [PeppolLookupStatus.NOT_REACHABLE]: "The participant is not registered on Peppol.",
888
+ [PeppolLookupStatus.PENDING]: "The directory lookup is still resolving \u2014 ask again in a few seconds.",
889
+ [PeppolLookupStatus.UNKNOWN]: "Unknown lookup status."
890
+ };
891
+ function describePeppolLookupStatus(status) {
892
+ return DESCRIPTIONS[status];
893
+ }
894
+
895
+ // src/dto/ComplianceDeliveryDecision.ts
896
+ var ComplianceDeliveryDecision = class _ComplianceDeliveryDecision {
897
+ constructor(action, lookup) {
898
+ this.action = action;
899
+ this.lookup = lookup;
900
+ }
901
+ action;
902
+ lookup;
903
+ /** True unless the buyer is not on the network. */
904
+ get sendsEInvoice() {
905
+ return this.action !== ComplianceDeliveryAction.REGULAR_INVOICE;
906
+ }
907
+ static fromLookup(lookup) {
908
+ switch (lookup.status) {
909
+ case PeppolLookupStatus.REACHABLE:
910
+ return new _ComplianceDeliveryDecision(ComplianceDeliveryAction.EINVOICE, lookup);
911
+ case PeppolLookupStatus.NOT_REACHABLE:
912
+ return new _ComplianceDeliveryDecision(ComplianceDeliveryAction.REGULAR_INVOICE, lookup);
913
+ default:
914
+ return new _ComplianceDeliveryDecision(ComplianceDeliveryAction.EINVOICE_PENDING, lookup);
915
+ }
916
+ }
917
+ };
918
+
508
919
  // src/enums/ComplianceEnrollmentStatus.ts
509
920
  var ComplianceEnrollmentStatus = {
510
921
  PENDING_DATA: "pending_data",
@@ -516,17 +927,17 @@ var ComplianceEnrollmentStatus = {
516
927
  ERROR_ACTIVATION: "error_activation",
517
928
  UNKNOWN: "unknown"
518
929
  };
519
- var VALUES = Object.values(ComplianceEnrollmentStatus);
930
+ var VALUES2 = Object.values(ComplianceEnrollmentStatus);
520
931
  function toComplianceEnrollmentStatus(value) {
521
932
  if (typeof value === "string") {
522
933
  const normalized = value.trim().toLowerCase();
523
- if (VALUES.includes(normalized)) {
934
+ if (VALUES2.includes(normalized)) {
524
935
  return normalized;
525
936
  }
526
937
  }
527
938
  return ComplianceEnrollmentStatus.UNKNOWN;
528
939
  }
529
- var DESCRIPTIONS = {
940
+ var DESCRIPTIONS2 = {
530
941
  [ComplianceEnrollmentStatus.PENDING_DATA]: "Awaiting merchant data before provisioning can start.",
531
942
  [ComplianceEnrollmentStatus.ACCOUNT_CREATED]: "Provider account exists. Regime not yet activated.",
532
943
  [ComplianceEnrollmentStatus.REGIME_ACTIVATED]: "Compliance regime activated at provider. Ready to submit transactions.",
@@ -537,7 +948,7 @@ var DESCRIPTIONS = {
537
948
  [ComplianceEnrollmentStatus.UNKNOWN]: "Unknown status."
538
949
  };
539
950
  function describeComplianceEnrollmentStatus(status) {
540
- return DESCRIPTIONS[status];
951
+ return DESCRIPTIONS2[status];
541
952
  }
542
953
 
543
954
  // src/enums/ComplianceService.ts
@@ -546,33 +957,33 @@ var ComplianceService = {
546
957
  E_INVOICING: "e_invoicing",
547
958
  UNKNOWN: "unknown"
548
959
  };
549
- var VALUES2 = Object.values(ComplianceService);
960
+ var VALUES3 = Object.values(ComplianceService);
550
961
  function toComplianceService(value) {
551
962
  if (typeof value === "string") {
552
963
  const normalized = value.trim().toLowerCase();
553
- if (VALUES2.includes(normalized)) {
964
+ if (VALUES3.includes(normalized)) {
554
965
  return normalized;
555
966
  }
556
967
  }
557
968
  return ComplianceService.UNKNOWN;
558
969
  }
559
- var DESCRIPTIONS2 = {
970
+ var DESCRIPTIONS3 = {
560
971
  [ComplianceService.E_REPORTING]: "E-reporting of transaction data to the tax authority (France: DGFiP Flux 10).",
561
972
  [ComplianceService.E_INVOICING]: "Structured e-invoices delivered to the buyer (Norway: EHF / Peppol BIS 3.0).",
562
973
  [ComplianceService.UNKNOWN]: "Unknown service."
563
974
  };
564
975
  function describeComplianceService(service) {
565
- return DESCRIPTIONS2[service];
976
+ return DESCRIPTIONS3[service];
566
977
  }
567
978
 
568
979
  // src/dto/ComplianceEnrollment.ts
569
- function asString(value, fallback = "") {
980
+ function asString3(value, fallback = "") {
570
981
  return typeof value === "string" ? value : fallback;
571
982
  }
572
- function asNullableString(value) {
983
+ function asNullableString3(value) {
573
984
  return typeof value === "string" ? value : null;
574
985
  }
575
- function asNumber(value) {
986
+ function asNumber2(value) {
576
987
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
577
988
  }
578
989
  var ComplianceEnrollment = class _ComplianceEnrollment {
@@ -619,29 +1030,114 @@ var ComplianceEnrollment = class _ComplianceEnrollment {
619
1030
  static fromArray(data) {
620
1031
  const regimeConfig = typeof data["regime_config"] === "object" && data["regime_config"] !== null && !Array.isArray(data["regime_config"]) ? data["regime_config"] : null;
621
1032
  return new _ComplianceEnrollment(
622
- asNumber(data["id"]),
623
- asNumber(data["company_id"]),
624
- asString(data["country"]),
625
- asString(data["regime"]),
626
- asString(data["provider"]),
1033
+ asNumber2(data["id"]),
1034
+ asNumber2(data["company_id"]),
1035
+ asString3(data["country"]),
1036
+ asString3(data["regime"]),
1037
+ asString3(data["provider"]),
627
1038
  toComplianceEnrollmentStatus(data["status"]),
628
- asString(data["status_label"]),
629
- asNullableString(data["status_error"]),
630
- asNullableString(data["provider_account_id"]),
631
- asNullableString(data["tax_id"]),
632
- asNullableString(data["company_register_id"]),
633
- asNullableString(data["company_register_scheme"]),
1039
+ asString3(data["status_label"]),
1040
+ asNullableString3(data["status_error"]),
1041
+ asNullableString3(data["provider_account_id"]),
1042
+ asNullableString3(data["tax_id"]),
1043
+ asNullableString3(data["company_register_id"]),
1044
+ asNullableString3(data["company_register_scheme"]),
634
1045
  regimeConfig,
635
- asNullableString(data["reporting_start_date"]),
636
- asNullableString(data["notification_email"]),
1046
+ asNullableString3(data["reporting_start_date"]),
1047
+ asNullableString3(data["notification_email"]),
637
1048
  data["auto_send"] === true,
638
- asNullableString(data["created_at"]),
639
- asNullableString(data["updated_at"]),
1049
+ asNullableString3(data["created_at"]),
1050
+ asNullableString3(data["updated_at"]),
640
1051
  toComplianceService(data["service"])
641
1052
  );
642
1053
  }
643
1054
  };
644
1055
 
1056
+ // src/dto/ComplianceOfferings.ts
1057
+ function asRecord3(value) {
1058
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
1059
+ }
1060
+ function asRecordList2(value) {
1061
+ return Array.isArray(value) ? value.filter((row) => asRecord3(row) !== null) : [];
1062
+ }
1063
+ function asString4(value, fallback = "") {
1064
+ return typeof value === "string" ? value : fallback;
1065
+ }
1066
+ function asNullableString4(value) {
1067
+ return typeof value === "string" ? value : null;
1068
+ }
1069
+ function asNullableNumber2(value) {
1070
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1071
+ if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
1072
+ return null;
1073
+ }
1074
+ function asStringList(value) {
1075
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
1076
+ }
1077
+ function toRequires(value) {
1078
+ const record = asRecord3(value);
1079
+ return record === null ? null : { country: asString4(record["country"]), service: asString4(record["service"]) };
1080
+ }
1081
+ var ComplianceOfferings = class _ComplianceOfferings {
1082
+ constructor(offerings, coverage, access, accessByCountry) {
1083
+ this.offerings = offerings;
1084
+ this.coverage = coverage;
1085
+ this.access = access;
1086
+ this.accessByCountry = accessByCountry;
1087
+ }
1088
+ offerings;
1089
+ coverage;
1090
+ access;
1091
+ accessByCountry;
1092
+ static fromArray(data) {
1093
+ const offerings = asRecordList2(data["data"]).map((row) => ({
1094
+ country: asString4(row["country"]),
1095
+ service: asString4(row["service"]),
1096
+ currency: asString4(row["currency"], "EUR"),
1097
+ active: row["active"] === true,
1098
+ viaStripe: row["via_stripe"] === true,
1099
+ plan: asNullableString4(row["plan"]),
1100
+ status: asNullableString4(row["status"]),
1101
+ enrolled: row["enrolled"] === true,
1102
+ plans: asRecordList2(row["plans"]).map((plan) => ({
1103
+ key: asString4(plan["key"]),
1104
+ monthlyFee: asNullableNumber2(plan["monthly_fee"]),
1105
+ setupFee: asNullableNumber2(plan["setup_fee"]),
1106
+ usageTiers: Array.isArray(plan["usage_tiers"]) ? plan["usage_tiers"] : [],
1107
+ volumeLimit: plan["volume_limit"] ?? null,
1108
+ contactOnly: plan["contact_only"] === true,
1109
+ checkoutAvailable: plan["checkout_available"] === true,
1110
+ requires: toRequires(plan["requires"]),
1111
+ eligible: plan["eligible"] === true
1112
+ }))
1113
+ }));
1114
+ const coverage = asRecordList2(data["coverage"]).map((row) => ({
1115
+ country: asString4(row["country"]),
1116
+ availability: asString4(row["availability"]),
1117
+ services: asStringList(row["services"]),
1118
+ network: asNullableString4(row["network"]),
1119
+ format: asNullableString4(row["format"]),
1120
+ mandate: asNullableString4(row["mandate"]),
1121
+ support: asString4(row["support"], "yes")
1122
+ }));
1123
+ const access = asRecord3(data["access"]) ?? {};
1124
+ const accessByCountry = {};
1125
+ for (const [country, services] of Object.entries(asRecord3(data["access_by_country"]) ?? {})) {
1126
+ const flags = {};
1127
+ for (const [service, value] of Object.entries(asRecord3(services) ?? {})) {
1128
+ flags[service] = value === true;
1129
+ }
1130
+ accessByCountry[country] = flags;
1131
+ }
1132
+ return new _ComplianceOfferings(
1133
+ offerings,
1134
+ coverage,
1135
+ { eReporting: access["e_reporting"] === true, eInvoicing: access["e_invoicing"] === true },
1136
+ accessByCountry
1137
+ );
1138
+ }
1139
+ };
1140
+
645
1141
  // src/dto/ComplianceEnrollmentPage.ts
646
1142
  var ComplianceEnrollmentPage = class _ComplianceEnrollmentPage {
647
1143
  constructor(rows, currentPage, perPage, total, lastPage) {
@@ -687,17 +1183,17 @@ var ComplianceTaxReportState = {
687
1183
  ERROR: "error",
688
1184
  UNKNOWN: "unknown"
689
1185
  };
690
- var VALUES3 = Object.values(ComplianceTaxReportState);
1186
+ var VALUES4 = Object.values(ComplianceTaxReportState);
691
1187
  function toComplianceTaxReportState(value) {
692
1188
  if (typeof value === "string") {
693
1189
  const normalized = value.trim().toLowerCase();
694
- if (VALUES3.includes(normalized)) {
1190
+ if (VALUES4.includes(normalized)) {
695
1191
  return normalized;
696
1192
  }
697
1193
  }
698
1194
  return ComplianceTaxReportState.UNKNOWN;
699
1195
  }
700
- var DESCRIPTIONS3 = {
1196
+ var DESCRIPTIONS4 = {
701
1197
  [ComplianceTaxReportState.NEW]: "Created at provider, waiting for the daily ledger batch.",
702
1198
  [ComplianceTaxReportState.SENT]: "Ledger transmitted to PPF.",
703
1199
  [ComplianceTaxReportState.ACKNOWLEDGED]: "PPF acknowledged receipt of the ledger.",
@@ -707,20 +1203,20 @@ var DESCRIPTIONS3 = {
707
1203
  [ComplianceTaxReportState.UNKNOWN]: "Unknown state."
708
1204
  };
709
1205
  function describeComplianceTaxReportState(state) {
710
- return DESCRIPTIONS3[state];
1206
+ return DESCRIPTIONS4[state];
711
1207
  }
712
1208
 
713
1209
  // src/dto/ComplianceTaxReport.ts
714
- function asString2(value, fallback = "") {
1210
+ function asString5(value, fallback = "") {
715
1211
  return typeof value === "string" ? value : fallback;
716
1212
  }
717
- function asNullableString2(value) {
1213
+ function asNullableString5(value) {
718
1214
  return typeof value === "string" ? value : null;
719
1215
  }
720
- function asNumber2(value) {
1216
+ function asNumber3(value) {
721
1217
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
722
1218
  }
723
- function asNullableNumber(value) {
1219
+ function asNullableNumber3(value) {
724
1220
  return typeof value === "number" ? value : null;
725
1221
  }
726
1222
  function asMoney(value) {
@@ -771,25 +1267,25 @@ var ComplianceTaxReport = class _ComplianceTaxReport {
771
1267
  updatedAt;
772
1268
  static fromArray(data) {
773
1269
  return new _ComplianceTaxReport(
774
- asNumber2(data["id"]),
775
- asNumber2(data["company_id"]),
776
- asNullableNumber(data["compliance_transaction_id"]),
777
- asNullableNumber(data["ledger_id"]),
778
- asString2(data["country"]),
779
- asString2(data["regime"]),
1270
+ asNumber3(data["id"]),
1271
+ asNumber3(data["company_id"]),
1272
+ asNullableNumber3(data["compliance_transaction_id"]),
1273
+ asNullableNumber3(data["ledger_id"]),
1274
+ asString5(data["country"]),
1275
+ asString5(data["regime"]),
780
1276
  toComplianceTaxReportState(data["state"]),
781
- asString2(data["state_label"]),
1277
+ asString5(data["state_label"]),
782
1278
  data["is_terminal"] === true,
783
- asNullableString2(data["provider_tax_report_id"]),
784
- asNullableString2(data["provider_ledger_id"]),
1279
+ asNullableString5(data["provider_tax_report_id"]),
1280
+ asNullableString5(data["provider_ledger_id"]),
785
1281
  asMoney(data["base_amount"]),
786
1282
  asMoney(data["tax_amount"]),
787
1283
  asMoney(data["total_amount"]),
788
- asNullableString2(data["refusal_reason"]),
789
- asNullableString2(data["registered_at"]),
790
- asNullableString2(data["refused_at"]),
791
- asNullableString2(data["created_at"]),
792
- asNullableString2(data["updated_at"])
1284
+ asNullableString5(data["refusal_reason"]),
1285
+ asNullableString5(data["registered_at"]),
1286
+ asNullableString5(data["refused_at"]),
1287
+ asNullableString5(data["created_at"]),
1288
+ asNullableString5(data["updated_at"])
793
1289
  );
794
1290
  }
795
1291
  };
@@ -835,23 +1331,23 @@ var ComplianceDocumentType = {
835
1331
  CREDIT_NOTE: "credit_note",
836
1332
  UNKNOWN: "unknown"
837
1333
  };
838
- var VALUES4 = Object.values(ComplianceDocumentType);
1334
+ var VALUES5 = Object.values(ComplianceDocumentType);
839
1335
  function toComplianceDocumentType(value) {
840
1336
  if (typeof value === "string") {
841
1337
  const normalized = value.trim().toLowerCase();
842
- if (VALUES4.includes(normalized)) {
1338
+ if (VALUES5.includes(normalized)) {
843
1339
  return normalized;
844
1340
  }
845
1341
  }
846
1342
  return ComplianceDocumentType.UNKNOWN;
847
1343
  }
848
- var DESCRIPTIONS4 = {
1344
+ var DESCRIPTIONS5 = {
849
1345
  [ComplianceDocumentType.INVOICE]: "Commercial invoice.",
850
1346
  [ComplianceDocumentType.CREDIT_NOTE]: "Credit note correcting a previously issued invoice.",
851
1347
  [ComplianceDocumentType.UNKNOWN]: "Unknown document type."
852
1348
  };
853
1349
  function describeComplianceDocumentType(type) {
854
- return DESCRIPTIONS4[type];
1350
+ return DESCRIPTIONS5[type];
855
1351
  }
856
1352
 
857
1353
  // src/enums/ComplianceTransactionState.ts
@@ -860,27 +1356,30 @@ var ComplianceTransactionState = {
860
1356
  SENDING: "sending",
861
1357
  SUBMITTED: "submitted",
862
1358
  ERROR: "error",
1359
+ /** E-invoicing only: the buyer cannot receive e-invoices — nothing was sent or billed; send your regular invoice. */
1360
+ NOT_REQUIRED: "not_required",
863
1361
  UNKNOWN: "unknown"
864
1362
  };
865
- var VALUES5 = Object.values(ComplianceTransactionState);
1363
+ var VALUES6 = Object.values(ComplianceTransactionState);
866
1364
  function toComplianceTransactionState(value) {
867
1365
  if (typeof value === "string") {
868
1366
  const normalized = value.trim().toLowerCase();
869
- if (VALUES5.includes(normalized)) {
1367
+ if (VALUES6.includes(normalized)) {
870
1368
  return normalized;
871
1369
  }
872
1370
  }
873
1371
  return ComplianceTransactionState.UNKNOWN;
874
1372
  }
875
- var DESCRIPTIONS5 = {
1373
+ var DESCRIPTIONS6 = {
876
1374
  [ComplianceTransactionState.PENDING]: "Recorded locally, not yet submitted to the provider.",
877
1375
  [ComplianceTransactionState.SENDING]: "Submission to the provider in progress.",
878
1376
  [ComplianceTransactionState.SUBMITTED]: "Successfully submitted to the provider.",
879
1377
  [ComplianceTransactionState.ERROR]: "Submission failed. See submissionError.",
1378
+ [ComplianceTransactionState.NOT_REQUIRED]: "The buyer cannot receive e-invoices, so none was sent or billed. Send your regular invoice instead.",
880
1379
  [ComplianceTransactionState.UNKNOWN]: "Unknown state."
881
1380
  };
882
1381
  function describeComplianceTransactionState(state) {
883
- return DESCRIPTIONS5[state];
1382
+ return DESCRIPTIONS6[state];
884
1383
  }
885
1384
 
886
1385
  // src/enums/ComplianceTransactionType.ts
@@ -892,17 +1391,17 @@ var ComplianceTransactionType = {
892
1391
  CROSSBORDER_INBOUND: "crossborder_inbound",
893
1392
  UNKNOWN: "unknown"
894
1393
  };
895
- var VALUES6 = Object.values(ComplianceTransactionType);
1394
+ var VALUES7 = Object.values(ComplianceTransactionType);
896
1395
  function toComplianceTransactionType(value) {
897
1396
  if (typeof value === "string") {
898
1397
  const normalized = value.trim().toLowerCase();
899
- if (VALUES6.includes(normalized)) {
1398
+ if (VALUES7.includes(normalized)) {
900
1399
  return normalized;
901
1400
  }
902
1401
  }
903
1402
  return ComplianceTransactionType.UNKNOWN;
904
1403
  }
905
- var DESCRIPTIONS6 = {
1404
+ var DESCRIPTIONS7 = {
906
1405
  [ComplianceTransactionType.B2C_OUTBOUND]: "Sale to a private person in the merchant country (DGFiP Flux 10.3).",
907
1406
  [ComplianceTransactionType.B2B_DOMESTIC_OUTBOUND]: "Sale to a domestic business (DGFiP Flux 1, Phase 2).",
908
1407
  [ComplianceTransactionType.B2B_DOMESTIC_INBOUND]: "Purchase from a domestic business (DGFiP Flux 1, Phase 2).",
@@ -911,17 +1410,17 @@ var DESCRIPTIONS6 = {
911
1410
  [ComplianceTransactionType.UNKNOWN]: "Unknown transaction type."
912
1411
  };
913
1412
  function describeComplianceTransactionType(type) {
914
- return DESCRIPTIONS6[type];
1413
+ return DESCRIPTIONS7[type];
915
1414
  }
916
1415
 
917
1416
  // src/dto/ComplianceTransaction.ts
918
- function asString3(value, fallback = "") {
1417
+ function asString6(value, fallback = "") {
919
1418
  return typeof value === "string" ? value : fallback;
920
1419
  }
921
- function asNullableString3(value) {
1420
+ function asNullableString6(value) {
922
1421
  return typeof value === "string" ? value : null;
923
1422
  }
924
- function asNumber3(value) {
1423
+ function asNumber4(value) {
925
1424
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
926
1425
  }
927
1426
  function asMoney2(value) {
@@ -929,34 +1428,55 @@ function asMoney2(value) {
929
1428
  if (typeof value === "number" && Number.isFinite(value)) return String(value);
930
1429
  return "0.00";
931
1430
  }
932
- function asNullableNumber2(value) {
1431
+ function asNullableNumber4(value) {
933
1432
  if (typeof value === "number" && Number.isFinite(value)) return value;
934
1433
  if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
935
1434
  return null;
936
1435
  }
937
- function asRecord(value) {
1436
+ function asRecord4(value) {
938
1437
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
939
1438
  }
940
- function asRecordList(value) {
941
- return Array.isArray(value) ? value.filter((row) => asRecord(row) !== null) : [];
1439
+ function asRecordList3(value) {
1440
+ return Array.isArray(value) ? value.filter((row) => asRecord4(row) !== null) : [];
1441
+ }
1442
+ function toInvoicePdf(value) {
1443
+ const pdf = asRecord4(value);
1444
+ if (pdf === null) return null;
1445
+ return {
1446
+ filename: asNullableString6(pdf["filename"]),
1447
+ size: asNullableNumber4(pdf["size"]),
1448
+ attached: pdf["attached"] === true
1449
+ };
1450
+ }
1451
+ function toEurEquivalent(value) {
1452
+ const eur = asRecord4(value);
1453
+ if (eur === null) return null;
1454
+ return {
1455
+ subtotal: asMoney2(eur["subtotal"]),
1456
+ taxAmount: asMoney2(eur["tax_amount"]),
1457
+ total: asMoney2(eur["total"]),
1458
+ rate: asString6(eur["rate"]),
1459
+ rateDate: asNullableString6(eur["rate_date"]),
1460
+ source: asString6(eur["source"], "ECB")
1461
+ };
942
1462
  }
943
1463
  function toInvoiceLines(providerPayload) {
944
- const invoice = asRecord(asRecord(providerPayload)?.["invoice"]);
945
- return asRecordList(invoice?.["invoice_lines_attributes"]).map((line) => ({
946
- description: asString3(line["description"]),
947
- quantity: asNullableNumber2(line["quantity"]),
1464
+ const invoice = asRecord4(asRecord4(providerPayload)?.["invoice"]);
1465
+ return asRecordList3(invoice?.["invoice_lines_attributes"]).map((line) => ({
1466
+ description: asString6(line["description"]),
1467
+ quantity: asNullableNumber4(line["quantity"]),
948
1468
  price: asMoney2(line["price"]),
949
- unit: asNullableNumber2(line["unit"]),
950
- taxes: asRecordList(line["taxes_attributes"]).map((tax) => ({
951
- name: asString3(tax["name"]),
952
- percent: asNullableNumber2(tax["percent"]),
953
- category: asNullableString3(tax["category"]),
954
- comment: asNullableString3(tax["comment"])
1469
+ unit: asNullableNumber4(line["unit"]),
1470
+ taxes: asRecordList3(line["taxes_attributes"]).map((tax) => ({
1471
+ name: asString6(tax["name"]),
1472
+ percent: asNullableNumber4(tax["percent"]),
1473
+ category: asNullableString6(tax["category"]),
1474
+ comment: asNullableString6(tax["comment"])
955
1475
  }))
956
1476
  }));
957
1477
  }
958
1478
  var ComplianceTransaction = class _ComplianceTransaction {
959
- constructor(id, companyId, complianceEnrollmentId, country, regime, transactionType, transactionTypeLabel, state, stateLabel, invoiceNumber, invoiceDate, dueDate, currency, subtotal, taxAmount, total, counterpartyName, counterpartyCountry, counterpartyVatNumber, isPaid, paidAt, providerInvoiceId, submissionError, reportedAt, taxReport, createdAt, updatedAt, counterpartyRegisterId = null, counterpartyAddress = null, counterpartyCity = null, counterpartyPostalcode = null, counterpartyEmail = null, buyerReference = null, documentType = ComplianceDocumentType.INVOICE, amendedNumber = null, amendedDate = null, providerState = null, invoiceLines = []) {
1479
+ constructor(id, companyId, complianceEnrollmentId, country, regime, transactionType, transactionTypeLabel, state, stateLabel, invoiceNumber, invoiceDate, dueDate, currency, subtotal, taxAmount, total, counterpartyName, counterpartyCountry, counterpartyVatNumber, isPaid, paidAt, providerInvoiceId, submissionError, reportedAt, taxReport, createdAt, updatedAt, counterpartyRegisterId = null, counterpartyAddress = null, counterpartyCity = null, counterpartyPostalcode = null, counterpartyEmail = null, buyerReference = null, documentType = ComplianceDocumentType.INVOICE, amendedNumber = null, amendedDate = null, providerState = null, invoiceLines = [], documentUrl = null, deliveryCheck = null, invoicePdf = null, internalReference = null, pdfUrl = null, buyerRejected = false, rejectionReason = null, eurEquivalent = null) {
960
1480
  this.id = id;
961
1481
  this.companyId = companyId;
962
1482
  this.complianceEnrollmentId = complianceEnrollmentId;
@@ -995,6 +1515,14 @@ var ComplianceTransaction = class _ComplianceTransaction {
995
1515
  this.amendedDate = amendedDate;
996
1516
  this.providerState = providerState;
997
1517
  this.invoiceLines = invoiceLines;
1518
+ this.documentUrl = documentUrl;
1519
+ this.deliveryCheck = deliveryCheck;
1520
+ this.invoicePdf = invoicePdf;
1521
+ this.internalReference = internalReference;
1522
+ this.pdfUrl = pdfUrl;
1523
+ this.buyerRejected = buyerRejected;
1524
+ this.rejectionReason = rejectionReason;
1525
+ this.eurEquivalent = eurEquivalent;
998
1526
  }
999
1527
  id;
1000
1528
  companyId;
@@ -1034,50 +1562,66 @@ var ComplianceTransaction = class _ComplianceTransaction {
1034
1562
  amendedDate;
1035
1563
  providerState;
1036
1564
  invoiceLines;
1565
+ documentUrl;
1566
+ deliveryCheck;
1567
+ invoicePdf;
1568
+ internalReference;
1569
+ pdfUrl;
1570
+ buyerRejected;
1571
+ rejectionReason;
1572
+ eurEquivalent;
1037
1573
  get isCreditNote() {
1038
1574
  return this.documentType === ComplianceDocumentType.CREDIT_NOTE;
1039
1575
  }
1040
1576
  static fromArray(data) {
1041
1577
  const taxReport = typeof data["tax_report"] === "object" && data["tax_report"] !== null && !Array.isArray(data["tax_report"]) ? ComplianceTaxReport.fromArray(data["tax_report"]) : void 0;
1042
1578
  return new _ComplianceTransaction(
1043
- asNumber3(data["id"]),
1044
- asNumber3(data["company_id"]),
1045
- asNumber3(data["compliance_enrollment_id"]),
1046
- asString3(data["country"]),
1047
- asString3(data["regime"]),
1579
+ asNumber4(data["id"]),
1580
+ asNumber4(data["company_id"]),
1581
+ asNumber4(data["compliance_enrollment_id"]),
1582
+ asString6(data["country"]),
1583
+ asString6(data["regime"]),
1048
1584
  toComplianceTransactionType(data["transaction_type"]),
1049
- asString3(data["transaction_type_label"]),
1585
+ asString6(data["transaction_type_label"]),
1050
1586
  toComplianceTransactionState(data["state"]),
1051
- asString3(data["state_label"]),
1052
- asString3(data["invoice_number"]),
1053
- asNullableString3(data["invoice_date"]),
1054
- asNullableString3(data["due_date"]),
1055
- asString3(data["currency"], "EUR"),
1587
+ asString6(data["state_label"]),
1588
+ asString6(data["invoice_number"]),
1589
+ asNullableString6(data["invoice_date"]),
1590
+ asNullableString6(data["due_date"]),
1591
+ asString6(data["currency"], "EUR"),
1056
1592
  asMoney2(data["subtotal"]),
1057
1593
  asMoney2(data["tax_amount"]),
1058
1594
  asMoney2(data["total"]),
1059
- asNullableString3(data["counterparty_name"]),
1060
- asNullableString3(data["counterparty_country"]),
1061
- asNullableString3(data["counterparty_vat_number"]),
1595
+ asNullableString6(data["counterparty_name"]),
1596
+ asNullableString6(data["counterparty_country"]),
1597
+ asNullableString6(data["counterparty_vat_number"]),
1062
1598
  data["is_paid"] === true,
1063
- asNullableString3(data["paid_at"]),
1064
- asNullableString3(data["provider_invoice_id"]),
1065
- asNullableString3(data["submission_error"]),
1066
- asNullableString3(data["reported_at"]),
1599
+ asNullableString6(data["paid_at"]),
1600
+ asNullableString6(data["provider_invoice_id"]),
1601
+ asNullableString6(data["submission_error"]),
1602
+ asNullableString6(data["reported_at"]),
1067
1603
  taxReport,
1068
- asNullableString3(data["created_at"]),
1069
- asNullableString3(data["updated_at"]),
1070
- asNullableString3(data["counterparty_register_id"]),
1071
- asNullableString3(data["counterparty_address"]),
1072
- asNullableString3(data["counterparty_city"]),
1073
- asNullableString3(data["counterparty_postalcode"]),
1074
- asNullableString3(data["counterparty_email"]),
1075
- asNullableString3(data["buyer_reference"]),
1604
+ asNullableString6(data["created_at"]),
1605
+ asNullableString6(data["updated_at"]),
1606
+ asNullableString6(data["counterparty_register_id"]),
1607
+ asNullableString6(data["counterparty_address"]),
1608
+ asNullableString6(data["counterparty_city"]),
1609
+ asNullableString6(data["counterparty_postalcode"]),
1610
+ asNullableString6(data["counterparty_email"]),
1611
+ asNullableString6(data["buyer_reference"]),
1076
1612
  data["document_type"] === void 0 || data["document_type"] === null ? ComplianceDocumentType.INVOICE : toComplianceDocumentType(data["document_type"]),
1077
- asNullableString3(data["amended_number"]),
1078
- asNullableString3(data["amended_date"]),
1079
- asNullableString3(data["provider_state"]),
1080
- toInvoiceLines(data["provider_payload"])
1613
+ asNullableString6(data["amended_number"]),
1614
+ asNullableString6(data["amended_date"]),
1615
+ asNullableString6(data["provider_state"]),
1616
+ toInvoiceLines(data["provider_payload"]),
1617
+ asNullableString6(data["document_url"]),
1618
+ asRecord4(data["delivery_check"]),
1619
+ toInvoicePdf(data["invoice_pdf"]),
1620
+ asNullableString6(data["internal_reference"]),
1621
+ asNullableString6(data["pdf_url"]),
1622
+ data["buyer_rejected"] === true,
1623
+ asNullableString6(data["rejection_reason"]),
1624
+ toEurEquivalent(data["eur_equivalent"])
1081
1625
  );
1082
1626
  }
1083
1627
  };
@@ -1126,48 +1670,21 @@ var ImportResult = class _ImportResult {
1126
1670
  }
1127
1671
  created;
1128
1672
  skippedDuplicates;
1129
- errors;
1130
- static fromArray(data) {
1131
- const errors = (Array.isArray(data["errors"]) ? data["errors"] : []).filter(
1132
- (row) => typeof row === "string"
1133
- );
1134
- const num = (value) => typeof value === "number" ? value : 0;
1135
- return new _ImportResult(num(data["created"]), num(data["skipped_duplicates"]), errors);
1136
- }
1137
- };
1138
-
1139
- // src/enums/PeppolLookupStatus.ts
1140
- var PeppolLookupStatus = {
1141
- REACHABLE: "reachable",
1142
- NOT_REACHABLE: "not_reachable",
1143
- PENDING: "pending",
1144
- UNKNOWN: "unknown"
1145
- };
1146
- var VALUES7 = Object.values(PeppolLookupStatus);
1147
- function toPeppolLookupStatus(value) {
1148
- if (typeof value === "string") {
1149
- const normalized = value.trim().toLowerCase();
1150
- if (VALUES7.includes(normalized)) {
1151
- return normalized;
1152
- }
1673
+ errors;
1674
+ static fromArray(data) {
1675
+ const errors = (Array.isArray(data["errors"]) ? data["errors"] : []).filter(
1676
+ (row) => typeof row === "string"
1677
+ );
1678
+ const num = (value) => typeof value === "number" ? value : 0;
1679
+ return new _ImportResult(num(data["created"]), num(data["skipped_duplicates"]), errors);
1153
1680
  }
1154
- return PeppolLookupStatus.UNKNOWN;
1155
- }
1156
- var DESCRIPTIONS7 = {
1157
- [PeppolLookupStatus.REACHABLE]: "The participant is registered on Peppol and can receive e-invoices.",
1158
- [PeppolLookupStatus.NOT_REACHABLE]: "The participant is not registered on Peppol.",
1159
- [PeppolLookupStatus.PENDING]: "The directory lookup is still resolving \u2014 ask again in a few seconds.",
1160
- [PeppolLookupStatus.UNKNOWN]: "Unknown lookup status."
1161
1681
  };
1162
- function describePeppolLookupStatus(status) {
1163
- return DESCRIPTIONS7[status];
1164
- }
1165
1682
 
1166
1683
  // src/dto/PeppolLookupResult.ts
1167
- function asString4(value, fallback = "") {
1684
+ function asString7(value, fallback = "") {
1168
1685
  return typeof value === "string" ? value : fallback;
1169
1686
  }
1170
- function asNullableString4(value) {
1687
+ function asNullableString7(value) {
1171
1688
  return typeof value === "string" ? value : null;
1172
1689
  }
1173
1690
  var PeppolLookupResult = class _PeppolLookupResult {
@@ -1198,21 +1715,21 @@ var PeppolLookupResult = class _PeppolLookupResult {
1198
1715
  return new _PeppolLookupResult(
1199
1716
  toPeppolLookupStatus(data["status"]),
1200
1717
  typeof data["reachable"] === "boolean" ? data["reachable"] : null,
1201
- asString4(data["country"]),
1202
- asString4(data["scheme"]),
1203
- asString4(data["id"]),
1718
+ asString7(data["country"]),
1719
+ asString7(data["scheme"]),
1720
+ asString7(data["id"]),
1204
1721
  documentTypes,
1205
- asNullableString4(data["transport_type_code"]),
1206
- asNullableString4(data["checked_at"])
1722
+ asNullableString7(data["transport_type_code"]),
1723
+ asNullableString7(data["checked_at"])
1207
1724
  );
1208
1725
  }
1209
1726
  };
1210
1727
 
1211
1728
  // src/dto/RegistryCompany.ts
1212
- function asString5(value, fallback = "") {
1729
+ function asString8(value, fallback = "") {
1213
1730
  return typeof value === "string" ? value : fallback;
1214
1731
  }
1215
- function asNullableString5(value) {
1732
+ function asNullableString8(value) {
1216
1733
  return typeof value === "string" ? value : null;
1217
1734
  }
1218
1735
  var RegistryCompany = class _RegistryCompany {
@@ -1244,27 +1761,27 @@ var RegistryCompany = class _RegistryCompany {
1244
1761
  country;
1245
1762
  static fromArray(data) {
1246
1763
  return new _RegistryCompany(
1247
- asString5(data["org_number"]),
1248
- asString5(data["company_name"]),
1249
- asNullableString5(data["organisation_form"]),
1764
+ asString8(data["org_number"]),
1765
+ asString8(data["company_name"]),
1766
+ asNullableString8(data["organisation_form"]),
1250
1767
  data["vat_registered"] === true,
1251
- asNullableString5(data["vat_number"]),
1768
+ asNullableString8(data["vat_number"]),
1252
1769
  data["enterprise_register"] === true,
1253
1770
  data["bankrupt"] === true,
1254
1771
  data["under_liquidation"] === true,
1255
- asNullableString5(data["address"]),
1256
- asNullableString5(data["postalcode"]),
1257
- asNullableString5(data["city"]),
1258
- asString5(data["country"])
1772
+ asNullableString8(data["address"]),
1773
+ asNullableString8(data["postalcode"]),
1774
+ asNullableString8(data["city"]),
1775
+ asString8(data["country"])
1259
1776
  );
1260
1777
  }
1261
1778
  };
1262
1779
 
1263
1780
  // src/dto/RevenueStatistics.ts
1264
- function asString6(value, fallback = "") {
1781
+ function asString9(value, fallback = "") {
1265
1782
  return typeof value === "string" ? value : fallback;
1266
1783
  }
1267
- function asNumber4(value) {
1784
+ function asNumber5(value) {
1268
1785
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
1269
1786
  }
1270
1787
  function asMoney3(value) {
@@ -1276,7 +1793,7 @@ function asRecords(value) {
1276
1793
  }
1277
1794
  function toTotals(data) {
1278
1795
  return {
1279
- count: asNumber4(data["count"]),
1796
+ count: asNumber5(data["count"]),
1280
1797
  subtotal: asMoney3(data["subtotal"]),
1281
1798
  taxAmount: asMoney3(data["tax_amount"]),
1282
1799
  total: asMoney3(data["total"])
@@ -1313,35 +1830,35 @@ var RevenueStatistics = class _RevenueStatistics {
1313
1830
  typeof data["totals"] === "object" && data["totals"] !== null ? data["totals"] : {}
1314
1831
  );
1315
1832
  const timeSeries = asRecords(data["time_series"]).map((row) => ({
1316
- bucket: asString6(row["bucket"]),
1833
+ bucket: asString9(row["bucket"]),
1317
1834
  ...toTotals(row)
1318
1835
  }));
1319
1836
  const byType = asRecords(data["by_type"]).map((row) => ({
1320
- type: asString6(row["type"]),
1321
- typeLabel: asString6(row["type_label"]),
1322
- count: asNumber4(row["count"]),
1837
+ type: asString9(row["type"]),
1838
+ typeLabel: asString9(row["type_label"]),
1839
+ count: asNumber5(row["count"]),
1323
1840
  total: asMoney3(row["total"])
1324
1841
  }));
1325
1842
  const byCountry = asRecords(data["by_country"]).map((row) => ({
1326
- country: asString6(row["country"], "unknown"),
1327
- count: asNumber4(row["count"]),
1843
+ country: asString9(row["country"], "unknown"),
1844
+ count: asNumber5(row["count"]),
1328
1845
  total: asMoney3(row["total"])
1329
1846
  }));
1330
1847
  const byState = asRecords(data["by_state"]).map((row) => ({
1331
- state: asString6(row["state"]),
1332
- stateLabel: asString6(row["state_label"]),
1333
- count: asNumber4(row["count"]),
1848
+ state: asString9(row["state"]),
1849
+ stateLabel: asString9(row["state_label"]),
1850
+ count: asNumber5(row["count"]),
1334
1851
  total: asMoney3(row["total"])
1335
1852
  }));
1336
1853
  const byCurrency = asRecords(data["by_currency"]).map((row) => ({
1337
- currency: asString6(row["currency"]),
1854
+ currency: asString9(row["currency"]),
1338
1855
  ...toTotals(row)
1339
1856
  }));
1340
1857
  return new _RevenueStatistics(
1341
- asString6(range["date_from"]) || void 0,
1342
- asString6(range["date_to"]) || void 0,
1343
- asString6(range["interval"]) || void 0,
1344
- asString6(data["primary_currency"], "EUR"),
1858
+ asString9(range["date_from"]) || void 0,
1859
+ asString9(range["date_to"]) || void 0,
1860
+ asString9(range["interval"]) || void 0,
1861
+ asString9(data["primary_currency"], "EUR"),
1345
1862
  data["is_multi_currency"] === true,
1346
1863
  totals,
1347
1864
  timeSeries,
@@ -1354,10 +1871,10 @@ var RevenueStatistics = class _RevenueStatistics {
1354
1871
  };
1355
1872
 
1356
1873
  // src/dto/SireneLookupResult.ts
1357
- function asString7(value, fallback = "") {
1874
+ function asString10(value, fallback = "") {
1358
1875
  return typeof value === "string" ? value : fallback;
1359
1876
  }
1360
- function asNullableString6(value) {
1877
+ function asNullableString9(value) {
1361
1878
  return typeof value === "string" ? value : null;
1362
1879
  }
1363
1880
  var SireneLookupResult = class _SireneLookupResult {
@@ -1383,24 +1900,24 @@ var SireneLookupResult = class _SireneLookupResult {
1383
1900
  postalcode;
1384
1901
  static fromArray(data) {
1385
1902
  return new _SireneLookupResult(
1386
- asString7(data["company_name"]),
1387
- asString7(data["siren"]),
1388
- asNullableString6(data["siret"]),
1389
- asNullableString6(data["naf_code"]),
1390
- asNullableString6(data["naf_full"]),
1391
- asNullableString6(data["enterprise_size"]),
1392
- asNullableString6(data["address"]),
1393
- asNullableString6(data["city"]),
1394
- asNullableString6(data["postalcode"])
1903
+ asString10(data["company_name"]),
1904
+ asString10(data["siren"]),
1905
+ asNullableString9(data["siret"]),
1906
+ asNullableString9(data["naf_code"]),
1907
+ asNullableString9(data["naf_full"]),
1908
+ asNullableString9(data["enterprise_size"]),
1909
+ asNullableString9(data["address"]),
1910
+ asNullableString9(data["city"]),
1911
+ asNullableString9(data["postalcode"])
1395
1912
  );
1396
1913
  }
1397
1914
  };
1398
1915
 
1399
1916
  // src/dto/VatRates.ts
1400
- function asString8(value, fallback = "") {
1917
+ function asString11(value, fallback = "") {
1401
1918
  return typeof value === "string" ? value : fallback;
1402
1919
  }
1403
- function asNumber5(value) {
1920
+ function asNumber6(value) {
1404
1921
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
1405
1922
  }
1406
1923
  var VatRates = class _VatRates {
@@ -1414,13 +1931,13 @@ var VatRates = class _VatRates {
1414
1931
  rates;
1415
1932
  static fromArray(data) {
1416
1933
  const rates = (Array.isArray(data["rates"]) ? data["rates"] : []).filter((row) => typeof row === "object" && row !== null).map((row) => ({
1417
- percent: asNumber5(row["percent"]),
1418
- category: asString8(row["category"]),
1419
- labelKey: asString8(row["label_key"]),
1420
- taxName: asString8(row["tax_name"]),
1934
+ percent: asNumber6(row["percent"]),
1935
+ category: asString11(row["category"]),
1936
+ labelKey: asString11(row["label_key"]),
1937
+ taxName: asString11(row["tax_name"]),
1421
1938
  requiresVatex: row["requires_vatex"] === true
1422
1939
  }));
1423
- return new _VatRates(asString8(data["country"]), asString8(data["currency"], "EUR"), rates);
1940
+ return new _VatRates(asString11(data["country"]), asString11(data["currency"], "EUR"), rates);
1424
1941
  }
1425
1942
  };
1426
1943
 
@@ -1474,15 +1991,39 @@ var NorwegianOrgNumber = {
1474
1991
  }
1475
1992
  };
1476
1993
 
1994
+ // src/enums/ComplianceTaxCategory.ts
1995
+ var ComplianceTaxCategory = {
1996
+ STANDARD: "S",
1997
+ REDUCED: "AA",
1998
+ SUPER_REDUCED: "AAA",
1999
+ INTRA_COMMUNITY: "K",
2000
+ EXPORT: "G",
2001
+ REVERSE_CHARGE: "AE",
2002
+ EXEMPT: "E",
2003
+ ZERO: "Z"
2004
+ };
2005
+ var CODES = Object.values(ComplianceTaxCategory);
2006
+ function parseComplianceTaxCategory(value) {
2007
+ if (typeof value !== "string") return null;
2008
+ const code = value.trim().toUpperCase();
2009
+ return CODES.includes(code) ? code : null;
2010
+ }
2011
+ var COMPLIANCE_TAX_CATEGORY_CODES = CODES;
2012
+
1477
2013
  // src/endpoints/EReportingEndpoint.ts
1478
2014
  var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
1479
2015
  var NAF_CODE_REGEX = /^\d{2}$/;
1480
2016
  var COUNTRY_REGEX = /^[A-Za-z]{2}$/;
1481
2017
  var PEPPOL_SCHEME_REGEX = /^\d{4}$/;
2018
+ var INVOICE_PDF_MAX_BYTES = 10 * 1024 * 1024;
2019
+ var INVOICE_PDF_MAGIC = [37, 80, 68, 70, 45];
1482
2020
  var INTERVALS = ["day", "week", "month"];
1483
2021
  var SERVICES = ["e_reporting", "e_invoicing"];
1484
2022
  var ENTERPRISE_SIZES = ["micro", "pme", "eti", "ge"];
1485
2023
  var TYPE_OPERATIONS = ["services", "goods", "mixed"];
2024
+ var NOTIFICATION_LANGUAGES = ["de", "en"];
2025
+ var NOTIFICATION_DIGESTS = ["off", "daily", "weekly"];
2026
+ var NOTIFICATION_MAX_RECIPIENTS = 5;
1486
2027
  var TRANSACTION_TYPES = [
1487
2028
  "b2c_outbound",
1488
2029
  "b2b_domestic_outbound",
@@ -1653,6 +2194,19 @@ var EReportingEndpoint = class {
1653
2194
  const data = await this.parseJsonResponse(response);
1654
2195
  return PeppolLookupResult.fromArray(data);
1655
2196
  }
2197
+ /**
2198
+ * What to do with an invoice to this buyer: peppolLookup() turned into an action,
2199
+ * e.g. in a shop checkout when a business buyer enters its number.
2200
+ *
2201
+ * - `einvoice`: create the transaction, Taxora sends the e-invoice;
2202
+ * - `regular_invoice`: the buyer is not on the network — send your regular invoice
2203
+ * yourself (a transaction would end as not_required and is not billed);
2204
+ * - `einvoice_pending`: no definite answer yet — create the transaction anyway,
2205
+ * Taxora asks the directory again before sending.
2206
+ */
2207
+ async decideDelivery(country, id, scheme) {
2208
+ return ComplianceDeliveryDecision.fromLookup(await this.peppolLookup(country, id, scheme));
2209
+ }
1656
2210
  // ---------------------------------------------------------------------
1657
2211
  // Transactions
1658
2212
  // ---------------------------------------------------------------------
@@ -1677,6 +2231,9 @@ var EReportingEndpoint = class {
1677
2231
  this.validateInvoiceLines(input.invoiceLines);
1678
2232
  this.validateOptionalCodes(input);
1679
2233
  this.validateBuyerFields(input);
2234
+ this.validateInternalReference(input.internalReference);
2235
+ this.validateInvoicePdfFilename(input);
2236
+ this.validateDocument(input);
1680
2237
  const body = {
1681
2238
  compliance_enrollment_id: input.complianceEnrollmentId,
1682
2239
  transaction_type: input.transactionType,
@@ -1692,23 +2249,104 @@ var EReportingEndpoint = class {
1692
2249
  const data = await this.parseJsonResponse(response);
1693
2250
  return ComplianceTransaction.fromArray(data);
1694
2251
  }
1695
- /** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type / enrollment / country. */
1696
- async listTransactions(filters = {}) {
1697
- if (filters.complianceEnrollmentId !== void 0) {
1698
- this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
2252
+ /**
2253
+ * Issue a credit note for an invoice you sent through Taxora. Everything that must
2254
+ * match the corrected invoice is taken from `original`: enrollment, transaction type,
2255
+ * currency, buyer (name, register id, address, e-mail, VAT number, buyer reference) —
2256
+ * and the reference to the corrected invoice (number and date) is set for you.
2257
+ *
2258
+ * Without invoiceLines and amounts it credits the whole invoice: lines and amounts are
2259
+ * taken over as they are (the document type, not the sign, makes it a credit note).
2260
+ * For a partial credit pass invoiceLines, subtotal, taxAmount and total together.
2261
+ *
2262
+ * Not possible for an invoice that was never sent to the buyer (state not_required)
2263
+ * or for a credit note. A buyer rejected the invoice? Correct it with a credit note and
2264
+ * send a new invoice (`original.buyerRejected`).
2265
+ */
2266
+ async createCreditNote(original, input) {
2267
+ if (original.documentType === ComplianceDocumentType.CREDIT_NOTE) {
2268
+ throw new TaxoraException("A credit note cannot be credited again; issue a new invoice instead.");
1699
2269
  }
1700
- if (filters.country !== void 0 && !COUNTRY_REGEX.test(filters.country)) {
1701
- throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
2270
+ if (original.state === ComplianceTransactionState.NOT_REQUIRED) {
2271
+ throw new TaxoraException(
2272
+ "The invoice was never sent as an e-invoice (the buyer is not on the network), so there is nothing to credit; correct your regular invoice instead."
2273
+ );
1702
2274
  }
1703
- const params = new URLSearchParams();
1704
- if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
1705
- if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
1706
- if (filters.state !== void 0) params.set("state", filters.state);
1707
- if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
1708
- if (filters.complianceEnrollmentId !== void 0) {
1709
- params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
2275
+ if (original.invoiceNumber === "") {
2276
+ throw new TaxoraException("The original transaction has no invoice number.");
1710
2277
  }
1711
- if (filters.country !== void 0) params.set("country", filters.country.toUpperCase());
2278
+ if (original.transactionType === "unknown") {
2279
+ throw new TaxoraException("The original transaction has an unknown transaction type.");
2280
+ }
2281
+ const partial = [input.invoiceLines, input.subtotal, input.taxAmount, input.total];
2282
+ if (![0, 4].includes(partial.filter((value) => value !== void 0).length)) {
2283
+ throw new TaxoraException("For a partial credit pass invoiceLines, subtotal, taxAmount and total together.");
2284
+ }
2285
+ let invoiceLines;
2286
+ let subtotal = input.subtotal;
2287
+ let taxAmount = input.taxAmount;
2288
+ let total = input.total;
2289
+ if (input.invoiceLines !== void 0) {
2290
+ invoiceLines = input.invoiceLines;
2291
+ } else {
2292
+ invoiceLines = original.invoiceLines.map((line) => ({
2293
+ description: line.description,
2294
+ quantity: line.quantity ?? 0,
2295
+ price: Number(line.price),
2296
+ ...line.unit !== null ? { unit: line.unit } : {},
2297
+ taxes: line.taxes.map((tax) => ({
2298
+ name: tax.name,
2299
+ percent: tax.percent ?? 0,
2300
+ category: this.storedTaxCategory(tax.category),
2301
+ ...tax.comment !== null ? { comment: tax.comment } : {}
2302
+ }))
2303
+ }));
2304
+ if (invoiceLines.length === 0) {
2305
+ throw new TaxoraException(
2306
+ "The original transaction carries no invoice lines; pass invoiceLines and the amounts explicitly."
2307
+ );
2308
+ }
2309
+ subtotal = Number(original.subtotal);
2310
+ taxAmount = Number(original.taxAmount);
2311
+ total = Number(original.total);
2312
+ }
2313
+ const buyer = Object.fromEntries(
2314
+ Object.entries({
2315
+ counterpartyName: original.counterpartyName,
2316
+ counterpartyCountry: original.counterpartyCountry,
2317
+ counterpartyVatNumber: original.counterpartyVatNumber,
2318
+ counterpartyRegisterId: original.counterpartyRegisterId,
2319
+ counterpartyAddress: original.counterpartyAddress,
2320
+ counterpartyCity: original.counterpartyCity,
2321
+ counterpartyPostalcode: original.counterpartyPostalcode,
2322
+ counterpartyEmail: original.counterpartyEmail,
2323
+ buyerReference: original.buyerReference
2324
+ }).filter(([, value]) => value !== null && value !== "")
2325
+ );
2326
+ return this.createTransaction({
2327
+ complianceEnrollmentId: original.complianceEnrollmentId,
2328
+ transactionType: original.transactionType,
2329
+ invoiceNumber: input.creditNoteNumber,
2330
+ invoiceDate: input.invoiceDate ?? /* @__PURE__ */ new Date(),
2331
+ subtotal: subtotal ?? 0,
2332
+ total: total ?? 0,
2333
+ invoiceLines,
2334
+ currency: original.currency,
2335
+ ...taxAmount !== void 0 ? { taxAmount } : {},
2336
+ ...buyer,
2337
+ isCreditNote: true,
2338
+ amendedNumber: original.invoiceNumber,
2339
+ ...original.invoiceDate !== null ? { amendedDate: original.invoiceDate } : {},
2340
+ ...input.extraInfo !== void 0 ? { extraInfo: input.extraInfo } : {},
2341
+ ...input.internalReference !== void 0 ? { internalReference: input.internalReference } : {},
2342
+ ...input.submitNow !== void 0 ? { submitNow: input.submitNow } : {},
2343
+ ...input.invoicePdf !== void 0 ? { invoicePdf: input.invoicePdf } : {},
2344
+ ...input.invoicePdfFilename !== void 0 ? { invoicePdfFilename: input.invoicePdfFilename } : {}
2345
+ });
2346
+ }
2347
+ /** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type / enrollment / country / search / internal reference / provider state. */
2348
+ async listTransactions(filters = {}) {
2349
+ const params = this.buildTransactionFilterParams(filters);
1712
2350
  if (filters.page !== void 0) params.set("page", String(filters.page));
1713
2351
  if (filters.perPage !== void 0) params.set("per_page", String(filters.perPage));
1714
2352
  const queryString = params.toString();
@@ -1716,6 +2354,27 @@ var EReportingEndpoint = class {
1716
2354
  const response = await this.sendRequest("GET", url);
1717
2355
  return ComplianceTransactionPage.fromResponse(await this.parseJsonResponse(response));
1718
2356
  }
2357
+ /**
2358
+ * All transactions matching the filters as raw CSV for bookkeeping
2359
+ * (semicolon-separated) — the same filters as listTransactions() without
2360
+ * pagination. Columns: invoice_number, internal_reference, invoice_date,
2361
+ * due_date, country, transaction_type, document_type, state, delivery_state,
2362
+ * buyer_rejected, rejection_reason, counterparty_name, counterparty_country,
2363
+ * counterparty_vat_number, currency, subtotal, tax_amount, total, eur_rate,
2364
+ * eur_rate_date, eur_subtotal, eur_tax_amount, eur_total, provider_invoice_id,
2365
+ * created_at. The eur_* columns hold the ECB reference conversion for non-EUR
2366
+ * invoices (empty for EUR). Max 50,000 rows server-side. The body is returned
2367
+ * verbatim, a UTF-8 BOM included when the API sends one.
2368
+ */
2369
+ async exportTransactionsCsv(filters = {}) {
2370
+ const queryString = this.buildTransactionFilterParams(filters).toString();
2371
+ const url = `${this.baseUrl}/compliance/transactions/export.csv${queryString ? `?${queryString}` : ""}`;
2372
+ const response = await this.sendDownloadRequest(url, "text/csv, application/json");
2373
+ if (!response.ok) {
2374
+ await this.parseJsonResponse(response);
2375
+ }
2376
+ return new TextDecoder("utf-8", { ignoreBOM: true }).decode(await response.arrayBuffer());
2377
+ }
1719
2378
  /** Get a single transaction by id (includes its tax report when one exists). */
1720
2379
  async getTransaction(id) {
1721
2380
  this.validateId(id, "id");
@@ -1731,6 +2390,8 @@ var EReportingEndpoint = class {
1731
2390
  if (input.invoiceLines !== void 0) this.validateInvoiceLines(input.invoiceLines);
1732
2391
  this.validateOptionalCodes(input);
1733
2392
  this.validateBuyerFields(input);
2393
+ this.validateInternalReference(input.internalReference);
2394
+ this.validateInvoicePdfFilename(input);
1734
2395
  const body = {};
1735
2396
  if (input.transactionType !== void 0) body["transaction_type"] = input.transactionType;
1736
2397
  if (input.invoiceNumber !== void 0) body["invoice_number"] = input.invoiceNumber;
@@ -1765,6 +2426,47 @@ var EReportingEndpoint = class {
1765
2426
  const data = await this.parseJsonResponse(response);
1766
2427
  return ComplianceTransaction.fromArray(data);
1767
2428
  }
2429
+ /**
2430
+ * Download the legal e-invoice document of a sent transaction (e-invoicing,
2431
+ * e.g. the EHF/UBL XML for Norway). Available once `documentUrl` on the
2432
+ * transaction is set.
2433
+ *
2434
+ * Errors: 404 (HttpException) for an unknown transaction; 409 (HttpException)
2435
+ * when no document exists (yet) — e.g. e-reporting, not sent, or the network
2436
+ * has not produced it yet (try again in a few minutes); 502 on provider failure.
2437
+ */
2438
+ async getTransactionDocument(id) {
2439
+ this.validateId(id, "id");
2440
+ const response = await this.sendDownloadRequest(`${this.baseUrl}/compliance/transactions/${id}/document`, "*/*");
2441
+ if (!response.ok) {
2442
+ await this.parseJsonResponse(response);
2443
+ }
2444
+ return {
2445
+ content: new Uint8Array(await response.arrayBuffer()),
2446
+ contentType: response.headers.get("content-type") ?? "application/octet-stream",
2447
+ filename: this.filenameFromDisposition(response.headers.get("content-disposition"))
2448
+ };
2449
+ }
2450
+ /**
2451
+ * Download the invoice as a readable PDF — the seller's attached PDF, or the
2452
+ * provider's rendering of the e-invoice. Available once `pdfUrl` on the
2453
+ * transaction is set.
2454
+ *
2455
+ * Errors: 404 (HttpException) for an unknown transaction; 409 (HttpException)
2456
+ * when no PDF exists (yet); 502 on provider failure.
2457
+ */
2458
+ async getTransactionPdf(id) {
2459
+ this.validateId(id, "id");
2460
+ const response = await this.sendDownloadRequest(`${this.baseUrl}/compliance/transactions/${id}/pdf`, "*/*");
2461
+ if (!response.ok) {
2462
+ await this.parseJsonResponse(response);
2463
+ }
2464
+ return {
2465
+ content: new Uint8Array(await response.arrayBuffer()),
2466
+ contentType: response.headers.get("content-type") ?? "application/octet-stream",
2467
+ filename: this.filenameFromDisposition(response.headers.get("content-disposition"))
2468
+ };
2469
+ }
1768
2470
  /**
1769
2471
  * Bulk-import transactions from a semicolon-separated CSV (one row per invoice
1770
2472
  * line; rows sharing an invoice_number are grouped into one transaction).
@@ -1796,6 +2498,9 @@ var EReportingEndpoint = class {
1796
2498
  * months and a monthly interval.
1797
2499
  */
1798
2500
  async getRevenueStatistics(filters = {}) {
2501
+ if (filters.complianceEnrollmentId !== void 0) {
2502
+ this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
2503
+ }
1799
2504
  if (filters.interval !== void 0 && !INTERVALS.includes(filters.interval)) {
1800
2505
  throw new TaxoraException(
1801
2506
  `Invalid statistics interval "${String(filters.interval)}". Expected one of: ${INTERVALS.join(", ")}.`
@@ -1807,12 +2512,42 @@ var EReportingEndpoint = class {
1807
2512
  if (filters.interval !== void 0) params.set("interval", filters.interval);
1808
2513
  if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
1809
2514
  if (filters.state !== void 0) params.set("state", filters.state);
2515
+ if (filters.complianceEnrollmentId !== void 0) {
2516
+ params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
2517
+ }
1810
2518
  const queryString = params.toString();
1811
2519
  const url = `${this.baseUrl}/compliance/revenue-statistics${queryString ? `?${queryString}` : ""}`;
1812
2520
  const response = await this.sendRequest("GET", url);
1813
2521
  const data = await this.parseJsonResponse(response);
1814
2522
  return RevenueStatistics.fromArray(data);
1815
2523
  }
2524
+ /**
2525
+ * Net and VAT per month, currency and tax rate as raw CSV (semicolon-separated,
2526
+ * computed from the invoice lines) — for reconciling the reported figures with
2527
+ * your VAT return. Same filters as getRevenueStatistics() (without
2528
+ * `interval`); defaults to the last 12 months. The body is returned verbatim,
2529
+ * a UTF-8 BOM included when the API sends one.
2530
+ */
2531
+ async exportRevenueTaxRatesCsv(filters = {}) {
2532
+ if (filters.complianceEnrollmentId !== void 0) {
2533
+ this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
2534
+ }
2535
+ const params = new URLSearchParams();
2536
+ if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
2537
+ if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
2538
+ if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
2539
+ if (filters.state !== void 0) params.set("state", filters.state);
2540
+ if (filters.complianceEnrollmentId !== void 0) {
2541
+ params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
2542
+ }
2543
+ const queryString = params.toString();
2544
+ const url = `${this.baseUrl}/compliance/revenue-statistics/tax-rates.csv${queryString ? `?${queryString}` : ""}`;
2545
+ const response = await this.sendDownloadRequest(url, "text/csv, application/json");
2546
+ if (!response.ok) {
2547
+ await this.parseJsonResponse(response);
2548
+ }
2549
+ return new TextDecoder("utf-8", { ignoreBOM: true }).decode(await response.arrayBuffer());
2550
+ }
1816
2551
  /**
1817
2552
  * The canonical VAT rates & DGFiP tax categories for a reporting country
1818
2553
  * (the seller's reporting-country rates, not the counterparty's). Falls back
@@ -1827,12 +2562,19 @@ var EReportingEndpoint = class {
1827
2562
  const data = await this.parseJsonResponse(response);
1828
2563
  return VatRates.fromArray(data);
1829
2564
  }
1830
- /** Paginated list of the company's tax reports (newest first), optionally filtered by state. */
1831
- async listTaxReports(page = 1, perPage = 25, state) {
2565
+ /**
2566
+ * Paginated list of the company's tax reports (newest first), optionally
2567
+ * filtered by state and by country (2-letter ISO code, case-insensitive).
2568
+ */
2569
+ async listTaxReports(page = 1, perPage = 25, state, country) {
2570
+ if (country !== void 0 && !COUNTRY_REGEX.test(country)) {
2571
+ throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
2572
+ }
1832
2573
  const params = new URLSearchParams();
1833
2574
  params.set("page", String(page));
1834
2575
  params.set("per_page", String(perPage));
1835
2576
  if (state !== void 0) params.set("state", state);
2577
+ if (country !== void 0) params.set("country", country.toUpperCase());
1836
2578
  const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/tax-reports?${params.toString()}`);
1837
2579
  return ComplianceTaxReportPage.fromResponse(await this.parseJsonResponse(response));
1838
2580
  }
@@ -1874,8 +2616,177 @@ var EReportingEndpoint = class {
1874
2616
  await this.parseJsonResponse(response);
1875
2617
  }
1876
2618
  // ---------------------------------------------------------------------
2619
+ // Self-service plans (Stripe) — not gated by compliance access
2620
+ // ---------------------------------------------------------------------
2621
+ /**
2622
+ * Bookable compliance plans per country (net EUR prices), every country
2623
+ * Taxora reaches, and which services the company has unlocked. Works for
2624
+ * companies without compliance access.
2625
+ */
2626
+ async getOfferings() {
2627
+ const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/offerings`);
2628
+ return ComplianceOfferings.fromArray(await this.parseJsonResponse(response));
2629
+ }
2630
+ /**
2631
+ * Start a Stripe Checkout for a plan (see getOfferings() — `checkoutAvailable`).
2632
+ * Open the returned `url` in the browser. After payment Stripe redirects to
2633
+ * the Taxora portal, which activates the plan; call confirmCheckout() with the
2634
+ * session id to activate it from your side (idempotent).
2635
+ *
2636
+ * Errors: 422 (ValidationException) for an unknown or contact-only plan or a
2637
+ * plan that is already active; 502 (HttpException) on a Stripe error.
2638
+ */
2639
+ async createCheckout(country, plan) {
2640
+ if (!COUNTRY_REGEX.test(country)) {
2641
+ throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
2642
+ }
2643
+ if (plan.trim() === "" || plan.length > 30) {
2644
+ throw new TaxoraException("plan must be a non-empty plan key of at most 30 characters.");
2645
+ }
2646
+ const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/checkout`, {
2647
+ country: country.toUpperCase(),
2648
+ plan
2649
+ });
2650
+ const data = await this.parseJsonResponse(response);
2651
+ return { id: String(data["id"] ?? ""), url: String(data["url"] ?? "") };
2652
+ }
2653
+ /** Confirm a finished Stripe Checkout and activate its plan. Idempotent. */
2654
+ async confirmCheckout(sessionId) {
2655
+ if (sessionId.trim() === "" || sessionId.length > 255) {
2656
+ throw new TaxoraException("sessionId must be a non-empty string of at most 255 characters.");
2657
+ }
2658
+ const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/checkout/confirm`, {
2659
+ session_id: sessionId
2660
+ });
2661
+ const data = await this.parseJsonResponse(response);
2662
+ const str = (value) => typeof value === "string" ? value : null;
2663
+ return {
2664
+ active: data["active"] === true,
2665
+ country: str(data["country"]),
2666
+ service: str(data["service"]),
2667
+ plan: str(data["plan"]),
2668
+ status: str(data["status"])
2669
+ };
2670
+ }
2671
+ /**
2672
+ * URL of the Stripe billing portal for the company's compliance plans
2673
+ * (invoices, payment method, cancellation). The portal returns to the Taxora
2674
+ * portal. 404 (HttpException) when no plan was bought via Stripe.
2675
+ */
2676
+ async createBillingPortalSession() {
2677
+ const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/billing-portal`);
2678
+ const data = await this.parseJsonResponse(response);
2679
+ return String(data["url"] ?? "");
2680
+ }
2681
+ // ---------------------------------------------------------------------
2682
+ // Notification emails
2683
+ // ---------------------------------------------------------------------
2684
+ /**
2685
+ * The company's opt-in e-reporting / e-invoicing emails: recipients, language,
2686
+ * the "buyer not on Peppol" email and the digest. Readable by every member.
2687
+ */
2688
+ async getNotificationSettings() {
2689
+ const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/notification-settings`);
2690
+ return this.toNotificationSettings(await this.parseJsonResponse(response));
2691
+ }
2692
+ /**
2693
+ * Change the company's e-reporting / e-invoicing emails; only the provided
2694
+ * fields change. `notOnPeppol` only reports invoices held back after it was
2695
+ * switched on.
2696
+ *
2697
+ * Errors: 422 (ValidationException) when an email is switched on without any
2698
+ * recipient; 403 (HttpException, body code `admin_only` or `read_only_role`)
2699
+ * for users who are not company admins.
2700
+ */
2701
+ async updateNotificationSettings(input) {
2702
+ const body = {};
2703
+ if (input.recipients !== void 0) {
2704
+ if (!Array.isArray(input.recipients)) {
2705
+ throw new TaxoraException("recipients must be an array of email addresses.");
2706
+ }
2707
+ if (input.recipients.length > NOTIFICATION_MAX_RECIPIENTS) {
2708
+ throw new TaxoraException(`recipients must not contain more than ${NOTIFICATION_MAX_RECIPIENTS} entries.`);
2709
+ }
2710
+ body["recipients"] = input.recipients.map((recipient, index) => {
2711
+ const email = typeof recipient === "string" ? recipient.trim() : recipient;
2712
+ this.validateEmail(email, `recipients[${index}]`);
2713
+ if (email.length > 191) {
2714
+ throw new TaxoraException(`recipients[${index}] must not exceed 191 characters.`);
2715
+ }
2716
+ return email;
2717
+ });
2718
+ }
2719
+ if (input.language !== void 0) {
2720
+ if (!NOTIFICATION_LANGUAGES.includes(input.language)) {
2721
+ throw new TaxoraException(
2722
+ `Invalid language "${String(input.language)}". Expected one of: ${NOTIFICATION_LANGUAGES.join(", ")}.`
2723
+ );
2724
+ }
2725
+ body["language"] = input.language;
2726
+ }
2727
+ if (input.notOnPeppol !== void 0) {
2728
+ if (typeof input.notOnPeppol !== "boolean") {
2729
+ throw new TaxoraException("notOnPeppol must be a boolean.");
2730
+ }
2731
+ body["not_on_peppol"] = input.notOnPeppol;
2732
+ }
2733
+ if (input.digest !== void 0) {
2734
+ if (!NOTIFICATION_DIGESTS.includes(input.digest)) {
2735
+ throw new TaxoraException(
2736
+ `Invalid digest "${String(input.digest)}". Expected one of: ${NOTIFICATION_DIGESTS.join(", ")}.`
2737
+ );
2738
+ }
2739
+ body["digest"] = input.digest;
2740
+ }
2741
+ if (Object.keys(body).length === 0) {
2742
+ throw new TaxoraException("updateNotificationSettings requires at least one field to update.");
2743
+ }
2744
+ const response = await this.sendRequest("PUT", `${this.baseUrl}/compliance/notification-settings`, body);
2745
+ return this.toNotificationSettings(await this.parseJsonResponse(response));
2746
+ }
2747
+ toNotificationSettings(data) {
2748
+ const recipients = Array.isArray(data["recipients"]) ? data["recipients"].filter((r) => typeof r === "string" && r !== "") : [];
2749
+ const digest = data["digest"];
2750
+ return {
2751
+ recipients,
2752
+ language: data["language"] === "en" ? "en" : "de",
2753
+ notOnPeppol: data["not_on_peppol"] === true,
2754
+ digest: digest === "daily" || digest === "weekly" ? digest : "off",
2755
+ digestSentAt: typeof data["digest_sent_at"] === "string" ? data["digest_sent_at"] : null
2756
+ };
2757
+ }
2758
+ // ---------------------------------------------------------------------
1877
2759
  // Input validation (fail fast, before any request is sent)
1878
2760
  // ---------------------------------------------------------------------
2761
+ /** Validated transaction-list filters as query params (shared by listTransactions and exportTransactionsCsv). */
2762
+ buildTransactionFilterParams(filters) {
2763
+ if (filters.complianceEnrollmentId !== void 0) {
2764
+ this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
2765
+ }
2766
+ if (filters.country !== void 0 && !COUNTRY_REGEX.test(filters.country)) {
2767
+ throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
2768
+ }
2769
+ if (filters.search !== void 0 && filters.search.length > 100) {
2770
+ throw new TaxoraException("search must not exceed 100 characters.");
2771
+ }
2772
+ this.validateInternalReference(filters.internalReference);
2773
+ if (filters.providerState !== void 0 && !/^[a-z_]{1,30}$/.test(filters.providerState)) {
2774
+ throw new TaxoraException('providerState must be 1-30 lowercase letters or underscores (e.g. "refused").');
2775
+ }
2776
+ const params = new URLSearchParams();
2777
+ if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
2778
+ if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
2779
+ if (filters.state !== void 0) params.set("state", filters.state);
2780
+ if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
2781
+ if (filters.complianceEnrollmentId !== void 0) {
2782
+ params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
2783
+ }
2784
+ if (filters.country !== void 0) params.set("country", filters.country.toUpperCase());
2785
+ if (filters.search !== void 0) params.set("search", filters.search);
2786
+ if (filters.internalReference !== void 0) params.set("internal_reference", filters.internalReference);
2787
+ if (filters.providerState !== void 0) params.set("provider_state", filters.providerState);
2788
+ return params;
2789
+ }
1879
2790
  validateEnrollmentInput(input) {
1880
2791
  if (input.siret === void 0 && input.siren === void 0) {
1881
2792
  throw new TaxoraException("Either siret or siren is required.");
@@ -2001,14 +2912,101 @@ var EReportingEndpoint = class {
2001
2912
  if (line.taxes.length === 0) {
2002
2913
  throw new TaxoraException(`invoiceLines[${index}].taxes must contain at least one tax.`);
2003
2914
  }
2915
+ line.taxes.forEach((tax, taxIndex) => {
2916
+ const where = `invoiceLines[${index}].taxes[${taxIndex}]`;
2917
+ const category = parseComplianceTaxCategory(tax.category);
2918
+ if (category === null) {
2919
+ throw new TaxoraException(
2920
+ `${where}.category "${String(tax.category)}" is unknown. Valid: ${COMPLIANCE_TAX_CATEGORY_CODES.join(", ")}.`
2921
+ );
2922
+ }
2923
+ if (!Number.isFinite(tax.percent) || tax.percent < 0 || tax.percent > 100) {
2924
+ throw new TaxoraException(`${where}.percent must be between 0 and 100.`);
2925
+ }
2926
+ if (category === "E" && (tax.comment === void 0 || tax.comment.trim() === "")) {
2927
+ throw new TaxoraException(
2928
+ `${where}: an exempt (category E) tax needs the exemption code (VATEX) as comment.`
2929
+ );
2930
+ }
2931
+ if (tax.comment !== void 0 && tax.comment.length > 255) {
2932
+ throw new TaxoraException(`${where}.comment must not exceed 255 characters.`);
2933
+ }
2934
+ });
2004
2935
  });
2005
2936
  }
2937
+ validateInvoicePdfFilename(input) {
2938
+ if (input.invoicePdfFilename === void 0) return;
2939
+ if (input.invoicePdfFilename.length > 120) {
2940
+ throw new TaxoraException("invoicePdfFilename must not exceed 120 characters.");
2941
+ }
2942
+ if (input.invoicePdf === void 0) {
2943
+ throw new TaxoraException("invoicePdfFilename requires invoicePdf.");
2944
+ }
2945
+ }
2946
+ validateInternalReference(internalReference) {
2947
+ if (internalReference !== void 0 && internalReference.length > 100) {
2948
+ throw new TaxoraException("internalReference must not exceed 100 characters.");
2949
+ }
2950
+ }
2951
+ /**
2952
+ * Relations that cannot be right: a due date before the invoice date, a credit note without (or an
2953
+ * invoice with) a corrected invoice, a credit note that reuses the number it corrects, a corrected
2954
+ * invoice dated after the credit note.
2955
+ */
2956
+ validateDocument(input) {
2957
+ for (const [field, value] of [
2958
+ ["subtotal", input.subtotal],
2959
+ ["taxAmount", input.taxAmount],
2960
+ ["total", input.total]
2961
+ ]) {
2962
+ if (value !== void 0 && !Number.isFinite(Number(value))) {
2963
+ throw new TaxoraException(`${field} must be a finite number.`);
2964
+ }
2965
+ }
2966
+ for (const line of input.invoiceLines ?? []) {
2967
+ if (!Number.isFinite(line.quantity) || !Number.isFinite(line.price)) {
2968
+ throw new TaxoraException("Every invoice line needs a numeric quantity and price.");
2969
+ }
2970
+ }
2971
+ const invoiceDate = this.formatDate(input.invoiceDate);
2972
+ if (input.dueDate !== void 0 && this.formatDate(input.dueDate) < invoiceDate) {
2973
+ throw new TaxoraException("dueDate must not be before invoiceDate.");
2974
+ }
2975
+ if (input.counterpartyVatNumber !== void 0 && input.counterpartyVatNumber.length > 50) {
2976
+ throw new TaxoraException("counterpartyVatNumber must not exceed 50 characters.");
2977
+ }
2978
+ const amended = input.amendedNumber?.trim() ?? "";
2979
+ if (input.isCreditNote === true && amended === "") {
2980
+ throw new TaxoraException("A credit note needs amendedNumber, the number of the invoice it corrects.");
2981
+ }
2982
+ if (input.isCreditNote !== true && amended !== "") {
2983
+ throw new TaxoraException("amendedNumber is only for credit notes; set isCreditNote to issue one.");
2984
+ }
2985
+ if (amended !== "" && amended === input.invoiceNumber.trim()) {
2986
+ throw new TaxoraException(
2987
+ "A credit note needs its own number; it cannot reuse the number of the invoice it corrects."
2988
+ );
2989
+ }
2990
+ if (input.amendedDate !== void 0 && this.formatDate(input.amendedDate) > invoiceDate) {
2991
+ throw new TaxoraException("amendedDate (the corrected invoice) must not be after the credit note date.");
2992
+ }
2993
+ }
2994
+ /** A category read back from a stored invoice; one that is not a known code cannot be re-sent. */
2995
+ storedTaxCategory(category) {
2996
+ const parsed = parseComplianceTaxCategory(category);
2997
+ if (parsed === null) {
2998
+ throw new TaxoraException(
2999
+ `The original invoice has an unknown tax category "${String(category)}"; pass invoiceLines and the amounts explicitly.`
3000
+ );
3001
+ }
3002
+ return parsed;
3003
+ }
2006
3004
  validateOptionalCodes(input) {
2007
- if (input.currency !== void 0 && input.currency.length !== 3) {
2008
- throw new TaxoraException("currency must be a 3-character ISO 4217 code.");
3005
+ if (input.currency !== void 0 && !/^[A-Za-z]{3}$/.test(input.currency)) {
3006
+ throw new TaxoraException("currency must be an ISO 4217 code, three letters (e.g. NOK, EUR).");
2009
3007
  }
2010
- if (input.counterpartyCountry !== void 0 && input.counterpartyCountry.length !== 2) {
2011
- throw new TaxoraException("counterpartyCountry must be a 2-character ISO 3166-1 alpha-2 code.");
3008
+ if (input.counterpartyCountry !== void 0 && !/^[A-Za-z]{2}$/.test(input.counterpartyCountry)) {
3009
+ throw new TaxoraException("counterpartyCountry must be an ISO 3166-1 alpha-2 code, two letters (e.g. NO).");
2012
3010
  }
2013
3011
  }
2014
3012
  /**
@@ -2051,7 +3049,7 @@ var EReportingEndpoint = class {
2051
3049
  const taxBuilt = {
2052
3050
  name: tax.name,
2053
3051
  percent: tax.percent,
2054
- category: tax.category
3052
+ category: parseComplianceTaxCategory(tax.category) ?? tax.category
2055
3053
  };
2056
3054
  if (tax.comment !== void 0) taxBuilt["comment"] = tax.comment;
2057
3055
  return taxBuilt;
@@ -2064,10 +3062,10 @@ var EReportingEndpoint = class {
2064
3062
  /** Applies the optional scalar fields shared by create and update. */
2065
3063
  applyOptionalTransactionFields(body, input) {
2066
3064
  if (input.dueDate !== void 0) body["due_date"] = this.formatDate(input.dueDate);
2067
- if (input.currency !== void 0) body["currency"] = input.currency;
3065
+ if (input.currency !== void 0) body["currency"] = input.currency.toUpperCase();
2068
3066
  if (input.taxAmount !== void 0) body["tax_amount"] = input.taxAmount;
2069
3067
  if (input.counterpartyName !== void 0) body["counterparty_name"] = input.counterpartyName;
2070
- if (input.counterpartyCountry !== void 0) body["counterparty_country"] = input.counterpartyCountry;
3068
+ if (input.counterpartyCountry !== void 0) body["counterparty_country"] = input.counterpartyCountry.toUpperCase();
2071
3069
  if (input.counterpartyVatNumber !== void 0) body["counterparty_vat_number"] = input.counterpartyVatNumber;
2072
3070
  if (input.providerContactId !== void 0) body["provider_contact_id"] = input.providerContactId;
2073
3071
  if (input.paymentMethod !== void 0) body["payment_method"] = input.paymentMethod;
@@ -2084,6 +3082,29 @@ var EReportingEndpoint = class {
2084
3082
  if (input.amendedNumber !== void 0) body["amended_number"] = input.amendedNumber;
2085
3083
  if (input.amendedDate !== void 0) body["amended_date"] = this.formatDate(input.amendedDate);
2086
3084
  if (input.extraInfo !== void 0) body["extra_info"] = input.extraInfo;
3085
+ if (input.internalReference !== void 0) body["internal_reference"] = input.internalReference;
3086
+ if (input.invoicePdf !== void 0) body["invoice_pdf"] = this.encodeInvoicePdf(input.invoicePdf);
3087
+ if (input.invoicePdfFilename !== void 0) body["invoice_pdf_filename"] = input.invoicePdfFilename;
3088
+ }
3089
+ /** Bytes → base64 (validated as a PDF of at most 10 MB); strings are taken as already base64. */
3090
+ encodeInvoicePdf(pdf) {
3091
+ if (typeof pdf === "string") {
3092
+ if (pdf.trim() === "") {
3093
+ throw new TaxoraException("invoicePdf must not be empty.");
3094
+ }
3095
+ return pdf;
3096
+ }
3097
+ const bytes = pdf instanceof Uint8Array ? pdf : new Uint8Array(pdf);
3098
+ if (bytes.byteLength === 0) {
3099
+ throw new TaxoraException("invoicePdf must not be empty.");
3100
+ }
3101
+ if (bytes.byteLength > INVOICE_PDF_MAX_BYTES) {
3102
+ throw new TaxoraException("invoicePdf must not exceed 10 MB.");
3103
+ }
3104
+ if (!INVOICE_PDF_MAGIC.every((byte, index) => bytes[index] === byte)) {
3105
+ throw new TaxoraException('invoicePdf must be a PDF file (content must start with "%PDF-").');
3106
+ }
3107
+ return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
2087
3108
  }
2088
3109
  // ---------------------------------------------------------------------
2089
3110
  // HTTP plumbing (kept in sync with SmartEnrichmentEndpoint)
@@ -2107,6 +3128,30 @@ var EReportingEndpoint = class {
2107
3128
  const send = () => this.httpClient.request(method, url, options);
2108
3129
  return method === "GET" ? withResponseRetries(this.retryPolicy, "e-reporting request", send) : send();
2109
3130
  }
3131
+ /** GET for a non-JSON download (CSV / document): no JSON Content-Type, retried like other GETs. */
3132
+ async sendDownloadRequest(url, accept) {
3133
+ const headers = this.buildHeaders(false);
3134
+ headers["Accept"] = accept;
3135
+ return withResponseRetries(
3136
+ this.retryPolicy,
3137
+ "e-reporting download",
3138
+ () => this.httpClient.request("GET", url, { headers })
3139
+ );
3140
+ }
3141
+ /** `attachment; filename="x.xml"` (or RFC 5987 `filename*=UTF-8''x.xml`) → "x.xml". */
3142
+ filenameFromDisposition(header) {
3143
+ if (header === null) return null;
3144
+ const extended = /filename\*\s*=\s*(?:UTF-8|utf-8)''([^;]+)/.exec(header);
3145
+ if (extended?.[1] !== void 0) {
3146
+ try {
3147
+ return decodeURIComponent(extended[1].trim());
3148
+ } catch {
3149
+ }
3150
+ }
3151
+ const plain = /filename\s*=\s*"([^"]*)"|filename\s*=\s*([^;]+)/.exec(header);
3152
+ const name = (plain?.[1] ?? plain?.[2])?.trim();
3153
+ return name !== void 0 && name !== "" ? name : null;
3154
+ }
2110
3155
  /** Multipart upload: Content-Type is left to fetch so the boundary is set correctly. */
2111
3156
  async sendMultipartRequest(method, url, form) {
2112
3157
  return this.httpClient.request(method, url, { headers: this.buildHeaders(false), body: form });
@@ -2688,13 +3733,13 @@ var SmartEnrichmentHistoryPage = class _SmartEnrichmentHistoryPage {
2688
3733
  };
2689
3734
 
2690
3735
  // src/dto/SmartEnrichmentStatistics.ts
2691
- function asString9(value, fallback = "") {
3736
+ function asString12(value, fallback = "") {
2692
3737
  return typeof value === "string" ? value : fallback;
2693
3738
  }
2694
- function asNumber6(value) {
3739
+ function asNumber7(value) {
2695
3740
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
2696
3741
  }
2697
- function asRecord2(value) {
3742
+ function asRecord5(value) {
2698
3743
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
2699
3744
  }
2700
3745
  function asRecords2(value) {
@@ -2717,39 +3762,39 @@ var SmartEnrichmentStatistics = class _SmartEnrichmentStatistics {
2717
3762
  byOutcome;
2718
3763
  confidenceBuckets;
2719
3764
  static fromArray(data) {
2720
- const range = asRecord2(data["range"]);
2721
- const totals = asRecord2(data["totals"]);
3765
+ const range = asRecord5(data["range"]);
3766
+ const totals = asRecord5(data["totals"]);
2722
3767
  const timeSeries = asRecords2(data["time_series"]).map((row) => ({
2723
- bucket: asString9(row["bucket"]),
2724
- jobs: asNumber6(row["jobs"]),
2725
- items: asNumber6(row["items"]),
2726
- found: asNumber6(row["found"])
3768
+ bucket: asString12(row["bucket"]),
3769
+ jobs: asNumber7(row["jobs"]),
3770
+ items: asNumber7(row["items"]),
3771
+ found: asNumber7(row["found"])
2727
3772
  }));
2728
3773
  const bySource = asRecords2(data["by_source"]).map((row) => ({
2729
- source: asString9(row["source"]),
2730
- jobs: asNumber6(row["jobs"]),
2731
- found: asNumber6(row["found"])
3774
+ source: asString12(row["source"]),
3775
+ jobs: asNumber7(row["jobs"]),
3776
+ found: asNumber7(row["found"])
2732
3777
  }));
2733
3778
  const byOutcome = asRecords2(data["by_outcome"]).map((row) => ({
2734
- outcome: asString9(row["outcome"]),
2735
- count: asNumber6(row["count"])
3779
+ outcome: asString12(row["outcome"]),
3780
+ count: asNumber7(row["count"])
2736
3781
  }));
2737
3782
  const confidenceBuckets = asRecords2(data["confidence_buckets"]).map((row) => ({
2738
- bucket: asString9(row["bucket"]),
2739
- count: asNumber6(row["count"])
3783
+ bucket: asString12(row["bucket"]),
3784
+ count: asNumber7(row["count"])
2740
3785
  }));
2741
3786
  return new _SmartEnrichmentStatistics(
2742
3787
  {
2743
- dateFrom: asString9(range["date_from"]),
2744
- dateTo: asString9(range["date_to"]),
2745
- interval: asString9(range["interval"])
3788
+ dateFrom: asString12(range["date_from"]),
3789
+ dateTo: asString12(range["date_to"]),
3790
+ interval: asString12(range["interval"])
2746
3791
  },
2747
3792
  {
2748
- jobs: asNumber6(totals["jobs"]),
2749
- items: asNumber6(totals["items"]),
2750
- found: asNumber6(totals["found"]),
2751
- foundRate: asNumber6(totals["found_rate"]),
2752
- avgConfidence: asNumber6(totals["avg_confidence"])
3793
+ jobs: asNumber7(totals["jobs"]),
3794
+ items: asNumber7(totals["items"]),
3795
+ found: asNumber7(totals["found"]),
3796
+ foundRate: asNumber7(totals["found_rate"]),
3797
+ avgConfidence: asNumber7(totals["avg_confidence"])
2753
3798
  },
2754
3799
  timeSeries,
2755
3800
  bySource,
@@ -4029,7 +5074,7 @@ var AuthRetryHttpClient = class {
4029
5074
  };
4030
5075
 
4031
5076
  // src/version.ts
4032
- var SDK_VERSION = "1.8.0";
5077
+ var SDK_VERSION = "1.9.0";
4033
5078
 
4034
5079
  // src/http/SdkVersionHttpClient.ts
4035
5080
  var SDK_VERSION_HEADER = "X-Taxora-SDK-Version";
@@ -4056,6 +5101,7 @@ var TaxoraClient = class {
4056
5101
  company;
4057
5102
  smartEnrichment;
4058
5103
  eReporting;
5104
+ billing;
4059
5105
  baseUrl;
4060
5106
  constructor(options) {
4061
5107
  const environment = options.environment ?? Environment.SANDBOX;
@@ -4076,6 +5122,7 @@ var TaxoraClient = class {
4076
5122
  retryPolicy
4077
5123
  );
4078
5124
  this.eReporting = new EReportingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
5125
+ this.billing = new BillingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
4079
5126
  }
4080
5127
  };
4081
5128
 
@@ -4097,6 +5144,68 @@ var Language = {
4097
5144
  GERMAN: "de"
4098
5145
  };
4099
5146
 
5147
+ // src/support/calculateInvoiceTotals.ts
5148
+ function round2(value) {
5149
+ const magnitude = Math.round(Number((Math.abs(value) * 100).toPrecision(15)));
5150
+ return (value < 0 ? -magnitude : magnitude) / 100;
5151
+ }
5152
+ function calculateInvoiceTotals(lines, options = {}) {
5153
+ let net = 0;
5154
+ let tax = 0;
5155
+ for (const line of lines) {
5156
+ const lineNet = round2(line.quantity * line.price);
5157
+ const percent = line.taxes.reduce((sum, t) => sum + t.percent, 0);
5158
+ net += lineNet;
5159
+ tax += round2(lineNet * percent / 100);
5160
+ }
5161
+ const subtotal = round2(net);
5162
+ const taxAmount = round2(tax);
5163
+ const total = round2(subtotal + taxAmount);
5164
+ let roundingDifference = 0;
5165
+ if (options.expectedTotal !== void 0) {
5166
+ if (!Number.isFinite(options.expectedTotal)) {
5167
+ throw new Error("The expected total must be numeric.");
5168
+ }
5169
+ const tolerance = options.tolerance ?? 0.05;
5170
+ roundingDifference = round2(options.expectedTotal - total);
5171
+ if (Math.abs(roundingDifference) > tolerance) {
5172
+ throw new Error(
5173
+ `The expected total ${options.expectedTotal.toFixed(2)} differs from the invoice lines (${total.toFixed(2)}) by ${roundingDifference.toFixed(2)}, more than the rounding tolerance of ${tolerance.toFixed(2)}. Check quantities, prices and tax rates.`
5174
+ );
5175
+ }
5176
+ }
5177
+ return { subtotal, taxAmount, total, roundingDifference };
5178
+ }
5179
+
5180
+ // src/support/complianceTax.ts
5181
+ var ComplianceTax = {
5182
+ /**
5183
+ * A taxed rate above 0 %, category S (Norway: 25 / 15 / 12 % are all S). Where a country files reduced rates
5184
+ * under their own category (France: AA / AAA), pass it as `category`.
5185
+ */
5186
+ rate(percent, name = "VAT", category = ComplianceTaxCategory.STANDARD) {
5187
+ if (!Number.isFinite(percent) || percent <= 0) {
5188
+ throw new Error("rate() needs a percent above 0; for 0 % use zeroRated(), exempt() or reverseCharge().");
5189
+ }
5190
+ return { name, percent, category };
5191
+ },
5192
+ /** 0 % but taxable (category Z). */
5193
+ zeroRated(name = "VAT") {
5194
+ return { name, percent: 0, category: ComplianceTaxCategory.ZERO };
5195
+ },
5196
+ /** Exempt from VAT (category E); the VATEX exemption code is mandatory. */
5197
+ exempt(exemptionCode, name = "VAT") {
5198
+ if (exemptionCode.trim() === "") {
5199
+ throw new Error("An exempt (category E) tax needs the exemption code (VATEX).");
5200
+ }
5201
+ return { name, percent: 0, category: ComplianceTaxCategory.EXEMPT, comment: exemptionCode };
5202
+ },
5203
+ /** Reverse charge (category AE); the mandatory legal text is added by Taxora. */
5204
+ reverseCharge(name = "VAT") {
5205
+ return { name, percent: 0, category: ComplianceTaxCategory.REVERSE_CHARGE };
5206
+ }
5207
+ };
5208
+
4100
5209
  // src/webhooks/WebhookSignature.ts
4101
5210
  import { createHmac, timingSafeEqual } from "crypto";
4102
5211
  var WEBHOOK_SIGNATURE_HEADER = "Taxora-Signature";
@@ -4215,18 +5324,29 @@ function toBytes(rawBody) {
4215
5324
  "invalid_body"
4216
5325
  );
4217
5326
  }
5327
+
5328
+ // src/webhooks/WebhookEvent.ts
5329
+ function isTransactionBuyerRejected(object) {
5330
+ return object.buyer_rejected === true;
5331
+ }
4218
5332
  export {
4219
5333
  ApiVersion,
4220
5334
  AuthEndpoint,
4221
5335
  AuthRetryHttpClient,
4222
5336
  AuthenticationException,
5337
+ BillingEndpoint,
4223
5338
  CompanyAddress,
4224
5339
  CompanyEndpoint,
5340
+ ComplianceDeliveryAction,
5341
+ ComplianceDeliveryDecision,
4225
5342
  ComplianceDocumentType,
4226
5343
  ComplianceEnrollment,
4227
5344
  ComplianceEnrollmentPage,
4228
5345
  ComplianceEnrollmentStatus,
5346
+ ComplianceOfferings,
4229
5347
  ComplianceService,
5348
+ ComplianceTax,
5349
+ ComplianceTaxCategory,
4230
5350
  ComplianceTaxReport,
4231
5351
  ComplianceTaxReportPage,
4232
5352
  ComplianceTaxReportState,
@@ -4235,6 +5355,8 @@ export {
4235
5355
  ComplianceTransactionState,
4236
5356
  ComplianceTransactionType,
4237
5357
  DEFAULT_WEBHOOK_TOLERANCE_SECONDS,
5358
+ EInvoicingQuota,
5359
+ EInvoicingQuotaPeriod,
4238
5360
  EReportingEndpoint,
4239
5361
  Environment,
4240
5362
  FetchHttpClient,
@@ -4269,6 +5391,8 @@ export {
4269
5391
  TaxoraClientFactory,
4270
5392
  TaxoraException,
4271
5393
  Token,
5394
+ UsageSummary,
5395
+ UsageSummaryPage,
4272
5396
  ValidationException,
4273
5397
  VatCertificateExport,
4274
5398
  VatCollection,
@@ -4280,6 +5404,7 @@ export {
4280
5404
  WEBHOOK_ID_HEADER,
4281
5405
  WEBHOOK_SIGNATURE_HEADER,
4282
5406
  WebhookSignatureVerificationError,
5407
+ calculateInvoiceTotals,
4283
5408
  computeWebhookSignature,
4284
5409
  constructWebhookEvent,
4285
5410
  describeApiError,
@@ -4297,6 +5422,8 @@ export {
4297
5422
  generateWebhookSignatureHeader,
4298
5423
  getBaseUrl,
4299
5424
  getFailedVatStates,
5425
+ isTransactionBuyerRejected,
5426
+ parseComplianceTaxCategory,
4300
5427
  parseLocalTaxNumbersCsv,
4301
5428
  parseWebhookSignatureHeader,
4302
5429
  toComplianceDocumentType,