@taxora/sdk 1.2.0 → 1.3.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
@@ -323,13 +323,7 @@ async function withResponseRetries(policy, operation, send) {
323
323
  if (!policy.shouldRetry(response.status, attempt, retryAfter)) {
324
324
  if (attempt > 1 && policy.isRetryable(response.status, retryAfter)) {
325
325
  const body = await response.text();
326
- const last = new HttpException(
327
- describeApiError(body, response.status),
328
- response.status,
329
- body,
330
- {},
331
- retryAfter
332
- );
326
+ const last = new HttpException(describeApiError(body, response.status), response.status, body, {}, retryAfter);
333
327
  throw HttpException.afterAttempts(operation, attempt, last);
334
328
  }
335
329
  return response;
@@ -546,6 +540,31 @@ function describeComplianceEnrollmentStatus(status) {
546
540
  return DESCRIPTIONS[status];
547
541
  }
548
542
 
543
+ // src/enums/ComplianceService.ts
544
+ var ComplianceService = {
545
+ E_REPORTING: "e_reporting",
546
+ E_INVOICING: "e_invoicing",
547
+ UNKNOWN: "unknown"
548
+ };
549
+ var VALUES2 = Object.values(ComplianceService);
550
+ function toComplianceService(value) {
551
+ if (typeof value === "string") {
552
+ const normalized = value.trim().toLowerCase();
553
+ if (VALUES2.includes(normalized)) {
554
+ return normalized;
555
+ }
556
+ }
557
+ return ComplianceService.UNKNOWN;
558
+ }
559
+ var DESCRIPTIONS2 = {
560
+ [ComplianceService.E_REPORTING]: "E-reporting of transaction data to the tax authority (France: DGFiP Flux 10).",
561
+ [ComplianceService.E_INVOICING]: "Structured e-invoices delivered to the buyer (Norway: EHF / Peppol BIS 3.0).",
562
+ [ComplianceService.UNKNOWN]: "Unknown service."
563
+ };
564
+ function describeComplianceService(service) {
565
+ return DESCRIPTIONS2[service];
566
+ }
567
+
549
568
  // src/dto/ComplianceEnrollment.ts
550
569
  function asString(value, fallback = "") {
551
570
  return typeof value === "string" ? value : fallback;
@@ -557,7 +576,7 @@ function asNumber(value) {
557
576
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
558
577
  }
559
578
  var ComplianceEnrollment = class _ComplianceEnrollment {
560
- constructor(id, companyId, country, regime, provider, status, statusLabel, statusError, providerAccountId, taxId, companyRegisterId, companyRegisterScheme, regimeConfig, reportingStartDate, notificationEmail, autoSend, createdAt, updatedAt) {
579
+ constructor(id, companyId, country, regime, provider, status, statusLabel, statusError, providerAccountId, taxId, companyRegisterId, companyRegisterScheme, regimeConfig, reportingStartDate, notificationEmail, autoSend, createdAt, updatedAt, service = ComplianceService.UNKNOWN) {
561
580
  this.id = id;
562
581
  this.companyId = companyId;
563
582
  this.country = country;
@@ -576,6 +595,7 @@ var ComplianceEnrollment = class _ComplianceEnrollment {
576
595
  this.autoSend = autoSend;
577
596
  this.createdAt = createdAt;
578
597
  this.updatedAt = updatedAt;
598
+ this.service = service;
579
599
  }
580
600
  id;
581
601
  companyId;
@@ -595,6 +615,7 @@ var ComplianceEnrollment = class _ComplianceEnrollment {
595
615
  autoSend;
596
616
  createdAt;
597
617
  updatedAt;
618
+ service;
598
619
  static fromArray(data) {
599
620
  const regimeConfig = typeof data["regime_config"] === "object" && data["regime_config"] !== null && !Array.isArray(data["regime_config"]) ? data["regime_config"] : null;
600
621
  return new _ComplianceEnrollment(
@@ -615,7 +636,8 @@ var ComplianceEnrollment = class _ComplianceEnrollment {
615
636
  asNullableString(data["notification_email"]),
616
637
  data["auto_send"] === true,
617
638
  asNullableString(data["created_at"]),
618
- asNullableString(data["updated_at"])
639
+ asNullableString(data["updated_at"]),
640
+ toComplianceService(data["service"])
619
641
  );
620
642
  }
621
643
  };
@@ -665,17 +687,17 @@ var ComplianceTaxReportState = {
665
687
  ERROR: "error",
666
688
  UNKNOWN: "unknown"
667
689
  };
668
- var VALUES2 = Object.values(ComplianceTaxReportState);
690
+ var VALUES3 = Object.values(ComplianceTaxReportState);
669
691
  function toComplianceTaxReportState(value) {
670
692
  if (typeof value === "string") {
671
693
  const normalized = value.trim().toLowerCase();
672
- if (VALUES2.includes(normalized)) {
694
+ if (VALUES3.includes(normalized)) {
673
695
  return normalized;
674
696
  }
675
697
  }
676
698
  return ComplianceTaxReportState.UNKNOWN;
677
699
  }
678
- var DESCRIPTIONS2 = {
700
+ var DESCRIPTIONS3 = {
679
701
  [ComplianceTaxReportState.NEW]: "Created at provider, waiting for the daily ledger batch.",
680
702
  [ComplianceTaxReportState.SENT]: "Ledger transmitted to PPF.",
681
703
  [ComplianceTaxReportState.ACKNOWLEDGED]: "PPF acknowledged receipt of the ledger.",
@@ -685,7 +707,7 @@ var DESCRIPTIONS2 = {
685
707
  [ComplianceTaxReportState.UNKNOWN]: "Unknown state."
686
708
  };
687
709
  function describeComplianceTaxReportState(state) {
688
- return DESCRIPTIONS2[state];
710
+ return DESCRIPTIONS3[state];
689
711
  }
690
712
 
691
713
  // src/dto/ComplianceTaxReport.ts
@@ -807,6 +829,31 @@ var ComplianceTaxReportPage = class _ComplianceTaxReportPage {
807
829
  }
808
830
  };
809
831
 
832
+ // src/enums/ComplianceDocumentType.ts
833
+ var ComplianceDocumentType = {
834
+ INVOICE: "invoice",
835
+ CREDIT_NOTE: "credit_note",
836
+ UNKNOWN: "unknown"
837
+ };
838
+ var VALUES4 = Object.values(ComplianceDocumentType);
839
+ function toComplianceDocumentType(value) {
840
+ if (typeof value === "string") {
841
+ const normalized = value.trim().toLowerCase();
842
+ if (VALUES4.includes(normalized)) {
843
+ return normalized;
844
+ }
845
+ }
846
+ return ComplianceDocumentType.UNKNOWN;
847
+ }
848
+ var DESCRIPTIONS4 = {
849
+ [ComplianceDocumentType.INVOICE]: "Commercial invoice.",
850
+ [ComplianceDocumentType.CREDIT_NOTE]: "Credit note correcting a previously issued invoice.",
851
+ [ComplianceDocumentType.UNKNOWN]: "Unknown document type."
852
+ };
853
+ function describeComplianceDocumentType(type) {
854
+ return DESCRIPTIONS4[type];
855
+ }
856
+
810
857
  // src/enums/ComplianceTransactionState.ts
811
858
  var ComplianceTransactionState = {
812
859
  PENDING: "pending",
@@ -815,17 +862,17 @@ var ComplianceTransactionState = {
815
862
  ERROR: "error",
816
863
  UNKNOWN: "unknown"
817
864
  };
818
- var VALUES3 = Object.values(ComplianceTransactionState);
865
+ var VALUES5 = Object.values(ComplianceTransactionState);
819
866
  function toComplianceTransactionState(value) {
820
867
  if (typeof value === "string") {
821
868
  const normalized = value.trim().toLowerCase();
822
- if (VALUES3.includes(normalized)) {
869
+ if (VALUES5.includes(normalized)) {
823
870
  return normalized;
824
871
  }
825
872
  }
826
873
  return ComplianceTransactionState.UNKNOWN;
827
874
  }
828
- var DESCRIPTIONS3 = {
875
+ var DESCRIPTIONS5 = {
829
876
  [ComplianceTransactionState.PENDING]: "Recorded locally, not yet submitted to the provider.",
830
877
  [ComplianceTransactionState.SENDING]: "Submission to the provider in progress.",
831
878
  [ComplianceTransactionState.SUBMITTED]: "Successfully submitted to the provider.",
@@ -833,7 +880,7 @@ var DESCRIPTIONS3 = {
833
880
  [ComplianceTransactionState.UNKNOWN]: "Unknown state."
834
881
  };
835
882
  function describeComplianceTransactionState(state) {
836
- return DESCRIPTIONS3[state];
883
+ return DESCRIPTIONS5[state];
837
884
  }
838
885
 
839
886
  // src/enums/ComplianceTransactionType.ts
@@ -845,17 +892,17 @@ var ComplianceTransactionType = {
845
892
  CROSSBORDER_INBOUND: "crossborder_inbound",
846
893
  UNKNOWN: "unknown"
847
894
  };
848
- var VALUES4 = Object.values(ComplianceTransactionType);
895
+ var VALUES6 = Object.values(ComplianceTransactionType);
849
896
  function toComplianceTransactionType(value) {
850
897
  if (typeof value === "string") {
851
898
  const normalized = value.trim().toLowerCase();
852
- if (VALUES4.includes(normalized)) {
899
+ if (VALUES6.includes(normalized)) {
853
900
  return normalized;
854
901
  }
855
902
  }
856
903
  return ComplianceTransactionType.UNKNOWN;
857
904
  }
858
- var DESCRIPTIONS4 = {
905
+ var DESCRIPTIONS6 = {
859
906
  [ComplianceTransactionType.B2C_OUTBOUND]: "Sale to a private person in the merchant country (DGFiP Flux 10.3).",
860
907
  [ComplianceTransactionType.B2B_DOMESTIC_OUTBOUND]: "Sale to a domestic business (DGFiP Flux 1, Phase 2).",
861
908
  [ComplianceTransactionType.B2B_DOMESTIC_INBOUND]: "Purchase from a domestic business (DGFiP Flux 1, Phase 2).",
@@ -864,7 +911,7 @@ var DESCRIPTIONS4 = {
864
911
  [ComplianceTransactionType.UNKNOWN]: "Unknown transaction type."
865
912
  };
866
913
  function describeComplianceTransactionType(type) {
867
- return DESCRIPTIONS4[type];
914
+ return DESCRIPTIONS6[type];
868
915
  }
869
916
 
870
917
  // src/dto/ComplianceTransaction.ts
@@ -882,8 +929,34 @@ function asMoney2(value) {
882
929
  if (typeof value === "number" && Number.isFinite(value)) return String(value);
883
930
  return "0.00";
884
931
  }
932
+ function asNullableNumber2(value) {
933
+ if (typeof value === "number" && Number.isFinite(value)) return value;
934
+ if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
935
+ return null;
936
+ }
937
+ function asRecord(value) {
938
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
939
+ }
940
+ function asRecordList(value) {
941
+ return Array.isArray(value) ? value.filter((row) => asRecord(row) !== null) : [];
942
+ }
943
+ 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"]),
948
+ 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"])
955
+ }))
956
+ }));
957
+ }
885
958
  var ComplianceTransaction = class _ComplianceTransaction {
886
- 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) {
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 = []) {
887
960
  this.id = id;
888
961
  this.companyId = companyId;
889
962
  this.complianceEnrollmentId = complianceEnrollmentId;
@@ -911,6 +984,17 @@ var ComplianceTransaction = class _ComplianceTransaction {
911
984
  this.taxReport = taxReport;
912
985
  this.createdAt = createdAt;
913
986
  this.updatedAt = updatedAt;
987
+ this.counterpartyRegisterId = counterpartyRegisterId;
988
+ this.counterpartyAddress = counterpartyAddress;
989
+ this.counterpartyCity = counterpartyCity;
990
+ this.counterpartyPostalcode = counterpartyPostalcode;
991
+ this.counterpartyEmail = counterpartyEmail;
992
+ this.buyerReference = buyerReference;
993
+ this.documentType = documentType;
994
+ this.amendedNumber = amendedNumber;
995
+ this.amendedDate = amendedDate;
996
+ this.providerState = providerState;
997
+ this.invoiceLines = invoiceLines;
914
998
  }
915
999
  id;
916
1000
  companyId;
@@ -939,6 +1023,20 @@ var ComplianceTransaction = class _ComplianceTransaction {
939
1023
  taxReport;
940
1024
  createdAt;
941
1025
  updatedAt;
1026
+ counterpartyRegisterId;
1027
+ counterpartyAddress;
1028
+ counterpartyCity;
1029
+ counterpartyPostalcode;
1030
+ counterpartyEmail;
1031
+ buyerReference;
1032
+ documentType;
1033
+ amendedNumber;
1034
+ amendedDate;
1035
+ providerState;
1036
+ invoiceLines;
1037
+ get isCreditNote() {
1038
+ return this.documentType === ComplianceDocumentType.CREDIT_NOTE;
1039
+ }
942
1040
  static fromArray(data) {
943
1041
  const taxReport = typeof data["tax_report"] === "object" && data["tax_report"] !== null && !Array.isArray(data["tax_report"]) ? ComplianceTaxReport.fromArray(data["tax_report"]) : void 0;
944
1042
  return new _ComplianceTransaction(
@@ -968,7 +1066,18 @@ var ComplianceTransaction = class _ComplianceTransaction {
968
1066
  asNullableString3(data["reported_at"]),
969
1067
  taxReport,
970
1068
  asNullableString3(data["created_at"]),
971
- asNullableString3(data["updated_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"]),
1076
+ 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"])
972
1081
  );
973
1082
  }
974
1083
  };
@@ -1027,10 +1136,134 @@ var ImportResult = class _ImportResult {
1027
1136
  }
1028
1137
  };
1029
1138
 
1030
- // src/dto/RevenueStatistics.ts
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
+ }
1153
+ }
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
+ };
1162
+ function describePeppolLookupStatus(status) {
1163
+ return DESCRIPTIONS7[status];
1164
+ }
1165
+
1166
+ // src/dto/PeppolLookupResult.ts
1031
1167
  function asString4(value, fallback = "") {
1032
1168
  return typeof value === "string" ? value : fallback;
1033
1169
  }
1170
+ function asNullableString4(value) {
1171
+ return typeof value === "string" ? value : null;
1172
+ }
1173
+ var PeppolLookupResult = class _PeppolLookupResult {
1174
+ constructor(status, reachable, country, scheme, id, documentTypes, transportTypeCode, checkedAt) {
1175
+ this.status = status;
1176
+ this.reachable = reachable;
1177
+ this.country = country;
1178
+ this.scheme = scheme;
1179
+ this.id = id;
1180
+ this.documentTypes = documentTypes;
1181
+ this.transportTypeCode = transportTypeCode;
1182
+ this.checkedAt = checkedAt;
1183
+ }
1184
+ status;
1185
+ reachable;
1186
+ country;
1187
+ scheme;
1188
+ id;
1189
+ documentTypes;
1190
+ transportTypeCode;
1191
+ checkedAt;
1192
+ /** True while the directory is still resolving — ask again in a few seconds. */
1193
+ get isPending() {
1194
+ return this.status === PeppolLookupStatus.PENDING;
1195
+ }
1196
+ static fromArray(data) {
1197
+ const documentTypes = Array.isArray(data["document_types"]) ? data["document_types"].filter((t) => typeof t === "string") : [];
1198
+ return new _PeppolLookupResult(
1199
+ toPeppolLookupStatus(data["status"]),
1200
+ typeof data["reachable"] === "boolean" ? data["reachable"] : null,
1201
+ asString4(data["country"]),
1202
+ asString4(data["scheme"]),
1203
+ asString4(data["id"]),
1204
+ documentTypes,
1205
+ asNullableString4(data["transport_type_code"]),
1206
+ asNullableString4(data["checked_at"])
1207
+ );
1208
+ }
1209
+ };
1210
+
1211
+ // src/dto/RegistryCompany.ts
1212
+ function asString5(value, fallback = "") {
1213
+ return typeof value === "string" ? value : fallback;
1214
+ }
1215
+ function asNullableString5(value) {
1216
+ return typeof value === "string" ? value : null;
1217
+ }
1218
+ var RegistryCompany = class _RegistryCompany {
1219
+ constructor(orgNumber, companyName, organisationForm, vatRegistered, vatNumber, enterpriseRegister, bankrupt, underLiquidation, address, postalcode, city, country) {
1220
+ this.orgNumber = orgNumber;
1221
+ this.companyName = companyName;
1222
+ this.organisationForm = organisationForm;
1223
+ this.vatRegistered = vatRegistered;
1224
+ this.vatNumber = vatNumber;
1225
+ this.enterpriseRegister = enterpriseRegister;
1226
+ this.bankrupt = bankrupt;
1227
+ this.underLiquidation = underLiquidation;
1228
+ this.address = address;
1229
+ this.postalcode = postalcode;
1230
+ this.city = city;
1231
+ this.country = country;
1232
+ }
1233
+ orgNumber;
1234
+ companyName;
1235
+ organisationForm;
1236
+ vatRegistered;
1237
+ vatNumber;
1238
+ enterpriseRegister;
1239
+ bankrupt;
1240
+ underLiquidation;
1241
+ address;
1242
+ postalcode;
1243
+ city;
1244
+ country;
1245
+ static fromArray(data) {
1246
+ return new _RegistryCompany(
1247
+ asString5(data["org_number"]),
1248
+ asString5(data["company_name"]),
1249
+ asNullableString5(data["organisation_form"]),
1250
+ data["vat_registered"] === true,
1251
+ asNullableString5(data["vat_number"]),
1252
+ data["enterprise_register"] === true,
1253
+ data["bankrupt"] === true,
1254
+ data["under_liquidation"] === true,
1255
+ asNullableString5(data["address"]),
1256
+ asNullableString5(data["postalcode"]),
1257
+ asNullableString5(data["city"]),
1258
+ asString5(data["country"])
1259
+ );
1260
+ }
1261
+ };
1262
+
1263
+ // src/dto/RevenueStatistics.ts
1264
+ function asString6(value, fallback = "") {
1265
+ return typeof value === "string" ? value : fallback;
1266
+ }
1034
1267
  function asNumber4(value) {
1035
1268
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
1036
1269
  }
@@ -1080,35 +1313,35 @@ var RevenueStatistics = class _RevenueStatistics {
1080
1313
  typeof data["totals"] === "object" && data["totals"] !== null ? data["totals"] : {}
1081
1314
  );
1082
1315
  const timeSeries = asRecords(data["time_series"]).map((row) => ({
1083
- bucket: asString4(row["bucket"]),
1316
+ bucket: asString6(row["bucket"]),
1084
1317
  ...toTotals(row)
1085
1318
  }));
1086
1319
  const byType = asRecords(data["by_type"]).map((row) => ({
1087
- type: asString4(row["type"]),
1088
- typeLabel: asString4(row["type_label"]),
1320
+ type: asString6(row["type"]),
1321
+ typeLabel: asString6(row["type_label"]),
1089
1322
  count: asNumber4(row["count"]),
1090
1323
  total: asMoney3(row["total"])
1091
1324
  }));
1092
1325
  const byCountry = asRecords(data["by_country"]).map((row) => ({
1093
- country: asString4(row["country"], "unknown"),
1326
+ country: asString6(row["country"], "unknown"),
1094
1327
  count: asNumber4(row["count"]),
1095
1328
  total: asMoney3(row["total"])
1096
1329
  }));
1097
1330
  const byState = asRecords(data["by_state"]).map((row) => ({
1098
- state: asString4(row["state"]),
1099
- stateLabel: asString4(row["state_label"]),
1331
+ state: asString6(row["state"]),
1332
+ stateLabel: asString6(row["state_label"]),
1100
1333
  count: asNumber4(row["count"]),
1101
1334
  total: asMoney3(row["total"])
1102
1335
  }));
1103
1336
  const byCurrency = asRecords(data["by_currency"]).map((row) => ({
1104
- currency: asString4(row["currency"]),
1337
+ currency: asString6(row["currency"]),
1105
1338
  ...toTotals(row)
1106
1339
  }));
1107
1340
  return new _RevenueStatistics(
1108
- asString4(range["date_from"]) || void 0,
1109
- asString4(range["date_to"]) || void 0,
1110
- asString4(range["interval"]) || void 0,
1111
- asString4(data["primary_currency"], "EUR"),
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"),
1112
1345
  data["is_multi_currency"] === true,
1113
1346
  totals,
1114
1347
  timeSeries,
@@ -1121,10 +1354,10 @@ var RevenueStatistics = class _RevenueStatistics {
1121
1354
  };
1122
1355
 
1123
1356
  // src/dto/SireneLookupResult.ts
1124
- function asString5(value, fallback = "") {
1357
+ function asString7(value, fallback = "") {
1125
1358
  return typeof value === "string" ? value : fallback;
1126
1359
  }
1127
- function asNullableString4(value) {
1360
+ function asNullableString6(value) {
1128
1361
  return typeof value === "string" ? value : null;
1129
1362
  }
1130
1363
  var SireneLookupResult = class _SireneLookupResult {
@@ -1150,21 +1383,21 @@ var SireneLookupResult = class _SireneLookupResult {
1150
1383
  postalcode;
1151
1384
  static fromArray(data) {
1152
1385
  return new _SireneLookupResult(
1153
- asString5(data["company_name"]),
1154
- asString5(data["siren"]),
1155
- asNullableString4(data["siret"]),
1156
- asNullableString4(data["naf_code"]),
1157
- asNullableString4(data["naf_full"]),
1158
- asNullableString4(data["enterprise_size"]),
1159
- asNullableString4(data["address"]),
1160
- asNullableString4(data["city"]),
1161
- asNullableString4(data["postalcode"])
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"])
1162
1395
  );
1163
1396
  }
1164
1397
  };
1165
1398
 
1166
1399
  // src/dto/VatRates.ts
1167
- function asString6(value, fallback = "") {
1400
+ function asString8(value, fallback = "") {
1168
1401
  return typeof value === "string" ? value : fallback;
1169
1402
  }
1170
1403
  function asNumber5(value) {
@@ -1182,12 +1415,12 @@ var VatRates = class _VatRates {
1182
1415
  static fromArray(data) {
1183
1416
  const rates = (Array.isArray(data["rates"]) ? data["rates"] : []).filter((row) => typeof row === "object" && row !== null).map((row) => ({
1184
1417
  percent: asNumber5(row["percent"]),
1185
- category: asString6(row["category"]),
1186
- labelKey: asString6(row["label_key"]),
1187
- taxName: asString6(row["tax_name"]),
1418
+ category: asString8(row["category"]),
1419
+ labelKey: asString8(row["label_key"]),
1420
+ taxName: asString8(row["tax_name"]),
1188
1421
  requiresVatex: row["requires_vatex"] === true
1189
1422
  }));
1190
- return new _VatRates(asString6(data["country"]), asString6(data["currency"], "EUR"), rates);
1423
+ return new _VatRates(asString8(data["country"]), asString8(data["currency"], "EUR"), rates);
1191
1424
  }
1192
1425
  };
1193
1426
 
@@ -1206,10 +1439,48 @@ var ValidationException = class extends TaxoraException {
1206
1439
  }
1207
1440
  };
1208
1441
 
1442
+ // src/support/NorwegianOrgNumber.ts
1443
+ var WEIGHTS = [3, 2, 7, 6, 5, 4, 3, 2];
1444
+ var NorwegianOrgNumber = {
1445
+ /** Peppol identifier scheme for Norwegian organisation numbers. */
1446
+ PEPPOL_SCHEME: "0192",
1447
+ /**
1448
+ * Strips whitespace, dots, dashes, a "0192:" Peppol scheme prefix, the "NO"
1449
+ * prefix and the "MVA" suffix: "NO 923 609 016 MVA", "923609016MVA" and
1450
+ * "923 609 016" all become "923609016". Does not validate.
1451
+ */
1452
+ normalize(value) {
1453
+ let v = (value ?? "").trim().toUpperCase().replace(/[\s.-]/g, "");
1454
+ if (v.startsWith("0192:")) v = v.slice(5);
1455
+ if (v.startsWith("NO")) v = v.slice(2);
1456
+ if (v.endsWith("MVA")) v = v.slice(0, -3);
1457
+ return v;
1458
+ },
1459
+ /** True when the (normalized) value is 9 digits with a valid mod-11 check digit. */
1460
+ isValid(value) {
1461
+ const digits = NorwegianOrgNumber.normalize(value);
1462
+ if (!/^\d{9}$/.test(digits)) return false;
1463
+ let sum = 0;
1464
+ for (let i = 0; i < WEIGHTS.length; i++) {
1465
+ sum += Number(digits[i]) * WEIGHTS[i];
1466
+ }
1467
+ let check = 11 - sum % 11;
1468
+ if (check === 11) check = 0;
1469
+ return check !== 10 && check === Number(digits[8]);
1470
+ },
1471
+ /** "NO923609016MVA" — the Norwegian VAT number of an MVA-registered entity. Does not validate. */
1472
+ toVatNumber(value) {
1473
+ return `NO${NorwegianOrgNumber.normalize(value)}MVA`;
1474
+ }
1475
+ };
1476
+
1209
1477
  // src/endpoints/EReportingEndpoint.ts
1210
1478
  var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
1211
1479
  var NAF_CODE_REGEX = /^\d{2}$/;
1480
+ var COUNTRY_REGEX = /^[A-Za-z]{2}$/;
1481
+ var PEPPOL_SCHEME_REGEX = /^\d{4}$/;
1212
1482
  var INTERVALS = ["day", "week", "month"];
1483
+ var SERVICES = ["e_reporting", "e_invoicing"];
1213
1484
  var ENTERPRISE_SIZES = ["micro", "pme", "eti", "ge"];
1214
1485
  var TYPE_OPERATIONS = ["services", "goods", "mixed"];
1215
1486
  var TRANSACTION_TYPES = [
@@ -1237,14 +1508,23 @@ var EReportingEndpoint = class {
1237
1508
  // ---------------------------------------------------------------------
1238
1509
  /**
1239
1510
  * Create a compliance enrollment and provision the provider account. By
1240
- * default the DGFiP regime is activated right away; pass `autoActivate: false`
1511
+ * default the country regime is activated right away (France: DGFiP tax
1512
+ * report setting; Norway: Peppol registration); pass `autoActivate: false`
1241
1513
  * to only create the account.
1242
1514
  *
1243
- * On provider failure the API responds 502 (mapped to an HttpException) and
1244
- * the enrollment is persisted in `error_account`/`error_activation` state so
1515
+ * The input shape depends on `country`: omitted/"FR" takes the France input
1516
+ * (unchanged, backwards compatible), "NO" the Norway input — see also the
1517
+ * createNorwayEnrollment() shortcut.
1518
+ *
1519
+ * Errors: 403 (HttpException, body code `service_not_active`) when the company
1520
+ * lacks the requested service; on provider failure 502 (HttpException) — the
1521
+ * enrollment is then persisted in `error_account`/`error_activation` state so
1245
1522
  * provisioning can be retried.
1246
1523
  */
1247
1524
  async createEnrollment(input) {
1525
+ if (this.isNorwayEnrollmentInput(input)) {
1526
+ return this.createEnrollmentFromBody(this.buildNorwayEnrollmentBody(input));
1527
+ }
1248
1528
  this.validateEnrollmentInput(input);
1249
1529
  const body = {
1250
1530
  email: input.email,
@@ -1254,6 +1534,7 @@ var EReportingEndpoint = class {
1254
1534
  reporting_start_date: this.formatDate(input.reportingStartDate)
1255
1535
  };
1256
1536
  if (input.country !== void 0) body["country"] = input.country;
1537
+ if (input.service !== void 0) body["service"] = input.service;
1257
1538
  if (input.regime !== void 0) body["regime"] = input.regime;
1258
1539
  if (input.siret !== void 0) body["siret"] = input.siret;
1259
1540
  if (input.siren !== void 0) body["siren"] = input.siren;
@@ -1264,9 +1545,15 @@ var EReportingEndpoint = class {
1264
1545
  if (input.postalcode !== void 0) body["postalcode"] = input.postalcode;
1265
1546
  if (input.province !== void 0) body["province"] = input.province;
1266
1547
  if (input.autoActivate !== void 0) body["auto_activate"] = input.autoActivate;
1267
- const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/enrollments`, body);
1268
- const data = await this.parseJsonResponse(response);
1269
- return ComplianceEnrollment.fromArray(data);
1548
+ return this.createEnrollmentFromBody(body);
1549
+ }
1550
+ /**
1551
+ * Enroll the company for Norway e-invoicing (EHF / Peppol BIS 3.0) — shortcut
1552
+ * for createEnrollment({ country: 'NO', ... }). The organisation number is
1553
+ * checked (mod-11) and normalized to 9 digits before sending.
1554
+ */
1555
+ async createNorwayEnrollment(input) {
1556
+ return this.createEnrollment({ ...input, country: "NO" });
1270
1557
  }
1271
1558
  /** Paginated list of the company's enrollments (newest first). `perPage` is capped at 100 server-side. */
1272
1559
  async listEnrollments(page = 1, perPage = 25) {
@@ -1300,6 +1587,72 @@ var EReportingEndpoint = class {
1300
1587
  const data = await this.parseJsonResponse(response);
1301
1588
  return SireneLookupResult.fromArray(data);
1302
1589
  }
1590
+ /**
1591
+ * Look up companies in a national company register — currently Norway
1592
+ * (Brønnøysund Enhetsregisteret). `q` is an organisation number, a "NO…MVA"
1593
+ * VAT number or a company name; an exact number match yields one result.
1594
+ * Ideal for prefilling createNorwayEnrollment() and the buyer fields of an
1595
+ * e-invoice. For France use sireneLookup().
1596
+ *
1597
+ * Errors: 404 (HttpException) when nothing is found, 422 (ValidationException)
1598
+ * on invalid input / unsupported country, 429 (HttpException) when the
1599
+ * per-user rate limit (30/min) is exceeded.
1600
+ */
1601
+ async registryLookup(country, q) {
1602
+ const normalizedCountry = this.normalizeLookupCountry(country);
1603
+ const query = q.trim();
1604
+ if (query === "") {
1605
+ throw new TaxoraException("q must not be empty.");
1606
+ }
1607
+ if (normalizedCountry === "NO") {
1608
+ const digits = NorwegianOrgNumber.normalize(query);
1609
+ if (/^\d{9}$/.test(digits) && !NorwegianOrgNumber.isValid(digits)) {
1610
+ throw new TaxoraException(`Invalid Norwegian organisation number "${query}" (check digit).`);
1611
+ }
1612
+ }
1613
+ const params = new URLSearchParams();
1614
+ params.set("country", normalizedCountry);
1615
+ params.set("q", query);
1616
+ const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/registry-lookup?${params.toString()}`);
1617
+ const data = await this.parseJsonResponse(response);
1618
+ const results = Array.isArray(data["results"]) ? data["results"] : [];
1619
+ return results.filter((row) => typeof row === "object" && row !== null && !Array.isArray(row)).map((row) => RegistryCompany.fromArray(row));
1620
+ }
1621
+ /**
1622
+ * Check whether a participant (typically the buyer) can receive e-invoices
1623
+ * over Peppol. For Norway pass the organisation number (scheme defaults to
1624
+ * "0192"): `reachable === true` means the buyer is registered in ELMA and must
1625
+ * receive an EHF e-invoice. The directory resolves asynchronously —
1626
+ * `status === 'pending'` (`reachable === null`) means ask again in a few
1627
+ * seconds. Free of charge; definite answers are cached for 24h server-side.
1628
+ *
1629
+ * A Norwegian organisation number is checked (mod-11) and normalized before
1630
+ * sending. Errors: 422 (ValidationException) on invalid input, 502
1631
+ * (HttpException) on provider failure.
1632
+ */
1633
+ async peppolLookup(country, id, scheme) {
1634
+ const normalizedCountry = this.normalizeLookupCountry(country);
1635
+ let participantId = id.trim();
1636
+ if (participantId === "") {
1637
+ throw new TaxoraException("id must not be empty.");
1638
+ }
1639
+ if (scheme !== void 0 && !PEPPOL_SCHEME_REGEX.test(scheme)) {
1640
+ throw new TaxoraException('scheme must be a 4-digit Peppol identifier scheme, e.g. "0192".');
1641
+ }
1642
+ if (normalizedCountry === "NO" && (scheme === void 0 || scheme === NorwegianOrgNumber.PEPPOL_SCHEME)) {
1643
+ if (!NorwegianOrgNumber.isValid(participantId)) {
1644
+ throw new TaxoraException(`Invalid Norwegian organisation number "${participantId}".`);
1645
+ }
1646
+ participantId = NorwegianOrgNumber.normalize(participantId);
1647
+ }
1648
+ const params = new URLSearchParams();
1649
+ params.set("country", normalizedCountry);
1650
+ params.set("id", participantId);
1651
+ if (scheme !== void 0) params.set("scheme", scheme);
1652
+ const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/peppol-lookup?${params.toString()}`);
1653
+ const data = await this.parseJsonResponse(response);
1654
+ return PeppolLookupResult.fromArray(data);
1655
+ }
1303
1656
  // ---------------------------------------------------------------------
1304
1657
  // Transactions
1305
1658
  // ---------------------------------------------------------------------
@@ -1323,6 +1676,7 @@ var EReportingEndpoint = class {
1323
1676
  this.validateInvoiceNumber(input.invoiceNumber);
1324
1677
  this.validateInvoiceLines(input.invoiceLines);
1325
1678
  this.validateOptionalCodes(input);
1679
+ this.validateBuyerFields(input);
1326
1680
  const body = {
1327
1681
  compliance_enrollment_id: input.complianceEnrollmentId,
1328
1682
  transaction_type: input.transactionType,
@@ -1338,13 +1692,23 @@ var EReportingEndpoint = class {
1338
1692
  const data = await this.parseJsonResponse(response);
1339
1693
  return ComplianceTransaction.fromArray(data);
1340
1694
  }
1341
- /** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type. */
1695
+ /** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type / enrollment / country. */
1342
1696
  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
+ }
1343
1703
  const params = new URLSearchParams();
1344
1704
  if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
1345
1705
  if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
1346
1706
  if (filters.state !== void 0) params.set("state", filters.state);
1347
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());
1348
1712
  if (filters.page !== void 0) params.set("page", String(filters.page));
1349
1713
  if (filters.perPage !== void 0) params.set("per_page", String(filters.perPage));
1350
1714
  const queryString = params.toString();
@@ -1366,6 +1730,7 @@ var EReportingEndpoint = class {
1366
1730
  if (input.invoiceNumber !== void 0) this.validateInvoiceNumber(input.invoiceNumber);
1367
1731
  if (input.invoiceLines !== void 0) this.validateInvoiceLines(input.invoiceLines);
1368
1732
  this.validateOptionalCodes(input);
1733
+ this.validateBuyerFields(input);
1369
1734
  const body = {};
1370
1735
  if (input.transactionType !== void 0) body["transaction_type"] = input.transactionType;
1371
1736
  if (input.invoiceNumber !== void 0) body["invoice_number"] = input.invoiceNumber;
@@ -1488,6 +1853,14 @@ var EReportingEndpoint = class {
1488
1853
  * returned as HTTP 400 (surfaced as HttpException), not 422.
1489
1854
  */
1490
1855
  async requestEReportingAccess(input = {}) {
1856
+ if (input.service !== void 0) this.validateService(input.service);
1857
+ if (input.countries !== void 0) {
1858
+ input.countries.forEach((country, index) => {
1859
+ if (!COUNTRY_REGEX.test(country)) {
1860
+ throw new TaxoraException(`countries[${index}] must be a 2-character ISO 3166-1 alpha-2 code.`);
1861
+ }
1862
+ });
1863
+ }
1491
1864
  const body = {};
1492
1865
  if (input.name !== void 0) body["name"] = input.name;
1493
1866
  if (input.email !== void 0) body["email"] = input.email;
@@ -1495,6 +1868,8 @@ var EReportingEndpoint = class {
1495
1868
  if (input.phone !== void 0) body["phone"] = input.phone;
1496
1869
  if (input.message !== void 0) body["message"] = input.message;
1497
1870
  if (input.language !== void 0) body["language"] = input.language;
1871
+ if (input.service !== void 0) body["service"] = input.service;
1872
+ if (input.countries !== void 0) body["countries"] = input.countries.map((c) => c.toUpperCase());
1498
1873
  const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/e-reporting-request`, body);
1499
1874
  await this.parseJsonResponse(response);
1500
1875
  }
@@ -1530,6 +1905,73 @@ var EReportingEndpoint = class {
1530
1905
  if (input.country !== void 0 && input.country.length !== 2) {
1531
1906
  throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
1532
1907
  }
1908
+ if (input.service !== void 0) this.validateService(input.service);
1909
+ }
1910
+ isNorwayEnrollmentInput(input) {
1911
+ return typeof input.country === "string" && input.country.trim().toUpperCase() === "NO";
1912
+ }
1913
+ /** Validates the Norway input and builds its wire body (org number normalized to 9 digits). */
1914
+ buildNorwayEnrollmentBody(input) {
1915
+ if (typeof input.orgNumber !== "string" || !NorwegianOrgNumber.isValid(input.orgNumber)) {
1916
+ throw new TaxoraException(
1917
+ "orgNumber must be a valid 9-digit Norwegian organisation number (mod-11 check digit)."
1918
+ );
1919
+ }
1920
+ for (const field of ["companyName", "address", "city", "postalcode"]) {
1921
+ if (typeof input[field] !== "string" || input[field].trim() === "") {
1922
+ throw new TaxoraException(`${field} is required for a Norway enrollment.`);
1923
+ }
1924
+ }
1925
+ this.validateEmail(input.email, "email");
1926
+ if (input.service !== void 0) this.validateService(input.service);
1927
+ if (input.legalCountry !== void 0 && !COUNTRY_REGEX.test(input.legalCountry)) {
1928
+ throw new TaxoraException("legalCountry must be a 2-character ISO 3166-1 alpha-2 code.");
1929
+ }
1930
+ const body = {
1931
+ country: "NO",
1932
+ org_number: NorwegianOrgNumber.normalize(input.orgNumber),
1933
+ company_name: input.companyName,
1934
+ address: input.address,
1935
+ city: input.city,
1936
+ postalcode: input.postalcode,
1937
+ email: input.email
1938
+ };
1939
+ if (input.service !== void 0) body["service"] = input.service;
1940
+ if (input.regime !== void 0) body["regime"] = input.regime;
1941
+ if (input.vatNumber !== void 0) body["vat_number"] = input.vatNumber;
1942
+ if (input.province !== void 0) body["province"] = input.province;
1943
+ if (input.vatRegistered !== void 0) body["vat_registered"] = input.vatRegistered;
1944
+ if (input.enterpriseRegister !== void 0) body["enterprise_register"] = input.enterpriseRegister;
1945
+ if (input.reception !== void 0) body["reception"] = input.reception;
1946
+ if (input.legalCountry !== void 0) body["legal_country"] = input.legalCountry.toUpperCase();
1947
+ if (input.reportingStartDate !== void 0) {
1948
+ body["reporting_start_date"] = this.formatDate(input.reportingStartDate);
1949
+ }
1950
+ if (input.autoActivate !== void 0) body["auto_activate"] = input.autoActivate;
1951
+ return body;
1952
+ }
1953
+ async createEnrollmentFromBody(body) {
1954
+ const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/enrollments`, body);
1955
+ const data = await this.parseJsonResponse(response);
1956
+ return ComplianceEnrollment.fromArray(data);
1957
+ }
1958
+ validateService(service) {
1959
+ if (!SERVICES.includes(service)) {
1960
+ throw new TaxoraException(`Invalid service "${String(service)}". Expected one of: ${SERVICES.join(", ")}.`);
1961
+ }
1962
+ }
1963
+ validateEmail(email, name) {
1964
+ if (typeof email !== "string" || email.trim() === "" || !email.includes("@")) {
1965
+ throw new TaxoraException(`${name} must be a valid email address.`);
1966
+ }
1967
+ }
1968
+ /** Lookup routes take a 2-letter country; the API expects it upper-case. */
1969
+ normalizeLookupCountry(country) {
1970
+ const normalized = country.trim().toUpperCase();
1971
+ if (!COUNTRY_REGEX.test(normalized)) {
1972
+ throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
1973
+ }
1974
+ return normalized;
1533
1975
  }
1534
1976
  validateId(id, name) {
1535
1977
  if (!Number.isInteger(id) || id <= 0) {
@@ -1569,6 +2011,33 @@ var EReportingEndpoint = class {
1569
2011
  throw new TaxoraException("counterpartyCountry must be a 2-character ISO 3166-1 alpha-2 code.");
1570
2012
  }
1571
2013
  }
2014
+ /**
2015
+ * Format checks for the buyer/document fields. Country rules (e.g. the Norway
2016
+ * org number being required) depend on the enrollment and stay server-side;
2017
+ * a register id is only check-digit validated when the buyer is Norwegian.
2018
+ */
2019
+ validateBuyerFields(input) {
2020
+ if (input.counterpartyRegisterId !== void 0) {
2021
+ if (input.counterpartyRegisterId.length > 30) {
2022
+ throw new TaxoraException("counterpartyRegisterId must not exceed 30 characters.");
2023
+ }
2024
+ if (input.counterpartyCountry?.toUpperCase() === "NO" && !NorwegianOrgNumber.isValid(input.counterpartyRegisterId)) {
2025
+ throw new TaxoraException(
2026
+ "counterpartyRegisterId must be a valid 9-digit Norwegian organisation number (mod-11 check digit)."
2027
+ );
2028
+ }
2029
+ }
2030
+ if (input.counterpartyEmail !== void 0) this.validateEmail(input.counterpartyEmail, "counterpartyEmail");
2031
+ if (input.buyerReference !== void 0 && input.buyerReference.length > 100) {
2032
+ throw new TaxoraException("buyerReference must not exceed 100 characters.");
2033
+ }
2034
+ if (input.amendedNumber !== void 0 && input.amendedNumber.length > 50) {
2035
+ throw new TaxoraException("amendedNumber must not exceed 50 characters.");
2036
+ }
2037
+ if (input.extraInfo !== void 0 && input.extraInfo.length > 1e3) {
2038
+ throw new TaxoraException("extraInfo must not exceed 1000 characters.");
2039
+ }
2040
+ }
1572
2041
  // ---------------------------------------------------------------------
1573
2042
  // Body building (camelCase input → snake_case wire format)
1574
2043
  // ---------------------------------------------------------------------
@@ -1605,6 +2074,16 @@ var EReportingEndpoint = class {
1605
2074
  if (input.remittanceInformation !== void 0) body["remittance_information"] = input.remittanceInformation;
1606
2075
  if (input.paymentMethodText !== void 0) body["payment_method_text"] = input.paymentMethodText;
1607
2076
  if (input.paymentTerms !== void 0) body["payment_terms"] = input.paymentTerms;
2077
+ if (input.counterpartyRegisterId !== void 0) body["counterparty_register_id"] = input.counterpartyRegisterId;
2078
+ if (input.counterpartyAddress !== void 0) body["counterparty_address"] = input.counterpartyAddress;
2079
+ if (input.counterpartyCity !== void 0) body["counterparty_city"] = input.counterpartyCity;
2080
+ if (input.counterpartyPostalcode !== void 0) body["counterparty_postalcode"] = input.counterpartyPostalcode;
2081
+ if (input.counterpartyEmail !== void 0) body["counterparty_email"] = input.counterpartyEmail;
2082
+ if (input.buyerReference !== void 0) body["buyer_reference"] = input.buyerReference;
2083
+ if (input.isCreditNote !== void 0) body["is_credit_note"] = input.isCreditNote;
2084
+ if (input.amendedNumber !== void 0) body["amended_number"] = input.amendedNumber;
2085
+ if (input.amendedDate !== void 0) body["amended_date"] = this.formatDate(input.amendedDate);
2086
+ if (input.extraInfo !== void 0) body["extra_info"] = input.extraInfo;
1608
2087
  }
1609
2088
  // ---------------------------------------------------------------------
1610
2089
  // HTTP plumbing (kept in sync with SmartEnrichmentEndpoint)
@@ -1727,22 +2206,22 @@ var SmartEnrichmentMode = {
1727
2206
  DEFAULT: "default",
1728
2207
  COMPLEX: "complex"
1729
2208
  };
1730
- var VALUES5 = Object.values(SmartEnrichmentMode);
2209
+ var VALUES8 = Object.values(SmartEnrichmentMode);
1731
2210
  function toSmartEnrichmentMode(value) {
1732
2211
  if (typeof value === "string") {
1733
2212
  const normalized = value.trim().toLowerCase();
1734
- if (VALUES5.includes(normalized)) {
2213
+ if (VALUES8.includes(normalized)) {
1735
2214
  return normalized;
1736
2215
  }
1737
2216
  }
1738
2217
  return void 0;
1739
2218
  }
1740
- var DESCRIPTIONS5 = {
2219
+ var DESCRIPTIONS8 = {
1741
2220
  [SmartEnrichmentMode.DEFAULT]: "One provider searches; a second is consulted only if it finds nothing.",
1742
2221
  [SmartEnrichmentMode.COMPLEX]: "Two providers search independently and are cross-checked against the tax authority. Costs more."
1743
2222
  };
1744
2223
  function describeSmartEnrichmentMode(mode) {
1745
- return DESCRIPTIONS5[mode];
2224
+ return DESCRIPTIONS8[mode];
1746
2225
  }
1747
2226
 
1748
2227
  // src/enums/SmartEnrichmentStatus.ts
@@ -1756,17 +2235,17 @@ var SmartEnrichmentStatus = {
1756
2235
  FAILED: "failed",
1757
2236
  UNKNOWN: "unknown"
1758
2237
  };
1759
- var VALUES6 = Object.values(SmartEnrichmentStatus);
2238
+ var VALUES9 = Object.values(SmartEnrichmentStatus);
1760
2239
  function toSmartEnrichmentStatus(value) {
1761
2240
  if (typeof value === "string") {
1762
2241
  const normalized = value.trim().toLowerCase();
1763
- if (VALUES6.includes(normalized)) {
2242
+ if (VALUES9.includes(normalized)) {
1764
2243
  return normalized;
1765
2244
  }
1766
2245
  }
1767
2246
  return SmartEnrichmentStatus.UNKNOWN;
1768
2247
  }
1769
- var DESCRIPTIONS6 = {
2248
+ var DESCRIPTIONS9 = {
1770
2249
  [SmartEnrichmentStatus.FOUND]: "A VAT number was resolved and confirmed.",
1771
2250
  [SmartEnrichmentStatus.NO_VAT_EXISTS]: "The company was identified but has no VAT number.",
1772
2251
  [SmartEnrichmentStatus.NOT_FOUND]: "No sufficiently-confident match could be resolved.",
@@ -1777,7 +2256,7 @@ var DESCRIPTIONS6 = {
1777
2256
  [SmartEnrichmentStatus.UNKNOWN]: "Unknown status."
1778
2257
  };
1779
2258
  function describeSmartEnrichmentStatus(status) {
1780
- return DESCRIPTIONS6[status];
2259
+ return DESCRIPTIONS9[status];
1781
2260
  }
1782
2261
 
1783
2262
  // src/dto/SmartEnrichmentResource.ts
@@ -1995,13 +2474,13 @@ var SmartEnrichmentHistoryPage = class _SmartEnrichmentHistoryPage {
1995
2474
  };
1996
2475
 
1997
2476
  // src/dto/SmartEnrichmentStatistics.ts
1998
- function asString7(value, fallback = "") {
2477
+ function asString9(value, fallback = "") {
1999
2478
  return typeof value === "string" ? value : fallback;
2000
2479
  }
2001
2480
  function asNumber6(value) {
2002
2481
  return typeof value === "number" ? value : Number(value ?? 0) || 0;
2003
2482
  }
2004
- function asRecord(value) {
2483
+ function asRecord2(value) {
2005
2484
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
2006
2485
  }
2007
2486
  function asRecords2(value) {
@@ -2024,32 +2503,32 @@ var SmartEnrichmentStatistics = class _SmartEnrichmentStatistics {
2024
2503
  byOutcome;
2025
2504
  confidenceBuckets;
2026
2505
  static fromArray(data) {
2027
- const range = asRecord(data["range"]);
2028
- const totals = asRecord(data["totals"]);
2506
+ const range = asRecord2(data["range"]);
2507
+ const totals = asRecord2(data["totals"]);
2029
2508
  const timeSeries = asRecords2(data["time_series"]).map((row) => ({
2030
- bucket: asString7(row["bucket"]),
2509
+ bucket: asString9(row["bucket"]),
2031
2510
  jobs: asNumber6(row["jobs"]),
2032
2511
  items: asNumber6(row["items"]),
2033
2512
  found: asNumber6(row["found"])
2034
2513
  }));
2035
2514
  const bySource = asRecords2(data["by_source"]).map((row) => ({
2036
- source: asString7(row["source"]),
2515
+ source: asString9(row["source"]),
2037
2516
  jobs: asNumber6(row["jobs"]),
2038
2517
  found: asNumber6(row["found"])
2039
2518
  }));
2040
2519
  const byOutcome = asRecords2(data["by_outcome"]).map((row) => ({
2041
- outcome: asString7(row["outcome"]),
2520
+ outcome: asString9(row["outcome"]),
2042
2521
  count: asNumber6(row["count"])
2043
2522
  }));
2044
2523
  const confidenceBuckets = asRecords2(data["confidence_buckets"]).map((row) => ({
2045
- bucket: asString7(row["bucket"]),
2524
+ bucket: asString9(row["bucket"]),
2046
2525
  count: asNumber6(row["count"])
2047
2526
  }));
2048
2527
  return new _SmartEnrichmentStatistics(
2049
2528
  {
2050
- dateFrom: asString7(range["date_from"]),
2051
- dateTo: asString7(range["date_to"]),
2052
- interval: asString7(range["interval"])
2529
+ dateFrom: asString9(range["date_from"]),
2530
+ dateTo: asString9(range["date_to"]),
2531
+ interval: asString9(range["interval"])
2053
2532
  },
2054
2533
  {
2055
2534
  jobs: asNumber6(totals["jobs"]),
@@ -2264,7 +2743,10 @@ var SmartEnrichmentEndpoint = class {
2264
2743
  async parseJsonResponse(response, acceptedStatuses = [200]) {
2265
2744
  const responseText = await response.text();
2266
2745
  if (response.status === 422) {
2267
- throw new ValidationException(jsonErrorMessage(responseText) ?? "Validation failed", this.parseValidationErrors(responseText));
2746
+ throw new ValidationException(
2747
+ jsonErrorMessage(responseText) ?? "Validation failed",
2748
+ this.parseValidationErrors(responseText)
2749
+ );
2268
2750
  }
2269
2751
  if (!response.ok && !acceptedStatuses.includes(response.status)) {
2270
2752
  throw new HttpException(
@@ -2298,7 +2780,10 @@ var SmartEnrichmentEndpoint = class {
2298
2780
  async parseRawJson(response) {
2299
2781
  const responseText = await response.text();
2300
2782
  if (response.status === 422) {
2301
- throw new ValidationException(jsonErrorMessage(responseText) ?? "Validation failed", this.parseValidationErrors(responseText));
2783
+ throw new ValidationException(
2784
+ jsonErrorMessage(responseText) ?? "Validation failed",
2785
+ this.parseValidationErrors(responseText)
2786
+ );
2302
2787
  }
2303
2788
  if (!response.ok) {
2304
2789
  throw new HttpException(
@@ -2327,7 +2812,10 @@ var SmartEnrichmentEndpoint = class {
2327
2812
  async parseCsvResponse(response) {
2328
2813
  const responseText = await response.text();
2329
2814
  if (response.status === 422) {
2330
- throw new ValidationException(jsonErrorMessage(responseText) ?? "Validation failed", this.parseValidationErrors(responseText));
2815
+ throw new ValidationException(
2816
+ jsonErrorMessage(responseText) ?? "Validation failed",
2817
+ this.parseValidationErrors(responseText)
2818
+ );
2331
2819
  }
2332
2820
  if (!response.ok) {
2333
2821
  throw new HttpException(
@@ -2373,14 +2861,14 @@ var VatState = {
2373
2861
  function getFailedVatStates() {
2374
2862
  return [VatState.INVALID, VatState.FRAUD];
2375
2863
  }
2376
- var DESCRIPTIONS7 = {
2864
+ var DESCRIPTIONS10 = {
2377
2865
  [VatState.VALID]: "The VAT number is valid and active.",
2378
2866
  [VatState.INVALID]: "The VAT number is invalid or not registered.",
2379
2867
  [VatState.FRAUD]: "The VAT number has been flagged as potentially fraudulent.",
2380
2868
  [VatState.UNKNOWN]: "The VAT number state could not be determined."
2381
2869
  };
2382
2870
  function describeVatState(state) {
2383
- return DESCRIPTIONS7[state];
2871
+ return DESCRIPTIONS10[state];
2384
2872
  }
2385
2873
 
2386
2874
  // src/dto/CompanyAddress.ts
@@ -3112,7 +3600,7 @@ var AuthRetryHttpClient = class {
3112
3600
  };
3113
3601
 
3114
3602
  // src/version.ts
3115
- var SDK_VERSION = "1.2.0";
3603
+ var SDK_VERSION = "1.3.0";
3116
3604
 
3117
3605
  // src/http/SdkVersionHttpClient.ts
3118
3606
  var SDK_VERSION_HEADER = "X-Taxora-SDK-Version";
@@ -3151,7 +3639,13 @@ var TaxoraClient = class {
3151
3639
  const retryClient = new AuthRetryHttpClient(versionedClient, tokenStorage, () => this.auth.refresh());
3152
3640
  this.vat = new VatEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
3153
3641
  this.company = new CompanyEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
3154
- this.smartEnrichment = new SmartEnrichmentEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
3642
+ this.smartEnrichment = new SmartEnrichmentEndpoint(
3643
+ this.baseUrl,
3644
+ options.apiKey,
3645
+ tokenStorage,
3646
+ retryClient,
3647
+ retryPolicy
3648
+ );
3155
3649
  this.eReporting = new EReportingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
3156
3650
  }
3157
3651
  };
@@ -3173,6 +3667,125 @@ var Language = {
3173
3667
  ENGLISH: "en",
3174
3668
  GERMAN: "de"
3175
3669
  };
3670
+
3671
+ // src/webhooks/WebhookSignature.ts
3672
+ import { createHmac, timingSafeEqual } from "crypto";
3673
+ var WEBHOOK_SIGNATURE_HEADER = "Taxora-Signature";
3674
+ var WEBHOOK_ID_HEADER = "Taxora-Webhook-Id";
3675
+ var DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;
3676
+ var SCHEME = "v1";
3677
+ var WebhookSignatureVerificationError = class extends TaxoraException {
3678
+ reason;
3679
+ constructor(message, reason, context = {}) {
3680
+ super(message, { ...context, reason });
3681
+ this.name = "WebhookSignatureVerificationError";
3682
+ this.reason = reason;
3683
+ }
3684
+ };
3685
+ function parseWebhookSignatureHeader(header) {
3686
+ const value = Array.isArray(header) ? header[0] : header;
3687
+ if (typeof value !== "string" || value.trim() === "") {
3688
+ throw new WebhookSignatureVerificationError(
3689
+ `Missing ${WEBHOOK_SIGNATURE_HEADER} header. Is signing enabled for this webhook endpoint?`,
3690
+ "missing_header"
3691
+ );
3692
+ }
3693
+ let timestamp = null;
3694
+ const signatures = [];
3695
+ for (const part of value.split(",")) {
3696
+ const separator = part.indexOf("=");
3697
+ if (separator === -1) continue;
3698
+ const key = part.slice(0, separator).trim();
3699
+ const val = part.slice(separator + 1).trim();
3700
+ if (key === "t" && /^\d+$/.test(val)) {
3701
+ timestamp = Number(val);
3702
+ } else if (key === SCHEME && /^[0-9a-f]{64}$/i.test(val)) {
3703
+ signatures.push(val.toLowerCase());
3704
+ }
3705
+ }
3706
+ if (timestamp === null || signatures.length === 0) {
3707
+ throw new WebhookSignatureVerificationError(
3708
+ `Malformed ${WEBHOOK_SIGNATURE_HEADER} header \u2014 expected "t=<unix>,v1=<hex>".`,
3709
+ "malformed_header"
3710
+ );
3711
+ }
3712
+ return { timestamp, signatures };
3713
+ }
3714
+ function computeWebhookSignature(rawBody, timestamp, secret) {
3715
+ return createHmac("sha256", secret).update(`${timestamp}.`).update(toBytes(rawBody)).digest("hex");
3716
+ }
3717
+ function generateWebhookSignatureHeader(rawBody, secret, timestamp = Math.floor(Date.now() / 1e3)) {
3718
+ const secrets = normalizeSecrets(secret);
3719
+ return [`t=${timestamp}`, ...secrets.map((s) => `${SCHEME}=${computeWebhookSignature(rawBody, timestamp, s)}`)].join(
3720
+ ","
3721
+ );
3722
+ }
3723
+ function verifyWebhookSignature(rawBody, header, secret, options = {}) {
3724
+ const secrets = normalizeSecrets(secret);
3725
+ const bytes = toBytes(rawBody);
3726
+ const { timestamp, signatures } = parseWebhookSignatureHeader(header);
3727
+ const tolerance = options.tolerance ?? DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
3728
+ const now = options.now ?? Math.floor(Date.now() / 1e3);
3729
+ if (tolerance > 0 && Math.abs(now - timestamp) > tolerance) {
3730
+ throw new WebhookSignatureVerificationError(
3731
+ `Timestamp outside the tolerance window (${tolerance}s). Possible replay \u2014 or check the server clock.`,
3732
+ "timestamp_outside_tolerance",
3733
+ { timestamp, now, tolerance }
3734
+ );
3735
+ }
3736
+ const received = signatures.map((s) => Buffer.from(s, "hex"));
3737
+ for (const s of secrets) {
3738
+ const expected = Buffer.from(computeWebhookSignature(bytes, timestamp, s), "hex");
3739
+ let matched = false;
3740
+ for (const candidate of received) {
3741
+ if (candidate.length === expected.length && timingSafeEqual(candidate, expected)) {
3742
+ matched = true;
3743
+ }
3744
+ }
3745
+ if (matched) return true;
3746
+ }
3747
+ throw new WebhookSignatureVerificationError(
3748
+ 'No signature matches the expected signature for this payload. Make sure you pass the raw request body (not re-serialised JSON) and the full secret including the "whsec_" prefix.',
3749
+ "no_matching_signature"
3750
+ );
3751
+ }
3752
+ function constructWebhookEvent(rawBody, header, secret, options = {}) {
3753
+ verifyWebhookSignature(rawBody, header, secret, options);
3754
+ let parsed;
3755
+ try {
3756
+ parsed = JSON.parse(Buffer.from(toBytes(rawBody)).toString("utf8"));
3757
+ } catch {
3758
+ throw new WebhookSignatureVerificationError("Webhook payload is not valid JSON.", "invalid_payload");
3759
+ }
3760
+ if (typeof parsed !== "object" || parsed === null || typeof parsed.id !== "string" || typeof parsed.event !== "string" || typeof parsed.data !== "object") {
3761
+ throw new WebhookSignatureVerificationError(
3762
+ "Webhook payload does not have the expected { id, event, timestamp, data } shape.",
3763
+ "invalid_payload"
3764
+ );
3765
+ }
3766
+ return parsed;
3767
+ }
3768
+ function normalizeSecrets(secret) {
3769
+ const list = (typeof secret === "string" ? [secret] : [...secret]).filter(
3770
+ (s) => typeof s === "string" && s !== ""
3771
+ );
3772
+ if (list.length === 0) {
3773
+ throw new WebhookSignatureVerificationError(
3774
+ "No webhook signing secret given. Copy it from the Taxora dashboard (Developer \u2192 Webhooks).",
3775
+ "missing_secret"
3776
+ );
3777
+ }
3778
+ return list;
3779
+ }
3780
+ function toBytes(rawBody) {
3781
+ if (typeof rawBody === "string") return Buffer.from(rawBody, "utf8");
3782
+ if (rawBody instanceof Uint8Array) return rawBody;
3783
+ if (rawBody instanceof ArrayBuffer) return new Uint8Array(rawBody);
3784
+ throw new WebhookSignatureVerificationError(
3785
+ "Webhook body must be the raw request body (string, Buffer or Uint8Array). It looks like it was already parsed as JSON \u2014 use express.raw() / request.text() instead of a JSON body parser.",
3786
+ "invalid_body"
3787
+ );
3788
+ }
3176
3789
  export {
3177
3790
  ApiVersion,
3178
3791
  AuthEndpoint,
@@ -3180,9 +3793,11 @@ export {
3180
3793
  AuthenticationException,
3181
3794
  CompanyAddress,
3182
3795
  CompanyEndpoint,
3796
+ ComplianceDocumentType,
3183
3797
  ComplianceEnrollment,
3184
3798
  ComplianceEnrollmentPage,
3185
3799
  ComplianceEnrollmentStatus,
3800
+ ComplianceService,
3186
3801
  ComplianceTaxReport,
3187
3802
  ComplianceTaxReportPage,
3188
3803
  ComplianceTaxReportState,
@@ -3190,6 +3805,7 @@ export {
3190
3805
  ComplianceTransactionPage,
3191
3806
  ComplianceTransactionState,
3192
3807
  ComplianceTransactionType,
3808
+ DEFAULT_WEBHOOK_TOLERANCE_SECONDS,
3193
3809
  EReportingEndpoint,
3194
3810
  Environment,
3195
3811
  FetchHttpClient,
@@ -3198,8 +3814,12 @@ export {
3198
3814
  InMemoryTokenStorage,
3199
3815
  Language,
3200
3816
  LoginIdentifier,
3817
+ NorwegianOrgNumber,
3818
+ PeppolLookupResult,
3819
+ PeppolLookupStatus,
3201
3820
  ProviderDocument,
3202
3821
  ProviderDocumentLine,
3822
+ RegistryCompany,
3203
3823
  RetryPolicy,
3204
3824
  RevenueStatistics,
3205
3825
  ScoreBreakdown,
@@ -3224,21 +3844,35 @@ export {
3224
3844
  VatResource,
3225
3845
  VatState,
3226
3846
  VatValidationAddressInput,
3847
+ WEBHOOK_ID_HEADER,
3848
+ WEBHOOK_SIGNATURE_HEADER,
3849
+ WebhookSignatureVerificationError,
3850
+ computeWebhookSignature,
3851
+ constructWebhookEvent,
3227
3852
  describeApiError,
3853
+ describeComplianceDocumentType,
3228
3854
  describeComplianceEnrollmentStatus,
3855
+ describeComplianceService,
3229
3856
  describeComplianceTaxReportState,
3230
3857
  describeComplianceTransactionState,
3231
3858
  describeComplianceTransactionType,
3859
+ describePeppolLookupStatus,
3232
3860
  describeSmartEnrichmentMode,
3233
3861
  describeSmartEnrichmentStatus,
3234
3862
  describeVatState,
3863
+ generateWebhookSignatureHeader,
3235
3864
  getBaseUrl,
3236
3865
  getFailedVatStates,
3866
+ parseWebhookSignatureHeader,
3867
+ toComplianceDocumentType,
3237
3868
  toComplianceEnrollmentStatus,
3869
+ toComplianceService,
3238
3870
  toComplianceTaxReportState,
3239
3871
  toComplianceTransactionState,
3240
3872
  toComplianceTransactionType,
3873
+ toPeppolLookupStatus,
3241
3874
  toSmartEnrichmentMode,
3242
- toSmartEnrichmentStatus
3875
+ toSmartEnrichmentStatus,
3876
+ verifyWebhookSignature
3243
3877
  };
3244
3878
  //# sourceMappingURL=index.js.map