@taxora/sdk 1.7.1 → 1.9.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,213 @@ var AuthEndpoint = class {
440
440
  }
441
441
  };
442
442
 
443
+ // src/dto/UsageSummary.ts
444
+ function asRecord(value) {
445
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
446
+ }
447
+ function asRecordList(value) {
448
+ return Array.isArray(value) ? value.filter((row) => asRecord(row) !== null) : [];
449
+ }
450
+ function asString(value, fallback = "") {
451
+ return typeof value === "string" ? value : fallback;
452
+ }
453
+ function asNullableString(value) {
454
+ return typeof value === "string" ? value : null;
455
+ }
456
+ function asNumber(value) {
457
+ return typeof value === "number" ? value : Number(value ?? 0) || 0;
458
+ }
459
+ function asNullableNumber(value) {
460
+ if (value === null || value === void 0 || value === "") return null;
461
+ const num = Number(value);
462
+ return Number.isFinite(num) ? num : null;
463
+ }
464
+ var UsageSummary = class _UsageSummary {
465
+ constructor(id, companyId, feature, country, regime, billingPeriod, eventCount, billableAmount, state, stateLabel, periodClosedAt, billingTriggeredAt, billedAt, stripeInvoiceId, stripeSubscriptionItemId, billingNotes, events, createdAt, updatedAt) {
466
+ this.id = id;
467
+ this.companyId = companyId;
468
+ this.feature = feature;
469
+ this.country = country;
470
+ this.regime = regime;
471
+ this.billingPeriod = billingPeriod;
472
+ this.eventCount = eventCount;
473
+ this.billableAmount = billableAmount;
474
+ this.state = state;
475
+ this.stateLabel = stateLabel;
476
+ this.periodClosedAt = periodClosedAt;
477
+ this.billingTriggeredAt = billingTriggeredAt;
478
+ this.billedAt = billedAt;
479
+ this.stripeInvoiceId = stripeInvoiceId;
480
+ this.stripeSubscriptionItemId = stripeSubscriptionItemId;
481
+ this.billingNotes = billingNotes;
482
+ this.events = events;
483
+ this.createdAt = createdAt;
484
+ this.updatedAt = updatedAt;
485
+ }
486
+ id;
487
+ companyId;
488
+ feature;
489
+ country;
490
+ regime;
491
+ billingPeriod;
492
+ eventCount;
493
+ billableAmount;
494
+ state;
495
+ stateLabel;
496
+ periodClosedAt;
497
+ billingTriggeredAt;
498
+ billedAt;
499
+ stripeInvoiceId;
500
+ stripeSubscriptionItemId;
501
+ billingNotes;
502
+ events;
503
+ createdAt;
504
+ updatedAt;
505
+ static fromArray(data) {
506
+ const amount = data["billable_amount"];
507
+ return new _UsageSummary(
508
+ asNumber(data["id"]),
509
+ asNumber(data["company_id"]),
510
+ asString(data["feature"]),
511
+ asNullableString(data["country"]),
512
+ asNullableString(data["regime"]),
513
+ asString(data["billing_period"]),
514
+ asNumber(data["event_count"]),
515
+ typeof amount === "string" ? amount : typeof amount === "number" ? String(amount) : null,
516
+ asNullableString(data["state"]),
517
+ asNullableString(data["state_label"]),
518
+ asNullableString(data["period_closed_at"]),
519
+ asNullableString(data["billing_triggered_at"]),
520
+ asNullableString(data["billed_at"]),
521
+ asNullableString(data["stripe_invoice_id"]),
522
+ asNullableString(data["stripe_subscription_item_id"]),
523
+ asNullableString(data["billing_notes"]),
524
+ asRecordList(data["events"]).map((event) => ({
525
+ id: asNumber(event["id"]),
526
+ usageSummaryId: asNullableNumber(event["usage_summary_id"]),
527
+ feature: asString(event["feature"]),
528
+ country: asNullableString(event["country"]),
529
+ regime: asNullableString(event["regime"]),
530
+ billingPeriod: asString(event["billing_period"]),
531
+ resourceType: asNullableString(event["resource_type"]),
532
+ resourceId: asNullableNumber(event["resource_id"]),
533
+ stripeUsageRecordId: asNullableString(event["stripe_usage_record_id"]),
534
+ stripeReportedAt: asNullableString(event["stripe_reported_at"]),
535
+ createdAt: asNullableString(event["created_at"])
536
+ })),
537
+ asNullableString(data["created_at"]),
538
+ asNullableString(data["updated_at"])
539
+ );
540
+ }
541
+ };
542
+ var UsageSummaryPage = class _UsageSummaryPage {
543
+ constructor(rows, currentPage, perPage, total, lastPage) {
544
+ this.rows = rows;
545
+ this.currentPage = currentPage;
546
+ this.perPage = perPage;
547
+ this.total = total;
548
+ this.lastPage = lastPage;
549
+ }
550
+ rows;
551
+ currentPage;
552
+ perPage;
553
+ total;
554
+ lastPage;
555
+ static fromResponse(payload) {
556
+ const obj = asRecord(payload) ?? {};
557
+ const rows = asRecordList(obj["data"]).map(UsageSummary.fromArray);
558
+ const meta = asRecord(obj["meta"]) ?? {};
559
+ const num = (value, fallback) => typeof value === "number" ? value : fallback;
560
+ return new _UsageSummaryPage(
561
+ rows,
562
+ num(meta["current_page"], 1),
563
+ num(meta["per_page"], rows.length),
564
+ num(meta["total"], rows.length),
565
+ num(meta["last_page"], 1)
566
+ );
567
+ }
568
+ [Symbol.iterator]() {
569
+ return this.rows[Symbol.iterator]();
570
+ }
571
+ get length() {
572
+ return this.rows.length;
573
+ }
574
+ };
575
+
576
+ // src/endpoints/BillingEndpoint.ts
577
+ var PERIOD_REGEX = /^\d{4}-(0[1-9]|1[0-2])$/;
578
+ var BillingEndpoint = class {
579
+ constructor(baseUrl, apiKey, tokenStorage, httpClient, retryPolicy = new RetryPolicy()) {
580
+ this.baseUrl = baseUrl;
581
+ this.apiKey = apiKey;
582
+ this.tokenStorage = tokenStorage;
583
+ this.httpClient = httpClient;
584
+ this.retryPolicy = retryPolicy;
585
+ }
586
+ baseUrl;
587
+ apiKey;
588
+ tokenStorage;
589
+ httpClient;
590
+ retryPolicy;
591
+ /** Paginated usage summaries, newest billing period first. `perPage` is clamped to 1-100 server-side. */
592
+ async listUsageSummaries(page = 1, perPage = 25) {
593
+ const params = new URLSearchParams();
594
+ params.set("page", String(page));
595
+ params.set("per_page", String(perPage));
596
+ const data = await this.getJson(`${this.baseUrl}/billing/usage-summaries?${params.toString()}`);
597
+ return UsageSummaryPage.fromResponse(data);
598
+ }
599
+ /**
600
+ * The usage summaries of one billing period (YYYY-MM), one per feature,
601
+ * each with its usage events. A period without usage yields a 404 (HttpException).
602
+ */
603
+ async getUsageSummaries(period) {
604
+ if (!PERIOD_REGEX.test(period)) {
605
+ throw new TaxoraException(`Invalid billing period "${period}". Expected YYYY-MM, e.g. "2026-09".`);
606
+ }
607
+ const data = await this.getJson(`${this.baseUrl}/billing/usage-summaries/${period}`);
608
+ return Array.isArray(data) ? data.filter((row) => typeof row === "object" && row !== null).map(UsageSummary.fromArray) : [];
609
+ }
610
+ buildHeaders() {
611
+ const headers = {
612
+ "Content-Type": "application/json",
613
+ "x-api-key": this.apiKey
614
+ };
615
+ const token = this.tokenStorage.get();
616
+ if (token) {
617
+ headers["Authorization"] = `Bearer ${token.accessToken}`;
618
+ }
619
+ return headers;
620
+ }
621
+ /** GET + envelope unwrap (`{success, data}` → data, which may be an object or an array). */
622
+ async getJson(url) {
623
+ return withRetries(this.retryPolicy, "billing request", async () => {
624
+ const response = await this.httpClient.request("GET", url, { headers: this.buildHeaders() });
625
+ const responseText = await response.text();
626
+ if (!response.ok) {
627
+ throw new HttpException(
628
+ describeApiError(responseText, response.status),
629
+ response.status,
630
+ responseText,
631
+ {},
632
+ response.headers.get("retry-after")
633
+ );
634
+ }
635
+ let parsed;
636
+ try {
637
+ parsed = JSON.parse(responseText);
638
+ } catch {
639
+ throw new HttpException("Invalid JSON response", response.status, responseText);
640
+ }
641
+ if (typeof parsed !== "object" || parsed === null) {
642
+ throw new HttpException("Unexpected response format", response.status, responseText);
643
+ }
644
+ const obj = parsed;
645
+ return obj["data"] !== void 0 && obj["data"] !== null ? obj["data"] : obj;
646
+ });
647
+ }
648
+ };
649
+
443
650
  // src/dto/CompanyResource.ts
444
651
  function normalizeCompanyResource(data) {
445
652
  const apiRateLimit = typeof data["api_rate_limit"] === "number" ? data["api_rate_limit"] : void 0;
@@ -566,13 +773,13 @@ function describeComplianceService(service) {
566
773
  }
567
774
 
568
775
  // src/dto/ComplianceEnrollment.ts
569
- function asString(value, fallback = "") {
776
+ function asString2(value, fallback = "") {
570
777
  return typeof value === "string" ? value : fallback;
571
778
  }
572
- function asNullableString(value) {
779
+ function asNullableString2(value) {
573
780
  return typeof value === "string" ? value : null;
574
781
  }
575
- function asNumber(value) {
782
+ function asNumber2(value) {
576
783
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
577
784
  }
578
785
  var ComplianceEnrollment = class _ComplianceEnrollment {
@@ -619,29 +826,114 @@ var ComplianceEnrollment = class _ComplianceEnrollment {
619
826
  static fromArray(data) {
620
827
  const regimeConfig = typeof data["regime_config"] === "object" && data["regime_config"] !== null && !Array.isArray(data["regime_config"]) ? data["regime_config"] : null;
621
828
  return new _ComplianceEnrollment(
622
- asNumber(data["id"]),
623
- asNumber(data["company_id"]),
624
- asString(data["country"]),
625
- asString(data["regime"]),
626
- asString(data["provider"]),
829
+ asNumber2(data["id"]),
830
+ asNumber2(data["company_id"]),
831
+ asString2(data["country"]),
832
+ asString2(data["regime"]),
833
+ asString2(data["provider"]),
627
834
  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"]),
835
+ asString2(data["status_label"]),
836
+ asNullableString2(data["status_error"]),
837
+ asNullableString2(data["provider_account_id"]),
838
+ asNullableString2(data["tax_id"]),
839
+ asNullableString2(data["company_register_id"]),
840
+ asNullableString2(data["company_register_scheme"]),
634
841
  regimeConfig,
635
- asNullableString(data["reporting_start_date"]),
636
- asNullableString(data["notification_email"]),
842
+ asNullableString2(data["reporting_start_date"]),
843
+ asNullableString2(data["notification_email"]),
637
844
  data["auto_send"] === true,
638
- asNullableString(data["created_at"]),
639
- asNullableString(data["updated_at"]),
845
+ asNullableString2(data["created_at"]),
846
+ asNullableString2(data["updated_at"]),
640
847
  toComplianceService(data["service"])
641
848
  );
642
849
  }
643
850
  };
644
851
 
852
+ // src/dto/ComplianceOfferings.ts
853
+ function asRecord2(value) {
854
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
855
+ }
856
+ function asRecordList2(value) {
857
+ return Array.isArray(value) ? value.filter((row) => asRecord2(row) !== null) : [];
858
+ }
859
+ function asString3(value, fallback = "") {
860
+ return typeof value === "string" ? value : fallback;
861
+ }
862
+ function asNullableString3(value) {
863
+ return typeof value === "string" ? value : null;
864
+ }
865
+ function asNullableNumber2(value) {
866
+ if (typeof value === "number" && Number.isFinite(value)) return value;
867
+ if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
868
+ return null;
869
+ }
870
+ function asStringList(value) {
871
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
872
+ }
873
+ function toRequires(value) {
874
+ const record = asRecord2(value);
875
+ return record === null ? null : { country: asString3(record["country"]), service: asString3(record["service"]) };
876
+ }
877
+ var ComplianceOfferings = class _ComplianceOfferings {
878
+ constructor(offerings, coverage, access, accessByCountry) {
879
+ this.offerings = offerings;
880
+ this.coverage = coverage;
881
+ this.access = access;
882
+ this.accessByCountry = accessByCountry;
883
+ }
884
+ offerings;
885
+ coverage;
886
+ access;
887
+ accessByCountry;
888
+ static fromArray(data) {
889
+ const offerings = asRecordList2(data["data"]).map((row) => ({
890
+ country: asString3(row["country"]),
891
+ service: asString3(row["service"]),
892
+ currency: asString3(row["currency"], "EUR"),
893
+ active: row["active"] === true,
894
+ viaStripe: row["via_stripe"] === true,
895
+ plan: asNullableString3(row["plan"]),
896
+ status: asNullableString3(row["status"]),
897
+ enrolled: row["enrolled"] === true,
898
+ plans: asRecordList2(row["plans"]).map((plan) => ({
899
+ key: asString3(plan["key"]),
900
+ monthlyFee: asNullableNumber2(plan["monthly_fee"]),
901
+ setupFee: asNullableNumber2(plan["setup_fee"]),
902
+ usageTiers: Array.isArray(plan["usage_tiers"]) ? plan["usage_tiers"] : [],
903
+ volumeLimit: plan["volume_limit"] ?? null,
904
+ contactOnly: plan["contact_only"] === true,
905
+ checkoutAvailable: plan["checkout_available"] === true,
906
+ requires: toRequires(plan["requires"]),
907
+ eligible: plan["eligible"] === true
908
+ }))
909
+ }));
910
+ const coverage = asRecordList2(data["coverage"]).map((row) => ({
911
+ country: asString3(row["country"]),
912
+ availability: asString3(row["availability"]),
913
+ services: asStringList(row["services"]),
914
+ network: asNullableString3(row["network"]),
915
+ format: asNullableString3(row["format"]),
916
+ mandate: asNullableString3(row["mandate"]),
917
+ support: asString3(row["support"], "yes")
918
+ }));
919
+ const access = asRecord2(data["access"]) ?? {};
920
+ const accessByCountry = {};
921
+ for (const [country, services] of Object.entries(asRecord2(data["access_by_country"]) ?? {})) {
922
+ const flags = {};
923
+ for (const [service, value] of Object.entries(asRecord2(services) ?? {})) {
924
+ flags[service] = value === true;
925
+ }
926
+ accessByCountry[country] = flags;
927
+ }
928
+ return new _ComplianceOfferings(
929
+ offerings,
930
+ coverage,
931
+ { eReporting: access["e_reporting"] === true, eInvoicing: access["e_invoicing"] === true },
932
+ accessByCountry
933
+ );
934
+ }
935
+ };
936
+
645
937
  // src/dto/ComplianceEnrollmentPage.ts
646
938
  var ComplianceEnrollmentPage = class _ComplianceEnrollmentPage {
647
939
  constructor(rows, currentPage, perPage, total, lastPage) {
@@ -711,16 +1003,16 @@ function describeComplianceTaxReportState(state) {
711
1003
  }
712
1004
 
713
1005
  // src/dto/ComplianceTaxReport.ts
714
- function asString2(value, fallback = "") {
1006
+ function asString4(value, fallback = "") {
715
1007
  return typeof value === "string" ? value : fallback;
716
1008
  }
717
- function asNullableString2(value) {
1009
+ function asNullableString4(value) {
718
1010
  return typeof value === "string" ? value : null;
719
1011
  }
720
- function asNumber2(value) {
1012
+ function asNumber3(value) {
721
1013
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
722
1014
  }
723
- function asNullableNumber(value) {
1015
+ function asNullableNumber3(value) {
724
1016
  return typeof value === "number" ? value : null;
725
1017
  }
726
1018
  function asMoney(value) {
@@ -771,25 +1063,25 @@ var ComplianceTaxReport = class _ComplianceTaxReport {
771
1063
  updatedAt;
772
1064
  static fromArray(data) {
773
1065
  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"]),
1066
+ asNumber3(data["id"]),
1067
+ asNumber3(data["company_id"]),
1068
+ asNullableNumber3(data["compliance_transaction_id"]),
1069
+ asNullableNumber3(data["ledger_id"]),
1070
+ asString4(data["country"]),
1071
+ asString4(data["regime"]),
780
1072
  toComplianceTaxReportState(data["state"]),
781
- asString2(data["state_label"]),
1073
+ asString4(data["state_label"]),
782
1074
  data["is_terminal"] === true,
783
- asNullableString2(data["provider_tax_report_id"]),
784
- asNullableString2(data["provider_ledger_id"]),
1075
+ asNullableString4(data["provider_tax_report_id"]),
1076
+ asNullableString4(data["provider_ledger_id"]),
785
1077
  asMoney(data["base_amount"]),
786
1078
  asMoney(data["tax_amount"]),
787
1079
  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"])
1080
+ asNullableString4(data["refusal_reason"]),
1081
+ asNullableString4(data["registered_at"]),
1082
+ asNullableString4(data["refused_at"]),
1083
+ asNullableString4(data["created_at"]),
1084
+ asNullableString4(data["updated_at"])
793
1085
  );
794
1086
  }
795
1087
  };
@@ -860,6 +1152,8 @@ var ComplianceTransactionState = {
860
1152
  SENDING: "sending",
861
1153
  SUBMITTED: "submitted",
862
1154
  ERROR: "error",
1155
+ /** E-invoicing only: the buyer cannot receive e-invoices — nothing was sent or billed; send your regular invoice. */
1156
+ NOT_REQUIRED: "not_required",
863
1157
  UNKNOWN: "unknown"
864
1158
  };
865
1159
  var VALUES5 = Object.values(ComplianceTransactionState);
@@ -877,6 +1171,7 @@ var DESCRIPTIONS5 = {
877
1171
  [ComplianceTransactionState.SENDING]: "Submission to the provider in progress.",
878
1172
  [ComplianceTransactionState.SUBMITTED]: "Successfully submitted to the provider.",
879
1173
  [ComplianceTransactionState.ERROR]: "Submission failed. See submissionError.",
1174
+ [ComplianceTransactionState.NOT_REQUIRED]: "The buyer cannot receive e-invoices, so none was sent or billed. Send your regular invoice instead.",
880
1175
  [ComplianceTransactionState.UNKNOWN]: "Unknown state."
881
1176
  };
882
1177
  function describeComplianceTransactionState(state) {
@@ -915,13 +1210,13 @@ function describeComplianceTransactionType(type) {
915
1210
  }
916
1211
 
917
1212
  // src/dto/ComplianceTransaction.ts
918
- function asString3(value, fallback = "") {
1213
+ function asString5(value, fallback = "") {
919
1214
  return typeof value === "string" ? value : fallback;
920
1215
  }
921
- function asNullableString3(value) {
1216
+ function asNullableString5(value) {
922
1217
  return typeof value === "string" ? value : null;
923
1218
  }
924
- function asNumber3(value) {
1219
+ function asNumber4(value) {
925
1220
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
926
1221
  }
927
1222
  function asMoney2(value) {
@@ -929,34 +1224,55 @@ function asMoney2(value) {
929
1224
  if (typeof value === "number" && Number.isFinite(value)) return String(value);
930
1225
  return "0.00";
931
1226
  }
932
- function asNullableNumber2(value) {
1227
+ function asNullableNumber4(value) {
933
1228
  if (typeof value === "number" && Number.isFinite(value)) return value;
934
1229
  if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
935
1230
  return null;
936
1231
  }
937
- function asRecord(value) {
1232
+ function asRecord3(value) {
938
1233
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
939
1234
  }
940
- function asRecordList(value) {
941
- return Array.isArray(value) ? value.filter((row) => asRecord(row) !== null) : [];
1235
+ function asRecordList3(value) {
1236
+ return Array.isArray(value) ? value.filter((row) => asRecord3(row) !== null) : [];
1237
+ }
1238
+ function toInvoicePdf(value) {
1239
+ const pdf = asRecord3(value);
1240
+ if (pdf === null) return null;
1241
+ return {
1242
+ filename: asNullableString5(pdf["filename"]),
1243
+ size: asNullableNumber4(pdf["size"]),
1244
+ attached: pdf["attached"] === true
1245
+ };
1246
+ }
1247
+ function toEurEquivalent(value) {
1248
+ const eur = asRecord3(value);
1249
+ if (eur === null) return null;
1250
+ return {
1251
+ subtotal: asMoney2(eur["subtotal"]),
1252
+ taxAmount: asMoney2(eur["tax_amount"]),
1253
+ total: asMoney2(eur["total"]),
1254
+ rate: asString5(eur["rate"]),
1255
+ rateDate: asNullableString5(eur["rate_date"]),
1256
+ source: asString5(eur["source"], "ECB")
1257
+ };
942
1258
  }
943
1259
  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"]),
1260
+ const invoice = asRecord3(asRecord3(providerPayload)?.["invoice"]);
1261
+ return asRecordList3(invoice?.["invoice_lines_attributes"]).map((line) => ({
1262
+ description: asString5(line["description"]),
1263
+ quantity: asNullableNumber4(line["quantity"]),
948
1264
  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"])
1265
+ unit: asNullableNumber4(line["unit"]),
1266
+ taxes: asRecordList3(line["taxes_attributes"]).map((tax) => ({
1267
+ name: asString5(tax["name"]),
1268
+ percent: asNullableNumber4(tax["percent"]),
1269
+ category: asNullableString5(tax["category"]),
1270
+ comment: asNullableString5(tax["comment"])
955
1271
  }))
956
1272
  }));
957
1273
  }
958
1274
  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 = []) {
1275
+ 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
1276
  this.id = id;
961
1277
  this.companyId = companyId;
962
1278
  this.complianceEnrollmentId = complianceEnrollmentId;
@@ -995,6 +1311,14 @@ var ComplianceTransaction = class _ComplianceTransaction {
995
1311
  this.amendedDate = amendedDate;
996
1312
  this.providerState = providerState;
997
1313
  this.invoiceLines = invoiceLines;
1314
+ this.documentUrl = documentUrl;
1315
+ this.deliveryCheck = deliveryCheck;
1316
+ this.invoicePdf = invoicePdf;
1317
+ this.internalReference = internalReference;
1318
+ this.pdfUrl = pdfUrl;
1319
+ this.buyerRejected = buyerRejected;
1320
+ this.rejectionReason = rejectionReason;
1321
+ this.eurEquivalent = eurEquivalent;
998
1322
  }
999
1323
  id;
1000
1324
  companyId;
@@ -1034,50 +1358,66 @@ var ComplianceTransaction = class _ComplianceTransaction {
1034
1358
  amendedDate;
1035
1359
  providerState;
1036
1360
  invoiceLines;
1361
+ documentUrl;
1362
+ deliveryCheck;
1363
+ invoicePdf;
1364
+ internalReference;
1365
+ pdfUrl;
1366
+ buyerRejected;
1367
+ rejectionReason;
1368
+ eurEquivalent;
1037
1369
  get isCreditNote() {
1038
1370
  return this.documentType === ComplianceDocumentType.CREDIT_NOTE;
1039
1371
  }
1040
1372
  static fromArray(data) {
1041
1373
  const taxReport = typeof data["tax_report"] === "object" && data["tax_report"] !== null && !Array.isArray(data["tax_report"]) ? ComplianceTaxReport.fromArray(data["tax_report"]) : void 0;
1042
1374
  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"]),
1375
+ asNumber4(data["id"]),
1376
+ asNumber4(data["company_id"]),
1377
+ asNumber4(data["compliance_enrollment_id"]),
1378
+ asString5(data["country"]),
1379
+ asString5(data["regime"]),
1048
1380
  toComplianceTransactionType(data["transaction_type"]),
1049
- asString3(data["transaction_type_label"]),
1381
+ asString5(data["transaction_type_label"]),
1050
1382
  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"),
1383
+ asString5(data["state_label"]),
1384
+ asString5(data["invoice_number"]),
1385
+ asNullableString5(data["invoice_date"]),
1386
+ asNullableString5(data["due_date"]),
1387
+ asString5(data["currency"], "EUR"),
1056
1388
  asMoney2(data["subtotal"]),
1057
1389
  asMoney2(data["tax_amount"]),
1058
1390
  asMoney2(data["total"]),
1059
- asNullableString3(data["counterparty_name"]),
1060
- asNullableString3(data["counterparty_country"]),
1061
- asNullableString3(data["counterparty_vat_number"]),
1391
+ asNullableString5(data["counterparty_name"]),
1392
+ asNullableString5(data["counterparty_country"]),
1393
+ asNullableString5(data["counterparty_vat_number"]),
1062
1394
  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"]),
1395
+ asNullableString5(data["paid_at"]),
1396
+ asNullableString5(data["provider_invoice_id"]),
1397
+ asNullableString5(data["submission_error"]),
1398
+ asNullableString5(data["reported_at"]),
1067
1399
  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"]),
1400
+ asNullableString5(data["created_at"]),
1401
+ asNullableString5(data["updated_at"]),
1402
+ asNullableString5(data["counterparty_register_id"]),
1403
+ asNullableString5(data["counterparty_address"]),
1404
+ asNullableString5(data["counterparty_city"]),
1405
+ asNullableString5(data["counterparty_postalcode"]),
1406
+ asNullableString5(data["counterparty_email"]),
1407
+ asNullableString5(data["buyer_reference"]),
1076
1408
  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"])
1409
+ asNullableString5(data["amended_number"]),
1410
+ asNullableString5(data["amended_date"]),
1411
+ asNullableString5(data["provider_state"]),
1412
+ toInvoiceLines(data["provider_payload"]),
1413
+ asNullableString5(data["document_url"]),
1414
+ asRecord3(data["delivery_check"]),
1415
+ toInvoicePdf(data["invoice_pdf"]),
1416
+ asNullableString5(data["internal_reference"]),
1417
+ asNullableString5(data["pdf_url"]),
1418
+ data["buyer_rejected"] === true,
1419
+ asNullableString5(data["rejection_reason"]),
1420
+ toEurEquivalent(data["eur_equivalent"])
1081
1421
  );
1082
1422
  }
1083
1423
  };
@@ -1164,10 +1504,10 @@ function describePeppolLookupStatus(status) {
1164
1504
  }
1165
1505
 
1166
1506
  // src/dto/PeppolLookupResult.ts
1167
- function asString4(value, fallback = "") {
1507
+ function asString6(value, fallback = "") {
1168
1508
  return typeof value === "string" ? value : fallback;
1169
1509
  }
1170
- function asNullableString4(value) {
1510
+ function asNullableString6(value) {
1171
1511
  return typeof value === "string" ? value : null;
1172
1512
  }
1173
1513
  var PeppolLookupResult = class _PeppolLookupResult {
@@ -1198,21 +1538,21 @@ var PeppolLookupResult = class _PeppolLookupResult {
1198
1538
  return new _PeppolLookupResult(
1199
1539
  toPeppolLookupStatus(data["status"]),
1200
1540
  typeof data["reachable"] === "boolean" ? data["reachable"] : null,
1201
- asString4(data["country"]),
1202
- asString4(data["scheme"]),
1203
- asString4(data["id"]),
1541
+ asString6(data["country"]),
1542
+ asString6(data["scheme"]),
1543
+ asString6(data["id"]),
1204
1544
  documentTypes,
1205
- asNullableString4(data["transport_type_code"]),
1206
- asNullableString4(data["checked_at"])
1545
+ asNullableString6(data["transport_type_code"]),
1546
+ asNullableString6(data["checked_at"])
1207
1547
  );
1208
1548
  }
1209
1549
  };
1210
1550
 
1211
1551
  // src/dto/RegistryCompany.ts
1212
- function asString5(value, fallback = "") {
1552
+ function asString7(value, fallback = "") {
1213
1553
  return typeof value === "string" ? value : fallback;
1214
1554
  }
1215
- function asNullableString5(value) {
1555
+ function asNullableString7(value) {
1216
1556
  return typeof value === "string" ? value : null;
1217
1557
  }
1218
1558
  var RegistryCompany = class _RegistryCompany {
@@ -1244,27 +1584,27 @@ var RegistryCompany = class _RegistryCompany {
1244
1584
  country;
1245
1585
  static fromArray(data) {
1246
1586
  return new _RegistryCompany(
1247
- asString5(data["org_number"]),
1248
- asString5(data["company_name"]),
1249
- asNullableString5(data["organisation_form"]),
1587
+ asString7(data["org_number"]),
1588
+ asString7(data["company_name"]),
1589
+ asNullableString7(data["organisation_form"]),
1250
1590
  data["vat_registered"] === true,
1251
- asNullableString5(data["vat_number"]),
1591
+ asNullableString7(data["vat_number"]),
1252
1592
  data["enterprise_register"] === true,
1253
1593
  data["bankrupt"] === true,
1254
1594
  data["under_liquidation"] === true,
1255
- asNullableString5(data["address"]),
1256
- asNullableString5(data["postalcode"]),
1257
- asNullableString5(data["city"]),
1258
- asString5(data["country"])
1595
+ asNullableString7(data["address"]),
1596
+ asNullableString7(data["postalcode"]),
1597
+ asNullableString7(data["city"]),
1598
+ asString7(data["country"])
1259
1599
  );
1260
1600
  }
1261
1601
  };
1262
1602
 
1263
1603
  // src/dto/RevenueStatistics.ts
1264
- function asString6(value, fallback = "") {
1604
+ function asString8(value, fallback = "") {
1265
1605
  return typeof value === "string" ? value : fallback;
1266
1606
  }
1267
- function asNumber4(value) {
1607
+ function asNumber5(value) {
1268
1608
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
1269
1609
  }
1270
1610
  function asMoney3(value) {
@@ -1276,7 +1616,7 @@ function asRecords(value) {
1276
1616
  }
1277
1617
  function toTotals(data) {
1278
1618
  return {
1279
- count: asNumber4(data["count"]),
1619
+ count: asNumber5(data["count"]),
1280
1620
  subtotal: asMoney3(data["subtotal"]),
1281
1621
  taxAmount: asMoney3(data["tax_amount"]),
1282
1622
  total: asMoney3(data["total"])
@@ -1313,35 +1653,35 @@ var RevenueStatistics = class _RevenueStatistics {
1313
1653
  typeof data["totals"] === "object" && data["totals"] !== null ? data["totals"] : {}
1314
1654
  );
1315
1655
  const timeSeries = asRecords(data["time_series"]).map((row) => ({
1316
- bucket: asString6(row["bucket"]),
1656
+ bucket: asString8(row["bucket"]),
1317
1657
  ...toTotals(row)
1318
1658
  }));
1319
1659
  const byType = asRecords(data["by_type"]).map((row) => ({
1320
- type: asString6(row["type"]),
1321
- typeLabel: asString6(row["type_label"]),
1322
- count: asNumber4(row["count"]),
1660
+ type: asString8(row["type"]),
1661
+ typeLabel: asString8(row["type_label"]),
1662
+ count: asNumber5(row["count"]),
1323
1663
  total: asMoney3(row["total"])
1324
1664
  }));
1325
1665
  const byCountry = asRecords(data["by_country"]).map((row) => ({
1326
- country: asString6(row["country"], "unknown"),
1327
- count: asNumber4(row["count"]),
1666
+ country: asString8(row["country"], "unknown"),
1667
+ count: asNumber5(row["count"]),
1328
1668
  total: asMoney3(row["total"])
1329
1669
  }));
1330
1670
  const byState = asRecords(data["by_state"]).map((row) => ({
1331
- state: asString6(row["state"]),
1332
- stateLabel: asString6(row["state_label"]),
1333
- count: asNumber4(row["count"]),
1671
+ state: asString8(row["state"]),
1672
+ stateLabel: asString8(row["state_label"]),
1673
+ count: asNumber5(row["count"]),
1334
1674
  total: asMoney3(row["total"])
1335
1675
  }));
1336
1676
  const byCurrency = asRecords(data["by_currency"]).map((row) => ({
1337
- currency: asString6(row["currency"]),
1677
+ currency: asString8(row["currency"]),
1338
1678
  ...toTotals(row)
1339
1679
  }));
1340
1680
  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"),
1681
+ asString8(range["date_from"]) || void 0,
1682
+ asString8(range["date_to"]) || void 0,
1683
+ asString8(range["interval"]) || void 0,
1684
+ asString8(data["primary_currency"], "EUR"),
1345
1685
  data["is_multi_currency"] === true,
1346
1686
  totals,
1347
1687
  timeSeries,
@@ -1354,10 +1694,10 @@ var RevenueStatistics = class _RevenueStatistics {
1354
1694
  };
1355
1695
 
1356
1696
  // src/dto/SireneLookupResult.ts
1357
- function asString7(value, fallback = "") {
1697
+ function asString9(value, fallback = "") {
1358
1698
  return typeof value === "string" ? value : fallback;
1359
1699
  }
1360
- function asNullableString6(value) {
1700
+ function asNullableString8(value) {
1361
1701
  return typeof value === "string" ? value : null;
1362
1702
  }
1363
1703
  var SireneLookupResult = class _SireneLookupResult {
@@ -1383,24 +1723,24 @@ var SireneLookupResult = class _SireneLookupResult {
1383
1723
  postalcode;
1384
1724
  static fromArray(data) {
1385
1725
  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"])
1726
+ asString9(data["company_name"]),
1727
+ asString9(data["siren"]),
1728
+ asNullableString8(data["siret"]),
1729
+ asNullableString8(data["naf_code"]),
1730
+ asNullableString8(data["naf_full"]),
1731
+ asNullableString8(data["enterprise_size"]),
1732
+ asNullableString8(data["address"]),
1733
+ asNullableString8(data["city"]),
1734
+ asNullableString8(data["postalcode"])
1395
1735
  );
1396
1736
  }
1397
1737
  };
1398
1738
 
1399
1739
  // src/dto/VatRates.ts
1400
- function asString8(value, fallback = "") {
1740
+ function asString10(value, fallback = "") {
1401
1741
  return typeof value === "string" ? value : fallback;
1402
1742
  }
1403
- function asNumber5(value) {
1743
+ function asNumber6(value) {
1404
1744
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
1405
1745
  }
1406
1746
  var VatRates = class _VatRates {
@@ -1414,13 +1754,13 @@ var VatRates = class _VatRates {
1414
1754
  rates;
1415
1755
  static fromArray(data) {
1416
1756
  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"]),
1757
+ percent: asNumber6(row["percent"]),
1758
+ category: asString10(row["category"]),
1759
+ labelKey: asString10(row["label_key"]),
1760
+ taxName: asString10(row["tax_name"]),
1421
1761
  requiresVatex: row["requires_vatex"] === true
1422
1762
  }));
1423
- return new _VatRates(asString8(data["country"]), asString8(data["currency"], "EUR"), rates);
1763
+ return new _VatRates(asString10(data["country"]), asString10(data["currency"], "EUR"), rates);
1424
1764
  }
1425
1765
  };
1426
1766
 
@@ -1479,10 +1819,15 @@ var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
1479
1819
  var NAF_CODE_REGEX = /^\d{2}$/;
1480
1820
  var COUNTRY_REGEX = /^[A-Za-z]{2}$/;
1481
1821
  var PEPPOL_SCHEME_REGEX = /^\d{4}$/;
1822
+ var INVOICE_PDF_MAX_BYTES = 10 * 1024 * 1024;
1823
+ var INVOICE_PDF_MAGIC = [37, 80, 68, 70, 45];
1482
1824
  var INTERVALS = ["day", "week", "month"];
1483
1825
  var SERVICES = ["e_reporting", "e_invoicing"];
1484
1826
  var ENTERPRISE_SIZES = ["micro", "pme", "eti", "ge"];
1485
1827
  var TYPE_OPERATIONS = ["services", "goods", "mixed"];
1828
+ var NOTIFICATION_LANGUAGES = ["de", "en"];
1829
+ var NOTIFICATION_DIGESTS = ["off", "daily", "weekly"];
1830
+ var NOTIFICATION_MAX_RECIPIENTS = 5;
1486
1831
  var TRANSACTION_TYPES = [
1487
1832
  "b2c_outbound",
1488
1833
  "b2b_domestic_outbound",
@@ -1677,6 +2022,8 @@ var EReportingEndpoint = class {
1677
2022
  this.validateInvoiceLines(input.invoiceLines);
1678
2023
  this.validateOptionalCodes(input);
1679
2024
  this.validateBuyerFields(input);
2025
+ this.validateInternalReference(input.internalReference);
2026
+ this.validateInvoicePdfFilename(input);
1680
2027
  const body = {
1681
2028
  compliance_enrollment_id: input.complianceEnrollmentId,
1682
2029
  transaction_type: input.transactionType,
@@ -1692,23 +2039,9 @@ var EReportingEndpoint = class {
1692
2039
  const data = await this.parseJsonResponse(response);
1693
2040
  return ComplianceTransaction.fromArray(data);
1694
2041
  }
1695
- /** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type / enrollment / country. */
2042
+ /** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type / enrollment / country / search / internal reference / provider state. */
1696
2043
  async listTransactions(filters = {}) {
1697
- if (filters.complianceEnrollmentId !== void 0) {
1698
- this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
1699
- }
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.");
1702
- }
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));
1710
- }
1711
- if (filters.country !== void 0) params.set("country", filters.country.toUpperCase());
2044
+ const params = this.buildTransactionFilterParams(filters);
1712
2045
  if (filters.page !== void 0) params.set("page", String(filters.page));
1713
2046
  if (filters.perPage !== void 0) params.set("per_page", String(filters.perPage));
1714
2047
  const queryString = params.toString();
@@ -1716,6 +2049,27 @@ var EReportingEndpoint = class {
1716
2049
  const response = await this.sendRequest("GET", url);
1717
2050
  return ComplianceTransactionPage.fromResponse(await this.parseJsonResponse(response));
1718
2051
  }
2052
+ /**
2053
+ * All transactions matching the filters as raw CSV for bookkeeping
2054
+ * (semicolon-separated) — the same filters as listTransactions() without
2055
+ * pagination. Columns: invoice_number, internal_reference, invoice_date,
2056
+ * due_date, country, transaction_type, document_type, state, delivery_state,
2057
+ * buyer_rejected, rejection_reason, counterparty_name, counterparty_country,
2058
+ * counterparty_vat_number, currency, subtotal, tax_amount, total, eur_rate,
2059
+ * eur_rate_date, eur_subtotal, eur_tax_amount, eur_total, provider_invoice_id,
2060
+ * created_at. The eur_* columns hold the ECB reference conversion for non-EUR
2061
+ * invoices (empty for EUR). Max 50,000 rows server-side. The body is returned
2062
+ * verbatim, a UTF-8 BOM included when the API sends one.
2063
+ */
2064
+ async exportTransactionsCsv(filters = {}) {
2065
+ const queryString = this.buildTransactionFilterParams(filters).toString();
2066
+ const url = `${this.baseUrl}/compliance/transactions/export.csv${queryString ? `?${queryString}` : ""}`;
2067
+ const response = await this.sendDownloadRequest(url, "text/csv, application/json");
2068
+ if (!response.ok) {
2069
+ await this.parseJsonResponse(response);
2070
+ }
2071
+ return new TextDecoder("utf-8", { ignoreBOM: true }).decode(await response.arrayBuffer());
2072
+ }
1719
2073
  /** Get a single transaction by id (includes its tax report when one exists). */
1720
2074
  async getTransaction(id) {
1721
2075
  this.validateId(id, "id");
@@ -1731,6 +2085,8 @@ var EReportingEndpoint = class {
1731
2085
  if (input.invoiceLines !== void 0) this.validateInvoiceLines(input.invoiceLines);
1732
2086
  this.validateOptionalCodes(input);
1733
2087
  this.validateBuyerFields(input);
2088
+ this.validateInternalReference(input.internalReference);
2089
+ this.validateInvoicePdfFilename(input);
1734
2090
  const body = {};
1735
2091
  if (input.transactionType !== void 0) body["transaction_type"] = input.transactionType;
1736
2092
  if (input.invoiceNumber !== void 0) body["invoice_number"] = input.invoiceNumber;
@@ -1765,6 +2121,47 @@ var EReportingEndpoint = class {
1765
2121
  const data = await this.parseJsonResponse(response);
1766
2122
  return ComplianceTransaction.fromArray(data);
1767
2123
  }
2124
+ /**
2125
+ * Download the legal e-invoice document of a sent transaction (e-invoicing,
2126
+ * e.g. the EHF/UBL XML for Norway). Available once `documentUrl` on the
2127
+ * transaction is set.
2128
+ *
2129
+ * Errors: 404 (HttpException) for an unknown transaction; 409 (HttpException)
2130
+ * when no document exists (yet) — e.g. e-reporting, not sent, or the network
2131
+ * has not produced it yet (try again in a few minutes); 502 on provider failure.
2132
+ */
2133
+ async getTransactionDocument(id) {
2134
+ this.validateId(id, "id");
2135
+ const response = await this.sendDownloadRequest(`${this.baseUrl}/compliance/transactions/${id}/document`, "*/*");
2136
+ if (!response.ok) {
2137
+ await this.parseJsonResponse(response);
2138
+ }
2139
+ return {
2140
+ content: new Uint8Array(await response.arrayBuffer()),
2141
+ contentType: response.headers.get("content-type") ?? "application/octet-stream",
2142
+ filename: this.filenameFromDisposition(response.headers.get("content-disposition"))
2143
+ };
2144
+ }
2145
+ /**
2146
+ * Download the invoice as a readable PDF — the seller's attached PDF, or the
2147
+ * provider's rendering of the e-invoice. Available once `pdfUrl` on the
2148
+ * transaction is set.
2149
+ *
2150
+ * Errors: 404 (HttpException) for an unknown transaction; 409 (HttpException)
2151
+ * when no PDF exists (yet); 502 on provider failure.
2152
+ */
2153
+ async getTransactionPdf(id) {
2154
+ this.validateId(id, "id");
2155
+ const response = await this.sendDownloadRequest(`${this.baseUrl}/compliance/transactions/${id}/pdf`, "*/*");
2156
+ if (!response.ok) {
2157
+ await this.parseJsonResponse(response);
2158
+ }
2159
+ return {
2160
+ content: new Uint8Array(await response.arrayBuffer()),
2161
+ contentType: response.headers.get("content-type") ?? "application/octet-stream",
2162
+ filename: this.filenameFromDisposition(response.headers.get("content-disposition"))
2163
+ };
2164
+ }
1768
2165
  /**
1769
2166
  * Bulk-import transactions from a semicolon-separated CSV (one row per invoice
1770
2167
  * line; rows sharing an invoice_number are grouped into one transaction).
@@ -1796,6 +2193,9 @@ var EReportingEndpoint = class {
1796
2193
  * months and a monthly interval.
1797
2194
  */
1798
2195
  async getRevenueStatistics(filters = {}) {
2196
+ if (filters.complianceEnrollmentId !== void 0) {
2197
+ this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
2198
+ }
1799
2199
  if (filters.interval !== void 0 && !INTERVALS.includes(filters.interval)) {
1800
2200
  throw new TaxoraException(
1801
2201
  `Invalid statistics interval "${String(filters.interval)}". Expected one of: ${INTERVALS.join(", ")}.`
@@ -1807,12 +2207,42 @@ var EReportingEndpoint = class {
1807
2207
  if (filters.interval !== void 0) params.set("interval", filters.interval);
1808
2208
  if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
1809
2209
  if (filters.state !== void 0) params.set("state", filters.state);
2210
+ if (filters.complianceEnrollmentId !== void 0) {
2211
+ params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
2212
+ }
1810
2213
  const queryString = params.toString();
1811
2214
  const url = `${this.baseUrl}/compliance/revenue-statistics${queryString ? `?${queryString}` : ""}`;
1812
2215
  const response = await this.sendRequest("GET", url);
1813
2216
  const data = await this.parseJsonResponse(response);
1814
2217
  return RevenueStatistics.fromArray(data);
1815
2218
  }
2219
+ /**
2220
+ * Net and VAT per month, currency and tax rate as raw CSV (semicolon-separated,
2221
+ * computed from the invoice lines) — for reconciling the reported figures with
2222
+ * your VAT return. Same filters as getRevenueStatistics() (without
2223
+ * `interval`); defaults to the last 12 months. The body is returned verbatim,
2224
+ * a UTF-8 BOM included when the API sends one.
2225
+ */
2226
+ async exportRevenueTaxRatesCsv(filters = {}) {
2227
+ if (filters.complianceEnrollmentId !== void 0) {
2228
+ this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
2229
+ }
2230
+ const params = new URLSearchParams();
2231
+ if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
2232
+ if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
2233
+ if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
2234
+ if (filters.state !== void 0) params.set("state", filters.state);
2235
+ if (filters.complianceEnrollmentId !== void 0) {
2236
+ params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
2237
+ }
2238
+ const queryString = params.toString();
2239
+ const url = `${this.baseUrl}/compliance/revenue-statistics/tax-rates.csv${queryString ? `?${queryString}` : ""}`;
2240
+ const response = await this.sendDownloadRequest(url, "text/csv, application/json");
2241
+ if (!response.ok) {
2242
+ await this.parseJsonResponse(response);
2243
+ }
2244
+ return new TextDecoder("utf-8", { ignoreBOM: true }).decode(await response.arrayBuffer());
2245
+ }
1816
2246
  /**
1817
2247
  * The canonical VAT rates & DGFiP tax categories for a reporting country
1818
2248
  * (the seller's reporting-country rates, not the counterparty's). Falls back
@@ -1827,12 +2257,19 @@ var EReportingEndpoint = class {
1827
2257
  const data = await this.parseJsonResponse(response);
1828
2258
  return VatRates.fromArray(data);
1829
2259
  }
1830
- /** Paginated list of the company's tax reports (newest first), optionally filtered by state. */
1831
- async listTaxReports(page = 1, perPage = 25, state) {
2260
+ /**
2261
+ * Paginated list of the company's tax reports (newest first), optionally
2262
+ * filtered by state and by country (2-letter ISO code, case-insensitive).
2263
+ */
2264
+ async listTaxReports(page = 1, perPage = 25, state, country) {
2265
+ if (country !== void 0 && !COUNTRY_REGEX.test(country)) {
2266
+ throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
2267
+ }
1832
2268
  const params = new URLSearchParams();
1833
2269
  params.set("page", String(page));
1834
2270
  params.set("per_page", String(perPage));
1835
2271
  if (state !== void 0) params.set("state", state);
2272
+ if (country !== void 0) params.set("country", country.toUpperCase());
1836
2273
  const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/tax-reports?${params.toString()}`);
1837
2274
  return ComplianceTaxReportPage.fromResponse(await this.parseJsonResponse(response));
1838
2275
  }
@@ -1874,8 +2311,177 @@ var EReportingEndpoint = class {
1874
2311
  await this.parseJsonResponse(response);
1875
2312
  }
1876
2313
  // ---------------------------------------------------------------------
2314
+ // Self-service plans (Stripe) — not gated by compliance access
2315
+ // ---------------------------------------------------------------------
2316
+ /**
2317
+ * Bookable compliance plans per country (net EUR prices), every country
2318
+ * Taxora reaches, and which services the company has unlocked. Works for
2319
+ * companies without compliance access.
2320
+ */
2321
+ async getOfferings() {
2322
+ const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/offerings`);
2323
+ return ComplianceOfferings.fromArray(await this.parseJsonResponse(response));
2324
+ }
2325
+ /**
2326
+ * Start a Stripe Checkout for a plan (see getOfferings() — `checkoutAvailable`).
2327
+ * Open the returned `url` in the browser. After payment Stripe redirects to
2328
+ * the Taxora portal, which activates the plan; call confirmCheckout() with the
2329
+ * session id to activate it from your side (idempotent).
2330
+ *
2331
+ * Errors: 422 (ValidationException) for an unknown or contact-only plan or a
2332
+ * plan that is already active; 502 (HttpException) on a Stripe error.
2333
+ */
2334
+ async createCheckout(country, plan) {
2335
+ if (!COUNTRY_REGEX.test(country)) {
2336
+ throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
2337
+ }
2338
+ if (plan.trim() === "" || plan.length > 30) {
2339
+ throw new TaxoraException("plan must be a non-empty plan key of at most 30 characters.");
2340
+ }
2341
+ const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/checkout`, {
2342
+ country: country.toUpperCase(),
2343
+ plan
2344
+ });
2345
+ const data = await this.parseJsonResponse(response);
2346
+ return { id: String(data["id"] ?? ""), url: String(data["url"] ?? "") };
2347
+ }
2348
+ /** Confirm a finished Stripe Checkout and activate its plan. Idempotent. */
2349
+ async confirmCheckout(sessionId) {
2350
+ if (sessionId.trim() === "" || sessionId.length > 255) {
2351
+ throw new TaxoraException("sessionId must be a non-empty string of at most 255 characters.");
2352
+ }
2353
+ const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/checkout/confirm`, {
2354
+ session_id: sessionId
2355
+ });
2356
+ const data = await this.parseJsonResponse(response);
2357
+ const str = (value) => typeof value === "string" ? value : null;
2358
+ return {
2359
+ active: data["active"] === true,
2360
+ country: str(data["country"]),
2361
+ service: str(data["service"]),
2362
+ plan: str(data["plan"]),
2363
+ status: str(data["status"])
2364
+ };
2365
+ }
2366
+ /**
2367
+ * URL of the Stripe billing portal for the company's compliance plans
2368
+ * (invoices, payment method, cancellation). The portal returns to the Taxora
2369
+ * portal. 404 (HttpException) when no plan was bought via Stripe.
2370
+ */
2371
+ async createBillingPortalSession() {
2372
+ const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/billing-portal`);
2373
+ const data = await this.parseJsonResponse(response);
2374
+ return String(data["url"] ?? "");
2375
+ }
2376
+ // ---------------------------------------------------------------------
2377
+ // Notification emails
2378
+ // ---------------------------------------------------------------------
2379
+ /**
2380
+ * The company's opt-in e-reporting / e-invoicing emails: recipients, language,
2381
+ * the "buyer not on Peppol" email and the digest. Readable by every member.
2382
+ */
2383
+ async getNotificationSettings() {
2384
+ const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/notification-settings`);
2385
+ return this.toNotificationSettings(await this.parseJsonResponse(response));
2386
+ }
2387
+ /**
2388
+ * Change the company's e-reporting / e-invoicing emails; only the provided
2389
+ * fields change. `notOnPeppol` only reports invoices held back after it was
2390
+ * switched on.
2391
+ *
2392
+ * Errors: 422 (ValidationException) when an email is switched on without any
2393
+ * recipient; 403 (HttpException, body code `admin_only` or `read_only_role`)
2394
+ * for users who are not company admins.
2395
+ */
2396
+ async updateNotificationSettings(input) {
2397
+ const body = {};
2398
+ if (input.recipients !== void 0) {
2399
+ if (!Array.isArray(input.recipients)) {
2400
+ throw new TaxoraException("recipients must be an array of email addresses.");
2401
+ }
2402
+ if (input.recipients.length > NOTIFICATION_MAX_RECIPIENTS) {
2403
+ throw new TaxoraException(`recipients must not contain more than ${NOTIFICATION_MAX_RECIPIENTS} entries.`);
2404
+ }
2405
+ body["recipients"] = input.recipients.map((recipient, index) => {
2406
+ const email = typeof recipient === "string" ? recipient.trim() : recipient;
2407
+ this.validateEmail(email, `recipients[${index}]`);
2408
+ if (email.length > 191) {
2409
+ throw new TaxoraException(`recipients[${index}] must not exceed 191 characters.`);
2410
+ }
2411
+ return email;
2412
+ });
2413
+ }
2414
+ if (input.language !== void 0) {
2415
+ if (!NOTIFICATION_LANGUAGES.includes(input.language)) {
2416
+ throw new TaxoraException(
2417
+ `Invalid language "${String(input.language)}". Expected one of: ${NOTIFICATION_LANGUAGES.join(", ")}.`
2418
+ );
2419
+ }
2420
+ body["language"] = input.language;
2421
+ }
2422
+ if (input.notOnPeppol !== void 0) {
2423
+ if (typeof input.notOnPeppol !== "boolean") {
2424
+ throw new TaxoraException("notOnPeppol must be a boolean.");
2425
+ }
2426
+ body["not_on_peppol"] = input.notOnPeppol;
2427
+ }
2428
+ if (input.digest !== void 0) {
2429
+ if (!NOTIFICATION_DIGESTS.includes(input.digest)) {
2430
+ throw new TaxoraException(
2431
+ `Invalid digest "${String(input.digest)}". Expected one of: ${NOTIFICATION_DIGESTS.join(", ")}.`
2432
+ );
2433
+ }
2434
+ body["digest"] = input.digest;
2435
+ }
2436
+ if (Object.keys(body).length === 0) {
2437
+ throw new TaxoraException("updateNotificationSettings requires at least one field to update.");
2438
+ }
2439
+ const response = await this.sendRequest("PUT", `${this.baseUrl}/compliance/notification-settings`, body);
2440
+ return this.toNotificationSettings(await this.parseJsonResponse(response));
2441
+ }
2442
+ toNotificationSettings(data) {
2443
+ const recipients = Array.isArray(data["recipients"]) ? data["recipients"].filter((r) => typeof r === "string" && r !== "") : [];
2444
+ const digest = data["digest"];
2445
+ return {
2446
+ recipients,
2447
+ language: data["language"] === "en" ? "en" : "de",
2448
+ notOnPeppol: data["not_on_peppol"] === true,
2449
+ digest: digest === "daily" || digest === "weekly" ? digest : "off",
2450
+ digestSentAt: typeof data["digest_sent_at"] === "string" ? data["digest_sent_at"] : null
2451
+ };
2452
+ }
2453
+ // ---------------------------------------------------------------------
1877
2454
  // Input validation (fail fast, before any request is sent)
1878
2455
  // ---------------------------------------------------------------------
2456
+ /** Validated transaction-list filters as query params (shared by listTransactions and exportTransactionsCsv). */
2457
+ buildTransactionFilterParams(filters) {
2458
+ if (filters.complianceEnrollmentId !== void 0) {
2459
+ this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
2460
+ }
2461
+ if (filters.country !== void 0 && !COUNTRY_REGEX.test(filters.country)) {
2462
+ throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
2463
+ }
2464
+ if (filters.search !== void 0 && filters.search.length > 100) {
2465
+ throw new TaxoraException("search must not exceed 100 characters.");
2466
+ }
2467
+ this.validateInternalReference(filters.internalReference);
2468
+ if (filters.providerState !== void 0 && !/^[a-z_]{1,30}$/.test(filters.providerState)) {
2469
+ throw new TaxoraException('providerState must be 1-30 lowercase letters or underscores (e.g. "refused").');
2470
+ }
2471
+ const params = new URLSearchParams();
2472
+ if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
2473
+ if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
2474
+ if (filters.state !== void 0) params.set("state", filters.state);
2475
+ if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
2476
+ if (filters.complianceEnrollmentId !== void 0) {
2477
+ params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
2478
+ }
2479
+ if (filters.country !== void 0) params.set("country", filters.country.toUpperCase());
2480
+ if (filters.search !== void 0) params.set("search", filters.search);
2481
+ if (filters.internalReference !== void 0) params.set("internal_reference", filters.internalReference);
2482
+ if (filters.providerState !== void 0) params.set("provider_state", filters.providerState);
2483
+ return params;
2484
+ }
1879
2485
  validateEnrollmentInput(input) {
1880
2486
  if (input.siret === void 0 && input.siren === void 0) {
1881
2487
  throw new TaxoraException("Either siret or siren is required.");
@@ -2003,6 +2609,20 @@ var EReportingEndpoint = class {
2003
2609
  }
2004
2610
  });
2005
2611
  }
2612
+ validateInvoicePdfFilename(input) {
2613
+ if (input.invoicePdfFilename === void 0) return;
2614
+ if (input.invoicePdfFilename.length > 120) {
2615
+ throw new TaxoraException("invoicePdfFilename must not exceed 120 characters.");
2616
+ }
2617
+ if (input.invoicePdf === void 0) {
2618
+ throw new TaxoraException("invoicePdfFilename requires invoicePdf.");
2619
+ }
2620
+ }
2621
+ validateInternalReference(internalReference) {
2622
+ if (internalReference !== void 0 && internalReference.length > 100) {
2623
+ throw new TaxoraException("internalReference must not exceed 100 characters.");
2624
+ }
2625
+ }
2006
2626
  validateOptionalCodes(input) {
2007
2627
  if (input.currency !== void 0 && input.currency.length !== 3) {
2008
2628
  throw new TaxoraException("currency must be a 3-character ISO 4217 code.");
@@ -2084,6 +2704,29 @@ var EReportingEndpoint = class {
2084
2704
  if (input.amendedNumber !== void 0) body["amended_number"] = input.amendedNumber;
2085
2705
  if (input.amendedDate !== void 0) body["amended_date"] = this.formatDate(input.amendedDate);
2086
2706
  if (input.extraInfo !== void 0) body["extra_info"] = input.extraInfo;
2707
+ if (input.internalReference !== void 0) body["internal_reference"] = input.internalReference;
2708
+ if (input.invoicePdf !== void 0) body["invoice_pdf"] = this.encodeInvoicePdf(input.invoicePdf);
2709
+ if (input.invoicePdfFilename !== void 0) body["invoice_pdf_filename"] = input.invoicePdfFilename;
2710
+ }
2711
+ /** Bytes → base64 (validated as a PDF of at most 10 MB); strings are taken as already base64. */
2712
+ encodeInvoicePdf(pdf) {
2713
+ if (typeof pdf === "string") {
2714
+ if (pdf.trim() === "") {
2715
+ throw new TaxoraException("invoicePdf must not be empty.");
2716
+ }
2717
+ return pdf;
2718
+ }
2719
+ const bytes = pdf instanceof Uint8Array ? pdf : new Uint8Array(pdf);
2720
+ if (bytes.byteLength === 0) {
2721
+ throw new TaxoraException("invoicePdf must not be empty.");
2722
+ }
2723
+ if (bytes.byteLength > INVOICE_PDF_MAX_BYTES) {
2724
+ throw new TaxoraException("invoicePdf must not exceed 10 MB.");
2725
+ }
2726
+ if (!INVOICE_PDF_MAGIC.every((byte, index) => bytes[index] === byte)) {
2727
+ throw new TaxoraException('invoicePdf must be a PDF file (content must start with "%PDF-").');
2728
+ }
2729
+ return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
2087
2730
  }
2088
2731
  // ---------------------------------------------------------------------
2089
2732
  // HTTP plumbing (kept in sync with SmartEnrichmentEndpoint)
@@ -2107,6 +2750,30 @@ var EReportingEndpoint = class {
2107
2750
  const send = () => this.httpClient.request(method, url, options);
2108
2751
  return method === "GET" ? withResponseRetries(this.retryPolicy, "e-reporting request", send) : send();
2109
2752
  }
2753
+ /** GET for a non-JSON download (CSV / document): no JSON Content-Type, retried like other GETs. */
2754
+ async sendDownloadRequest(url, accept) {
2755
+ const headers = this.buildHeaders(false);
2756
+ headers["Accept"] = accept;
2757
+ return withResponseRetries(
2758
+ this.retryPolicy,
2759
+ "e-reporting download",
2760
+ () => this.httpClient.request("GET", url, { headers })
2761
+ );
2762
+ }
2763
+ /** `attachment; filename="x.xml"` (or RFC 5987 `filename*=UTF-8''x.xml`) → "x.xml". */
2764
+ filenameFromDisposition(header) {
2765
+ if (header === null) return null;
2766
+ const extended = /filename\*\s*=\s*(?:UTF-8|utf-8)''([^;]+)/.exec(header);
2767
+ if (extended?.[1] !== void 0) {
2768
+ try {
2769
+ return decodeURIComponent(extended[1].trim());
2770
+ } catch {
2771
+ }
2772
+ }
2773
+ const plain = /filename\s*=\s*"([^"]*)"|filename\s*=\s*([^;]+)/.exec(header);
2774
+ const name = (plain?.[1] ?? plain?.[2])?.trim();
2775
+ return name !== void 0 && name !== "" ? name : null;
2776
+ }
2110
2777
  /** Multipart upload: Content-Type is left to fetch so the boundary is set correctly. */
2111
2778
  async sendMultipartRequest(method, url, form) {
2112
2779
  return this.httpClient.request(method, url, { headers: this.buildHeaders(false), body: form });
@@ -2201,6 +2868,73 @@ var EReportingEndpoint = class {
2201
2868
  }
2202
2869
  };
2203
2870
 
2871
+ // src/dto/SmartEnrichmentRecheck.ts
2872
+ var toIndexes = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "number" && Number.isInteger(v)) : [];
2873
+ var SmartEnrichmentRecheck = class _SmartEnrichmentRecheck {
2874
+ constructor(jobId, rows, checks, skipped, dryRun) {
2875
+ this.jobId = jobId;
2876
+ this.rows = rows;
2877
+ this.checks = checks;
2878
+ this.skipped = skipped;
2879
+ this.dryRun = dryRun;
2880
+ }
2881
+ jobId;
2882
+ rows;
2883
+ checks;
2884
+ skipped;
2885
+ dryRun;
2886
+ static fromArray(data) {
2887
+ const skipped = {};
2888
+ const rawSkipped = data["skipped"];
2889
+ if (typeof rawSkipped === "object" && rawSkipped !== null && !Array.isArray(rawSkipped)) {
2890
+ for (const [reason, count] of Object.entries(rawSkipped)) {
2891
+ if (typeof count === "number") skipped[reason] = count;
2892
+ }
2893
+ }
2894
+ return new _SmartEnrichmentRecheck(
2895
+ typeof data["jobId"] === "string" ? data["jobId"] : "",
2896
+ toIndexes(data["rows"]),
2897
+ typeof data["checks"] === "number" ? data["checks"] : 0,
2898
+ skipped,
2899
+ data["dryRun"] === true
2900
+ );
2901
+ }
2902
+ };
2903
+ var SmartEnrichmentRecheckState = class _SmartEnrichmentRecheckState {
2904
+ constructor(eligible, runs, maxPerRow, cooldownDays) {
2905
+ this.eligible = eligible;
2906
+ this.runs = runs;
2907
+ this.maxPerRow = maxPerRow;
2908
+ this.cooldownDays = cooldownDays;
2909
+ }
2910
+ eligible;
2911
+ runs;
2912
+ maxPerRow;
2913
+ cooldownDays;
2914
+ static fromArray(data) {
2915
+ const runs = {};
2916
+ const rawRuns = data["runs"];
2917
+ if (typeof rawRuns === "object" && rawRuns !== null && !Array.isArray(rawRuns)) {
2918
+ for (const [index, run] of Object.entries(rawRuns)) {
2919
+ if (!/^\d+$/.test(index) || typeof run !== "object" || run === null) continue;
2920
+ const { state, resolved } = run;
2921
+ if (state !== "queued" && state !== "running" && state !== "finished" && state !== "failed") continue;
2922
+ runs[Number(index)] = typeof resolved === "boolean" ? { state, resolved } : { state };
2923
+ }
2924
+ }
2925
+ return new _SmartEnrichmentRecheckState(
2926
+ toIndexes(data["eligible"]),
2927
+ runs,
2928
+ typeof data["maxPerRow"] === "number" ? data["maxPerRow"] : 3,
2929
+ typeof data["cooldownDays"] === "number" ? data["cooldownDays"] : 7
2930
+ );
2931
+ }
2932
+ /** True while any recheck of this lookup is queued or running. */
2933
+ isActive() {
2934
+ return Object.values(this.runs).some((run) => run.state === "queued" || run.state === "running");
2935
+ }
2936
+ };
2937
+
2204
2938
  // src/enums/SmartEnrichmentMode.ts
2205
2939
  var SmartEnrichmentMode = {
2206
2940
  DEFAULT: "default",
@@ -2330,7 +3064,7 @@ function toOfficialAddress(value) {
2330
3064
  return Object.keys(address).length > 0 ? address : void 0;
2331
3065
  }
2332
3066
  var SmartEnrichmentResource = class _SmartEnrichmentResource {
2333
- constructor(status, vatNumber, vatType, confidence, matchedCompanyName, matchedAddress, country, source, mode = void 0, providerVerdicts = [], addressQuality = void 0, rejectedCandidates = [], sourceUrl = void 0, registeredOwner = void 0, sourceUrls = [], taxNumber = void 0, vatValid = void 0, aiScore = void 0, nameMatch = void 0, officialName = void 0, officialAddress = void 0, taxNumberType = void 0, taxNumberLabel = void 0, taxNumberSource = void 0) {
3067
+ constructor(status, vatNumber, vatType, confidence, matchedCompanyName, matchedAddress, country, source, mode = void 0, providerVerdicts = [], addressQuality = void 0, rejectedCandidates = [], sourceUrl = void 0, registeredOwner = void 0, sourceUrls = [], taxNumber = void 0, vatValid = void 0, aiScore = void 0, nameMatch = void 0, officialName = void 0, officialAddress = void 0, taxNumberType = void 0, taxNumberLabel = void 0, taxNumberSource = void 0, recheckCount = void 0, lastRecheckedAt = void 0) {
2334
3068
  this.status = status;
2335
3069
  this.vatNumber = vatNumber;
2336
3070
  this.vatType = vatType;
@@ -2355,6 +3089,8 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
2355
3089
  this.taxNumberType = taxNumberType;
2356
3090
  this.taxNumberLabel = taxNumberLabel;
2357
3091
  this.taxNumberSource = taxNumberSource;
3092
+ this.recheckCount = recheckCount;
3093
+ this.lastRecheckedAt = lastRecheckedAt;
2358
3094
  }
2359
3095
  status;
2360
3096
  vatNumber;
@@ -2380,6 +3116,8 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
2380
3116
  taxNumberType;
2381
3117
  taxNumberLabel;
2382
3118
  taxNumberSource;
3119
+ recheckCount;
3120
+ lastRecheckedAt;
2383
3121
  /** True when the register knows the number under a different name than the one requested. */
2384
3122
  nameMismatch() {
2385
3123
  return this.nameMatch === "mismatch";
@@ -2430,7 +3168,9 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
2430
3168
  toOfficialAddress(data["officialAddress"]),
2431
3169
  text("taxNumberType"),
2432
3170
  text("taxNumberLabel"),
2433
- data["taxNumberSource"] === "vat_id" || data["taxNumberSource"] === "company_website" ? data["taxNumberSource"] : void 0
3171
+ data["taxNumberSource"] === "vat_id" || data["taxNumberSource"] === "company_website" ? data["taxNumberSource"] : void 0,
3172
+ num("recheckCount"),
3173
+ text("lastRecheckedAt")
2434
3174
  );
2435
3175
  }
2436
3176
  toArray() {
@@ -2458,20 +3198,23 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
2458
3198
  officialAddress: this.officialAddress ?? null,
2459
3199
  taxNumberType: this.taxNumberType ?? null,
2460
3200
  taxNumberLabel: this.taxNumberLabel ?? null,
2461
- taxNumberSource: this.taxNumberSource ?? null
3201
+ taxNumberSource: this.taxNumberSource ?? null,
3202
+ recheckCount: this.recheckCount ?? null,
3203
+ lastRecheckedAt: this.lastRecheckedAt ?? null
2462
3204
  };
2463
3205
  }
2464
3206
  };
2465
3207
 
2466
3208
  // src/dto/SmartEnrichmentJob.ts
2467
3209
  var SmartEnrichmentJob = class _SmartEnrichmentJob {
2468
- constructor(jobId, status, itemCount, completedCount, foundCount, results) {
3210
+ constructor(jobId, status, itemCount, completedCount, foundCount, results, recheck = void 0) {
2469
3211
  this.jobId = jobId;
2470
3212
  this.status = status;
2471
3213
  this.itemCount = itemCount;
2472
3214
  this.completedCount = completedCount;
2473
3215
  this.foundCount = foundCount;
2474
3216
  this.results = results;
3217
+ this.recheck = recheck;
2475
3218
  }
2476
3219
  jobId;
2477
3220
  status;
@@ -2479,6 +3222,7 @@ var SmartEnrichmentJob = class _SmartEnrichmentJob {
2479
3222
  completedCount;
2480
3223
  foundCount;
2481
3224
  results;
3225
+ recheck;
2482
3226
  static fromArray(data) {
2483
3227
  const num = (key) => {
2484
3228
  const v = data[key];
@@ -2496,7 +3240,8 @@ var SmartEnrichmentJob = class _SmartEnrichmentJob {
2496
3240
  num("itemCount"),
2497
3241
  num("completedCount"),
2498
3242
  num("foundCount"),
2499
- results
3243
+ results,
3244
+ typeof data["recheck"] === "object" && data["recheck"] !== null && !Array.isArray(data["recheck"]) ? SmartEnrichmentRecheckState.fromArray(data["recheck"]) : void 0
2500
3245
  );
2501
3246
  }
2502
3247
  isProcessing() {
@@ -2610,13 +3355,13 @@ var SmartEnrichmentHistoryPage = class _SmartEnrichmentHistoryPage {
2610
3355
  };
2611
3356
 
2612
3357
  // src/dto/SmartEnrichmentStatistics.ts
2613
- function asString9(value, fallback = "") {
3358
+ function asString11(value, fallback = "") {
2614
3359
  return typeof value === "string" ? value : fallback;
2615
3360
  }
2616
- function asNumber6(value) {
3361
+ function asNumber7(value) {
2617
3362
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
2618
3363
  }
2619
- function asRecord2(value) {
3364
+ function asRecord4(value) {
2620
3365
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
2621
3366
  }
2622
3367
  function asRecords2(value) {
@@ -2639,39 +3384,39 @@ var SmartEnrichmentStatistics = class _SmartEnrichmentStatistics {
2639
3384
  byOutcome;
2640
3385
  confidenceBuckets;
2641
3386
  static fromArray(data) {
2642
- const range = asRecord2(data["range"]);
2643
- const totals = asRecord2(data["totals"]);
3387
+ const range = asRecord4(data["range"]);
3388
+ const totals = asRecord4(data["totals"]);
2644
3389
  const timeSeries = asRecords2(data["time_series"]).map((row) => ({
2645
- bucket: asString9(row["bucket"]),
2646
- jobs: asNumber6(row["jobs"]),
2647
- items: asNumber6(row["items"]),
2648
- found: asNumber6(row["found"])
3390
+ bucket: asString11(row["bucket"]),
3391
+ jobs: asNumber7(row["jobs"]),
3392
+ items: asNumber7(row["items"]),
3393
+ found: asNumber7(row["found"])
2649
3394
  }));
2650
3395
  const bySource = asRecords2(data["by_source"]).map((row) => ({
2651
- source: asString9(row["source"]),
2652
- jobs: asNumber6(row["jobs"]),
2653
- found: asNumber6(row["found"])
3396
+ source: asString11(row["source"]),
3397
+ jobs: asNumber7(row["jobs"]),
3398
+ found: asNumber7(row["found"])
2654
3399
  }));
2655
3400
  const byOutcome = asRecords2(data["by_outcome"]).map((row) => ({
2656
- outcome: asString9(row["outcome"]),
2657
- count: asNumber6(row["count"])
3401
+ outcome: asString11(row["outcome"]),
3402
+ count: asNumber7(row["count"])
2658
3403
  }));
2659
3404
  const confidenceBuckets = asRecords2(data["confidence_buckets"]).map((row) => ({
2660
- bucket: asString9(row["bucket"]),
2661
- count: asNumber6(row["count"])
3405
+ bucket: asString11(row["bucket"]),
3406
+ count: asNumber7(row["count"])
2662
3407
  }));
2663
3408
  return new _SmartEnrichmentStatistics(
2664
3409
  {
2665
- dateFrom: asString9(range["date_from"]),
2666
- dateTo: asString9(range["date_to"]),
2667
- interval: asString9(range["interval"])
3410
+ dateFrom: asString11(range["date_from"]),
3411
+ dateTo: asString11(range["date_to"]),
3412
+ interval: asString11(range["interval"])
2668
3413
  },
2669
3414
  {
2670
- jobs: asNumber6(totals["jobs"]),
2671
- items: asNumber6(totals["items"]),
2672
- found: asNumber6(totals["found"]),
2673
- foundRate: asNumber6(totals["found_rate"]),
2674
- avgConfidence: asNumber6(totals["avg_confidence"])
3415
+ jobs: asNumber7(totals["jobs"]),
3416
+ items: asNumber7(totals["items"]),
3417
+ found: asNumber7(totals["found"]),
3418
+ foundRate: asNumber7(totals["found_rate"]),
3419
+ avgConfidence: asNumber7(totals["avg_confidence"])
2675
3420
  },
2676
3421
  timeSeries,
2677
3422
  bySource,
@@ -2742,6 +3487,41 @@ var SmartEnrichmentEndpoint = class {
2742
3487
  const data = await this.parseJsonResponse(response);
2743
3488
  return SmartEnrichmentJob.fromArray(data);
2744
3489
  }
3490
+ /**
3491
+ * Search again for rows of a finished lookup that have no usable answer (`not_found`, or a
3492
+ * number registered to another company). Without `rows` every row that qualifies is rechecked.
3493
+ *
3494
+ * Every recheck counts as a check like a new lookup (Deep Verify ×2), whether a VAT number is
3495
+ * found or not. A row can be rechecked up to 3 times, with 7 days between two rechecks;
3496
+ * `no_vat_exists` and found rows are never rechecked. Pass `dryRun` to see which rows would run
3497
+ * and what they cost without starting anything. Improved rows arrive via the
3498
+ * `enrichment.updated` webhook; `get(jobId).recheck` shows the progress.
3499
+ *
3500
+ * Throws a ValidationException when nothing can be rechecked, the lookup is still running, or
3501
+ * rechecks are not available (sandbox, flat-fee import), and an HttpException (429) when the
3502
+ * start credit or monthly hard cap does not cover it.
3503
+ */
3504
+ async recheck(jobId, options = {}) {
3505
+ this.validateJobId(jobId);
3506
+ const body = {};
3507
+ if (options.rows !== void 0) {
3508
+ if (options.rows.length === 0) {
3509
+ throw new TaxoraException("rows must not be empty; omit it to recheck every row that qualifies.");
3510
+ }
3511
+ if (options.rows.some((index) => !Number.isInteger(index) || index < 0)) {
3512
+ throw new TaxoraException("rows must contain 0-based row indexes (integers >= 0).");
3513
+ }
3514
+ body["rows"] = [...new Set(options.rows)];
3515
+ }
3516
+ if (options.dryRun) body["dryRun"] = true;
3517
+ const response = await this.sendRequest(
3518
+ "POST",
3519
+ `${this.baseUrl}/smart-enrichment/${encodeURIComponent(jobId)}/recheck`,
3520
+ body
3521
+ );
3522
+ const data = await this.parseJsonResponse(response, [200, 202]);
3523
+ return SmartEnrichmentRecheck.fromArray(data);
3524
+ }
2745
3525
  /**
2746
3526
  * Poll get(jobId) until the job is no longer processing (completed, failed or resolved).
2747
3527
  * Throws a TaxoraException when the job does not finish within `timeoutMs`.
@@ -3916,7 +4696,7 @@ var AuthRetryHttpClient = class {
3916
4696
  };
3917
4697
 
3918
4698
  // src/version.ts
3919
- var SDK_VERSION = "1.7.1";
4699
+ var SDK_VERSION = "1.9.0";
3920
4700
 
3921
4701
  // src/http/SdkVersionHttpClient.ts
3922
4702
  var SDK_VERSION_HEADER = "X-Taxora-SDK-Version";
@@ -3943,6 +4723,7 @@ var TaxoraClient = class {
3943
4723
  company;
3944
4724
  smartEnrichment;
3945
4725
  eReporting;
4726
+ billing;
3946
4727
  baseUrl;
3947
4728
  constructor(options) {
3948
4729
  const environment = options.environment ?? Environment.SANDBOX;
@@ -3963,6 +4744,7 @@ var TaxoraClient = class {
3963
4744
  retryPolicy
3964
4745
  );
3965
4746
  this.eReporting = new EReportingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
4747
+ this.billing = new BillingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
3966
4748
  }
3967
4749
  };
3968
4750
 
@@ -4102,17 +4884,24 @@ function toBytes(rawBody) {
4102
4884
  "invalid_body"
4103
4885
  );
4104
4886
  }
4887
+
4888
+ // src/webhooks/WebhookEvent.ts
4889
+ function isTransactionBuyerRejected(object) {
4890
+ return object.buyer_rejected === true;
4891
+ }
4105
4892
  export {
4106
4893
  ApiVersion,
4107
4894
  AuthEndpoint,
4108
4895
  AuthRetryHttpClient,
4109
4896
  AuthenticationException,
4897
+ BillingEndpoint,
4110
4898
  CompanyAddress,
4111
4899
  CompanyEndpoint,
4112
4900
  ComplianceDocumentType,
4113
4901
  ComplianceEnrollment,
4114
4902
  ComplianceEnrollmentPage,
4115
4903
  ComplianceEnrollmentStatus,
4904
+ ComplianceOfferings,
4116
4905
  ComplianceService,
4117
4906
  ComplianceTaxReport,
4118
4907
  ComplianceTaxReportPage,
@@ -4146,6 +4935,8 @@ export {
4146
4935
  SmartEnrichmentHistoryRow,
4147
4936
  SmartEnrichmentJob,
4148
4937
  SmartEnrichmentMode,
4938
+ SmartEnrichmentRecheck,
4939
+ SmartEnrichmentRecheckState,
4149
4940
  SmartEnrichmentRejectionReason,
4150
4941
  SmartEnrichmentResource,
4151
4942
  SmartEnrichmentStatistics,
@@ -4154,6 +4945,8 @@ export {
4154
4945
  TaxoraClientFactory,
4155
4946
  TaxoraException,
4156
4947
  Token,
4948
+ UsageSummary,
4949
+ UsageSummaryPage,
4157
4950
  ValidationException,
4158
4951
  VatCertificateExport,
4159
4952
  VatCollection,
@@ -4182,6 +4975,7 @@ export {
4182
4975
  generateWebhookSignatureHeader,
4183
4976
  getBaseUrl,
4184
4977
  getFailedVatStates,
4978
+ isTransactionBuyerRejected,
4185
4979
  parseLocalTaxNumbersCsv,
4186
4980
  parseWebhookSignatureHeader,
4187
4981
  toComplianceDocumentType,