@taxora/sdk 1.8.0 → 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/CHANGELOG.md +68 -0
- package/README.md +185 -4
- package/dist/index.cjs +863 -179
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +478 -14
- package/dist/index.d.ts +478 -14
- package/dist/index.js +858 -179
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -24,12 +24,14 @@ __export(index_exports, {
|
|
|
24
24
|
AuthEndpoint: () => AuthEndpoint,
|
|
25
25
|
AuthRetryHttpClient: () => AuthRetryHttpClient,
|
|
26
26
|
AuthenticationException: () => AuthenticationException,
|
|
27
|
+
BillingEndpoint: () => BillingEndpoint,
|
|
27
28
|
CompanyAddress: () => CompanyAddress,
|
|
28
29
|
CompanyEndpoint: () => CompanyEndpoint,
|
|
29
30
|
ComplianceDocumentType: () => ComplianceDocumentType,
|
|
30
31
|
ComplianceEnrollment: () => ComplianceEnrollment,
|
|
31
32
|
ComplianceEnrollmentPage: () => ComplianceEnrollmentPage,
|
|
32
33
|
ComplianceEnrollmentStatus: () => ComplianceEnrollmentStatus,
|
|
34
|
+
ComplianceOfferings: () => ComplianceOfferings,
|
|
33
35
|
ComplianceService: () => ComplianceService,
|
|
34
36
|
ComplianceTaxReport: () => ComplianceTaxReport,
|
|
35
37
|
ComplianceTaxReportPage: () => ComplianceTaxReportPage,
|
|
@@ -73,6 +75,8 @@ __export(index_exports, {
|
|
|
73
75
|
TaxoraClientFactory: () => TaxoraClientFactory,
|
|
74
76
|
TaxoraException: () => TaxoraException,
|
|
75
77
|
Token: () => Token,
|
|
78
|
+
UsageSummary: () => UsageSummary,
|
|
79
|
+
UsageSummaryPage: () => UsageSummaryPage,
|
|
76
80
|
ValidationException: () => ValidationException,
|
|
77
81
|
VatCertificateExport: () => VatCertificateExport,
|
|
78
82
|
VatCollection: () => VatCollection,
|
|
@@ -101,6 +105,7 @@ __export(index_exports, {
|
|
|
101
105
|
generateWebhookSignatureHeader: () => generateWebhookSignatureHeader,
|
|
102
106
|
getBaseUrl: () => getBaseUrl,
|
|
103
107
|
getFailedVatStates: () => getFailedVatStates,
|
|
108
|
+
isTransactionBuyerRejected: () => isTransactionBuyerRejected,
|
|
104
109
|
parseLocalTaxNumbersCsv: () => parseLocalTaxNumbersCsv,
|
|
105
110
|
parseWebhookSignatureHeader: () => parseWebhookSignatureHeader,
|
|
106
111
|
toComplianceDocumentType: () => toComplianceDocumentType,
|
|
@@ -559,6 +564,213 @@ var AuthEndpoint = class {
|
|
|
559
564
|
}
|
|
560
565
|
};
|
|
561
566
|
|
|
567
|
+
// src/dto/UsageSummary.ts
|
|
568
|
+
function asRecord(value) {
|
|
569
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
570
|
+
}
|
|
571
|
+
function asRecordList(value) {
|
|
572
|
+
return Array.isArray(value) ? value.filter((row) => asRecord(row) !== null) : [];
|
|
573
|
+
}
|
|
574
|
+
function asString(value, fallback = "") {
|
|
575
|
+
return typeof value === "string" ? value : fallback;
|
|
576
|
+
}
|
|
577
|
+
function asNullableString(value) {
|
|
578
|
+
return typeof value === "string" ? value : null;
|
|
579
|
+
}
|
|
580
|
+
function asNumber(value) {
|
|
581
|
+
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
582
|
+
}
|
|
583
|
+
function asNullableNumber(value) {
|
|
584
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
585
|
+
const num = Number(value);
|
|
586
|
+
return Number.isFinite(num) ? num : null;
|
|
587
|
+
}
|
|
588
|
+
var UsageSummary = class _UsageSummary {
|
|
589
|
+
constructor(id, companyId, feature, country, regime, billingPeriod, eventCount, billableAmount, state, stateLabel, periodClosedAt, billingTriggeredAt, billedAt, stripeInvoiceId, stripeSubscriptionItemId, billingNotes, events, createdAt, updatedAt) {
|
|
590
|
+
this.id = id;
|
|
591
|
+
this.companyId = companyId;
|
|
592
|
+
this.feature = feature;
|
|
593
|
+
this.country = country;
|
|
594
|
+
this.regime = regime;
|
|
595
|
+
this.billingPeriod = billingPeriod;
|
|
596
|
+
this.eventCount = eventCount;
|
|
597
|
+
this.billableAmount = billableAmount;
|
|
598
|
+
this.state = state;
|
|
599
|
+
this.stateLabel = stateLabel;
|
|
600
|
+
this.periodClosedAt = periodClosedAt;
|
|
601
|
+
this.billingTriggeredAt = billingTriggeredAt;
|
|
602
|
+
this.billedAt = billedAt;
|
|
603
|
+
this.stripeInvoiceId = stripeInvoiceId;
|
|
604
|
+
this.stripeSubscriptionItemId = stripeSubscriptionItemId;
|
|
605
|
+
this.billingNotes = billingNotes;
|
|
606
|
+
this.events = events;
|
|
607
|
+
this.createdAt = createdAt;
|
|
608
|
+
this.updatedAt = updatedAt;
|
|
609
|
+
}
|
|
610
|
+
id;
|
|
611
|
+
companyId;
|
|
612
|
+
feature;
|
|
613
|
+
country;
|
|
614
|
+
regime;
|
|
615
|
+
billingPeriod;
|
|
616
|
+
eventCount;
|
|
617
|
+
billableAmount;
|
|
618
|
+
state;
|
|
619
|
+
stateLabel;
|
|
620
|
+
periodClosedAt;
|
|
621
|
+
billingTriggeredAt;
|
|
622
|
+
billedAt;
|
|
623
|
+
stripeInvoiceId;
|
|
624
|
+
stripeSubscriptionItemId;
|
|
625
|
+
billingNotes;
|
|
626
|
+
events;
|
|
627
|
+
createdAt;
|
|
628
|
+
updatedAt;
|
|
629
|
+
static fromArray(data) {
|
|
630
|
+
const amount = data["billable_amount"];
|
|
631
|
+
return new _UsageSummary(
|
|
632
|
+
asNumber(data["id"]),
|
|
633
|
+
asNumber(data["company_id"]),
|
|
634
|
+
asString(data["feature"]),
|
|
635
|
+
asNullableString(data["country"]),
|
|
636
|
+
asNullableString(data["regime"]),
|
|
637
|
+
asString(data["billing_period"]),
|
|
638
|
+
asNumber(data["event_count"]),
|
|
639
|
+
typeof amount === "string" ? amount : typeof amount === "number" ? String(amount) : null,
|
|
640
|
+
asNullableString(data["state"]),
|
|
641
|
+
asNullableString(data["state_label"]),
|
|
642
|
+
asNullableString(data["period_closed_at"]),
|
|
643
|
+
asNullableString(data["billing_triggered_at"]),
|
|
644
|
+
asNullableString(data["billed_at"]),
|
|
645
|
+
asNullableString(data["stripe_invoice_id"]),
|
|
646
|
+
asNullableString(data["stripe_subscription_item_id"]),
|
|
647
|
+
asNullableString(data["billing_notes"]),
|
|
648
|
+
asRecordList(data["events"]).map((event) => ({
|
|
649
|
+
id: asNumber(event["id"]),
|
|
650
|
+
usageSummaryId: asNullableNumber(event["usage_summary_id"]),
|
|
651
|
+
feature: asString(event["feature"]),
|
|
652
|
+
country: asNullableString(event["country"]),
|
|
653
|
+
regime: asNullableString(event["regime"]),
|
|
654
|
+
billingPeriod: asString(event["billing_period"]),
|
|
655
|
+
resourceType: asNullableString(event["resource_type"]),
|
|
656
|
+
resourceId: asNullableNumber(event["resource_id"]),
|
|
657
|
+
stripeUsageRecordId: asNullableString(event["stripe_usage_record_id"]),
|
|
658
|
+
stripeReportedAt: asNullableString(event["stripe_reported_at"]),
|
|
659
|
+
createdAt: asNullableString(event["created_at"])
|
|
660
|
+
})),
|
|
661
|
+
asNullableString(data["created_at"]),
|
|
662
|
+
asNullableString(data["updated_at"])
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
var UsageSummaryPage = class _UsageSummaryPage {
|
|
667
|
+
constructor(rows, currentPage, perPage, total, lastPage) {
|
|
668
|
+
this.rows = rows;
|
|
669
|
+
this.currentPage = currentPage;
|
|
670
|
+
this.perPage = perPage;
|
|
671
|
+
this.total = total;
|
|
672
|
+
this.lastPage = lastPage;
|
|
673
|
+
}
|
|
674
|
+
rows;
|
|
675
|
+
currentPage;
|
|
676
|
+
perPage;
|
|
677
|
+
total;
|
|
678
|
+
lastPage;
|
|
679
|
+
static fromResponse(payload) {
|
|
680
|
+
const obj = asRecord(payload) ?? {};
|
|
681
|
+
const rows = asRecordList(obj["data"]).map(UsageSummary.fromArray);
|
|
682
|
+
const meta = asRecord(obj["meta"]) ?? {};
|
|
683
|
+
const num = (value, fallback) => typeof value === "number" ? value : fallback;
|
|
684
|
+
return new _UsageSummaryPage(
|
|
685
|
+
rows,
|
|
686
|
+
num(meta["current_page"], 1),
|
|
687
|
+
num(meta["per_page"], rows.length),
|
|
688
|
+
num(meta["total"], rows.length),
|
|
689
|
+
num(meta["last_page"], 1)
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
[Symbol.iterator]() {
|
|
693
|
+
return this.rows[Symbol.iterator]();
|
|
694
|
+
}
|
|
695
|
+
get length() {
|
|
696
|
+
return this.rows.length;
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
// src/endpoints/BillingEndpoint.ts
|
|
701
|
+
var PERIOD_REGEX = /^\d{4}-(0[1-9]|1[0-2])$/;
|
|
702
|
+
var BillingEndpoint = class {
|
|
703
|
+
constructor(baseUrl, apiKey, tokenStorage, httpClient, retryPolicy = new RetryPolicy()) {
|
|
704
|
+
this.baseUrl = baseUrl;
|
|
705
|
+
this.apiKey = apiKey;
|
|
706
|
+
this.tokenStorage = tokenStorage;
|
|
707
|
+
this.httpClient = httpClient;
|
|
708
|
+
this.retryPolicy = retryPolicy;
|
|
709
|
+
}
|
|
710
|
+
baseUrl;
|
|
711
|
+
apiKey;
|
|
712
|
+
tokenStorage;
|
|
713
|
+
httpClient;
|
|
714
|
+
retryPolicy;
|
|
715
|
+
/** Paginated usage summaries, newest billing period first. `perPage` is clamped to 1-100 server-side. */
|
|
716
|
+
async listUsageSummaries(page = 1, perPage = 25) {
|
|
717
|
+
const params = new URLSearchParams();
|
|
718
|
+
params.set("page", String(page));
|
|
719
|
+
params.set("per_page", String(perPage));
|
|
720
|
+
const data = await this.getJson(`${this.baseUrl}/billing/usage-summaries?${params.toString()}`);
|
|
721
|
+
return UsageSummaryPage.fromResponse(data);
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* The usage summaries of one billing period (YYYY-MM), one per feature,
|
|
725
|
+
* each with its usage events. A period without usage yields a 404 (HttpException).
|
|
726
|
+
*/
|
|
727
|
+
async getUsageSummaries(period) {
|
|
728
|
+
if (!PERIOD_REGEX.test(period)) {
|
|
729
|
+
throw new TaxoraException(`Invalid billing period "${period}". Expected YYYY-MM, e.g. "2026-09".`);
|
|
730
|
+
}
|
|
731
|
+
const data = await this.getJson(`${this.baseUrl}/billing/usage-summaries/${period}`);
|
|
732
|
+
return Array.isArray(data) ? data.filter((row) => typeof row === "object" && row !== null).map(UsageSummary.fromArray) : [];
|
|
733
|
+
}
|
|
734
|
+
buildHeaders() {
|
|
735
|
+
const headers = {
|
|
736
|
+
"Content-Type": "application/json",
|
|
737
|
+
"x-api-key": this.apiKey
|
|
738
|
+
};
|
|
739
|
+
const token = this.tokenStorage.get();
|
|
740
|
+
if (token) {
|
|
741
|
+
headers["Authorization"] = `Bearer ${token.accessToken}`;
|
|
742
|
+
}
|
|
743
|
+
return headers;
|
|
744
|
+
}
|
|
745
|
+
/** GET + envelope unwrap (`{success, data}` → data, which may be an object or an array). */
|
|
746
|
+
async getJson(url) {
|
|
747
|
+
return withRetries(this.retryPolicy, "billing request", async () => {
|
|
748
|
+
const response = await this.httpClient.request("GET", url, { headers: this.buildHeaders() });
|
|
749
|
+
const responseText = await response.text();
|
|
750
|
+
if (!response.ok) {
|
|
751
|
+
throw new HttpException(
|
|
752
|
+
describeApiError(responseText, response.status),
|
|
753
|
+
response.status,
|
|
754
|
+
responseText,
|
|
755
|
+
{},
|
|
756
|
+
response.headers.get("retry-after")
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
let parsed;
|
|
760
|
+
try {
|
|
761
|
+
parsed = JSON.parse(responseText);
|
|
762
|
+
} catch {
|
|
763
|
+
throw new HttpException("Invalid JSON response", response.status, responseText);
|
|
764
|
+
}
|
|
765
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
766
|
+
throw new HttpException("Unexpected response format", response.status, responseText);
|
|
767
|
+
}
|
|
768
|
+
const obj = parsed;
|
|
769
|
+
return obj["data"] !== void 0 && obj["data"] !== null ? obj["data"] : obj;
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
|
|
562
774
|
// src/dto/CompanyResource.ts
|
|
563
775
|
function normalizeCompanyResource(data) {
|
|
564
776
|
const apiRateLimit = typeof data["api_rate_limit"] === "number" ? data["api_rate_limit"] : void 0;
|
|
@@ -685,13 +897,13 @@ function describeComplianceService(service) {
|
|
|
685
897
|
}
|
|
686
898
|
|
|
687
899
|
// src/dto/ComplianceEnrollment.ts
|
|
688
|
-
function
|
|
900
|
+
function asString2(value, fallback = "") {
|
|
689
901
|
return typeof value === "string" ? value : fallback;
|
|
690
902
|
}
|
|
691
|
-
function
|
|
903
|
+
function asNullableString2(value) {
|
|
692
904
|
return typeof value === "string" ? value : null;
|
|
693
905
|
}
|
|
694
|
-
function
|
|
906
|
+
function asNumber2(value) {
|
|
695
907
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
696
908
|
}
|
|
697
909
|
var ComplianceEnrollment = class _ComplianceEnrollment {
|
|
@@ -738,29 +950,114 @@ var ComplianceEnrollment = class _ComplianceEnrollment {
|
|
|
738
950
|
static fromArray(data) {
|
|
739
951
|
const regimeConfig = typeof data["regime_config"] === "object" && data["regime_config"] !== null && !Array.isArray(data["regime_config"]) ? data["regime_config"] : null;
|
|
740
952
|
return new _ComplianceEnrollment(
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
953
|
+
asNumber2(data["id"]),
|
|
954
|
+
asNumber2(data["company_id"]),
|
|
955
|
+
asString2(data["country"]),
|
|
956
|
+
asString2(data["regime"]),
|
|
957
|
+
asString2(data["provider"]),
|
|
746
958
|
toComplianceEnrollmentStatus(data["status"]),
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
959
|
+
asString2(data["status_label"]),
|
|
960
|
+
asNullableString2(data["status_error"]),
|
|
961
|
+
asNullableString2(data["provider_account_id"]),
|
|
962
|
+
asNullableString2(data["tax_id"]),
|
|
963
|
+
asNullableString2(data["company_register_id"]),
|
|
964
|
+
asNullableString2(data["company_register_scheme"]),
|
|
753
965
|
regimeConfig,
|
|
754
|
-
|
|
755
|
-
|
|
966
|
+
asNullableString2(data["reporting_start_date"]),
|
|
967
|
+
asNullableString2(data["notification_email"]),
|
|
756
968
|
data["auto_send"] === true,
|
|
757
|
-
|
|
758
|
-
|
|
969
|
+
asNullableString2(data["created_at"]),
|
|
970
|
+
asNullableString2(data["updated_at"]),
|
|
759
971
|
toComplianceService(data["service"])
|
|
760
972
|
);
|
|
761
973
|
}
|
|
762
974
|
};
|
|
763
975
|
|
|
976
|
+
// src/dto/ComplianceOfferings.ts
|
|
977
|
+
function asRecord2(value) {
|
|
978
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
979
|
+
}
|
|
980
|
+
function asRecordList2(value) {
|
|
981
|
+
return Array.isArray(value) ? value.filter((row) => asRecord2(row) !== null) : [];
|
|
982
|
+
}
|
|
983
|
+
function asString3(value, fallback = "") {
|
|
984
|
+
return typeof value === "string" ? value : fallback;
|
|
985
|
+
}
|
|
986
|
+
function asNullableString3(value) {
|
|
987
|
+
return typeof value === "string" ? value : null;
|
|
988
|
+
}
|
|
989
|
+
function asNullableNumber2(value) {
|
|
990
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
991
|
+
if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
|
|
992
|
+
return null;
|
|
993
|
+
}
|
|
994
|
+
function asStringList(value) {
|
|
995
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
996
|
+
}
|
|
997
|
+
function toRequires(value) {
|
|
998
|
+
const record = asRecord2(value);
|
|
999
|
+
return record === null ? null : { country: asString3(record["country"]), service: asString3(record["service"]) };
|
|
1000
|
+
}
|
|
1001
|
+
var ComplianceOfferings = class _ComplianceOfferings {
|
|
1002
|
+
constructor(offerings, coverage, access, accessByCountry) {
|
|
1003
|
+
this.offerings = offerings;
|
|
1004
|
+
this.coverage = coverage;
|
|
1005
|
+
this.access = access;
|
|
1006
|
+
this.accessByCountry = accessByCountry;
|
|
1007
|
+
}
|
|
1008
|
+
offerings;
|
|
1009
|
+
coverage;
|
|
1010
|
+
access;
|
|
1011
|
+
accessByCountry;
|
|
1012
|
+
static fromArray(data) {
|
|
1013
|
+
const offerings = asRecordList2(data["data"]).map((row) => ({
|
|
1014
|
+
country: asString3(row["country"]),
|
|
1015
|
+
service: asString3(row["service"]),
|
|
1016
|
+
currency: asString3(row["currency"], "EUR"),
|
|
1017
|
+
active: row["active"] === true,
|
|
1018
|
+
viaStripe: row["via_stripe"] === true,
|
|
1019
|
+
plan: asNullableString3(row["plan"]),
|
|
1020
|
+
status: asNullableString3(row["status"]),
|
|
1021
|
+
enrolled: row["enrolled"] === true,
|
|
1022
|
+
plans: asRecordList2(row["plans"]).map((plan) => ({
|
|
1023
|
+
key: asString3(plan["key"]),
|
|
1024
|
+
monthlyFee: asNullableNumber2(plan["monthly_fee"]),
|
|
1025
|
+
setupFee: asNullableNumber2(plan["setup_fee"]),
|
|
1026
|
+
usageTiers: Array.isArray(plan["usage_tiers"]) ? plan["usage_tiers"] : [],
|
|
1027
|
+
volumeLimit: plan["volume_limit"] ?? null,
|
|
1028
|
+
contactOnly: plan["contact_only"] === true,
|
|
1029
|
+
checkoutAvailable: plan["checkout_available"] === true,
|
|
1030
|
+
requires: toRequires(plan["requires"]),
|
|
1031
|
+
eligible: plan["eligible"] === true
|
|
1032
|
+
}))
|
|
1033
|
+
}));
|
|
1034
|
+
const coverage = asRecordList2(data["coverage"]).map((row) => ({
|
|
1035
|
+
country: asString3(row["country"]),
|
|
1036
|
+
availability: asString3(row["availability"]),
|
|
1037
|
+
services: asStringList(row["services"]),
|
|
1038
|
+
network: asNullableString3(row["network"]),
|
|
1039
|
+
format: asNullableString3(row["format"]),
|
|
1040
|
+
mandate: asNullableString3(row["mandate"]),
|
|
1041
|
+
support: asString3(row["support"], "yes")
|
|
1042
|
+
}));
|
|
1043
|
+
const access = asRecord2(data["access"]) ?? {};
|
|
1044
|
+
const accessByCountry = {};
|
|
1045
|
+
for (const [country, services] of Object.entries(asRecord2(data["access_by_country"]) ?? {})) {
|
|
1046
|
+
const flags = {};
|
|
1047
|
+
for (const [service, value] of Object.entries(asRecord2(services) ?? {})) {
|
|
1048
|
+
flags[service] = value === true;
|
|
1049
|
+
}
|
|
1050
|
+
accessByCountry[country] = flags;
|
|
1051
|
+
}
|
|
1052
|
+
return new _ComplianceOfferings(
|
|
1053
|
+
offerings,
|
|
1054
|
+
coverage,
|
|
1055
|
+
{ eReporting: access["e_reporting"] === true, eInvoicing: access["e_invoicing"] === true },
|
|
1056
|
+
accessByCountry
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
|
|
764
1061
|
// src/dto/ComplianceEnrollmentPage.ts
|
|
765
1062
|
var ComplianceEnrollmentPage = class _ComplianceEnrollmentPage {
|
|
766
1063
|
constructor(rows, currentPage, perPage, total, lastPage) {
|
|
@@ -830,16 +1127,16 @@ function describeComplianceTaxReportState(state) {
|
|
|
830
1127
|
}
|
|
831
1128
|
|
|
832
1129
|
// src/dto/ComplianceTaxReport.ts
|
|
833
|
-
function
|
|
1130
|
+
function asString4(value, fallback = "") {
|
|
834
1131
|
return typeof value === "string" ? value : fallback;
|
|
835
1132
|
}
|
|
836
|
-
function
|
|
1133
|
+
function asNullableString4(value) {
|
|
837
1134
|
return typeof value === "string" ? value : null;
|
|
838
1135
|
}
|
|
839
|
-
function
|
|
1136
|
+
function asNumber3(value) {
|
|
840
1137
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
841
1138
|
}
|
|
842
|
-
function
|
|
1139
|
+
function asNullableNumber3(value) {
|
|
843
1140
|
return typeof value === "number" ? value : null;
|
|
844
1141
|
}
|
|
845
1142
|
function asMoney(value) {
|
|
@@ -890,25 +1187,25 @@ var ComplianceTaxReport = class _ComplianceTaxReport {
|
|
|
890
1187
|
updatedAt;
|
|
891
1188
|
static fromArray(data) {
|
|
892
1189
|
return new _ComplianceTaxReport(
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1190
|
+
asNumber3(data["id"]),
|
|
1191
|
+
asNumber3(data["company_id"]),
|
|
1192
|
+
asNullableNumber3(data["compliance_transaction_id"]),
|
|
1193
|
+
asNullableNumber3(data["ledger_id"]),
|
|
1194
|
+
asString4(data["country"]),
|
|
1195
|
+
asString4(data["regime"]),
|
|
899
1196
|
toComplianceTaxReportState(data["state"]),
|
|
900
|
-
|
|
1197
|
+
asString4(data["state_label"]),
|
|
901
1198
|
data["is_terminal"] === true,
|
|
902
|
-
|
|
903
|
-
|
|
1199
|
+
asNullableString4(data["provider_tax_report_id"]),
|
|
1200
|
+
asNullableString4(data["provider_ledger_id"]),
|
|
904
1201
|
asMoney(data["base_amount"]),
|
|
905
1202
|
asMoney(data["tax_amount"]),
|
|
906
1203
|
asMoney(data["total_amount"]),
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
1204
|
+
asNullableString4(data["refusal_reason"]),
|
|
1205
|
+
asNullableString4(data["registered_at"]),
|
|
1206
|
+
asNullableString4(data["refused_at"]),
|
|
1207
|
+
asNullableString4(data["created_at"]),
|
|
1208
|
+
asNullableString4(data["updated_at"])
|
|
912
1209
|
);
|
|
913
1210
|
}
|
|
914
1211
|
};
|
|
@@ -979,6 +1276,8 @@ var ComplianceTransactionState = {
|
|
|
979
1276
|
SENDING: "sending",
|
|
980
1277
|
SUBMITTED: "submitted",
|
|
981
1278
|
ERROR: "error",
|
|
1279
|
+
/** E-invoicing only: the buyer cannot receive e-invoices — nothing was sent or billed; send your regular invoice. */
|
|
1280
|
+
NOT_REQUIRED: "not_required",
|
|
982
1281
|
UNKNOWN: "unknown"
|
|
983
1282
|
};
|
|
984
1283
|
var VALUES5 = Object.values(ComplianceTransactionState);
|
|
@@ -996,6 +1295,7 @@ var DESCRIPTIONS5 = {
|
|
|
996
1295
|
[ComplianceTransactionState.SENDING]: "Submission to the provider in progress.",
|
|
997
1296
|
[ComplianceTransactionState.SUBMITTED]: "Successfully submitted to the provider.",
|
|
998
1297
|
[ComplianceTransactionState.ERROR]: "Submission failed. See submissionError.",
|
|
1298
|
+
[ComplianceTransactionState.NOT_REQUIRED]: "The buyer cannot receive e-invoices, so none was sent or billed. Send your regular invoice instead.",
|
|
999
1299
|
[ComplianceTransactionState.UNKNOWN]: "Unknown state."
|
|
1000
1300
|
};
|
|
1001
1301
|
function describeComplianceTransactionState(state) {
|
|
@@ -1034,13 +1334,13 @@ function describeComplianceTransactionType(type) {
|
|
|
1034
1334
|
}
|
|
1035
1335
|
|
|
1036
1336
|
// src/dto/ComplianceTransaction.ts
|
|
1037
|
-
function
|
|
1337
|
+
function asString5(value, fallback = "") {
|
|
1038
1338
|
return typeof value === "string" ? value : fallback;
|
|
1039
1339
|
}
|
|
1040
|
-
function
|
|
1340
|
+
function asNullableString5(value) {
|
|
1041
1341
|
return typeof value === "string" ? value : null;
|
|
1042
1342
|
}
|
|
1043
|
-
function
|
|
1343
|
+
function asNumber4(value) {
|
|
1044
1344
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
1045
1345
|
}
|
|
1046
1346
|
function asMoney2(value) {
|
|
@@ -1048,34 +1348,55 @@ function asMoney2(value) {
|
|
|
1048
1348
|
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
1049
1349
|
return "0.00";
|
|
1050
1350
|
}
|
|
1051
|
-
function
|
|
1351
|
+
function asNullableNumber4(value) {
|
|
1052
1352
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
1053
1353
|
if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
|
|
1054
1354
|
return null;
|
|
1055
1355
|
}
|
|
1056
|
-
function
|
|
1356
|
+
function asRecord3(value) {
|
|
1057
1357
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
1058
1358
|
}
|
|
1059
|
-
function
|
|
1060
|
-
return Array.isArray(value) ? value.filter((row) =>
|
|
1359
|
+
function asRecordList3(value) {
|
|
1360
|
+
return Array.isArray(value) ? value.filter((row) => asRecord3(row) !== null) : [];
|
|
1361
|
+
}
|
|
1362
|
+
function toInvoicePdf(value) {
|
|
1363
|
+
const pdf = asRecord3(value);
|
|
1364
|
+
if (pdf === null) return null;
|
|
1365
|
+
return {
|
|
1366
|
+
filename: asNullableString5(pdf["filename"]),
|
|
1367
|
+
size: asNullableNumber4(pdf["size"]),
|
|
1368
|
+
attached: pdf["attached"] === true
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
function toEurEquivalent(value) {
|
|
1372
|
+
const eur = asRecord3(value);
|
|
1373
|
+
if (eur === null) return null;
|
|
1374
|
+
return {
|
|
1375
|
+
subtotal: asMoney2(eur["subtotal"]),
|
|
1376
|
+
taxAmount: asMoney2(eur["tax_amount"]),
|
|
1377
|
+
total: asMoney2(eur["total"]),
|
|
1378
|
+
rate: asString5(eur["rate"]),
|
|
1379
|
+
rateDate: asNullableString5(eur["rate_date"]),
|
|
1380
|
+
source: asString5(eur["source"], "ECB")
|
|
1381
|
+
};
|
|
1061
1382
|
}
|
|
1062
1383
|
function toInvoiceLines(providerPayload) {
|
|
1063
|
-
const invoice =
|
|
1064
|
-
return
|
|
1065
|
-
description:
|
|
1066
|
-
quantity:
|
|
1384
|
+
const invoice = asRecord3(asRecord3(providerPayload)?.["invoice"]);
|
|
1385
|
+
return asRecordList3(invoice?.["invoice_lines_attributes"]).map((line) => ({
|
|
1386
|
+
description: asString5(line["description"]),
|
|
1387
|
+
quantity: asNullableNumber4(line["quantity"]),
|
|
1067
1388
|
price: asMoney2(line["price"]),
|
|
1068
|
-
unit:
|
|
1069
|
-
taxes:
|
|
1070
|
-
name:
|
|
1071
|
-
percent:
|
|
1072
|
-
category:
|
|
1073
|
-
comment:
|
|
1389
|
+
unit: asNullableNumber4(line["unit"]),
|
|
1390
|
+
taxes: asRecordList3(line["taxes_attributes"]).map((tax) => ({
|
|
1391
|
+
name: asString5(tax["name"]),
|
|
1392
|
+
percent: asNullableNumber4(tax["percent"]),
|
|
1393
|
+
category: asNullableString5(tax["category"]),
|
|
1394
|
+
comment: asNullableString5(tax["comment"])
|
|
1074
1395
|
}))
|
|
1075
1396
|
}));
|
|
1076
1397
|
}
|
|
1077
1398
|
var ComplianceTransaction = class _ComplianceTransaction {
|
|
1078
|
-
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 = []) {
|
|
1399
|
+
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) {
|
|
1079
1400
|
this.id = id;
|
|
1080
1401
|
this.companyId = companyId;
|
|
1081
1402
|
this.complianceEnrollmentId = complianceEnrollmentId;
|
|
@@ -1114,6 +1435,14 @@ var ComplianceTransaction = class _ComplianceTransaction {
|
|
|
1114
1435
|
this.amendedDate = amendedDate;
|
|
1115
1436
|
this.providerState = providerState;
|
|
1116
1437
|
this.invoiceLines = invoiceLines;
|
|
1438
|
+
this.documentUrl = documentUrl;
|
|
1439
|
+
this.deliveryCheck = deliveryCheck;
|
|
1440
|
+
this.invoicePdf = invoicePdf;
|
|
1441
|
+
this.internalReference = internalReference;
|
|
1442
|
+
this.pdfUrl = pdfUrl;
|
|
1443
|
+
this.buyerRejected = buyerRejected;
|
|
1444
|
+
this.rejectionReason = rejectionReason;
|
|
1445
|
+
this.eurEquivalent = eurEquivalent;
|
|
1117
1446
|
}
|
|
1118
1447
|
id;
|
|
1119
1448
|
companyId;
|
|
@@ -1153,50 +1482,66 @@ var ComplianceTransaction = class _ComplianceTransaction {
|
|
|
1153
1482
|
amendedDate;
|
|
1154
1483
|
providerState;
|
|
1155
1484
|
invoiceLines;
|
|
1485
|
+
documentUrl;
|
|
1486
|
+
deliveryCheck;
|
|
1487
|
+
invoicePdf;
|
|
1488
|
+
internalReference;
|
|
1489
|
+
pdfUrl;
|
|
1490
|
+
buyerRejected;
|
|
1491
|
+
rejectionReason;
|
|
1492
|
+
eurEquivalent;
|
|
1156
1493
|
get isCreditNote() {
|
|
1157
1494
|
return this.documentType === ComplianceDocumentType.CREDIT_NOTE;
|
|
1158
1495
|
}
|
|
1159
1496
|
static fromArray(data) {
|
|
1160
1497
|
const taxReport = typeof data["tax_report"] === "object" && data["tax_report"] !== null && !Array.isArray(data["tax_report"]) ? ComplianceTaxReport.fromArray(data["tax_report"]) : void 0;
|
|
1161
1498
|
return new _ComplianceTransaction(
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1499
|
+
asNumber4(data["id"]),
|
|
1500
|
+
asNumber4(data["company_id"]),
|
|
1501
|
+
asNumber4(data["compliance_enrollment_id"]),
|
|
1502
|
+
asString5(data["country"]),
|
|
1503
|
+
asString5(data["regime"]),
|
|
1167
1504
|
toComplianceTransactionType(data["transaction_type"]),
|
|
1168
|
-
|
|
1505
|
+
asString5(data["transaction_type_label"]),
|
|
1169
1506
|
toComplianceTransactionState(data["state"]),
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1507
|
+
asString5(data["state_label"]),
|
|
1508
|
+
asString5(data["invoice_number"]),
|
|
1509
|
+
asNullableString5(data["invoice_date"]),
|
|
1510
|
+
asNullableString5(data["due_date"]),
|
|
1511
|
+
asString5(data["currency"], "EUR"),
|
|
1175
1512
|
asMoney2(data["subtotal"]),
|
|
1176
1513
|
asMoney2(data["tax_amount"]),
|
|
1177
1514
|
asMoney2(data["total"]),
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1515
|
+
asNullableString5(data["counterparty_name"]),
|
|
1516
|
+
asNullableString5(data["counterparty_country"]),
|
|
1517
|
+
asNullableString5(data["counterparty_vat_number"]),
|
|
1181
1518
|
data["is_paid"] === true,
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1519
|
+
asNullableString5(data["paid_at"]),
|
|
1520
|
+
asNullableString5(data["provider_invoice_id"]),
|
|
1521
|
+
asNullableString5(data["submission_error"]),
|
|
1522
|
+
asNullableString5(data["reported_at"]),
|
|
1186
1523
|
taxReport,
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1524
|
+
asNullableString5(data["created_at"]),
|
|
1525
|
+
asNullableString5(data["updated_at"]),
|
|
1526
|
+
asNullableString5(data["counterparty_register_id"]),
|
|
1527
|
+
asNullableString5(data["counterparty_address"]),
|
|
1528
|
+
asNullableString5(data["counterparty_city"]),
|
|
1529
|
+
asNullableString5(data["counterparty_postalcode"]),
|
|
1530
|
+
asNullableString5(data["counterparty_email"]),
|
|
1531
|
+
asNullableString5(data["buyer_reference"]),
|
|
1195
1532
|
data["document_type"] === void 0 || data["document_type"] === null ? ComplianceDocumentType.INVOICE : toComplianceDocumentType(data["document_type"]),
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
toInvoiceLines(data["provider_payload"])
|
|
1533
|
+
asNullableString5(data["amended_number"]),
|
|
1534
|
+
asNullableString5(data["amended_date"]),
|
|
1535
|
+
asNullableString5(data["provider_state"]),
|
|
1536
|
+
toInvoiceLines(data["provider_payload"]),
|
|
1537
|
+
asNullableString5(data["document_url"]),
|
|
1538
|
+
asRecord3(data["delivery_check"]),
|
|
1539
|
+
toInvoicePdf(data["invoice_pdf"]),
|
|
1540
|
+
asNullableString5(data["internal_reference"]),
|
|
1541
|
+
asNullableString5(data["pdf_url"]),
|
|
1542
|
+
data["buyer_rejected"] === true,
|
|
1543
|
+
asNullableString5(data["rejection_reason"]),
|
|
1544
|
+
toEurEquivalent(data["eur_equivalent"])
|
|
1200
1545
|
);
|
|
1201
1546
|
}
|
|
1202
1547
|
};
|
|
@@ -1283,10 +1628,10 @@ function describePeppolLookupStatus(status) {
|
|
|
1283
1628
|
}
|
|
1284
1629
|
|
|
1285
1630
|
// src/dto/PeppolLookupResult.ts
|
|
1286
|
-
function
|
|
1631
|
+
function asString6(value, fallback = "") {
|
|
1287
1632
|
return typeof value === "string" ? value : fallback;
|
|
1288
1633
|
}
|
|
1289
|
-
function
|
|
1634
|
+
function asNullableString6(value) {
|
|
1290
1635
|
return typeof value === "string" ? value : null;
|
|
1291
1636
|
}
|
|
1292
1637
|
var PeppolLookupResult = class _PeppolLookupResult {
|
|
@@ -1317,21 +1662,21 @@ var PeppolLookupResult = class _PeppolLookupResult {
|
|
|
1317
1662
|
return new _PeppolLookupResult(
|
|
1318
1663
|
toPeppolLookupStatus(data["status"]),
|
|
1319
1664
|
typeof data["reachable"] === "boolean" ? data["reachable"] : null,
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1665
|
+
asString6(data["country"]),
|
|
1666
|
+
asString6(data["scheme"]),
|
|
1667
|
+
asString6(data["id"]),
|
|
1323
1668
|
documentTypes,
|
|
1324
|
-
|
|
1325
|
-
|
|
1669
|
+
asNullableString6(data["transport_type_code"]),
|
|
1670
|
+
asNullableString6(data["checked_at"])
|
|
1326
1671
|
);
|
|
1327
1672
|
}
|
|
1328
1673
|
};
|
|
1329
1674
|
|
|
1330
1675
|
// src/dto/RegistryCompany.ts
|
|
1331
|
-
function
|
|
1676
|
+
function asString7(value, fallback = "") {
|
|
1332
1677
|
return typeof value === "string" ? value : fallback;
|
|
1333
1678
|
}
|
|
1334
|
-
function
|
|
1679
|
+
function asNullableString7(value) {
|
|
1335
1680
|
return typeof value === "string" ? value : null;
|
|
1336
1681
|
}
|
|
1337
1682
|
var RegistryCompany = class _RegistryCompany {
|
|
@@ -1363,27 +1708,27 @@ var RegistryCompany = class _RegistryCompany {
|
|
|
1363
1708
|
country;
|
|
1364
1709
|
static fromArray(data) {
|
|
1365
1710
|
return new _RegistryCompany(
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1711
|
+
asString7(data["org_number"]),
|
|
1712
|
+
asString7(data["company_name"]),
|
|
1713
|
+
asNullableString7(data["organisation_form"]),
|
|
1369
1714
|
data["vat_registered"] === true,
|
|
1370
|
-
|
|
1715
|
+
asNullableString7(data["vat_number"]),
|
|
1371
1716
|
data["enterprise_register"] === true,
|
|
1372
1717
|
data["bankrupt"] === true,
|
|
1373
1718
|
data["under_liquidation"] === true,
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1719
|
+
asNullableString7(data["address"]),
|
|
1720
|
+
asNullableString7(data["postalcode"]),
|
|
1721
|
+
asNullableString7(data["city"]),
|
|
1722
|
+
asString7(data["country"])
|
|
1378
1723
|
);
|
|
1379
1724
|
}
|
|
1380
1725
|
};
|
|
1381
1726
|
|
|
1382
1727
|
// src/dto/RevenueStatistics.ts
|
|
1383
|
-
function
|
|
1728
|
+
function asString8(value, fallback = "") {
|
|
1384
1729
|
return typeof value === "string" ? value : fallback;
|
|
1385
1730
|
}
|
|
1386
|
-
function
|
|
1731
|
+
function asNumber5(value) {
|
|
1387
1732
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
1388
1733
|
}
|
|
1389
1734
|
function asMoney3(value) {
|
|
@@ -1395,7 +1740,7 @@ function asRecords(value) {
|
|
|
1395
1740
|
}
|
|
1396
1741
|
function toTotals(data) {
|
|
1397
1742
|
return {
|
|
1398
|
-
count:
|
|
1743
|
+
count: asNumber5(data["count"]),
|
|
1399
1744
|
subtotal: asMoney3(data["subtotal"]),
|
|
1400
1745
|
taxAmount: asMoney3(data["tax_amount"]),
|
|
1401
1746
|
total: asMoney3(data["total"])
|
|
@@ -1432,35 +1777,35 @@ var RevenueStatistics = class _RevenueStatistics {
|
|
|
1432
1777
|
typeof data["totals"] === "object" && data["totals"] !== null ? data["totals"] : {}
|
|
1433
1778
|
);
|
|
1434
1779
|
const timeSeries = asRecords(data["time_series"]).map((row) => ({
|
|
1435
|
-
bucket:
|
|
1780
|
+
bucket: asString8(row["bucket"]),
|
|
1436
1781
|
...toTotals(row)
|
|
1437
1782
|
}));
|
|
1438
1783
|
const byType = asRecords(data["by_type"]).map((row) => ({
|
|
1439
|
-
type:
|
|
1440
|
-
typeLabel:
|
|
1441
|
-
count:
|
|
1784
|
+
type: asString8(row["type"]),
|
|
1785
|
+
typeLabel: asString8(row["type_label"]),
|
|
1786
|
+
count: asNumber5(row["count"]),
|
|
1442
1787
|
total: asMoney3(row["total"])
|
|
1443
1788
|
}));
|
|
1444
1789
|
const byCountry = asRecords(data["by_country"]).map((row) => ({
|
|
1445
|
-
country:
|
|
1446
|
-
count:
|
|
1790
|
+
country: asString8(row["country"], "unknown"),
|
|
1791
|
+
count: asNumber5(row["count"]),
|
|
1447
1792
|
total: asMoney3(row["total"])
|
|
1448
1793
|
}));
|
|
1449
1794
|
const byState = asRecords(data["by_state"]).map((row) => ({
|
|
1450
|
-
state:
|
|
1451
|
-
stateLabel:
|
|
1452
|
-
count:
|
|
1795
|
+
state: asString8(row["state"]),
|
|
1796
|
+
stateLabel: asString8(row["state_label"]),
|
|
1797
|
+
count: asNumber5(row["count"]),
|
|
1453
1798
|
total: asMoney3(row["total"])
|
|
1454
1799
|
}));
|
|
1455
1800
|
const byCurrency = asRecords(data["by_currency"]).map((row) => ({
|
|
1456
|
-
currency:
|
|
1801
|
+
currency: asString8(row["currency"]),
|
|
1457
1802
|
...toTotals(row)
|
|
1458
1803
|
}));
|
|
1459
1804
|
return new _RevenueStatistics(
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1805
|
+
asString8(range["date_from"]) || void 0,
|
|
1806
|
+
asString8(range["date_to"]) || void 0,
|
|
1807
|
+
asString8(range["interval"]) || void 0,
|
|
1808
|
+
asString8(data["primary_currency"], "EUR"),
|
|
1464
1809
|
data["is_multi_currency"] === true,
|
|
1465
1810
|
totals,
|
|
1466
1811
|
timeSeries,
|
|
@@ -1473,10 +1818,10 @@ var RevenueStatistics = class _RevenueStatistics {
|
|
|
1473
1818
|
};
|
|
1474
1819
|
|
|
1475
1820
|
// src/dto/SireneLookupResult.ts
|
|
1476
|
-
function
|
|
1821
|
+
function asString9(value, fallback = "") {
|
|
1477
1822
|
return typeof value === "string" ? value : fallback;
|
|
1478
1823
|
}
|
|
1479
|
-
function
|
|
1824
|
+
function asNullableString8(value) {
|
|
1480
1825
|
return typeof value === "string" ? value : null;
|
|
1481
1826
|
}
|
|
1482
1827
|
var SireneLookupResult = class _SireneLookupResult {
|
|
@@ -1502,24 +1847,24 @@ var SireneLookupResult = class _SireneLookupResult {
|
|
|
1502
1847
|
postalcode;
|
|
1503
1848
|
static fromArray(data) {
|
|
1504
1849
|
return new _SireneLookupResult(
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1850
|
+
asString9(data["company_name"]),
|
|
1851
|
+
asString9(data["siren"]),
|
|
1852
|
+
asNullableString8(data["siret"]),
|
|
1853
|
+
asNullableString8(data["naf_code"]),
|
|
1854
|
+
asNullableString8(data["naf_full"]),
|
|
1855
|
+
asNullableString8(data["enterprise_size"]),
|
|
1856
|
+
asNullableString8(data["address"]),
|
|
1857
|
+
asNullableString8(data["city"]),
|
|
1858
|
+
asNullableString8(data["postalcode"])
|
|
1514
1859
|
);
|
|
1515
1860
|
}
|
|
1516
1861
|
};
|
|
1517
1862
|
|
|
1518
1863
|
// src/dto/VatRates.ts
|
|
1519
|
-
function
|
|
1864
|
+
function asString10(value, fallback = "") {
|
|
1520
1865
|
return typeof value === "string" ? value : fallback;
|
|
1521
1866
|
}
|
|
1522
|
-
function
|
|
1867
|
+
function asNumber6(value) {
|
|
1523
1868
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
1524
1869
|
}
|
|
1525
1870
|
var VatRates = class _VatRates {
|
|
@@ -1533,13 +1878,13 @@ var VatRates = class _VatRates {
|
|
|
1533
1878
|
rates;
|
|
1534
1879
|
static fromArray(data) {
|
|
1535
1880
|
const rates = (Array.isArray(data["rates"]) ? data["rates"] : []).filter((row) => typeof row === "object" && row !== null).map((row) => ({
|
|
1536
|
-
percent:
|
|
1537
|
-
category:
|
|
1538
|
-
labelKey:
|
|
1539
|
-
taxName:
|
|
1881
|
+
percent: asNumber6(row["percent"]),
|
|
1882
|
+
category: asString10(row["category"]),
|
|
1883
|
+
labelKey: asString10(row["label_key"]),
|
|
1884
|
+
taxName: asString10(row["tax_name"]),
|
|
1540
1885
|
requiresVatex: row["requires_vatex"] === true
|
|
1541
1886
|
}));
|
|
1542
|
-
return new _VatRates(
|
|
1887
|
+
return new _VatRates(asString10(data["country"]), asString10(data["currency"], "EUR"), rates);
|
|
1543
1888
|
}
|
|
1544
1889
|
};
|
|
1545
1890
|
|
|
@@ -1598,10 +1943,15 @@ var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
|
1598
1943
|
var NAF_CODE_REGEX = /^\d{2}$/;
|
|
1599
1944
|
var COUNTRY_REGEX = /^[A-Za-z]{2}$/;
|
|
1600
1945
|
var PEPPOL_SCHEME_REGEX = /^\d{4}$/;
|
|
1946
|
+
var INVOICE_PDF_MAX_BYTES = 10 * 1024 * 1024;
|
|
1947
|
+
var INVOICE_PDF_MAGIC = [37, 80, 68, 70, 45];
|
|
1601
1948
|
var INTERVALS = ["day", "week", "month"];
|
|
1602
1949
|
var SERVICES = ["e_reporting", "e_invoicing"];
|
|
1603
1950
|
var ENTERPRISE_SIZES = ["micro", "pme", "eti", "ge"];
|
|
1604
1951
|
var TYPE_OPERATIONS = ["services", "goods", "mixed"];
|
|
1952
|
+
var NOTIFICATION_LANGUAGES = ["de", "en"];
|
|
1953
|
+
var NOTIFICATION_DIGESTS = ["off", "daily", "weekly"];
|
|
1954
|
+
var NOTIFICATION_MAX_RECIPIENTS = 5;
|
|
1605
1955
|
var TRANSACTION_TYPES = [
|
|
1606
1956
|
"b2c_outbound",
|
|
1607
1957
|
"b2b_domestic_outbound",
|
|
@@ -1796,6 +2146,8 @@ var EReportingEndpoint = class {
|
|
|
1796
2146
|
this.validateInvoiceLines(input.invoiceLines);
|
|
1797
2147
|
this.validateOptionalCodes(input);
|
|
1798
2148
|
this.validateBuyerFields(input);
|
|
2149
|
+
this.validateInternalReference(input.internalReference);
|
|
2150
|
+
this.validateInvoicePdfFilename(input);
|
|
1799
2151
|
const body = {
|
|
1800
2152
|
compliance_enrollment_id: input.complianceEnrollmentId,
|
|
1801
2153
|
transaction_type: input.transactionType,
|
|
@@ -1811,23 +2163,9 @@ var EReportingEndpoint = class {
|
|
|
1811
2163
|
const data = await this.parseJsonResponse(response);
|
|
1812
2164
|
return ComplianceTransaction.fromArray(data);
|
|
1813
2165
|
}
|
|
1814
|
-
/** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type / enrollment / country. */
|
|
2166
|
+
/** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type / enrollment / country / search / internal reference / provider state. */
|
|
1815
2167
|
async listTransactions(filters = {}) {
|
|
1816
|
-
|
|
1817
|
-
this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
|
|
1818
|
-
}
|
|
1819
|
-
if (filters.country !== void 0 && !COUNTRY_REGEX.test(filters.country)) {
|
|
1820
|
-
throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
|
|
1821
|
-
}
|
|
1822
|
-
const params = new URLSearchParams();
|
|
1823
|
-
if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
|
|
1824
|
-
if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
|
|
1825
|
-
if (filters.state !== void 0) params.set("state", filters.state);
|
|
1826
|
-
if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
|
|
1827
|
-
if (filters.complianceEnrollmentId !== void 0) {
|
|
1828
|
-
params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
|
|
1829
|
-
}
|
|
1830
|
-
if (filters.country !== void 0) params.set("country", filters.country.toUpperCase());
|
|
2168
|
+
const params = this.buildTransactionFilterParams(filters);
|
|
1831
2169
|
if (filters.page !== void 0) params.set("page", String(filters.page));
|
|
1832
2170
|
if (filters.perPage !== void 0) params.set("per_page", String(filters.perPage));
|
|
1833
2171
|
const queryString = params.toString();
|
|
@@ -1835,6 +2173,27 @@ var EReportingEndpoint = class {
|
|
|
1835
2173
|
const response = await this.sendRequest("GET", url);
|
|
1836
2174
|
return ComplianceTransactionPage.fromResponse(await this.parseJsonResponse(response));
|
|
1837
2175
|
}
|
|
2176
|
+
/**
|
|
2177
|
+
* All transactions matching the filters as raw CSV for bookkeeping
|
|
2178
|
+
* (semicolon-separated) — the same filters as listTransactions() without
|
|
2179
|
+
* pagination. Columns: invoice_number, internal_reference, invoice_date,
|
|
2180
|
+
* due_date, country, transaction_type, document_type, state, delivery_state,
|
|
2181
|
+
* buyer_rejected, rejection_reason, counterparty_name, counterparty_country,
|
|
2182
|
+
* counterparty_vat_number, currency, subtotal, tax_amount, total, eur_rate,
|
|
2183
|
+
* eur_rate_date, eur_subtotal, eur_tax_amount, eur_total, provider_invoice_id,
|
|
2184
|
+
* created_at. The eur_* columns hold the ECB reference conversion for non-EUR
|
|
2185
|
+
* invoices (empty for EUR). Max 50,000 rows server-side. The body is returned
|
|
2186
|
+
* verbatim, a UTF-8 BOM included when the API sends one.
|
|
2187
|
+
*/
|
|
2188
|
+
async exportTransactionsCsv(filters = {}) {
|
|
2189
|
+
const queryString = this.buildTransactionFilterParams(filters).toString();
|
|
2190
|
+
const url = `${this.baseUrl}/compliance/transactions/export.csv${queryString ? `?${queryString}` : ""}`;
|
|
2191
|
+
const response = await this.sendDownloadRequest(url, "text/csv, application/json");
|
|
2192
|
+
if (!response.ok) {
|
|
2193
|
+
await this.parseJsonResponse(response);
|
|
2194
|
+
}
|
|
2195
|
+
return new TextDecoder("utf-8", { ignoreBOM: true }).decode(await response.arrayBuffer());
|
|
2196
|
+
}
|
|
1838
2197
|
/** Get a single transaction by id (includes its tax report when one exists). */
|
|
1839
2198
|
async getTransaction(id) {
|
|
1840
2199
|
this.validateId(id, "id");
|
|
@@ -1850,6 +2209,8 @@ var EReportingEndpoint = class {
|
|
|
1850
2209
|
if (input.invoiceLines !== void 0) this.validateInvoiceLines(input.invoiceLines);
|
|
1851
2210
|
this.validateOptionalCodes(input);
|
|
1852
2211
|
this.validateBuyerFields(input);
|
|
2212
|
+
this.validateInternalReference(input.internalReference);
|
|
2213
|
+
this.validateInvoicePdfFilename(input);
|
|
1853
2214
|
const body = {};
|
|
1854
2215
|
if (input.transactionType !== void 0) body["transaction_type"] = input.transactionType;
|
|
1855
2216
|
if (input.invoiceNumber !== void 0) body["invoice_number"] = input.invoiceNumber;
|
|
@@ -1884,6 +2245,47 @@ var EReportingEndpoint = class {
|
|
|
1884
2245
|
const data = await this.parseJsonResponse(response);
|
|
1885
2246
|
return ComplianceTransaction.fromArray(data);
|
|
1886
2247
|
}
|
|
2248
|
+
/**
|
|
2249
|
+
* Download the legal e-invoice document of a sent transaction (e-invoicing,
|
|
2250
|
+
* e.g. the EHF/UBL XML for Norway). Available once `documentUrl` on the
|
|
2251
|
+
* transaction is set.
|
|
2252
|
+
*
|
|
2253
|
+
* Errors: 404 (HttpException) for an unknown transaction; 409 (HttpException)
|
|
2254
|
+
* when no document exists (yet) — e.g. e-reporting, not sent, or the network
|
|
2255
|
+
* has not produced it yet (try again in a few minutes); 502 on provider failure.
|
|
2256
|
+
*/
|
|
2257
|
+
async getTransactionDocument(id) {
|
|
2258
|
+
this.validateId(id, "id");
|
|
2259
|
+
const response = await this.sendDownloadRequest(`${this.baseUrl}/compliance/transactions/${id}/document`, "*/*");
|
|
2260
|
+
if (!response.ok) {
|
|
2261
|
+
await this.parseJsonResponse(response);
|
|
2262
|
+
}
|
|
2263
|
+
return {
|
|
2264
|
+
content: new Uint8Array(await response.arrayBuffer()),
|
|
2265
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
2266
|
+
filename: this.filenameFromDisposition(response.headers.get("content-disposition"))
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2269
|
+
/**
|
|
2270
|
+
* Download the invoice as a readable PDF — the seller's attached PDF, or the
|
|
2271
|
+
* provider's rendering of the e-invoice. Available once `pdfUrl` on the
|
|
2272
|
+
* transaction is set.
|
|
2273
|
+
*
|
|
2274
|
+
* Errors: 404 (HttpException) for an unknown transaction; 409 (HttpException)
|
|
2275
|
+
* when no PDF exists (yet); 502 on provider failure.
|
|
2276
|
+
*/
|
|
2277
|
+
async getTransactionPdf(id) {
|
|
2278
|
+
this.validateId(id, "id");
|
|
2279
|
+
const response = await this.sendDownloadRequest(`${this.baseUrl}/compliance/transactions/${id}/pdf`, "*/*");
|
|
2280
|
+
if (!response.ok) {
|
|
2281
|
+
await this.parseJsonResponse(response);
|
|
2282
|
+
}
|
|
2283
|
+
return {
|
|
2284
|
+
content: new Uint8Array(await response.arrayBuffer()),
|
|
2285
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
2286
|
+
filename: this.filenameFromDisposition(response.headers.get("content-disposition"))
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
1887
2289
|
/**
|
|
1888
2290
|
* Bulk-import transactions from a semicolon-separated CSV (one row per invoice
|
|
1889
2291
|
* line; rows sharing an invoice_number are grouped into one transaction).
|
|
@@ -1915,6 +2317,9 @@ var EReportingEndpoint = class {
|
|
|
1915
2317
|
* months and a monthly interval.
|
|
1916
2318
|
*/
|
|
1917
2319
|
async getRevenueStatistics(filters = {}) {
|
|
2320
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2321
|
+
this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
|
|
2322
|
+
}
|
|
1918
2323
|
if (filters.interval !== void 0 && !INTERVALS.includes(filters.interval)) {
|
|
1919
2324
|
throw new TaxoraException(
|
|
1920
2325
|
`Invalid statistics interval "${String(filters.interval)}". Expected one of: ${INTERVALS.join(", ")}.`
|
|
@@ -1926,12 +2331,42 @@ var EReportingEndpoint = class {
|
|
|
1926
2331
|
if (filters.interval !== void 0) params.set("interval", filters.interval);
|
|
1927
2332
|
if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
|
|
1928
2333
|
if (filters.state !== void 0) params.set("state", filters.state);
|
|
2334
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2335
|
+
params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
|
|
2336
|
+
}
|
|
1929
2337
|
const queryString = params.toString();
|
|
1930
2338
|
const url = `${this.baseUrl}/compliance/revenue-statistics${queryString ? `?${queryString}` : ""}`;
|
|
1931
2339
|
const response = await this.sendRequest("GET", url);
|
|
1932
2340
|
const data = await this.parseJsonResponse(response);
|
|
1933
2341
|
return RevenueStatistics.fromArray(data);
|
|
1934
2342
|
}
|
|
2343
|
+
/**
|
|
2344
|
+
* Net and VAT per month, currency and tax rate as raw CSV (semicolon-separated,
|
|
2345
|
+
* computed from the invoice lines) — for reconciling the reported figures with
|
|
2346
|
+
* your VAT return. Same filters as getRevenueStatistics() (without
|
|
2347
|
+
* `interval`); defaults to the last 12 months. The body is returned verbatim,
|
|
2348
|
+
* a UTF-8 BOM included when the API sends one.
|
|
2349
|
+
*/
|
|
2350
|
+
async exportRevenueTaxRatesCsv(filters = {}) {
|
|
2351
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2352
|
+
this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
|
|
2353
|
+
}
|
|
2354
|
+
const params = new URLSearchParams();
|
|
2355
|
+
if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
|
|
2356
|
+
if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
|
|
2357
|
+
if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
|
|
2358
|
+
if (filters.state !== void 0) params.set("state", filters.state);
|
|
2359
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2360
|
+
params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
|
|
2361
|
+
}
|
|
2362
|
+
const queryString = params.toString();
|
|
2363
|
+
const url = `${this.baseUrl}/compliance/revenue-statistics/tax-rates.csv${queryString ? `?${queryString}` : ""}`;
|
|
2364
|
+
const response = await this.sendDownloadRequest(url, "text/csv, application/json");
|
|
2365
|
+
if (!response.ok) {
|
|
2366
|
+
await this.parseJsonResponse(response);
|
|
2367
|
+
}
|
|
2368
|
+
return new TextDecoder("utf-8", { ignoreBOM: true }).decode(await response.arrayBuffer());
|
|
2369
|
+
}
|
|
1935
2370
|
/**
|
|
1936
2371
|
* The canonical VAT rates & DGFiP tax categories for a reporting country
|
|
1937
2372
|
* (the seller's reporting-country rates, not the counterparty's). Falls back
|
|
@@ -1946,12 +2381,19 @@ var EReportingEndpoint = class {
|
|
|
1946
2381
|
const data = await this.parseJsonResponse(response);
|
|
1947
2382
|
return VatRates.fromArray(data);
|
|
1948
2383
|
}
|
|
1949
|
-
/**
|
|
1950
|
-
|
|
2384
|
+
/**
|
|
2385
|
+
* Paginated list of the company's tax reports (newest first), optionally
|
|
2386
|
+
* filtered by state and by country (2-letter ISO code, case-insensitive).
|
|
2387
|
+
*/
|
|
2388
|
+
async listTaxReports(page = 1, perPage = 25, state, country) {
|
|
2389
|
+
if (country !== void 0 && !COUNTRY_REGEX.test(country)) {
|
|
2390
|
+
throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
|
|
2391
|
+
}
|
|
1951
2392
|
const params = new URLSearchParams();
|
|
1952
2393
|
params.set("page", String(page));
|
|
1953
2394
|
params.set("per_page", String(perPage));
|
|
1954
2395
|
if (state !== void 0) params.set("state", state);
|
|
2396
|
+
if (country !== void 0) params.set("country", country.toUpperCase());
|
|
1955
2397
|
const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/tax-reports?${params.toString()}`);
|
|
1956
2398
|
return ComplianceTaxReportPage.fromResponse(await this.parseJsonResponse(response));
|
|
1957
2399
|
}
|
|
@@ -1993,8 +2435,177 @@ var EReportingEndpoint = class {
|
|
|
1993
2435
|
await this.parseJsonResponse(response);
|
|
1994
2436
|
}
|
|
1995
2437
|
// ---------------------------------------------------------------------
|
|
2438
|
+
// Self-service plans (Stripe) — not gated by compliance access
|
|
2439
|
+
// ---------------------------------------------------------------------
|
|
2440
|
+
/**
|
|
2441
|
+
* Bookable compliance plans per country (net EUR prices), every country
|
|
2442
|
+
* Taxora reaches, and which services the company has unlocked. Works for
|
|
2443
|
+
* companies without compliance access.
|
|
2444
|
+
*/
|
|
2445
|
+
async getOfferings() {
|
|
2446
|
+
const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/offerings`);
|
|
2447
|
+
return ComplianceOfferings.fromArray(await this.parseJsonResponse(response));
|
|
2448
|
+
}
|
|
2449
|
+
/**
|
|
2450
|
+
* Start a Stripe Checkout for a plan (see getOfferings() — `checkoutAvailable`).
|
|
2451
|
+
* Open the returned `url` in the browser. After payment Stripe redirects to
|
|
2452
|
+
* the Taxora portal, which activates the plan; call confirmCheckout() with the
|
|
2453
|
+
* session id to activate it from your side (idempotent).
|
|
2454
|
+
*
|
|
2455
|
+
* Errors: 422 (ValidationException) for an unknown or contact-only plan or a
|
|
2456
|
+
* plan that is already active; 502 (HttpException) on a Stripe error.
|
|
2457
|
+
*/
|
|
2458
|
+
async createCheckout(country, plan) {
|
|
2459
|
+
if (!COUNTRY_REGEX.test(country)) {
|
|
2460
|
+
throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
|
|
2461
|
+
}
|
|
2462
|
+
if (plan.trim() === "" || plan.length > 30) {
|
|
2463
|
+
throw new TaxoraException("plan must be a non-empty plan key of at most 30 characters.");
|
|
2464
|
+
}
|
|
2465
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/checkout`, {
|
|
2466
|
+
country: country.toUpperCase(),
|
|
2467
|
+
plan
|
|
2468
|
+
});
|
|
2469
|
+
const data = await this.parseJsonResponse(response);
|
|
2470
|
+
return { id: String(data["id"] ?? ""), url: String(data["url"] ?? "") };
|
|
2471
|
+
}
|
|
2472
|
+
/** Confirm a finished Stripe Checkout and activate its plan. Idempotent. */
|
|
2473
|
+
async confirmCheckout(sessionId) {
|
|
2474
|
+
if (sessionId.trim() === "" || sessionId.length > 255) {
|
|
2475
|
+
throw new TaxoraException("sessionId must be a non-empty string of at most 255 characters.");
|
|
2476
|
+
}
|
|
2477
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/checkout/confirm`, {
|
|
2478
|
+
session_id: sessionId
|
|
2479
|
+
});
|
|
2480
|
+
const data = await this.parseJsonResponse(response);
|
|
2481
|
+
const str = (value) => typeof value === "string" ? value : null;
|
|
2482
|
+
return {
|
|
2483
|
+
active: data["active"] === true,
|
|
2484
|
+
country: str(data["country"]),
|
|
2485
|
+
service: str(data["service"]),
|
|
2486
|
+
plan: str(data["plan"]),
|
|
2487
|
+
status: str(data["status"])
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
/**
|
|
2491
|
+
* URL of the Stripe billing portal for the company's compliance plans
|
|
2492
|
+
* (invoices, payment method, cancellation). The portal returns to the Taxora
|
|
2493
|
+
* portal. 404 (HttpException) when no plan was bought via Stripe.
|
|
2494
|
+
*/
|
|
2495
|
+
async createBillingPortalSession() {
|
|
2496
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/billing-portal`);
|
|
2497
|
+
const data = await this.parseJsonResponse(response);
|
|
2498
|
+
return String(data["url"] ?? "");
|
|
2499
|
+
}
|
|
2500
|
+
// ---------------------------------------------------------------------
|
|
2501
|
+
// Notification emails
|
|
2502
|
+
// ---------------------------------------------------------------------
|
|
2503
|
+
/**
|
|
2504
|
+
* The company's opt-in e-reporting / e-invoicing emails: recipients, language,
|
|
2505
|
+
* the "buyer not on Peppol" email and the digest. Readable by every member.
|
|
2506
|
+
*/
|
|
2507
|
+
async getNotificationSettings() {
|
|
2508
|
+
const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/notification-settings`);
|
|
2509
|
+
return this.toNotificationSettings(await this.parseJsonResponse(response));
|
|
2510
|
+
}
|
|
2511
|
+
/**
|
|
2512
|
+
* Change the company's e-reporting / e-invoicing emails; only the provided
|
|
2513
|
+
* fields change. `notOnPeppol` only reports invoices held back after it was
|
|
2514
|
+
* switched on.
|
|
2515
|
+
*
|
|
2516
|
+
* Errors: 422 (ValidationException) when an email is switched on without any
|
|
2517
|
+
* recipient; 403 (HttpException, body code `admin_only` or `read_only_role`)
|
|
2518
|
+
* for users who are not company admins.
|
|
2519
|
+
*/
|
|
2520
|
+
async updateNotificationSettings(input) {
|
|
2521
|
+
const body = {};
|
|
2522
|
+
if (input.recipients !== void 0) {
|
|
2523
|
+
if (!Array.isArray(input.recipients)) {
|
|
2524
|
+
throw new TaxoraException("recipients must be an array of email addresses.");
|
|
2525
|
+
}
|
|
2526
|
+
if (input.recipients.length > NOTIFICATION_MAX_RECIPIENTS) {
|
|
2527
|
+
throw new TaxoraException(`recipients must not contain more than ${NOTIFICATION_MAX_RECIPIENTS} entries.`);
|
|
2528
|
+
}
|
|
2529
|
+
body["recipients"] = input.recipients.map((recipient, index) => {
|
|
2530
|
+
const email = typeof recipient === "string" ? recipient.trim() : recipient;
|
|
2531
|
+
this.validateEmail(email, `recipients[${index}]`);
|
|
2532
|
+
if (email.length > 191) {
|
|
2533
|
+
throw new TaxoraException(`recipients[${index}] must not exceed 191 characters.`);
|
|
2534
|
+
}
|
|
2535
|
+
return email;
|
|
2536
|
+
});
|
|
2537
|
+
}
|
|
2538
|
+
if (input.language !== void 0) {
|
|
2539
|
+
if (!NOTIFICATION_LANGUAGES.includes(input.language)) {
|
|
2540
|
+
throw new TaxoraException(
|
|
2541
|
+
`Invalid language "${String(input.language)}". Expected one of: ${NOTIFICATION_LANGUAGES.join(", ")}.`
|
|
2542
|
+
);
|
|
2543
|
+
}
|
|
2544
|
+
body["language"] = input.language;
|
|
2545
|
+
}
|
|
2546
|
+
if (input.notOnPeppol !== void 0) {
|
|
2547
|
+
if (typeof input.notOnPeppol !== "boolean") {
|
|
2548
|
+
throw new TaxoraException("notOnPeppol must be a boolean.");
|
|
2549
|
+
}
|
|
2550
|
+
body["not_on_peppol"] = input.notOnPeppol;
|
|
2551
|
+
}
|
|
2552
|
+
if (input.digest !== void 0) {
|
|
2553
|
+
if (!NOTIFICATION_DIGESTS.includes(input.digest)) {
|
|
2554
|
+
throw new TaxoraException(
|
|
2555
|
+
`Invalid digest "${String(input.digest)}". Expected one of: ${NOTIFICATION_DIGESTS.join(", ")}.`
|
|
2556
|
+
);
|
|
2557
|
+
}
|
|
2558
|
+
body["digest"] = input.digest;
|
|
2559
|
+
}
|
|
2560
|
+
if (Object.keys(body).length === 0) {
|
|
2561
|
+
throw new TaxoraException("updateNotificationSettings requires at least one field to update.");
|
|
2562
|
+
}
|
|
2563
|
+
const response = await this.sendRequest("PUT", `${this.baseUrl}/compliance/notification-settings`, body);
|
|
2564
|
+
return this.toNotificationSettings(await this.parseJsonResponse(response));
|
|
2565
|
+
}
|
|
2566
|
+
toNotificationSettings(data) {
|
|
2567
|
+
const recipients = Array.isArray(data["recipients"]) ? data["recipients"].filter((r) => typeof r === "string" && r !== "") : [];
|
|
2568
|
+
const digest = data["digest"];
|
|
2569
|
+
return {
|
|
2570
|
+
recipients,
|
|
2571
|
+
language: data["language"] === "en" ? "en" : "de",
|
|
2572
|
+
notOnPeppol: data["not_on_peppol"] === true,
|
|
2573
|
+
digest: digest === "daily" || digest === "weekly" ? digest : "off",
|
|
2574
|
+
digestSentAt: typeof data["digest_sent_at"] === "string" ? data["digest_sent_at"] : null
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
// ---------------------------------------------------------------------
|
|
1996
2578
|
// Input validation (fail fast, before any request is sent)
|
|
1997
2579
|
// ---------------------------------------------------------------------
|
|
2580
|
+
/** Validated transaction-list filters as query params (shared by listTransactions and exportTransactionsCsv). */
|
|
2581
|
+
buildTransactionFilterParams(filters) {
|
|
2582
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2583
|
+
this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
|
|
2584
|
+
}
|
|
2585
|
+
if (filters.country !== void 0 && !COUNTRY_REGEX.test(filters.country)) {
|
|
2586
|
+
throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
|
|
2587
|
+
}
|
|
2588
|
+
if (filters.search !== void 0 && filters.search.length > 100) {
|
|
2589
|
+
throw new TaxoraException("search must not exceed 100 characters.");
|
|
2590
|
+
}
|
|
2591
|
+
this.validateInternalReference(filters.internalReference);
|
|
2592
|
+
if (filters.providerState !== void 0 && !/^[a-z_]{1,30}$/.test(filters.providerState)) {
|
|
2593
|
+
throw new TaxoraException('providerState must be 1-30 lowercase letters or underscores (e.g. "refused").');
|
|
2594
|
+
}
|
|
2595
|
+
const params = new URLSearchParams();
|
|
2596
|
+
if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
|
|
2597
|
+
if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
|
|
2598
|
+
if (filters.state !== void 0) params.set("state", filters.state);
|
|
2599
|
+
if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
|
|
2600
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2601
|
+
params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
|
|
2602
|
+
}
|
|
2603
|
+
if (filters.country !== void 0) params.set("country", filters.country.toUpperCase());
|
|
2604
|
+
if (filters.search !== void 0) params.set("search", filters.search);
|
|
2605
|
+
if (filters.internalReference !== void 0) params.set("internal_reference", filters.internalReference);
|
|
2606
|
+
if (filters.providerState !== void 0) params.set("provider_state", filters.providerState);
|
|
2607
|
+
return params;
|
|
2608
|
+
}
|
|
1998
2609
|
validateEnrollmentInput(input) {
|
|
1999
2610
|
if (input.siret === void 0 && input.siren === void 0) {
|
|
2000
2611
|
throw new TaxoraException("Either siret or siren is required.");
|
|
@@ -2122,6 +2733,20 @@ var EReportingEndpoint = class {
|
|
|
2122
2733
|
}
|
|
2123
2734
|
});
|
|
2124
2735
|
}
|
|
2736
|
+
validateInvoicePdfFilename(input) {
|
|
2737
|
+
if (input.invoicePdfFilename === void 0) return;
|
|
2738
|
+
if (input.invoicePdfFilename.length > 120) {
|
|
2739
|
+
throw new TaxoraException("invoicePdfFilename must not exceed 120 characters.");
|
|
2740
|
+
}
|
|
2741
|
+
if (input.invoicePdf === void 0) {
|
|
2742
|
+
throw new TaxoraException("invoicePdfFilename requires invoicePdf.");
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
validateInternalReference(internalReference) {
|
|
2746
|
+
if (internalReference !== void 0 && internalReference.length > 100) {
|
|
2747
|
+
throw new TaxoraException("internalReference must not exceed 100 characters.");
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2125
2750
|
validateOptionalCodes(input) {
|
|
2126
2751
|
if (input.currency !== void 0 && input.currency.length !== 3) {
|
|
2127
2752
|
throw new TaxoraException("currency must be a 3-character ISO 4217 code.");
|
|
@@ -2203,6 +2828,29 @@ var EReportingEndpoint = class {
|
|
|
2203
2828
|
if (input.amendedNumber !== void 0) body["amended_number"] = input.amendedNumber;
|
|
2204
2829
|
if (input.amendedDate !== void 0) body["amended_date"] = this.formatDate(input.amendedDate);
|
|
2205
2830
|
if (input.extraInfo !== void 0) body["extra_info"] = input.extraInfo;
|
|
2831
|
+
if (input.internalReference !== void 0) body["internal_reference"] = input.internalReference;
|
|
2832
|
+
if (input.invoicePdf !== void 0) body["invoice_pdf"] = this.encodeInvoicePdf(input.invoicePdf);
|
|
2833
|
+
if (input.invoicePdfFilename !== void 0) body["invoice_pdf_filename"] = input.invoicePdfFilename;
|
|
2834
|
+
}
|
|
2835
|
+
/** Bytes → base64 (validated as a PDF of at most 10 MB); strings are taken as already base64. */
|
|
2836
|
+
encodeInvoicePdf(pdf) {
|
|
2837
|
+
if (typeof pdf === "string") {
|
|
2838
|
+
if (pdf.trim() === "") {
|
|
2839
|
+
throw new TaxoraException("invoicePdf must not be empty.");
|
|
2840
|
+
}
|
|
2841
|
+
return pdf;
|
|
2842
|
+
}
|
|
2843
|
+
const bytes = pdf instanceof Uint8Array ? pdf : new Uint8Array(pdf);
|
|
2844
|
+
if (bytes.byteLength === 0) {
|
|
2845
|
+
throw new TaxoraException("invoicePdf must not be empty.");
|
|
2846
|
+
}
|
|
2847
|
+
if (bytes.byteLength > INVOICE_PDF_MAX_BYTES) {
|
|
2848
|
+
throw new TaxoraException("invoicePdf must not exceed 10 MB.");
|
|
2849
|
+
}
|
|
2850
|
+
if (!INVOICE_PDF_MAGIC.every((byte, index) => bytes[index] === byte)) {
|
|
2851
|
+
throw new TaxoraException('invoicePdf must be a PDF file (content must start with "%PDF-").');
|
|
2852
|
+
}
|
|
2853
|
+
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
|
|
2206
2854
|
}
|
|
2207
2855
|
// ---------------------------------------------------------------------
|
|
2208
2856
|
// HTTP plumbing (kept in sync with SmartEnrichmentEndpoint)
|
|
@@ -2226,6 +2874,30 @@ var EReportingEndpoint = class {
|
|
|
2226
2874
|
const send = () => this.httpClient.request(method, url, options);
|
|
2227
2875
|
return method === "GET" ? withResponseRetries(this.retryPolicy, "e-reporting request", send) : send();
|
|
2228
2876
|
}
|
|
2877
|
+
/** GET for a non-JSON download (CSV / document): no JSON Content-Type, retried like other GETs. */
|
|
2878
|
+
async sendDownloadRequest(url, accept) {
|
|
2879
|
+
const headers = this.buildHeaders(false);
|
|
2880
|
+
headers["Accept"] = accept;
|
|
2881
|
+
return withResponseRetries(
|
|
2882
|
+
this.retryPolicy,
|
|
2883
|
+
"e-reporting download",
|
|
2884
|
+
() => this.httpClient.request("GET", url, { headers })
|
|
2885
|
+
);
|
|
2886
|
+
}
|
|
2887
|
+
/** `attachment; filename="x.xml"` (or RFC 5987 `filename*=UTF-8''x.xml`) → "x.xml". */
|
|
2888
|
+
filenameFromDisposition(header) {
|
|
2889
|
+
if (header === null) return null;
|
|
2890
|
+
const extended = /filename\*\s*=\s*(?:UTF-8|utf-8)''([^;]+)/.exec(header);
|
|
2891
|
+
if (extended?.[1] !== void 0) {
|
|
2892
|
+
try {
|
|
2893
|
+
return decodeURIComponent(extended[1].trim());
|
|
2894
|
+
} catch {
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
const plain = /filename\s*=\s*"([^"]*)"|filename\s*=\s*([^;]+)/.exec(header);
|
|
2898
|
+
const name = (plain?.[1] ?? plain?.[2])?.trim();
|
|
2899
|
+
return name !== void 0 && name !== "" ? name : null;
|
|
2900
|
+
}
|
|
2229
2901
|
/** Multipart upload: Content-Type is left to fetch so the boundary is set correctly. */
|
|
2230
2902
|
async sendMultipartRequest(method, url, form) {
|
|
2231
2903
|
return this.httpClient.request(method, url, { headers: this.buildHeaders(false), body: form });
|
|
@@ -2807,13 +3479,13 @@ var SmartEnrichmentHistoryPage = class _SmartEnrichmentHistoryPage {
|
|
|
2807
3479
|
};
|
|
2808
3480
|
|
|
2809
3481
|
// src/dto/SmartEnrichmentStatistics.ts
|
|
2810
|
-
function
|
|
3482
|
+
function asString11(value, fallback = "") {
|
|
2811
3483
|
return typeof value === "string" ? value : fallback;
|
|
2812
3484
|
}
|
|
2813
|
-
function
|
|
3485
|
+
function asNumber7(value) {
|
|
2814
3486
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
2815
3487
|
}
|
|
2816
|
-
function
|
|
3488
|
+
function asRecord4(value) {
|
|
2817
3489
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2818
3490
|
}
|
|
2819
3491
|
function asRecords2(value) {
|
|
@@ -2836,39 +3508,39 @@ var SmartEnrichmentStatistics = class _SmartEnrichmentStatistics {
|
|
|
2836
3508
|
byOutcome;
|
|
2837
3509
|
confidenceBuckets;
|
|
2838
3510
|
static fromArray(data) {
|
|
2839
|
-
const range =
|
|
2840
|
-
const totals =
|
|
3511
|
+
const range = asRecord4(data["range"]);
|
|
3512
|
+
const totals = asRecord4(data["totals"]);
|
|
2841
3513
|
const timeSeries = asRecords2(data["time_series"]).map((row) => ({
|
|
2842
|
-
bucket:
|
|
2843
|
-
jobs:
|
|
2844
|
-
items:
|
|
2845
|
-
found:
|
|
3514
|
+
bucket: asString11(row["bucket"]),
|
|
3515
|
+
jobs: asNumber7(row["jobs"]),
|
|
3516
|
+
items: asNumber7(row["items"]),
|
|
3517
|
+
found: asNumber7(row["found"])
|
|
2846
3518
|
}));
|
|
2847
3519
|
const bySource = asRecords2(data["by_source"]).map((row) => ({
|
|
2848
|
-
source:
|
|
2849
|
-
jobs:
|
|
2850
|
-
found:
|
|
3520
|
+
source: asString11(row["source"]),
|
|
3521
|
+
jobs: asNumber7(row["jobs"]),
|
|
3522
|
+
found: asNumber7(row["found"])
|
|
2851
3523
|
}));
|
|
2852
3524
|
const byOutcome = asRecords2(data["by_outcome"]).map((row) => ({
|
|
2853
|
-
outcome:
|
|
2854
|
-
count:
|
|
3525
|
+
outcome: asString11(row["outcome"]),
|
|
3526
|
+
count: asNumber7(row["count"])
|
|
2855
3527
|
}));
|
|
2856
3528
|
const confidenceBuckets = asRecords2(data["confidence_buckets"]).map((row) => ({
|
|
2857
|
-
bucket:
|
|
2858
|
-
count:
|
|
3529
|
+
bucket: asString11(row["bucket"]),
|
|
3530
|
+
count: asNumber7(row["count"])
|
|
2859
3531
|
}));
|
|
2860
3532
|
return new _SmartEnrichmentStatistics(
|
|
2861
3533
|
{
|
|
2862
|
-
dateFrom:
|
|
2863
|
-
dateTo:
|
|
2864
|
-
interval:
|
|
3534
|
+
dateFrom: asString11(range["date_from"]),
|
|
3535
|
+
dateTo: asString11(range["date_to"]),
|
|
3536
|
+
interval: asString11(range["interval"])
|
|
2865
3537
|
},
|
|
2866
3538
|
{
|
|
2867
|
-
jobs:
|
|
2868
|
-
items:
|
|
2869
|
-
found:
|
|
2870
|
-
foundRate:
|
|
2871
|
-
avgConfidence:
|
|
3539
|
+
jobs: asNumber7(totals["jobs"]),
|
|
3540
|
+
items: asNumber7(totals["items"]),
|
|
3541
|
+
found: asNumber7(totals["found"]),
|
|
3542
|
+
foundRate: asNumber7(totals["found_rate"]),
|
|
3543
|
+
avgConfidence: asNumber7(totals["avg_confidence"])
|
|
2872
3544
|
},
|
|
2873
3545
|
timeSeries,
|
|
2874
3546
|
bySource,
|
|
@@ -4148,7 +4820,7 @@ var AuthRetryHttpClient = class {
|
|
|
4148
4820
|
};
|
|
4149
4821
|
|
|
4150
4822
|
// src/version.ts
|
|
4151
|
-
var SDK_VERSION = "1.
|
|
4823
|
+
var SDK_VERSION = "1.9.0";
|
|
4152
4824
|
|
|
4153
4825
|
// src/http/SdkVersionHttpClient.ts
|
|
4154
4826
|
var SDK_VERSION_HEADER = "X-Taxora-SDK-Version";
|
|
@@ -4175,6 +4847,7 @@ var TaxoraClient = class {
|
|
|
4175
4847
|
company;
|
|
4176
4848
|
smartEnrichment;
|
|
4177
4849
|
eReporting;
|
|
4850
|
+
billing;
|
|
4178
4851
|
baseUrl;
|
|
4179
4852
|
constructor(options) {
|
|
4180
4853
|
const environment = options.environment ?? Environment.SANDBOX;
|
|
@@ -4195,6 +4868,7 @@ var TaxoraClient = class {
|
|
|
4195
4868
|
retryPolicy
|
|
4196
4869
|
);
|
|
4197
4870
|
this.eReporting = new EReportingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
|
|
4871
|
+
this.billing = new BillingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
|
|
4198
4872
|
}
|
|
4199
4873
|
};
|
|
4200
4874
|
|
|
@@ -4334,18 +5008,25 @@ function toBytes(rawBody) {
|
|
|
4334
5008
|
"invalid_body"
|
|
4335
5009
|
);
|
|
4336
5010
|
}
|
|
5011
|
+
|
|
5012
|
+
// src/webhooks/WebhookEvent.ts
|
|
5013
|
+
function isTransactionBuyerRejected(object) {
|
|
5014
|
+
return object.buyer_rejected === true;
|
|
5015
|
+
}
|
|
4337
5016
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4338
5017
|
0 && (module.exports = {
|
|
4339
5018
|
ApiVersion,
|
|
4340
5019
|
AuthEndpoint,
|
|
4341
5020
|
AuthRetryHttpClient,
|
|
4342
5021
|
AuthenticationException,
|
|
5022
|
+
BillingEndpoint,
|
|
4343
5023
|
CompanyAddress,
|
|
4344
5024
|
CompanyEndpoint,
|
|
4345
5025
|
ComplianceDocumentType,
|
|
4346
5026
|
ComplianceEnrollment,
|
|
4347
5027
|
ComplianceEnrollmentPage,
|
|
4348
5028
|
ComplianceEnrollmentStatus,
|
|
5029
|
+
ComplianceOfferings,
|
|
4349
5030
|
ComplianceService,
|
|
4350
5031
|
ComplianceTaxReport,
|
|
4351
5032
|
ComplianceTaxReportPage,
|
|
@@ -4389,6 +5070,8 @@ function toBytes(rawBody) {
|
|
|
4389
5070
|
TaxoraClientFactory,
|
|
4390
5071
|
TaxoraException,
|
|
4391
5072
|
Token,
|
|
5073
|
+
UsageSummary,
|
|
5074
|
+
UsageSummaryPage,
|
|
4392
5075
|
ValidationException,
|
|
4393
5076
|
VatCertificateExport,
|
|
4394
5077
|
VatCollection,
|
|
@@ -4417,6 +5100,7 @@ function toBytes(rawBody) {
|
|
|
4417
5100
|
generateWebhookSignatureHeader,
|
|
4418
5101
|
getBaseUrl,
|
|
4419
5102
|
getFailedVatStates,
|
|
5103
|
+
isTransactionBuyerRejected,
|
|
4420
5104
|
parseLocalTaxNumbersCsv,
|
|
4421
5105
|
parseWebhookSignatureHeader,
|
|
4422
5106
|
toComplianceDocumentType,
|