@taxora/sdk 1.8.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +97 -0
- package/README.md +239 -4
- package/dist/index.cjs +1382 -242
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +748 -63
- package/dist/index.d.ts +748 -63
- package/dist/index.js +1369 -242
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -24,13 +24,19 @@ __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,
|
|
30
|
+
ComplianceDeliveryAction: () => ComplianceDeliveryAction,
|
|
31
|
+
ComplianceDeliveryDecision: () => ComplianceDeliveryDecision,
|
|
29
32
|
ComplianceDocumentType: () => ComplianceDocumentType,
|
|
30
33
|
ComplianceEnrollment: () => ComplianceEnrollment,
|
|
31
34
|
ComplianceEnrollmentPage: () => ComplianceEnrollmentPage,
|
|
32
35
|
ComplianceEnrollmentStatus: () => ComplianceEnrollmentStatus,
|
|
36
|
+
ComplianceOfferings: () => ComplianceOfferings,
|
|
33
37
|
ComplianceService: () => ComplianceService,
|
|
38
|
+
ComplianceTax: () => ComplianceTax,
|
|
39
|
+
ComplianceTaxCategory: () => ComplianceTaxCategory,
|
|
34
40
|
ComplianceTaxReport: () => ComplianceTaxReport,
|
|
35
41
|
ComplianceTaxReportPage: () => ComplianceTaxReportPage,
|
|
36
42
|
ComplianceTaxReportState: () => ComplianceTaxReportState,
|
|
@@ -39,6 +45,8 @@ __export(index_exports, {
|
|
|
39
45
|
ComplianceTransactionState: () => ComplianceTransactionState,
|
|
40
46
|
ComplianceTransactionType: () => ComplianceTransactionType,
|
|
41
47
|
DEFAULT_WEBHOOK_TOLERANCE_SECONDS: () => DEFAULT_WEBHOOK_TOLERANCE_SECONDS,
|
|
48
|
+
EInvoicingQuota: () => EInvoicingQuota,
|
|
49
|
+
EInvoicingQuotaPeriod: () => EInvoicingQuotaPeriod,
|
|
42
50
|
EReportingEndpoint: () => EReportingEndpoint,
|
|
43
51
|
Environment: () => Environment,
|
|
44
52
|
FetchHttpClient: () => FetchHttpClient,
|
|
@@ -73,6 +81,8 @@ __export(index_exports, {
|
|
|
73
81
|
TaxoraClientFactory: () => TaxoraClientFactory,
|
|
74
82
|
TaxoraException: () => TaxoraException,
|
|
75
83
|
Token: () => Token,
|
|
84
|
+
UsageSummary: () => UsageSummary,
|
|
85
|
+
UsageSummaryPage: () => UsageSummaryPage,
|
|
76
86
|
ValidationException: () => ValidationException,
|
|
77
87
|
VatCertificateExport: () => VatCertificateExport,
|
|
78
88
|
VatCollection: () => VatCollection,
|
|
@@ -84,6 +94,7 @@ __export(index_exports, {
|
|
|
84
94
|
WEBHOOK_ID_HEADER: () => WEBHOOK_ID_HEADER,
|
|
85
95
|
WEBHOOK_SIGNATURE_HEADER: () => WEBHOOK_SIGNATURE_HEADER,
|
|
86
96
|
WebhookSignatureVerificationError: () => WebhookSignatureVerificationError,
|
|
97
|
+
calculateInvoiceTotals: () => calculateInvoiceTotals,
|
|
87
98
|
computeWebhookSignature: () => computeWebhookSignature,
|
|
88
99
|
constructWebhookEvent: () => constructWebhookEvent,
|
|
89
100
|
describeApiError: () => describeApiError,
|
|
@@ -101,6 +112,8 @@ __export(index_exports, {
|
|
|
101
112
|
generateWebhookSignatureHeader: () => generateWebhookSignatureHeader,
|
|
102
113
|
getBaseUrl: () => getBaseUrl,
|
|
103
114
|
getFailedVatStates: () => getFailedVatStates,
|
|
115
|
+
isTransactionBuyerRejected: () => isTransactionBuyerRejected,
|
|
116
|
+
parseComplianceTaxCategory: () => parseComplianceTaxCategory,
|
|
104
117
|
parseLocalTaxNumbersCsv: () => parseLocalTaxNumbersCsv,
|
|
105
118
|
parseWebhookSignatureHeader: () => parseWebhookSignatureHeader,
|
|
106
119
|
toComplianceDocumentType: () => toComplianceDocumentType,
|
|
@@ -559,6 +572,356 @@ var AuthEndpoint = class {
|
|
|
559
572
|
}
|
|
560
573
|
};
|
|
561
574
|
|
|
575
|
+
// src/dto/EInvoicingQuota.ts
|
|
576
|
+
function asString(value, fallback = "") {
|
|
577
|
+
return typeof value === "string" ? value : fallback;
|
|
578
|
+
}
|
|
579
|
+
function asNullableString(value) {
|
|
580
|
+
return typeof value === "string" ? value : null;
|
|
581
|
+
}
|
|
582
|
+
function asInt(value) {
|
|
583
|
+
return typeof value === "number" ? Math.trunc(value) : Number(value ?? 0) || 0;
|
|
584
|
+
}
|
|
585
|
+
function asRecord(value) {
|
|
586
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
587
|
+
}
|
|
588
|
+
var EInvoicingQuotaPeriod = class _EInvoicingQuotaPeriod {
|
|
589
|
+
constructor(period, used, included, freeUsed, freeRemaining, billableUnits, amount, monthlyFee, total, state, byCountry, billedAt, manualReference, monthlyFeeReference) {
|
|
590
|
+
this.period = period;
|
|
591
|
+
this.used = used;
|
|
592
|
+
this.included = included;
|
|
593
|
+
this.freeUsed = freeUsed;
|
|
594
|
+
this.freeRemaining = freeRemaining;
|
|
595
|
+
this.billableUnits = billableUnits;
|
|
596
|
+
this.amount = amount;
|
|
597
|
+
this.monthlyFee = monthlyFee;
|
|
598
|
+
this.total = total;
|
|
599
|
+
this.state = state;
|
|
600
|
+
this.byCountry = byCountry;
|
|
601
|
+
this.billedAt = billedAt;
|
|
602
|
+
this.manualReference = manualReference;
|
|
603
|
+
this.monthlyFeeReference = monthlyFeeReference;
|
|
604
|
+
}
|
|
605
|
+
period;
|
|
606
|
+
used;
|
|
607
|
+
included;
|
|
608
|
+
freeUsed;
|
|
609
|
+
freeRemaining;
|
|
610
|
+
billableUnits;
|
|
611
|
+
amount;
|
|
612
|
+
monthlyFee;
|
|
613
|
+
total;
|
|
614
|
+
state;
|
|
615
|
+
byCountry;
|
|
616
|
+
billedAt;
|
|
617
|
+
manualReference;
|
|
618
|
+
monthlyFeeReference;
|
|
619
|
+
static fromArray(data) {
|
|
620
|
+
const byCountry = {};
|
|
621
|
+
if (Array.isArray(data["by_country"])) {
|
|
622
|
+
for (const row of data["by_country"]) {
|
|
623
|
+
const entry = asRecord(row);
|
|
624
|
+
if (entry && typeof entry["country"] === "string") {
|
|
625
|
+
byCountry[entry["country"]] = asInt(entry["used"]);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return new _EInvoicingQuotaPeriod(
|
|
630
|
+
asString(data["period"]),
|
|
631
|
+
asInt(data["used"]),
|
|
632
|
+
asInt(data["included"]),
|
|
633
|
+
asInt(data["free_used"]),
|
|
634
|
+
asInt(data["free_remaining"]),
|
|
635
|
+
asInt(data["billable_units"]),
|
|
636
|
+
asString(data["amount"], "0.00"),
|
|
637
|
+
asNullableString(data["monthly_fee"]),
|
|
638
|
+
asString(data["total"], "0.00"),
|
|
639
|
+
asString(data["state"], "open"),
|
|
640
|
+
byCountry,
|
|
641
|
+
asNullableString(data["billed_at"]),
|
|
642
|
+
asNullableString(data["manual_reference"]),
|
|
643
|
+
asNullableString(data["monthly_fee_reference"])
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
var EInvoicingQuota = class _EInvoicingQuota {
|
|
648
|
+
constructor(billing, plan, currency, included, tiers, countries, monthlyFee, current, history) {
|
|
649
|
+
this.billing = billing;
|
|
650
|
+
this.plan = plan;
|
|
651
|
+
this.currency = currency;
|
|
652
|
+
this.included = included;
|
|
653
|
+
this.tiers = tiers;
|
|
654
|
+
this.countries = countries;
|
|
655
|
+
this.monthlyFee = monthlyFee;
|
|
656
|
+
this.current = current;
|
|
657
|
+
this.history = history;
|
|
658
|
+
}
|
|
659
|
+
billing;
|
|
660
|
+
plan;
|
|
661
|
+
currency;
|
|
662
|
+
included;
|
|
663
|
+
tiers;
|
|
664
|
+
countries;
|
|
665
|
+
monthlyFee;
|
|
666
|
+
current;
|
|
667
|
+
history;
|
|
668
|
+
static fromArray(data) {
|
|
669
|
+
const tiers = [];
|
|
670
|
+
if (Array.isArray(data["tiers"])) {
|
|
671
|
+
for (const row of data["tiers"]) {
|
|
672
|
+
const tier = asRecord(row);
|
|
673
|
+
if (tier) {
|
|
674
|
+
tiers.push({
|
|
675
|
+
upTo: tier["up_to"] === null || tier["up_to"] === void 0 ? null : asInt(tier["up_to"]),
|
|
676
|
+
unitPrice: asString(tier["unit_price"], "0")
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
const history = [];
|
|
682
|
+
if (Array.isArray(data["history"])) {
|
|
683
|
+
for (const row of data["history"]) {
|
|
684
|
+
const period = asRecord(row);
|
|
685
|
+
if (period) history.push(EInvoicingQuotaPeriod.fromArray(period));
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return new _EInvoicingQuota(
|
|
689
|
+
asString(data["billing"], "invoice"),
|
|
690
|
+
asNullableString(data["plan"]),
|
|
691
|
+
asString(data["currency"], "EUR"),
|
|
692
|
+
asInt(data["included"]),
|
|
693
|
+
tiers,
|
|
694
|
+
Array.isArray(data["countries"]) ? data["countries"].map(String) : [],
|
|
695
|
+
asNullableString(data["monthly_fee"]),
|
|
696
|
+
EInvoicingQuotaPeriod.fromArray(asRecord(data["current"]) ?? {}),
|
|
697
|
+
history
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
// src/dto/UsageSummary.ts
|
|
703
|
+
function asRecord2(value) {
|
|
704
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
705
|
+
}
|
|
706
|
+
function asRecordList(value) {
|
|
707
|
+
return Array.isArray(value) ? value.filter((row) => asRecord2(row) !== null) : [];
|
|
708
|
+
}
|
|
709
|
+
function asString2(value, fallback = "") {
|
|
710
|
+
return typeof value === "string" ? value : fallback;
|
|
711
|
+
}
|
|
712
|
+
function asNullableString2(value) {
|
|
713
|
+
return typeof value === "string" ? value : null;
|
|
714
|
+
}
|
|
715
|
+
function asNumber(value) {
|
|
716
|
+
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
717
|
+
}
|
|
718
|
+
function asNullableNumber(value) {
|
|
719
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
720
|
+
const num = Number(value);
|
|
721
|
+
return Number.isFinite(num) ? num : null;
|
|
722
|
+
}
|
|
723
|
+
var UsageSummary = class _UsageSummary {
|
|
724
|
+
constructor(id, companyId, feature, country, regime, billingPeriod, eventCount, billableAmount, state, stateLabel, periodClosedAt, billingTriggeredAt, billedAt, stripeInvoiceId, stripeSubscriptionItemId, billingNotes, events, createdAt, updatedAt) {
|
|
725
|
+
this.id = id;
|
|
726
|
+
this.companyId = companyId;
|
|
727
|
+
this.feature = feature;
|
|
728
|
+
this.country = country;
|
|
729
|
+
this.regime = regime;
|
|
730
|
+
this.billingPeriod = billingPeriod;
|
|
731
|
+
this.eventCount = eventCount;
|
|
732
|
+
this.billableAmount = billableAmount;
|
|
733
|
+
this.state = state;
|
|
734
|
+
this.stateLabel = stateLabel;
|
|
735
|
+
this.periodClosedAt = periodClosedAt;
|
|
736
|
+
this.billingTriggeredAt = billingTriggeredAt;
|
|
737
|
+
this.billedAt = billedAt;
|
|
738
|
+
this.stripeInvoiceId = stripeInvoiceId;
|
|
739
|
+
this.stripeSubscriptionItemId = stripeSubscriptionItemId;
|
|
740
|
+
this.billingNotes = billingNotes;
|
|
741
|
+
this.events = events;
|
|
742
|
+
this.createdAt = createdAt;
|
|
743
|
+
this.updatedAt = updatedAt;
|
|
744
|
+
}
|
|
745
|
+
id;
|
|
746
|
+
companyId;
|
|
747
|
+
feature;
|
|
748
|
+
country;
|
|
749
|
+
regime;
|
|
750
|
+
billingPeriod;
|
|
751
|
+
eventCount;
|
|
752
|
+
billableAmount;
|
|
753
|
+
state;
|
|
754
|
+
stateLabel;
|
|
755
|
+
periodClosedAt;
|
|
756
|
+
billingTriggeredAt;
|
|
757
|
+
billedAt;
|
|
758
|
+
stripeInvoiceId;
|
|
759
|
+
stripeSubscriptionItemId;
|
|
760
|
+
billingNotes;
|
|
761
|
+
events;
|
|
762
|
+
createdAt;
|
|
763
|
+
updatedAt;
|
|
764
|
+
static fromArray(data) {
|
|
765
|
+
const amount = data["billable_amount"];
|
|
766
|
+
return new _UsageSummary(
|
|
767
|
+
asNumber(data["id"]),
|
|
768
|
+
asNumber(data["company_id"]),
|
|
769
|
+
asString2(data["feature"]),
|
|
770
|
+
asNullableString2(data["country"]),
|
|
771
|
+
asNullableString2(data["regime"]),
|
|
772
|
+
asString2(data["billing_period"]),
|
|
773
|
+
asNumber(data["event_count"]),
|
|
774
|
+
typeof amount === "string" ? amount : typeof amount === "number" ? String(amount) : null,
|
|
775
|
+
asNullableString2(data["state"]),
|
|
776
|
+
asNullableString2(data["state_label"]),
|
|
777
|
+
asNullableString2(data["period_closed_at"]),
|
|
778
|
+
asNullableString2(data["billing_triggered_at"]),
|
|
779
|
+
asNullableString2(data["billed_at"]),
|
|
780
|
+
asNullableString2(data["stripe_invoice_id"]),
|
|
781
|
+
asNullableString2(data["stripe_subscription_item_id"]),
|
|
782
|
+
asNullableString2(data["billing_notes"]),
|
|
783
|
+
asRecordList(data["events"]).map((event) => ({
|
|
784
|
+
id: asNumber(event["id"]),
|
|
785
|
+
usageSummaryId: asNullableNumber(event["usage_summary_id"]),
|
|
786
|
+
feature: asString2(event["feature"]),
|
|
787
|
+
country: asNullableString2(event["country"]),
|
|
788
|
+
regime: asNullableString2(event["regime"]),
|
|
789
|
+
billingPeriod: asString2(event["billing_period"]),
|
|
790
|
+
resourceType: asNullableString2(event["resource_type"]),
|
|
791
|
+
resourceId: asNullableNumber(event["resource_id"]),
|
|
792
|
+
stripeUsageRecordId: asNullableString2(event["stripe_usage_record_id"]),
|
|
793
|
+
stripeReportedAt: asNullableString2(event["stripe_reported_at"]),
|
|
794
|
+
createdAt: asNullableString2(event["created_at"])
|
|
795
|
+
})),
|
|
796
|
+
asNullableString2(data["created_at"]),
|
|
797
|
+
asNullableString2(data["updated_at"])
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
var UsageSummaryPage = class _UsageSummaryPage {
|
|
802
|
+
constructor(rows, currentPage, perPage, total, lastPage) {
|
|
803
|
+
this.rows = rows;
|
|
804
|
+
this.currentPage = currentPage;
|
|
805
|
+
this.perPage = perPage;
|
|
806
|
+
this.total = total;
|
|
807
|
+
this.lastPage = lastPage;
|
|
808
|
+
}
|
|
809
|
+
rows;
|
|
810
|
+
currentPage;
|
|
811
|
+
perPage;
|
|
812
|
+
total;
|
|
813
|
+
lastPage;
|
|
814
|
+
static fromResponse(payload) {
|
|
815
|
+
const obj = asRecord2(payload) ?? {};
|
|
816
|
+
const rows = asRecordList(obj["data"]).map(UsageSummary.fromArray);
|
|
817
|
+
const meta = asRecord2(obj["meta"]) ?? {};
|
|
818
|
+
const num = (value, fallback) => typeof value === "number" ? value : fallback;
|
|
819
|
+
return new _UsageSummaryPage(
|
|
820
|
+
rows,
|
|
821
|
+
num(meta["current_page"], 1),
|
|
822
|
+
num(meta["per_page"], rows.length),
|
|
823
|
+
num(meta["total"], rows.length),
|
|
824
|
+
num(meta["last_page"], 1)
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
[Symbol.iterator]() {
|
|
828
|
+
return this.rows[Symbol.iterator]();
|
|
829
|
+
}
|
|
830
|
+
get length() {
|
|
831
|
+
return this.rows.length;
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
// src/endpoints/BillingEndpoint.ts
|
|
836
|
+
var PERIOD_REGEX = /^\d{4}-(0[1-9]|1[0-2])$/;
|
|
837
|
+
var BillingEndpoint = class {
|
|
838
|
+
constructor(baseUrl, apiKey, tokenStorage, httpClient, retryPolicy = new RetryPolicy()) {
|
|
839
|
+
this.baseUrl = baseUrl;
|
|
840
|
+
this.apiKey = apiKey;
|
|
841
|
+
this.tokenStorage = tokenStorage;
|
|
842
|
+
this.httpClient = httpClient;
|
|
843
|
+
this.retryPolicy = retryPolicy;
|
|
844
|
+
}
|
|
845
|
+
baseUrl;
|
|
846
|
+
apiKey;
|
|
847
|
+
tokenStorage;
|
|
848
|
+
httpClient;
|
|
849
|
+
retryPolicy;
|
|
850
|
+
/** Paginated usage summaries, newest billing period first. `perPage` is clamped to 1-100 server-side. */
|
|
851
|
+
async listUsageSummaries(page = 1, perPage = 25) {
|
|
852
|
+
const params = new URLSearchParams();
|
|
853
|
+
params.set("page", String(page));
|
|
854
|
+
params.set("per_page", String(perPage));
|
|
855
|
+
const data = await this.getJson(`${this.baseUrl}/billing/usage-summaries?${params.toString()}`);
|
|
856
|
+
return UsageSummaryPage.fromResponse(data);
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* The usage summaries of one billing period (YYYY-MM), one per feature,
|
|
860
|
+
* each with its usage events. A period without usage yields a 404 (HttpException).
|
|
861
|
+
*/
|
|
862
|
+
async getUsageSummaries(period) {
|
|
863
|
+
if (!PERIOD_REGEX.test(period)) {
|
|
864
|
+
throw new TaxoraException(`Invalid billing period "${period}". Expected YYYY-MM, e.g. "2026-09".`);
|
|
865
|
+
}
|
|
866
|
+
const data = await this.getJson(`${this.baseUrl}/billing/usage-summaries/${period}`);
|
|
867
|
+
return Array.isArray(data) ? data.filter((row) => typeof row === "object" && row !== null).map(UsageSummary.fromArray) : [];
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* The e-invoicing allowance: documents included per month, used, still free and
|
|
871
|
+
* what is due for the documents beyond it — one pool over all e-invoicing
|
|
872
|
+
* countries, current month first, then up to `months - 1` earlier months (1-24).
|
|
873
|
+
* Resolves to null when the company has no e-invoicing.
|
|
874
|
+
*/
|
|
875
|
+
async getEInvoicingQuota(months = 6) {
|
|
876
|
+
if (!Number.isInteger(months) || months < 1 || months > 24) {
|
|
877
|
+
throw new TaxoraException("months must be an integer between 1 and 24.");
|
|
878
|
+
}
|
|
879
|
+
const data = await this.getJson(`${this.baseUrl}/billing/einvoicing-quota?months=${months}`);
|
|
880
|
+
if (typeof data !== "object" || data === null || Array.isArray(data) || !("billing" in data)) {
|
|
881
|
+
return null;
|
|
882
|
+
}
|
|
883
|
+
return EInvoicingQuota.fromArray(data);
|
|
884
|
+
}
|
|
885
|
+
buildHeaders() {
|
|
886
|
+
const headers = {
|
|
887
|
+
"Content-Type": "application/json",
|
|
888
|
+
"x-api-key": this.apiKey
|
|
889
|
+
};
|
|
890
|
+
const token = this.tokenStorage.get();
|
|
891
|
+
if (token) {
|
|
892
|
+
headers["Authorization"] = `Bearer ${token.accessToken}`;
|
|
893
|
+
}
|
|
894
|
+
return headers;
|
|
895
|
+
}
|
|
896
|
+
/** GET + envelope unwrap (`{success, data}` → data, which may be an object or an array). */
|
|
897
|
+
async getJson(url) {
|
|
898
|
+
return withRetries(this.retryPolicy, "billing request", async () => {
|
|
899
|
+
const response = await this.httpClient.request("GET", url, { headers: this.buildHeaders() });
|
|
900
|
+
const responseText = await response.text();
|
|
901
|
+
if (!response.ok) {
|
|
902
|
+
throw new HttpException(
|
|
903
|
+
describeApiError(responseText, response.status),
|
|
904
|
+
response.status,
|
|
905
|
+
responseText,
|
|
906
|
+
{},
|
|
907
|
+
response.headers.get("retry-after")
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
let parsed;
|
|
911
|
+
try {
|
|
912
|
+
parsed = JSON.parse(responseText);
|
|
913
|
+
} catch {
|
|
914
|
+
throw new HttpException("Invalid JSON response", response.status, responseText);
|
|
915
|
+
}
|
|
916
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
917
|
+
throw new HttpException("Unexpected response format", response.status, responseText);
|
|
918
|
+
}
|
|
919
|
+
const obj = parsed;
|
|
920
|
+
return obj["data"] !== void 0 && obj["data"] !== null ? obj["data"] : obj;
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
|
|
562
925
|
// src/dto/CompanyResource.ts
|
|
563
926
|
function normalizeCompanyResource(data) {
|
|
564
927
|
const apiRateLimit = typeof data["api_rate_limit"] === "number" ? data["api_rate_limit"] : void 0;
|
|
@@ -624,6 +987,67 @@ var CompanyEndpoint = class {
|
|
|
624
987
|
}
|
|
625
988
|
};
|
|
626
989
|
|
|
990
|
+
// src/enums/ComplianceDeliveryAction.ts
|
|
991
|
+
var ComplianceDeliveryAction = {
|
|
992
|
+
/** The buyer can receive e-invoices: create the transaction, Taxora sends it. */
|
|
993
|
+
EINVOICE: "einvoice",
|
|
994
|
+
/** The buyer is not on the network: send your regular invoice yourself (a transaction would end as not_required and is not billed). */
|
|
995
|
+
REGULAR_INVOICE: "regular_invoice",
|
|
996
|
+
/** No definite directory answer yet: create the transaction anyway, Taxora asks again before sending. */
|
|
997
|
+
EINVOICE_PENDING: "einvoice_pending"
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
// src/enums/PeppolLookupStatus.ts
|
|
1001
|
+
var PeppolLookupStatus = {
|
|
1002
|
+
REACHABLE: "reachable",
|
|
1003
|
+
NOT_REACHABLE: "not_reachable",
|
|
1004
|
+
PENDING: "pending",
|
|
1005
|
+
UNKNOWN: "unknown"
|
|
1006
|
+
};
|
|
1007
|
+
var VALUES = Object.values(PeppolLookupStatus);
|
|
1008
|
+
function toPeppolLookupStatus(value) {
|
|
1009
|
+
if (typeof value === "string") {
|
|
1010
|
+
const normalized = value.trim().toLowerCase();
|
|
1011
|
+
if (VALUES.includes(normalized)) {
|
|
1012
|
+
return normalized;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
return PeppolLookupStatus.UNKNOWN;
|
|
1016
|
+
}
|
|
1017
|
+
var DESCRIPTIONS = {
|
|
1018
|
+
[PeppolLookupStatus.REACHABLE]: "The participant is registered on Peppol and can receive e-invoices.",
|
|
1019
|
+
[PeppolLookupStatus.NOT_REACHABLE]: "The participant is not registered on Peppol.",
|
|
1020
|
+
[PeppolLookupStatus.PENDING]: "The directory lookup is still resolving \u2014 ask again in a few seconds.",
|
|
1021
|
+
[PeppolLookupStatus.UNKNOWN]: "Unknown lookup status."
|
|
1022
|
+
};
|
|
1023
|
+
function describePeppolLookupStatus(status) {
|
|
1024
|
+
return DESCRIPTIONS[status];
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// src/dto/ComplianceDeliveryDecision.ts
|
|
1028
|
+
var ComplianceDeliveryDecision = class _ComplianceDeliveryDecision {
|
|
1029
|
+
constructor(action, lookup) {
|
|
1030
|
+
this.action = action;
|
|
1031
|
+
this.lookup = lookup;
|
|
1032
|
+
}
|
|
1033
|
+
action;
|
|
1034
|
+
lookup;
|
|
1035
|
+
/** True unless the buyer is not on the network. */
|
|
1036
|
+
get sendsEInvoice() {
|
|
1037
|
+
return this.action !== ComplianceDeliveryAction.REGULAR_INVOICE;
|
|
1038
|
+
}
|
|
1039
|
+
static fromLookup(lookup) {
|
|
1040
|
+
switch (lookup.status) {
|
|
1041
|
+
case PeppolLookupStatus.REACHABLE:
|
|
1042
|
+
return new _ComplianceDeliveryDecision(ComplianceDeliveryAction.EINVOICE, lookup);
|
|
1043
|
+
case PeppolLookupStatus.NOT_REACHABLE:
|
|
1044
|
+
return new _ComplianceDeliveryDecision(ComplianceDeliveryAction.REGULAR_INVOICE, lookup);
|
|
1045
|
+
default:
|
|
1046
|
+
return new _ComplianceDeliveryDecision(ComplianceDeliveryAction.EINVOICE_PENDING, lookup);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
|
|
627
1051
|
// src/enums/ComplianceEnrollmentStatus.ts
|
|
628
1052
|
var ComplianceEnrollmentStatus = {
|
|
629
1053
|
PENDING_DATA: "pending_data",
|
|
@@ -635,17 +1059,17 @@ var ComplianceEnrollmentStatus = {
|
|
|
635
1059
|
ERROR_ACTIVATION: "error_activation",
|
|
636
1060
|
UNKNOWN: "unknown"
|
|
637
1061
|
};
|
|
638
|
-
var
|
|
1062
|
+
var VALUES2 = Object.values(ComplianceEnrollmentStatus);
|
|
639
1063
|
function toComplianceEnrollmentStatus(value) {
|
|
640
1064
|
if (typeof value === "string") {
|
|
641
1065
|
const normalized = value.trim().toLowerCase();
|
|
642
|
-
if (
|
|
1066
|
+
if (VALUES2.includes(normalized)) {
|
|
643
1067
|
return normalized;
|
|
644
1068
|
}
|
|
645
1069
|
}
|
|
646
1070
|
return ComplianceEnrollmentStatus.UNKNOWN;
|
|
647
1071
|
}
|
|
648
|
-
var
|
|
1072
|
+
var DESCRIPTIONS2 = {
|
|
649
1073
|
[ComplianceEnrollmentStatus.PENDING_DATA]: "Awaiting merchant data before provisioning can start.",
|
|
650
1074
|
[ComplianceEnrollmentStatus.ACCOUNT_CREATED]: "Provider account exists. Regime not yet activated.",
|
|
651
1075
|
[ComplianceEnrollmentStatus.REGIME_ACTIVATED]: "Compliance regime activated at provider. Ready to submit transactions.",
|
|
@@ -656,7 +1080,7 @@ var DESCRIPTIONS = {
|
|
|
656
1080
|
[ComplianceEnrollmentStatus.UNKNOWN]: "Unknown status."
|
|
657
1081
|
};
|
|
658
1082
|
function describeComplianceEnrollmentStatus(status) {
|
|
659
|
-
return
|
|
1083
|
+
return DESCRIPTIONS2[status];
|
|
660
1084
|
}
|
|
661
1085
|
|
|
662
1086
|
// src/enums/ComplianceService.ts
|
|
@@ -665,33 +1089,33 @@ var ComplianceService = {
|
|
|
665
1089
|
E_INVOICING: "e_invoicing",
|
|
666
1090
|
UNKNOWN: "unknown"
|
|
667
1091
|
};
|
|
668
|
-
var
|
|
1092
|
+
var VALUES3 = Object.values(ComplianceService);
|
|
669
1093
|
function toComplianceService(value) {
|
|
670
1094
|
if (typeof value === "string") {
|
|
671
1095
|
const normalized = value.trim().toLowerCase();
|
|
672
|
-
if (
|
|
1096
|
+
if (VALUES3.includes(normalized)) {
|
|
673
1097
|
return normalized;
|
|
674
1098
|
}
|
|
675
1099
|
}
|
|
676
1100
|
return ComplianceService.UNKNOWN;
|
|
677
1101
|
}
|
|
678
|
-
var
|
|
1102
|
+
var DESCRIPTIONS3 = {
|
|
679
1103
|
[ComplianceService.E_REPORTING]: "E-reporting of transaction data to the tax authority (France: DGFiP Flux 10).",
|
|
680
1104
|
[ComplianceService.E_INVOICING]: "Structured e-invoices delivered to the buyer (Norway: EHF / Peppol BIS 3.0).",
|
|
681
1105
|
[ComplianceService.UNKNOWN]: "Unknown service."
|
|
682
1106
|
};
|
|
683
1107
|
function describeComplianceService(service) {
|
|
684
|
-
return
|
|
1108
|
+
return DESCRIPTIONS3[service];
|
|
685
1109
|
}
|
|
686
1110
|
|
|
687
1111
|
// src/dto/ComplianceEnrollment.ts
|
|
688
|
-
function
|
|
1112
|
+
function asString3(value, fallback = "") {
|
|
689
1113
|
return typeof value === "string" ? value : fallback;
|
|
690
1114
|
}
|
|
691
|
-
function
|
|
1115
|
+
function asNullableString3(value) {
|
|
692
1116
|
return typeof value === "string" ? value : null;
|
|
693
1117
|
}
|
|
694
|
-
function
|
|
1118
|
+
function asNumber2(value) {
|
|
695
1119
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
696
1120
|
}
|
|
697
1121
|
var ComplianceEnrollment = class _ComplianceEnrollment {
|
|
@@ -738,29 +1162,114 @@ var ComplianceEnrollment = class _ComplianceEnrollment {
|
|
|
738
1162
|
static fromArray(data) {
|
|
739
1163
|
const regimeConfig = typeof data["regime_config"] === "object" && data["regime_config"] !== null && !Array.isArray(data["regime_config"]) ? data["regime_config"] : null;
|
|
740
1164
|
return new _ComplianceEnrollment(
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
1165
|
+
asNumber2(data["id"]),
|
|
1166
|
+
asNumber2(data["company_id"]),
|
|
1167
|
+
asString3(data["country"]),
|
|
1168
|
+
asString3(data["regime"]),
|
|
1169
|
+
asString3(data["provider"]),
|
|
746
1170
|
toComplianceEnrollmentStatus(data["status"]),
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
1171
|
+
asString3(data["status_label"]),
|
|
1172
|
+
asNullableString3(data["status_error"]),
|
|
1173
|
+
asNullableString3(data["provider_account_id"]),
|
|
1174
|
+
asNullableString3(data["tax_id"]),
|
|
1175
|
+
asNullableString3(data["company_register_id"]),
|
|
1176
|
+
asNullableString3(data["company_register_scheme"]),
|
|
753
1177
|
regimeConfig,
|
|
754
|
-
|
|
755
|
-
|
|
1178
|
+
asNullableString3(data["reporting_start_date"]),
|
|
1179
|
+
asNullableString3(data["notification_email"]),
|
|
756
1180
|
data["auto_send"] === true,
|
|
757
|
-
|
|
758
|
-
|
|
1181
|
+
asNullableString3(data["created_at"]),
|
|
1182
|
+
asNullableString3(data["updated_at"]),
|
|
759
1183
|
toComplianceService(data["service"])
|
|
760
1184
|
);
|
|
761
1185
|
}
|
|
762
1186
|
};
|
|
763
1187
|
|
|
1188
|
+
// src/dto/ComplianceOfferings.ts
|
|
1189
|
+
function asRecord3(value) {
|
|
1190
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
1191
|
+
}
|
|
1192
|
+
function asRecordList2(value) {
|
|
1193
|
+
return Array.isArray(value) ? value.filter((row) => asRecord3(row) !== null) : [];
|
|
1194
|
+
}
|
|
1195
|
+
function asString4(value, fallback = "") {
|
|
1196
|
+
return typeof value === "string" ? value : fallback;
|
|
1197
|
+
}
|
|
1198
|
+
function asNullableString4(value) {
|
|
1199
|
+
return typeof value === "string" ? value : null;
|
|
1200
|
+
}
|
|
1201
|
+
function asNullableNumber2(value) {
|
|
1202
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
1203
|
+
if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
|
|
1204
|
+
return null;
|
|
1205
|
+
}
|
|
1206
|
+
function asStringList(value) {
|
|
1207
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
1208
|
+
}
|
|
1209
|
+
function toRequires(value) {
|
|
1210
|
+
const record = asRecord3(value);
|
|
1211
|
+
return record === null ? null : { country: asString4(record["country"]), service: asString4(record["service"]) };
|
|
1212
|
+
}
|
|
1213
|
+
var ComplianceOfferings = class _ComplianceOfferings {
|
|
1214
|
+
constructor(offerings, coverage, access, accessByCountry) {
|
|
1215
|
+
this.offerings = offerings;
|
|
1216
|
+
this.coverage = coverage;
|
|
1217
|
+
this.access = access;
|
|
1218
|
+
this.accessByCountry = accessByCountry;
|
|
1219
|
+
}
|
|
1220
|
+
offerings;
|
|
1221
|
+
coverage;
|
|
1222
|
+
access;
|
|
1223
|
+
accessByCountry;
|
|
1224
|
+
static fromArray(data) {
|
|
1225
|
+
const offerings = asRecordList2(data["data"]).map((row) => ({
|
|
1226
|
+
country: asString4(row["country"]),
|
|
1227
|
+
service: asString4(row["service"]),
|
|
1228
|
+
currency: asString4(row["currency"], "EUR"),
|
|
1229
|
+
active: row["active"] === true,
|
|
1230
|
+
viaStripe: row["via_stripe"] === true,
|
|
1231
|
+
plan: asNullableString4(row["plan"]),
|
|
1232
|
+
status: asNullableString4(row["status"]),
|
|
1233
|
+
enrolled: row["enrolled"] === true,
|
|
1234
|
+
plans: asRecordList2(row["plans"]).map((plan) => ({
|
|
1235
|
+
key: asString4(plan["key"]),
|
|
1236
|
+
monthlyFee: asNullableNumber2(plan["monthly_fee"]),
|
|
1237
|
+
setupFee: asNullableNumber2(plan["setup_fee"]),
|
|
1238
|
+
usageTiers: Array.isArray(plan["usage_tiers"]) ? plan["usage_tiers"] : [],
|
|
1239
|
+
volumeLimit: plan["volume_limit"] ?? null,
|
|
1240
|
+
contactOnly: plan["contact_only"] === true,
|
|
1241
|
+
checkoutAvailable: plan["checkout_available"] === true,
|
|
1242
|
+
requires: toRequires(plan["requires"]),
|
|
1243
|
+
eligible: plan["eligible"] === true
|
|
1244
|
+
}))
|
|
1245
|
+
}));
|
|
1246
|
+
const coverage = asRecordList2(data["coverage"]).map((row) => ({
|
|
1247
|
+
country: asString4(row["country"]),
|
|
1248
|
+
availability: asString4(row["availability"]),
|
|
1249
|
+
services: asStringList(row["services"]),
|
|
1250
|
+
network: asNullableString4(row["network"]),
|
|
1251
|
+
format: asNullableString4(row["format"]),
|
|
1252
|
+
mandate: asNullableString4(row["mandate"]),
|
|
1253
|
+
support: asString4(row["support"], "yes")
|
|
1254
|
+
}));
|
|
1255
|
+
const access = asRecord3(data["access"]) ?? {};
|
|
1256
|
+
const accessByCountry = {};
|
|
1257
|
+
for (const [country, services] of Object.entries(asRecord3(data["access_by_country"]) ?? {})) {
|
|
1258
|
+
const flags = {};
|
|
1259
|
+
for (const [service, value] of Object.entries(asRecord3(services) ?? {})) {
|
|
1260
|
+
flags[service] = value === true;
|
|
1261
|
+
}
|
|
1262
|
+
accessByCountry[country] = flags;
|
|
1263
|
+
}
|
|
1264
|
+
return new _ComplianceOfferings(
|
|
1265
|
+
offerings,
|
|
1266
|
+
coverage,
|
|
1267
|
+
{ eReporting: access["e_reporting"] === true, eInvoicing: access["e_invoicing"] === true },
|
|
1268
|
+
accessByCountry
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
|
|
764
1273
|
// src/dto/ComplianceEnrollmentPage.ts
|
|
765
1274
|
var ComplianceEnrollmentPage = class _ComplianceEnrollmentPage {
|
|
766
1275
|
constructor(rows, currentPage, perPage, total, lastPage) {
|
|
@@ -806,17 +1315,17 @@ var ComplianceTaxReportState = {
|
|
|
806
1315
|
ERROR: "error",
|
|
807
1316
|
UNKNOWN: "unknown"
|
|
808
1317
|
};
|
|
809
|
-
var
|
|
1318
|
+
var VALUES4 = Object.values(ComplianceTaxReportState);
|
|
810
1319
|
function toComplianceTaxReportState(value) {
|
|
811
1320
|
if (typeof value === "string") {
|
|
812
1321
|
const normalized = value.trim().toLowerCase();
|
|
813
|
-
if (
|
|
1322
|
+
if (VALUES4.includes(normalized)) {
|
|
814
1323
|
return normalized;
|
|
815
1324
|
}
|
|
816
1325
|
}
|
|
817
1326
|
return ComplianceTaxReportState.UNKNOWN;
|
|
818
1327
|
}
|
|
819
|
-
var
|
|
1328
|
+
var DESCRIPTIONS4 = {
|
|
820
1329
|
[ComplianceTaxReportState.NEW]: "Created at provider, waiting for the daily ledger batch.",
|
|
821
1330
|
[ComplianceTaxReportState.SENT]: "Ledger transmitted to PPF.",
|
|
822
1331
|
[ComplianceTaxReportState.ACKNOWLEDGED]: "PPF acknowledged receipt of the ledger.",
|
|
@@ -826,20 +1335,20 @@ var DESCRIPTIONS3 = {
|
|
|
826
1335
|
[ComplianceTaxReportState.UNKNOWN]: "Unknown state."
|
|
827
1336
|
};
|
|
828
1337
|
function describeComplianceTaxReportState(state) {
|
|
829
|
-
return
|
|
1338
|
+
return DESCRIPTIONS4[state];
|
|
830
1339
|
}
|
|
831
1340
|
|
|
832
1341
|
// src/dto/ComplianceTaxReport.ts
|
|
833
|
-
function
|
|
1342
|
+
function asString5(value, fallback = "") {
|
|
834
1343
|
return typeof value === "string" ? value : fallback;
|
|
835
1344
|
}
|
|
836
|
-
function
|
|
1345
|
+
function asNullableString5(value) {
|
|
837
1346
|
return typeof value === "string" ? value : null;
|
|
838
1347
|
}
|
|
839
|
-
function
|
|
1348
|
+
function asNumber3(value) {
|
|
840
1349
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
841
1350
|
}
|
|
842
|
-
function
|
|
1351
|
+
function asNullableNumber3(value) {
|
|
843
1352
|
return typeof value === "number" ? value : null;
|
|
844
1353
|
}
|
|
845
1354
|
function asMoney(value) {
|
|
@@ -890,25 +1399,25 @@ var ComplianceTaxReport = class _ComplianceTaxReport {
|
|
|
890
1399
|
updatedAt;
|
|
891
1400
|
static fromArray(data) {
|
|
892
1401
|
return new _ComplianceTaxReport(
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1402
|
+
asNumber3(data["id"]),
|
|
1403
|
+
asNumber3(data["company_id"]),
|
|
1404
|
+
asNullableNumber3(data["compliance_transaction_id"]),
|
|
1405
|
+
asNullableNumber3(data["ledger_id"]),
|
|
1406
|
+
asString5(data["country"]),
|
|
1407
|
+
asString5(data["regime"]),
|
|
899
1408
|
toComplianceTaxReportState(data["state"]),
|
|
900
|
-
|
|
1409
|
+
asString5(data["state_label"]),
|
|
901
1410
|
data["is_terminal"] === true,
|
|
902
|
-
|
|
903
|
-
|
|
1411
|
+
asNullableString5(data["provider_tax_report_id"]),
|
|
1412
|
+
asNullableString5(data["provider_ledger_id"]),
|
|
904
1413
|
asMoney(data["base_amount"]),
|
|
905
1414
|
asMoney(data["tax_amount"]),
|
|
906
1415
|
asMoney(data["total_amount"]),
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
1416
|
+
asNullableString5(data["refusal_reason"]),
|
|
1417
|
+
asNullableString5(data["registered_at"]),
|
|
1418
|
+
asNullableString5(data["refused_at"]),
|
|
1419
|
+
asNullableString5(data["created_at"]),
|
|
1420
|
+
asNullableString5(data["updated_at"])
|
|
912
1421
|
);
|
|
913
1422
|
}
|
|
914
1423
|
};
|
|
@@ -954,23 +1463,23 @@ var ComplianceDocumentType = {
|
|
|
954
1463
|
CREDIT_NOTE: "credit_note",
|
|
955
1464
|
UNKNOWN: "unknown"
|
|
956
1465
|
};
|
|
957
|
-
var
|
|
1466
|
+
var VALUES5 = Object.values(ComplianceDocumentType);
|
|
958
1467
|
function toComplianceDocumentType(value) {
|
|
959
1468
|
if (typeof value === "string") {
|
|
960
1469
|
const normalized = value.trim().toLowerCase();
|
|
961
|
-
if (
|
|
1470
|
+
if (VALUES5.includes(normalized)) {
|
|
962
1471
|
return normalized;
|
|
963
1472
|
}
|
|
964
1473
|
}
|
|
965
1474
|
return ComplianceDocumentType.UNKNOWN;
|
|
966
1475
|
}
|
|
967
|
-
var
|
|
1476
|
+
var DESCRIPTIONS5 = {
|
|
968
1477
|
[ComplianceDocumentType.INVOICE]: "Commercial invoice.",
|
|
969
1478
|
[ComplianceDocumentType.CREDIT_NOTE]: "Credit note correcting a previously issued invoice.",
|
|
970
1479
|
[ComplianceDocumentType.UNKNOWN]: "Unknown document type."
|
|
971
1480
|
};
|
|
972
1481
|
function describeComplianceDocumentType(type) {
|
|
973
|
-
return
|
|
1482
|
+
return DESCRIPTIONS5[type];
|
|
974
1483
|
}
|
|
975
1484
|
|
|
976
1485
|
// src/enums/ComplianceTransactionState.ts
|
|
@@ -979,27 +1488,30 @@ var ComplianceTransactionState = {
|
|
|
979
1488
|
SENDING: "sending",
|
|
980
1489
|
SUBMITTED: "submitted",
|
|
981
1490
|
ERROR: "error",
|
|
1491
|
+
/** E-invoicing only: the buyer cannot receive e-invoices — nothing was sent or billed; send your regular invoice. */
|
|
1492
|
+
NOT_REQUIRED: "not_required",
|
|
982
1493
|
UNKNOWN: "unknown"
|
|
983
1494
|
};
|
|
984
|
-
var
|
|
1495
|
+
var VALUES6 = Object.values(ComplianceTransactionState);
|
|
985
1496
|
function toComplianceTransactionState(value) {
|
|
986
1497
|
if (typeof value === "string") {
|
|
987
1498
|
const normalized = value.trim().toLowerCase();
|
|
988
|
-
if (
|
|
1499
|
+
if (VALUES6.includes(normalized)) {
|
|
989
1500
|
return normalized;
|
|
990
1501
|
}
|
|
991
1502
|
}
|
|
992
1503
|
return ComplianceTransactionState.UNKNOWN;
|
|
993
1504
|
}
|
|
994
|
-
var
|
|
1505
|
+
var DESCRIPTIONS6 = {
|
|
995
1506
|
[ComplianceTransactionState.PENDING]: "Recorded locally, not yet submitted to the provider.",
|
|
996
1507
|
[ComplianceTransactionState.SENDING]: "Submission to the provider in progress.",
|
|
997
1508
|
[ComplianceTransactionState.SUBMITTED]: "Successfully submitted to the provider.",
|
|
998
1509
|
[ComplianceTransactionState.ERROR]: "Submission failed. See submissionError.",
|
|
1510
|
+
[ComplianceTransactionState.NOT_REQUIRED]: "The buyer cannot receive e-invoices, so none was sent or billed. Send your regular invoice instead.",
|
|
999
1511
|
[ComplianceTransactionState.UNKNOWN]: "Unknown state."
|
|
1000
1512
|
};
|
|
1001
1513
|
function describeComplianceTransactionState(state) {
|
|
1002
|
-
return
|
|
1514
|
+
return DESCRIPTIONS6[state];
|
|
1003
1515
|
}
|
|
1004
1516
|
|
|
1005
1517
|
// src/enums/ComplianceTransactionType.ts
|
|
@@ -1011,17 +1523,17 @@ var ComplianceTransactionType = {
|
|
|
1011
1523
|
CROSSBORDER_INBOUND: "crossborder_inbound",
|
|
1012
1524
|
UNKNOWN: "unknown"
|
|
1013
1525
|
};
|
|
1014
|
-
var
|
|
1526
|
+
var VALUES7 = Object.values(ComplianceTransactionType);
|
|
1015
1527
|
function toComplianceTransactionType(value) {
|
|
1016
1528
|
if (typeof value === "string") {
|
|
1017
1529
|
const normalized = value.trim().toLowerCase();
|
|
1018
|
-
if (
|
|
1530
|
+
if (VALUES7.includes(normalized)) {
|
|
1019
1531
|
return normalized;
|
|
1020
1532
|
}
|
|
1021
1533
|
}
|
|
1022
1534
|
return ComplianceTransactionType.UNKNOWN;
|
|
1023
1535
|
}
|
|
1024
|
-
var
|
|
1536
|
+
var DESCRIPTIONS7 = {
|
|
1025
1537
|
[ComplianceTransactionType.B2C_OUTBOUND]: "Sale to a private person in the merchant country (DGFiP Flux 10.3).",
|
|
1026
1538
|
[ComplianceTransactionType.B2B_DOMESTIC_OUTBOUND]: "Sale to a domestic business (DGFiP Flux 1, Phase 2).",
|
|
1027
1539
|
[ComplianceTransactionType.B2B_DOMESTIC_INBOUND]: "Purchase from a domestic business (DGFiP Flux 1, Phase 2).",
|
|
@@ -1030,17 +1542,17 @@ var DESCRIPTIONS6 = {
|
|
|
1030
1542
|
[ComplianceTransactionType.UNKNOWN]: "Unknown transaction type."
|
|
1031
1543
|
};
|
|
1032
1544
|
function describeComplianceTransactionType(type) {
|
|
1033
|
-
return
|
|
1545
|
+
return DESCRIPTIONS7[type];
|
|
1034
1546
|
}
|
|
1035
1547
|
|
|
1036
1548
|
// src/dto/ComplianceTransaction.ts
|
|
1037
|
-
function
|
|
1549
|
+
function asString6(value, fallback = "") {
|
|
1038
1550
|
return typeof value === "string" ? value : fallback;
|
|
1039
1551
|
}
|
|
1040
|
-
function
|
|
1552
|
+
function asNullableString6(value) {
|
|
1041
1553
|
return typeof value === "string" ? value : null;
|
|
1042
1554
|
}
|
|
1043
|
-
function
|
|
1555
|
+
function asNumber4(value) {
|
|
1044
1556
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
1045
1557
|
}
|
|
1046
1558
|
function asMoney2(value) {
|
|
@@ -1048,34 +1560,55 @@ function asMoney2(value) {
|
|
|
1048
1560
|
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
1049
1561
|
return "0.00";
|
|
1050
1562
|
}
|
|
1051
|
-
function
|
|
1563
|
+
function asNullableNumber4(value) {
|
|
1052
1564
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
1053
1565
|
if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
|
|
1054
1566
|
return null;
|
|
1055
1567
|
}
|
|
1056
|
-
function
|
|
1568
|
+
function asRecord4(value) {
|
|
1057
1569
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
1058
1570
|
}
|
|
1059
|
-
function
|
|
1060
|
-
return Array.isArray(value) ? value.filter((row) =>
|
|
1571
|
+
function asRecordList3(value) {
|
|
1572
|
+
return Array.isArray(value) ? value.filter((row) => asRecord4(row) !== null) : [];
|
|
1573
|
+
}
|
|
1574
|
+
function toInvoicePdf(value) {
|
|
1575
|
+
const pdf = asRecord4(value);
|
|
1576
|
+
if (pdf === null) return null;
|
|
1577
|
+
return {
|
|
1578
|
+
filename: asNullableString6(pdf["filename"]),
|
|
1579
|
+
size: asNullableNumber4(pdf["size"]),
|
|
1580
|
+
attached: pdf["attached"] === true
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
function toEurEquivalent(value) {
|
|
1584
|
+
const eur = asRecord4(value);
|
|
1585
|
+
if (eur === null) return null;
|
|
1586
|
+
return {
|
|
1587
|
+
subtotal: asMoney2(eur["subtotal"]),
|
|
1588
|
+
taxAmount: asMoney2(eur["tax_amount"]),
|
|
1589
|
+
total: asMoney2(eur["total"]),
|
|
1590
|
+
rate: asString6(eur["rate"]),
|
|
1591
|
+
rateDate: asNullableString6(eur["rate_date"]),
|
|
1592
|
+
source: asString6(eur["source"], "ECB")
|
|
1593
|
+
};
|
|
1061
1594
|
}
|
|
1062
1595
|
function toInvoiceLines(providerPayload) {
|
|
1063
|
-
const invoice =
|
|
1064
|
-
return
|
|
1065
|
-
description:
|
|
1066
|
-
quantity:
|
|
1596
|
+
const invoice = asRecord4(asRecord4(providerPayload)?.["invoice"]);
|
|
1597
|
+
return asRecordList3(invoice?.["invoice_lines_attributes"]).map((line) => ({
|
|
1598
|
+
description: asString6(line["description"]),
|
|
1599
|
+
quantity: asNullableNumber4(line["quantity"]),
|
|
1067
1600
|
price: asMoney2(line["price"]),
|
|
1068
|
-
unit:
|
|
1069
|
-
taxes:
|
|
1070
|
-
name:
|
|
1071
|
-
percent:
|
|
1072
|
-
category:
|
|
1073
|
-
comment:
|
|
1601
|
+
unit: asNullableNumber4(line["unit"]),
|
|
1602
|
+
taxes: asRecordList3(line["taxes_attributes"]).map((tax) => ({
|
|
1603
|
+
name: asString6(tax["name"]),
|
|
1604
|
+
percent: asNullableNumber4(tax["percent"]),
|
|
1605
|
+
category: asNullableString6(tax["category"]),
|
|
1606
|
+
comment: asNullableString6(tax["comment"])
|
|
1074
1607
|
}))
|
|
1075
1608
|
}));
|
|
1076
1609
|
}
|
|
1077
1610
|
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 = []) {
|
|
1611
|
+
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
1612
|
this.id = id;
|
|
1080
1613
|
this.companyId = companyId;
|
|
1081
1614
|
this.complianceEnrollmentId = complianceEnrollmentId;
|
|
@@ -1114,6 +1647,14 @@ var ComplianceTransaction = class _ComplianceTransaction {
|
|
|
1114
1647
|
this.amendedDate = amendedDate;
|
|
1115
1648
|
this.providerState = providerState;
|
|
1116
1649
|
this.invoiceLines = invoiceLines;
|
|
1650
|
+
this.documentUrl = documentUrl;
|
|
1651
|
+
this.deliveryCheck = deliveryCheck;
|
|
1652
|
+
this.invoicePdf = invoicePdf;
|
|
1653
|
+
this.internalReference = internalReference;
|
|
1654
|
+
this.pdfUrl = pdfUrl;
|
|
1655
|
+
this.buyerRejected = buyerRejected;
|
|
1656
|
+
this.rejectionReason = rejectionReason;
|
|
1657
|
+
this.eurEquivalent = eurEquivalent;
|
|
1117
1658
|
}
|
|
1118
1659
|
id;
|
|
1119
1660
|
companyId;
|
|
@@ -1153,50 +1694,66 @@ var ComplianceTransaction = class _ComplianceTransaction {
|
|
|
1153
1694
|
amendedDate;
|
|
1154
1695
|
providerState;
|
|
1155
1696
|
invoiceLines;
|
|
1697
|
+
documentUrl;
|
|
1698
|
+
deliveryCheck;
|
|
1699
|
+
invoicePdf;
|
|
1700
|
+
internalReference;
|
|
1701
|
+
pdfUrl;
|
|
1702
|
+
buyerRejected;
|
|
1703
|
+
rejectionReason;
|
|
1704
|
+
eurEquivalent;
|
|
1156
1705
|
get isCreditNote() {
|
|
1157
1706
|
return this.documentType === ComplianceDocumentType.CREDIT_NOTE;
|
|
1158
1707
|
}
|
|
1159
1708
|
static fromArray(data) {
|
|
1160
1709
|
const taxReport = typeof data["tax_report"] === "object" && data["tax_report"] !== null && !Array.isArray(data["tax_report"]) ? ComplianceTaxReport.fromArray(data["tax_report"]) : void 0;
|
|
1161
1710
|
return new _ComplianceTransaction(
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1711
|
+
asNumber4(data["id"]),
|
|
1712
|
+
asNumber4(data["company_id"]),
|
|
1713
|
+
asNumber4(data["compliance_enrollment_id"]),
|
|
1714
|
+
asString6(data["country"]),
|
|
1715
|
+
asString6(data["regime"]),
|
|
1167
1716
|
toComplianceTransactionType(data["transaction_type"]),
|
|
1168
|
-
|
|
1717
|
+
asString6(data["transaction_type_label"]),
|
|
1169
1718
|
toComplianceTransactionState(data["state"]),
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1719
|
+
asString6(data["state_label"]),
|
|
1720
|
+
asString6(data["invoice_number"]),
|
|
1721
|
+
asNullableString6(data["invoice_date"]),
|
|
1722
|
+
asNullableString6(data["due_date"]),
|
|
1723
|
+
asString6(data["currency"], "EUR"),
|
|
1175
1724
|
asMoney2(data["subtotal"]),
|
|
1176
1725
|
asMoney2(data["tax_amount"]),
|
|
1177
1726
|
asMoney2(data["total"]),
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1727
|
+
asNullableString6(data["counterparty_name"]),
|
|
1728
|
+
asNullableString6(data["counterparty_country"]),
|
|
1729
|
+
asNullableString6(data["counterparty_vat_number"]),
|
|
1181
1730
|
data["is_paid"] === true,
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1731
|
+
asNullableString6(data["paid_at"]),
|
|
1732
|
+
asNullableString6(data["provider_invoice_id"]),
|
|
1733
|
+
asNullableString6(data["submission_error"]),
|
|
1734
|
+
asNullableString6(data["reported_at"]),
|
|
1186
1735
|
taxReport,
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1736
|
+
asNullableString6(data["created_at"]),
|
|
1737
|
+
asNullableString6(data["updated_at"]),
|
|
1738
|
+
asNullableString6(data["counterparty_register_id"]),
|
|
1739
|
+
asNullableString6(data["counterparty_address"]),
|
|
1740
|
+
asNullableString6(data["counterparty_city"]),
|
|
1741
|
+
asNullableString6(data["counterparty_postalcode"]),
|
|
1742
|
+
asNullableString6(data["counterparty_email"]),
|
|
1743
|
+
asNullableString6(data["buyer_reference"]),
|
|
1195
1744
|
data["document_type"] === void 0 || data["document_type"] === null ? ComplianceDocumentType.INVOICE : toComplianceDocumentType(data["document_type"]),
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
toInvoiceLines(data["provider_payload"])
|
|
1745
|
+
asNullableString6(data["amended_number"]),
|
|
1746
|
+
asNullableString6(data["amended_date"]),
|
|
1747
|
+
asNullableString6(data["provider_state"]),
|
|
1748
|
+
toInvoiceLines(data["provider_payload"]),
|
|
1749
|
+
asNullableString6(data["document_url"]),
|
|
1750
|
+
asRecord4(data["delivery_check"]),
|
|
1751
|
+
toInvoicePdf(data["invoice_pdf"]),
|
|
1752
|
+
asNullableString6(data["internal_reference"]),
|
|
1753
|
+
asNullableString6(data["pdf_url"]),
|
|
1754
|
+
data["buyer_rejected"] === true,
|
|
1755
|
+
asNullableString6(data["rejection_reason"]),
|
|
1756
|
+
toEurEquivalent(data["eur_equivalent"])
|
|
1200
1757
|
);
|
|
1201
1758
|
}
|
|
1202
1759
|
};
|
|
@@ -1245,48 +1802,21 @@ var ImportResult = class _ImportResult {
|
|
|
1245
1802
|
}
|
|
1246
1803
|
created;
|
|
1247
1804
|
skippedDuplicates;
|
|
1248
|
-
errors;
|
|
1249
|
-
static fromArray(data) {
|
|
1250
|
-
const errors = (Array.isArray(data["errors"]) ? data["errors"] : []).filter(
|
|
1251
|
-
(row) => typeof row === "string"
|
|
1252
|
-
);
|
|
1253
|
-
const num = (value) => typeof value === "number" ? value : 0;
|
|
1254
|
-
return new _ImportResult(num(data["created"]), num(data["skipped_duplicates"]), errors);
|
|
1255
|
-
}
|
|
1256
|
-
};
|
|
1257
|
-
|
|
1258
|
-
// src/enums/PeppolLookupStatus.ts
|
|
1259
|
-
var PeppolLookupStatus = {
|
|
1260
|
-
REACHABLE: "reachable",
|
|
1261
|
-
NOT_REACHABLE: "not_reachable",
|
|
1262
|
-
PENDING: "pending",
|
|
1263
|
-
UNKNOWN: "unknown"
|
|
1264
|
-
};
|
|
1265
|
-
var VALUES7 = Object.values(PeppolLookupStatus);
|
|
1266
|
-
function toPeppolLookupStatus(value) {
|
|
1267
|
-
if (typeof value === "string") {
|
|
1268
|
-
const normalized = value.trim().toLowerCase();
|
|
1269
|
-
if (VALUES7.includes(normalized)) {
|
|
1270
|
-
return normalized;
|
|
1271
|
-
}
|
|
1805
|
+
errors;
|
|
1806
|
+
static fromArray(data) {
|
|
1807
|
+
const errors = (Array.isArray(data["errors"]) ? data["errors"] : []).filter(
|
|
1808
|
+
(row) => typeof row === "string"
|
|
1809
|
+
);
|
|
1810
|
+
const num = (value) => typeof value === "number" ? value : 0;
|
|
1811
|
+
return new _ImportResult(num(data["created"]), num(data["skipped_duplicates"]), errors);
|
|
1272
1812
|
}
|
|
1273
|
-
return PeppolLookupStatus.UNKNOWN;
|
|
1274
|
-
}
|
|
1275
|
-
var DESCRIPTIONS7 = {
|
|
1276
|
-
[PeppolLookupStatus.REACHABLE]: "The participant is registered on Peppol and can receive e-invoices.",
|
|
1277
|
-
[PeppolLookupStatus.NOT_REACHABLE]: "The participant is not registered on Peppol.",
|
|
1278
|
-
[PeppolLookupStatus.PENDING]: "The directory lookup is still resolving \u2014 ask again in a few seconds.",
|
|
1279
|
-
[PeppolLookupStatus.UNKNOWN]: "Unknown lookup status."
|
|
1280
1813
|
};
|
|
1281
|
-
function describePeppolLookupStatus(status) {
|
|
1282
|
-
return DESCRIPTIONS7[status];
|
|
1283
|
-
}
|
|
1284
1814
|
|
|
1285
1815
|
// src/dto/PeppolLookupResult.ts
|
|
1286
|
-
function
|
|
1816
|
+
function asString7(value, fallback = "") {
|
|
1287
1817
|
return typeof value === "string" ? value : fallback;
|
|
1288
1818
|
}
|
|
1289
|
-
function
|
|
1819
|
+
function asNullableString7(value) {
|
|
1290
1820
|
return typeof value === "string" ? value : null;
|
|
1291
1821
|
}
|
|
1292
1822
|
var PeppolLookupResult = class _PeppolLookupResult {
|
|
@@ -1317,21 +1847,21 @@ var PeppolLookupResult = class _PeppolLookupResult {
|
|
|
1317
1847
|
return new _PeppolLookupResult(
|
|
1318
1848
|
toPeppolLookupStatus(data["status"]),
|
|
1319
1849
|
typeof data["reachable"] === "boolean" ? data["reachable"] : null,
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1850
|
+
asString7(data["country"]),
|
|
1851
|
+
asString7(data["scheme"]),
|
|
1852
|
+
asString7(data["id"]),
|
|
1323
1853
|
documentTypes,
|
|
1324
|
-
|
|
1325
|
-
|
|
1854
|
+
asNullableString7(data["transport_type_code"]),
|
|
1855
|
+
asNullableString7(data["checked_at"])
|
|
1326
1856
|
);
|
|
1327
1857
|
}
|
|
1328
1858
|
};
|
|
1329
1859
|
|
|
1330
1860
|
// src/dto/RegistryCompany.ts
|
|
1331
|
-
function
|
|
1861
|
+
function asString8(value, fallback = "") {
|
|
1332
1862
|
return typeof value === "string" ? value : fallback;
|
|
1333
1863
|
}
|
|
1334
|
-
function
|
|
1864
|
+
function asNullableString8(value) {
|
|
1335
1865
|
return typeof value === "string" ? value : null;
|
|
1336
1866
|
}
|
|
1337
1867
|
var RegistryCompany = class _RegistryCompany {
|
|
@@ -1363,27 +1893,27 @@ var RegistryCompany = class _RegistryCompany {
|
|
|
1363
1893
|
country;
|
|
1364
1894
|
static fromArray(data) {
|
|
1365
1895
|
return new _RegistryCompany(
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1896
|
+
asString8(data["org_number"]),
|
|
1897
|
+
asString8(data["company_name"]),
|
|
1898
|
+
asNullableString8(data["organisation_form"]),
|
|
1369
1899
|
data["vat_registered"] === true,
|
|
1370
|
-
|
|
1900
|
+
asNullableString8(data["vat_number"]),
|
|
1371
1901
|
data["enterprise_register"] === true,
|
|
1372
1902
|
data["bankrupt"] === true,
|
|
1373
1903
|
data["under_liquidation"] === true,
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1904
|
+
asNullableString8(data["address"]),
|
|
1905
|
+
asNullableString8(data["postalcode"]),
|
|
1906
|
+
asNullableString8(data["city"]),
|
|
1907
|
+
asString8(data["country"])
|
|
1378
1908
|
);
|
|
1379
1909
|
}
|
|
1380
1910
|
};
|
|
1381
1911
|
|
|
1382
1912
|
// src/dto/RevenueStatistics.ts
|
|
1383
|
-
function
|
|
1913
|
+
function asString9(value, fallback = "") {
|
|
1384
1914
|
return typeof value === "string" ? value : fallback;
|
|
1385
1915
|
}
|
|
1386
|
-
function
|
|
1916
|
+
function asNumber5(value) {
|
|
1387
1917
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
1388
1918
|
}
|
|
1389
1919
|
function asMoney3(value) {
|
|
@@ -1395,7 +1925,7 @@ function asRecords(value) {
|
|
|
1395
1925
|
}
|
|
1396
1926
|
function toTotals(data) {
|
|
1397
1927
|
return {
|
|
1398
|
-
count:
|
|
1928
|
+
count: asNumber5(data["count"]),
|
|
1399
1929
|
subtotal: asMoney3(data["subtotal"]),
|
|
1400
1930
|
taxAmount: asMoney3(data["tax_amount"]),
|
|
1401
1931
|
total: asMoney3(data["total"])
|
|
@@ -1432,35 +1962,35 @@ var RevenueStatistics = class _RevenueStatistics {
|
|
|
1432
1962
|
typeof data["totals"] === "object" && data["totals"] !== null ? data["totals"] : {}
|
|
1433
1963
|
);
|
|
1434
1964
|
const timeSeries = asRecords(data["time_series"]).map((row) => ({
|
|
1435
|
-
bucket:
|
|
1965
|
+
bucket: asString9(row["bucket"]),
|
|
1436
1966
|
...toTotals(row)
|
|
1437
1967
|
}));
|
|
1438
1968
|
const byType = asRecords(data["by_type"]).map((row) => ({
|
|
1439
|
-
type:
|
|
1440
|
-
typeLabel:
|
|
1441
|
-
count:
|
|
1969
|
+
type: asString9(row["type"]),
|
|
1970
|
+
typeLabel: asString9(row["type_label"]),
|
|
1971
|
+
count: asNumber5(row["count"]),
|
|
1442
1972
|
total: asMoney3(row["total"])
|
|
1443
1973
|
}));
|
|
1444
1974
|
const byCountry = asRecords(data["by_country"]).map((row) => ({
|
|
1445
|
-
country:
|
|
1446
|
-
count:
|
|
1975
|
+
country: asString9(row["country"], "unknown"),
|
|
1976
|
+
count: asNumber5(row["count"]),
|
|
1447
1977
|
total: asMoney3(row["total"])
|
|
1448
1978
|
}));
|
|
1449
1979
|
const byState = asRecords(data["by_state"]).map((row) => ({
|
|
1450
|
-
state:
|
|
1451
|
-
stateLabel:
|
|
1452
|
-
count:
|
|
1980
|
+
state: asString9(row["state"]),
|
|
1981
|
+
stateLabel: asString9(row["state_label"]),
|
|
1982
|
+
count: asNumber5(row["count"]),
|
|
1453
1983
|
total: asMoney3(row["total"])
|
|
1454
1984
|
}));
|
|
1455
1985
|
const byCurrency = asRecords(data["by_currency"]).map((row) => ({
|
|
1456
|
-
currency:
|
|
1986
|
+
currency: asString9(row["currency"]),
|
|
1457
1987
|
...toTotals(row)
|
|
1458
1988
|
}));
|
|
1459
1989
|
return new _RevenueStatistics(
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1990
|
+
asString9(range["date_from"]) || void 0,
|
|
1991
|
+
asString9(range["date_to"]) || void 0,
|
|
1992
|
+
asString9(range["interval"]) || void 0,
|
|
1993
|
+
asString9(data["primary_currency"], "EUR"),
|
|
1464
1994
|
data["is_multi_currency"] === true,
|
|
1465
1995
|
totals,
|
|
1466
1996
|
timeSeries,
|
|
@@ -1473,10 +2003,10 @@ var RevenueStatistics = class _RevenueStatistics {
|
|
|
1473
2003
|
};
|
|
1474
2004
|
|
|
1475
2005
|
// src/dto/SireneLookupResult.ts
|
|
1476
|
-
function
|
|
2006
|
+
function asString10(value, fallback = "") {
|
|
1477
2007
|
return typeof value === "string" ? value : fallback;
|
|
1478
2008
|
}
|
|
1479
|
-
function
|
|
2009
|
+
function asNullableString9(value) {
|
|
1480
2010
|
return typeof value === "string" ? value : null;
|
|
1481
2011
|
}
|
|
1482
2012
|
var SireneLookupResult = class _SireneLookupResult {
|
|
@@ -1502,24 +2032,24 @@ var SireneLookupResult = class _SireneLookupResult {
|
|
|
1502
2032
|
postalcode;
|
|
1503
2033
|
static fromArray(data) {
|
|
1504
2034
|
return new _SireneLookupResult(
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
2035
|
+
asString10(data["company_name"]),
|
|
2036
|
+
asString10(data["siren"]),
|
|
2037
|
+
asNullableString9(data["siret"]),
|
|
2038
|
+
asNullableString9(data["naf_code"]),
|
|
2039
|
+
asNullableString9(data["naf_full"]),
|
|
2040
|
+
asNullableString9(data["enterprise_size"]),
|
|
2041
|
+
asNullableString9(data["address"]),
|
|
2042
|
+
asNullableString9(data["city"]),
|
|
2043
|
+
asNullableString9(data["postalcode"])
|
|
1514
2044
|
);
|
|
1515
2045
|
}
|
|
1516
2046
|
};
|
|
1517
2047
|
|
|
1518
2048
|
// src/dto/VatRates.ts
|
|
1519
|
-
function
|
|
2049
|
+
function asString11(value, fallback = "") {
|
|
1520
2050
|
return typeof value === "string" ? value : fallback;
|
|
1521
2051
|
}
|
|
1522
|
-
function
|
|
2052
|
+
function asNumber6(value) {
|
|
1523
2053
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
1524
2054
|
}
|
|
1525
2055
|
var VatRates = class _VatRates {
|
|
@@ -1533,13 +2063,13 @@ var VatRates = class _VatRates {
|
|
|
1533
2063
|
rates;
|
|
1534
2064
|
static fromArray(data) {
|
|
1535
2065
|
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:
|
|
2066
|
+
percent: asNumber6(row["percent"]),
|
|
2067
|
+
category: asString11(row["category"]),
|
|
2068
|
+
labelKey: asString11(row["label_key"]),
|
|
2069
|
+
taxName: asString11(row["tax_name"]),
|
|
1540
2070
|
requiresVatex: row["requires_vatex"] === true
|
|
1541
2071
|
}));
|
|
1542
|
-
return new _VatRates(
|
|
2072
|
+
return new _VatRates(asString11(data["country"]), asString11(data["currency"], "EUR"), rates);
|
|
1543
2073
|
}
|
|
1544
2074
|
};
|
|
1545
2075
|
|
|
@@ -1593,15 +2123,39 @@ var NorwegianOrgNumber = {
|
|
|
1593
2123
|
}
|
|
1594
2124
|
};
|
|
1595
2125
|
|
|
2126
|
+
// src/enums/ComplianceTaxCategory.ts
|
|
2127
|
+
var ComplianceTaxCategory = {
|
|
2128
|
+
STANDARD: "S",
|
|
2129
|
+
REDUCED: "AA",
|
|
2130
|
+
SUPER_REDUCED: "AAA",
|
|
2131
|
+
INTRA_COMMUNITY: "K",
|
|
2132
|
+
EXPORT: "G",
|
|
2133
|
+
REVERSE_CHARGE: "AE",
|
|
2134
|
+
EXEMPT: "E",
|
|
2135
|
+
ZERO: "Z"
|
|
2136
|
+
};
|
|
2137
|
+
var CODES = Object.values(ComplianceTaxCategory);
|
|
2138
|
+
function parseComplianceTaxCategory(value) {
|
|
2139
|
+
if (typeof value !== "string") return null;
|
|
2140
|
+
const code = value.trim().toUpperCase();
|
|
2141
|
+
return CODES.includes(code) ? code : null;
|
|
2142
|
+
}
|
|
2143
|
+
var COMPLIANCE_TAX_CATEGORY_CODES = CODES;
|
|
2144
|
+
|
|
1596
2145
|
// src/endpoints/EReportingEndpoint.ts
|
|
1597
2146
|
var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
1598
2147
|
var NAF_CODE_REGEX = /^\d{2}$/;
|
|
1599
2148
|
var COUNTRY_REGEX = /^[A-Za-z]{2}$/;
|
|
1600
2149
|
var PEPPOL_SCHEME_REGEX = /^\d{4}$/;
|
|
2150
|
+
var INVOICE_PDF_MAX_BYTES = 10 * 1024 * 1024;
|
|
2151
|
+
var INVOICE_PDF_MAGIC = [37, 80, 68, 70, 45];
|
|
1601
2152
|
var INTERVALS = ["day", "week", "month"];
|
|
1602
2153
|
var SERVICES = ["e_reporting", "e_invoicing"];
|
|
1603
2154
|
var ENTERPRISE_SIZES = ["micro", "pme", "eti", "ge"];
|
|
1604
2155
|
var TYPE_OPERATIONS = ["services", "goods", "mixed"];
|
|
2156
|
+
var NOTIFICATION_LANGUAGES = ["de", "en"];
|
|
2157
|
+
var NOTIFICATION_DIGESTS = ["off", "daily", "weekly"];
|
|
2158
|
+
var NOTIFICATION_MAX_RECIPIENTS = 5;
|
|
1605
2159
|
var TRANSACTION_TYPES = [
|
|
1606
2160
|
"b2c_outbound",
|
|
1607
2161
|
"b2b_domestic_outbound",
|
|
@@ -1772,6 +2326,19 @@ var EReportingEndpoint = class {
|
|
|
1772
2326
|
const data = await this.parseJsonResponse(response);
|
|
1773
2327
|
return PeppolLookupResult.fromArray(data);
|
|
1774
2328
|
}
|
|
2329
|
+
/**
|
|
2330
|
+
* What to do with an invoice to this buyer: peppolLookup() turned into an action,
|
|
2331
|
+
* e.g. in a shop checkout when a business buyer enters its number.
|
|
2332
|
+
*
|
|
2333
|
+
* - `einvoice`: create the transaction, Taxora sends the e-invoice;
|
|
2334
|
+
* - `regular_invoice`: the buyer is not on the network — send your regular invoice
|
|
2335
|
+
* yourself (a transaction would end as not_required and is not billed);
|
|
2336
|
+
* - `einvoice_pending`: no definite answer yet — create the transaction anyway,
|
|
2337
|
+
* Taxora asks the directory again before sending.
|
|
2338
|
+
*/
|
|
2339
|
+
async decideDelivery(country, id, scheme) {
|
|
2340
|
+
return ComplianceDeliveryDecision.fromLookup(await this.peppolLookup(country, id, scheme));
|
|
2341
|
+
}
|
|
1775
2342
|
// ---------------------------------------------------------------------
|
|
1776
2343
|
// Transactions
|
|
1777
2344
|
// ---------------------------------------------------------------------
|
|
@@ -1796,6 +2363,9 @@ var EReportingEndpoint = class {
|
|
|
1796
2363
|
this.validateInvoiceLines(input.invoiceLines);
|
|
1797
2364
|
this.validateOptionalCodes(input);
|
|
1798
2365
|
this.validateBuyerFields(input);
|
|
2366
|
+
this.validateInternalReference(input.internalReference);
|
|
2367
|
+
this.validateInvoicePdfFilename(input);
|
|
2368
|
+
this.validateDocument(input);
|
|
1799
2369
|
const body = {
|
|
1800
2370
|
compliance_enrollment_id: input.complianceEnrollmentId,
|
|
1801
2371
|
transaction_type: input.transactionType,
|
|
@@ -1811,23 +2381,104 @@ var EReportingEndpoint = class {
|
|
|
1811
2381
|
const data = await this.parseJsonResponse(response);
|
|
1812
2382
|
return ComplianceTransaction.fromArray(data);
|
|
1813
2383
|
}
|
|
1814
|
-
/**
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
2384
|
+
/**
|
|
2385
|
+
* Issue a credit note for an invoice you sent through Taxora. Everything that must
|
|
2386
|
+
* match the corrected invoice is taken from `original`: enrollment, transaction type,
|
|
2387
|
+
* currency, buyer (name, register id, address, e-mail, VAT number, buyer reference) —
|
|
2388
|
+
* and the reference to the corrected invoice (number and date) is set for you.
|
|
2389
|
+
*
|
|
2390
|
+
* Without invoiceLines and amounts it credits the whole invoice: lines and amounts are
|
|
2391
|
+
* taken over as they are (the document type, not the sign, makes it a credit note).
|
|
2392
|
+
* For a partial credit pass invoiceLines, subtotal, taxAmount and total together.
|
|
2393
|
+
*
|
|
2394
|
+
* Not possible for an invoice that was never sent to the buyer (state not_required)
|
|
2395
|
+
* or for a credit note. A buyer rejected the invoice? Correct it with a credit note and
|
|
2396
|
+
* send a new invoice (`original.buyerRejected`).
|
|
2397
|
+
*/
|
|
2398
|
+
async createCreditNote(original, input) {
|
|
2399
|
+
if (original.documentType === ComplianceDocumentType.CREDIT_NOTE) {
|
|
2400
|
+
throw new TaxoraException("A credit note cannot be credited again; issue a new invoice instead.");
|
|
1818
2401
|
}
|
|
1819
|
-
if (
|
|
1820
|
-
throw new TaxoraException(
|
|
2402
|
+
if (original.state === ComplianceTransactionState.NOT_REQUIRED) {
|
|
2403
|
+
throw new TaxoraException(
|
|
2404
|
+
"The invoice was never sent as an e-invoice (the buyer is not on the network), so there is nothing to credit; correct your regular invoice instead."
|
|
2405
|
+
);
|
|
1821
2406
|
}
|
|
1822
|
-
|
|
1823
|
-
|
|
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));
|
|
2407
|
+
if (original.invoiceNumber === "") {
|
|
2408
|
+
throw new TaxoraException("The original transaction has no invoice number.");
|
|
1829
2409
|
}
|
|
1830
|
-
if (
|
|
2410
|
+
if (original.transactionType === "unknown") {
|
|
2411
|
+
throw new TaxoraException("The original transaction has an unknown transaction type.");
|
|
2412
|
+
}
|
|
2413
|
+
const partial = [input.invoiceLines, input.subtotal, input.taxAmount, input.total];
|
|
2414
|
+
if (![0, 4].includes(partial.filter((value) => value !== void 0).length)) {
|
|
2415
|
+
throw new TaxoraException("For a partial credit pass invoiceLines, subtotal, taxAmount and total together.");
|
|
2416
|
+
}
|
|
2417
|
+
let invoiceLines;
|
|
2418
|
+
let subtotal = input.subtotal;
|
|
2419
|
+
let taxAmount = input.taxAmount;
|
|
2420
|
+
let total = input.total;
|
|
2421
|
+
if (input.invoiceLines !== void 0) {
|
|
2422
|
+
invoiceLines = input.invoiceLines;
|
|
2423
|
+
} else {
|
|
2424
|
+
invoiceLines = original.invoiceLines.map((line) => ({
|
|
2425
|
+
description: line.description,
|
|
2426
|
+
quantity: line.quantity ?? 0,
|
|
2427
|
+
price: Number(line.price),
|
|
2428
|
+
...line.unit !== null ? { unit: line.unit } : {},
|
|
2429
|
+
taxes: line.taxes.map((tax) => ({
|
|
2430
|
+
name: tax.name,
|
|
2431
|
+
percent: tax.percent ?? 0,
|
|
2432
|
+
category: this.storedTaxCategory(tax.category),
|
|
2433
|
+
...tax.comment !== null ? { comment: tax.comment } : {}
|
|
2434
|
+
}))
|
|
2435
|
+
}));
|
|
2436
|
+
if (invoiceLines.length === 0) {
|
|
2437
|
+
throw new TaxoraException(
|
|
2438
|
+
"The original transaction carries no invoice lines; pass invoiceLines and the amounts explicitly."
|
|
2439
|
+
);
|
|
2440
|
+
}
|
|
2441
|
+
subtotal = Number(original.subtotal);
|
|
2442
|
+
taxAmount = Number(original.taxAmount);
|
|
2443
|
+
total = Number(original.total);
|
|
2444
|
+
}
|
|
2445
|
+
const buyer = Object.fromEntries(
|
|
2446
|
+
Object.entries({
|
|
2447
|
+
counterpartyName: original.counterpartyName,
|
|
2448
|
+
counterpartyCountry: original.counterpartyCountry,
|
|
2449
|
+
counterpartyVatNumber: original.counterpartyVatNumber,
|
|
2450
|
+
counterpartyRegisterId: original.counterpartyRegisterId,
|
|
2451
|
+
counterpartyAddress: original.counterpartyAddress,
|
|
2452
|
+
counterpartyCity: original.counterpartyCity,
|
|
2453
|
+
counterpartyPostalcode: original.counterpartyPostalcode,
|
|
2454
|
+
counterpartyEmail: original.counterpartyEmail,
|
|
2455
|
+
buyerReference: original.buyerReference
|
|
2456
|
+
}).filter(([, value]) => value !== null && value !== "")
|
|
2457
|
+
);
|
|
2458
|
+
return this.createTransaction({
|
|
2459
|
+
complianceEnrollmentId: original.complianceEnrollmentId,
|
|
2460
|
+
transactionType: original.transactionType,
|
|
2461
|
+
invoiceNumber: input.creditNoteNumber,
|
|
2462
|
+
invoiceDate: input.invoiceDate ?? /* @__PURE__ */ new Date(),
|
|
2463
|
+
subtotal: subtotal ?? 0,
|
|
2464
|
+
total: total ?? 0,
|
|
2465
|
+
invoiceLines,
|
|
2466
|
+
currency: original.currency,
|
|
2467
|
+
...taxAmount !== void 0 ? { taxAmount } : {},
|
|
2468
|
+
...buyer,
|
|
2469
|
+
isCreditNote: true,
|
|
2470
|
+
amendedNumber: original.invoiceNumber,
|
|
2471
|
+
...original.invoiceDate !== null ? { amendedDate: original.invoiceDate } : {},
|
|
2472
|
+
...input.extraInfo !== void 0 ? { extraInfo: input.extraInfo } : {},
|
|
2473
|
+
...input.internalReference !== void 0 ? { internalReference: input.internalReference } : {},
|
|
2474
|
+
...input.submitNow !== void 0 ? { submitNow: input.submitNow } : {},
|
|
2475
|
+
...input.invoicePdf !== void 0 ? { invoicePdf: input.invoicePdf } : {},
|
|
2476
|
+
...input.invoicePdfFilename !== void 0 ? { invoicePdfFilename: input.invoicePdfFilename } : {}
|
|
2477
|
+
});
|
|
2478
|
+
}
|
|
2479
|
+
/** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type / enrollment / country / search / internal reference / provider state. */
|
|
2480
|
+
async listTransactions(filters = {}) {
|
|
2481
|
+
const params = this.buildTransactionFilterParams(filters);
|
|
1831
2482
|
if (filters.page !== void 0) params.set("page", String(filters.page));
|
|
1832
2483
|
if (filters.perPage !== void 0) params.set("per_page", String(filters.perPage));
|
|
1833
2484
|
const queryString = params.toString();
|
|
@@ -1835,6 +2486,27 @@ var EReportingEndpoint = class {
|
|
|
1835
2486
|
const response = await this.sendRequest("GET", url);
|
|
1836
2487
|
return ComplianceTransactionPage.fromResponse(await this.parseJsonResponse(response));
|
|
1837
2488
|
}
|
|
2489
|
+
/**
|
|
2490
|
+
* All transactions matching the filters as raw CSV for bookkeeping
|
|
2491
|
+
* (semicolon-separated) — the same filters as listTransactions() without
|
|
2492
|
+
* pagination. Columns: invoice_number, internal_reference, invoice_date,
|
|
2493
|
+
* due_date, country, transaction_type, document_type, state, delivery_state,
|
|
2494
|
+
* buyer_rejected, rejection_reason, counterparty_name, counterparty_country,
|
|
2495
|
+
* counterparty_vat_number, currency, subtotal, tax_amount, total, eur_rate,
|
|
2496
|
+
* eur_rate_date, eur_subtotal, eur_tax_amount, eur_total, provider_invoice_id,
|
|
2497
|
+
* created_at. The eur_* columns hold the ECB reference conversion for non-EUR
|
|
2498
|
+
* invoices (empty for EUR). Max 50,000 rows server-side. The body is returned
|
|
2499
|
+
* verbatim, a UTF-8 BOM included when the API sends one.
|
|
2500
|
+
*/
|
|
2501
|
+
async exportTransactionsCsv(filters = {}) {
|
|
2502
|
+
const queryString = this.buildTransactionFilterParams(filters).toString();
|
|
2503
|
+
const url = `${this.baseUrl}/compliance/transactions/export.csv${queryString ? `?${queryString}` : ""}`;
|
|
2504
|
+
const response = await this.sendDownloadRequest(url, "text/csv, application/json");
|
|
2505
|
+
if (!response.ok) {
|
|
2506
|
+
await this.parseJsonResponse(response);
|
|
2507
|
+
}
|
|
2508
|
+
return new TextDecoder("utf-8", { ignoreBOM: true }).decode(await response.arrayBuffer());
|
|
2509
|
+
}
|
|
1838
2510
|
/** Get a single transaction by id (includes its tax report when one exists). */
|
|
1839
2511
|
async getTransaction(id) {
|
|
1840
2512
|
this.validateId(id, "id");
|
|
@@ -1850,6 +2522,8 @@ var EReportingEndpoint = class {
|
|
|
1850
2522
|
if (input.invoiceLines !== void 0) this.validateInvoiceLines(input.invoiceLines);
|
|
1851
2523
|
this.validateOptionalCodes(input);
|
|
1852
2524
|
this.validateBuyerFields(input);
|
|
2525
|
+
this.validateInternalReference(input.internalReference);
|
|
2526
|
+
this.validateInvoicePdfFilename(input);
|
|
1853
2527
|
const body = {};
|
|
1854
2528
|
if (input.transactionType !== void 0) body["transaction_type"] = input.transactionType;
|
|
1855
2529
|
if (input.invoiceNumber !== void 0) body["invoice_number"] = input.invoiceNumber;
|
|
@@ -1884,6 +2558,47 @@ var EReportingEndpoint = class {
|
|
|
1884
2558
|
const data = await this.parseJsonResponse(response);
|
|
1885
2559
|
return ComplianceTransaction.fromArray(data);
|
|
1886
2560
|
}
|
|
2561
|
+
/**
|
|
2562
|
+
* Download the legal e-invoice document of a sent transaction (e-invoicing,
|
|
2563
|
+
* e.g. the EHF/UBL XML for Norway). Available once `documentUrl` on the
|
|
2564
|
+
* transaction is set.
|
|
2565
|
+
*
|
|
2566
|
+
* Errors: 404 (HttpException) for an unknown transaction; 409 (HttpException)
|
|
2567
|
+
* when no document exists (yet) — e.g. e-reporting, not sent, or the network
|
|
2568
|
+
* has not produced it yet (try again in a few minutes); 502 on provider failure.
|
|
2569
|
+
*/
|
|
2570
|
+
async getTransactionDocument(id) {
|
|
2571
|
+
this.validateId(id, "id");
|
|
2572
|
+
const response = await this.sendDownloadRequest(`${this.baseUrl}/compliance/transactions/${id}/document`, "*/*");
|
|
2573
|
+
if (!response.ok) {
|
|
2574
|
+
await this.parseJsonResponse(response);
|
|
2575
|
+
}
|
|
2576
|
+
return {
|
|
2577
|
+
content: new Uint8Array(await response.arrayBuffer()),
|
|
2578
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
2579
|
+
filename: this.filenameFromDisposition(response.headers.get("content-disposition"))
|
|
2580
|
+
};
|
|
2581
|
+
}
|
|
2582
|
+
/**
|
|
2583
|
+
* Download the invoice as a readable PDF — the seller's attached PDF, or the
|
|
2584
|
+
* provider's rendering of the e-invoice. Available once `pdfUrl` on the
|
|
2585
|
+
* transaction is set.
|
|
2586
|
+
*
|
|
2587
|
+
* Errors: 404 (HttpException) for an unknown transaction; 409 (HttpException)
|
|
2588
|
+
* when no PDF exists (yet); 502 on provider failure.
|
|
2589
|
+
*/
|
|
2590
|
+
async getTransactionPdf(id) {
|
|
2591
|
+
this.validateId(id, "id");
|
|
2592
|
+
const response = await this.sendDownloadRequest(`${this.baseUrl}/compliance/transactions/${id}/pdf`, "*/*");
|
|
2593
|
+
if (!response.ok) {
|
|
2594
|
+
await this.parseJsonResponse(response);
|
|
2595
|
+
}
|
|
2596
|
+
return {
|
|
2597
|
+
content: new Uint8Array(await response.arrayBuffer()),
|
|
2598
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
2599
|
+
filename: this.filenameFromDisposition(response.headers.get("content-disposition"))
|
|
2600
|
+
};
|
|
2601
|
+
}
|
|
1887
2602
|
/**
|
|
1888
2603
|
* Bulk-import transactions from a semicolon-separated CSV (one row per invoice
|
|
1889
2604
|
* line; rows sharing an invoice_number are grouped into one transaction).
|
|
@@ -1915,6 +2630,9 @@ var EReportingEndpoint = class {
|
|
|
1915
2630
|
* months and a monthly interval.
|
|
1916
2631
|
*/
|
|
1917
2632
|
async getRevenueStatistics(filters = {}) {
|
|
2633
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2634
|
+
this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
|
|
2635
|
+
}
|
|
1918
2636
|
if (filters.interval !== void 0 && !INTERVALS.includes(filters.interval)) {
|
|
1919
2637
|
throw new TaxoraException(
|
|
1920
2638
|
`Invalid statistics interval "${String(filters.interval)}". Expected one of: ${INTERVALS.join(", ")}.`
|
|
@@ -1926,12 +2644,42 @@ var EReportingEndpoint = class {
|
|
|
1926
2644
|
if (filters.interval !== void 0) params.set("interval", filters.interval);
|
|
1927
2645
|
if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
|
|
1928
2646
|
if (filters.state !== void 0) params.set("state", filters.state);
|
|
2647
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2648
|
+
params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
|
|
2649
|
+
}
|
|
1929
2650
|
const queryString = params.toString();
|
|
1930
2651
|
const url = `${this.baseUrl}/compliance/revenue-statistics${queryString ? `?${queryString}` : ""}`;
|
|
1931
2652
|
const response = await this.sendRequest("GET", url);
|
|
1932
2653
|
const data = await this.parseJsonResponse(response);
|
|
1933
2654
|
return RevenueStatistics.fromArray(data);
|
|
1934
2655
|
}
|
|
2656
|
+
/**
|
|
2657
|
+
* Net and VAT per month, currency and tax rate as raw CSV (semicolon-separated,
|
|
2658
|
+
* computed from the invoice lines) — for reconciling the reported figures with
|
|
2659
|
+
* your VAT return. Same filters as getRevenueStatistics() (without
|
|
2660
|
+
* `interval`); defaults to the last 12 months. The body is returned verbatim,
|
|
2661
|
+
* a UTF-8 BOM included when the API sends one.
|
|
2662
|
+
*/
|
|
2663
|
+
async exportRevenueTaxRatesCsv(filters = {}) {
|
|
2664
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2665
|
+
this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
|
|
2666
|
+
}
|
|
2667
|
+
const params = new URLSearchParams();
|
|
2668
|
+
if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
|
|
2669
|
+
if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
|
|
2670
|
+
if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
|
|
2671
|
+
if (filters.state !== void 0) params.set("state", filters.state);
|
|
2672
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2673
|
+
params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
|
|
2674
|
+
}
|
|
2675
|
+
const queryString = params.toString();
|
|
2676
|
+
const url = `${this.baseUrl}/compliance/revenue-statistics/tax-rates.csv${queryString ? `?${queryString}` : ""}`;
|
|
2677
|
+
const response = await this.sendDownloadRequest(url, "text/csv, application/json");
|
|
2678
|
+
if (!response.ok) {
|
|
2679
|
+
await this.parseJsonResponse(response);
|
|
2680
|
+
}
|
|
2681
|
+
return new TextDecoder("utf-8", { ignoreBOM: true }).decode(await response.arrayBuffer());
|
|
2682
|
+
}
|
|
1935
2683
|
/**
|
|
1936
2684
|
* The canonical VAT rates & DGFiP tax categories for a reporting country
|
|
1937
2685
|
* (the seller's reporting-country rates, not the counterparty's). Falls back
|
|
@@ -1946,12 +2694,19 @@ var EReportingEndpoint = class {
|
|
|
1946
2694
|
const data = await this.parseJsonResponse(response);
|
|
1947
2695
|
return VatRates.fromArray(data);
|
|
1948
2696
|
}
|
|
1949
|
-
/**
|
|
1950
|
-
|
|
2697
|
+
/**
|
|
2698
|
+
* Paginated list of the company's tax reports (newest first), optionally
|
|
2699
|
+
* filtered by state and by country (2-letter ISO code, case-insensitive).
|
|
2700
|
+
*/
|
|
2701
|
+
async listTaxReports(page = 1, perPage = 25, state, country) {
|
|
2702
|
+
if (country !== void 0 && !COUNTRY_REGEX.test(country)) {
|
|
2703
|
+
throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
|
|
2704
|
+
}
|
|
1951
2705
|
const params = new URLSearchParams();
|
|
1952
2706
|
params.set("page", String(page));
|
|
1953
2707
|
params.set("per_page", String(perPage));
|
|
1954
2708
|
if (state !== void 0) params.set("state", state);
|
|
2709
|
+
if (country !== void 0) params.set("country", country.toUpperCase());
|
|
1955
2710
|
const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/tax-reports?${params.toString()}`);
|
|
1956
2711
|
return ComplianceTaxReportPage.fromResponse(await this.parseJsonResponse(response));
|
|
1957
2712
|
}
|
|
@@ -1993,8 +2748,177 @@ var EReportingEndpoint = class {
|
|
|
1993
2748
|
await this.parseJsonResponse(response);
|
|
1994
2749
|
}
|
|
1995
2750
|
// ---------------------------------------------------------------------
|
|
2751
|
+
// Self-service plans (Stripe) — not gated by compliance access
|
|
2752
|
+
// ---------------------------------------------------------------------
|
|
2753
|
+
/**
|
|
2754
|
+
* Bookable compliance plans per country (net EUR prices), every country
|
|
2755
|
+
* Taxora reaches, and which services the company has unlocked. Works for
|
|
2756
|
+
* companies without compliance access.
|
|
2757
|
+
*/
|
|
2758
|
+
async getOfferings() {
|
|
2759
|
+
const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/offerings`);
|
|
2760
|
+
return ComplianceOfferings.fromArray(await this.parseJsonResponse(response));
|
|
2761
|
+
}
|
|
2762
|
+
/**
|
|
2763
|
+
* Start a Stripe Checkout for a plan (see getOfferings() — `checkoutAvailable`).
|
|
2764
|
+
* Open the returned `url` in the browser. After payment Stripe redirects to
|
|
2765
|
+
* the Taxora portal, which activates the plan; call confirmCheckout() with the
|
|
2766
|
+
* session id to activate it from your side (idempotent).
|
|
2767
|
+
*
|
|
2768
|
+
* Errors: 422 (ValidationException) for an unknown or contact-only plan or a
|
|
2769
|
+
* plan that is already active; 502 (HttpException) on a Stripe error.
|
|
2770
|
+
*/
|
|
2771
|
+
async createCheckout(country, plan) {
|
|
2772
|
+
if (!COUNTRY_REGEX.test(country)) {
|
|
2773
|
+
throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
|
|
2774
|
+
}
|
|
2775
|
+
if (plan.trim() === "" || plan.length > 30) {
|
|
2776
|
+
throw new TaxoraException("plan must be a non-empty plan key of at most 30 characters.");
|
|
2777
|
+
}
|
|
2778
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/checkout`, {
|
|
2779
|
+
country: country.toUpperCase(),
|
|
2780
|
+
plan
|
|
2781
|
+
});
|
|
2782
|
+
const data = await this.parseJsonResponse(response);
|
|
2783
|
+
return { id: String(data["id"] ?? ""), url: String(data["url"] ?? "") };
|
|
2784
|
+
}
|
|
2785
|
+
/** Confirm a finished Stripe Checkout and activate its plan. Idempotent. */
|
|
2786
|
+
async confirmCheckout(sessionId) {
|
|
2787
|
+
if (sessionId.trim() === "" || sessionId.length > 255) {
|
|
2788
|
+
throw new TaxoraException("sessionId must be a non-empty string of at most 255 characters.");
|
|
2789
|
+
}
|
|
2790
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/checkout/confirm`, {
|
|
2791
|
+
session_id: sessionId
|
|
2792
|
+
});
|
|
2793
|
+
const data = await this.parseJsonResponse(response);
|
|
2794
|
+
const str = (value) => typeof value === "string" ? value : null;
|
|
2795
|
+
return {
|
|
2796
|
+
active: data["active"] === true,
|
|
2797
|
+
country: str(data["country"]),
|
|
2798
|
+
service: str(data["service"]),
|
|
2799
|
+
plan: str(data["plan"]),
|
|
2800
|
+
status: str(data["status"])
|
|
2801
|
+
};
|
|
2802
|
+
}
|
|
2803
|
+
/**
|
|
2804
|
+
* URL of the Stripe billing portal for the company's compliance plans
|
|
2805
|
+
* (invoices, payment method, cancellation). The portal returns to the Taxora
|
|
2806
|
+
* portal. 404 (HttpException) when no plan was bought via Stripe.
|
|
2807
|
+
*/
|
|
2808
|
+
async createBillingPortalSession() {
|
|
2809
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/billing-portal`);
|
|
2810
|
+
const data = await this.parseJsonResponse(response);
|
|
2811
|
+
return String(data["url"] ?? "");
|
|
2812
|
+
}
|
|
2813
|
+
// ---------------------------------------------------------------------
|
|
2814
|
+
// Notification emails
|
|
2815
|
+
// ---------------------------------------------------------------------
|
|
2816
|
+
/**
|
|
2817
|
+
* The company's opt-in e-reporting / e-invoicing emails: recipients, language,
|
|
2818
|
+
* the "buyer not on Peppol" email and the digest. Readable by every member.
|
|
2819
|
+
*/
|
|
2820
|
+
async getNotificationSettings() {
|
|
2821
|
+
const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/notification-settings`);
|
|
2822
|
+
return this.toNotificationSettings(await this.parseJsonResponse(response));
|
|
2823
|
+
}
|
|
2824
|
+
/**
|
|
2825
|
+
* Change the company's e-reporting / e-invoicing emails; only the provided
|
|
2826
|
+
* fields change. `notOnPeppol` only reports invoices held back after it was
|
|
2827
|
+
* switched on.
|
|
2828
|
+
*
|
|
2829
|
+
* Errors: 422 (ValidationException) when an email is switched on without any
|
|
2830
|
+
* recipient; 403 (HttpException, body code `admin_only` or `read_only_role`)
|
|
2831
|
+
* for users who are not company admins.
|
|
2832
|
+
*/
|
|
2833
|
+
async updateNotificationSettings(input) {
|
|
2834
|
+
const body = {};
|
|
2835
|
+
if (input.recipients !== void 0) {
|
|
2836
|
+
if (!Array.isArray(input.recipients)) {
|
|
2837
|
+
throw new TaxoraException("recipients must be an array of email addresses.");
|
|
2838
|
+
}
|
|
2839
|
+
if (input.recipients.length > NOTIFICATION_MAX_RECIPIENTS) {
|
|
2840
|
+
throw new TaxoraException(`recipients must not contain more than ${NOTIFICATION_MAX_RECIPIENTS} entries.`);
|
|
2841
|
+
}
|
|
2842
|
+
body["recipients"] = input.recipients.map((recipient, index) => {
|
|
2843
|
+
const email = typeof recipient === "string" ? recipient.trim() : recipient;
|
|
2844
|
+
this.validateEmail(email, `recipients[${index}]`);
|
|
2845
|
+
if (email.length > 191) {
|
|
2846
|
+
throw new TaxoraException(`recipients[${index}] must not exceed 191 characters.`);
|
|
2847
|
+
}
|
|
2848
|
+
return email;
|
|
2849
|
+
});
|
|
2850
|
+
}
|
|
2851
|
+
if (input.language !== void 0) {
|
|
2852
|
+
if (!NOTIFICATION_LANGUAGES.includes(input.language)) {
|
|
2853
|
+
throw new TaxoraException(
|
|
2854
|
+
`Invalid language "${String(input.language)}". Expected one of: ${NOTIFICATION_LANGUAGES.join(", ")}.`
|
|
2855
|
+
);
|
|
2856
|
+
}
|
|
2857
|
+
body["language"] = input.language;
|
|
2858
|
+
}
|
|
2859
|
+
if (input.notOnPeppol !== void 0) {
|
|
2860
|
+
if (typeof input.notOnPeppol !== "boolean") {
|
|
2861
|
+
throw new TaxoraException("notOnPeppol must be a boolean.");
|
|
2862
|
+
}
|
|
2863
|
+
body["not_on_peppol"] = input.notOnPeppol;
|
|
2864
|
+
}
|
|
2865
|
+
if (input.digest !== void 0) {
|
|
2866
|
+
if (!NOTIFICATION_DIGESTS.includes(input.digest)) {
|
|
2867
|
+
throw new TaxoraException(
|
|
2868
|
+
`Invalid digest "${String(input.digest)}". Expected one of: ${NOTIFICATION_DIGESTS.join(", ")}.`
|
|
2869
|
+
);
|
|
2870
|
+
}
|
|
2871
|
+
body["digest"] = input.digest;
|
|
2872
|
+
}
|
|
2873
|
+
if (Object.keys(body).length === 0) {
|
|
2874
|
+
throw new TaxoraException("updateNotificationSettings requires at least one field to update.");
|
|
2875
|
+
}
|
|
2876
|
+
const response = await this.sendRequest("PUT", `${this.baseUrl}/compliance/notification-settings`, body);
|
|
2877
|
+
return this.toNotificationSettings(await this.parseJsonResponse(response));
|
|
2878
|
+
}
|
|
2879
|
+
toNotificationSettings(data) {
|
|
2880
|
+
const recipients = Array.isArray(data["recipients"]) ? data["recipients"].filter((r) => typeof r === "string" && r !== "") : [];
|
|
2881
|
+
const digest = data["digest"];
|
|
2882
|
+
return {
|
|
2883
|
+
recipients,
|
|
2884
|
+
language: data["language"] === "en" ? "en" : "de",
|
|
2885
|
+
notOnPeppol: data["not_on_peppol"] === true,
|
|
2886
|
+
digest: digest === "daily" || digest === "weekly" ? digest : "off",
|
|
2887
|
+
digestSentAt: typeof data["digest_sent_at"] === "string" ? data["digest_sent_at"] : null
|
|
2888
|
+
};
|
|
2889
|
+
}
|
|
2890
|
+
// ---------------------------------------------------------------------
|
|
1996
2891
|
// Input validation (fail fast, before any request is sent)
|
|
1997
2892
|
// ---------------------------------------------------------------------
|
|
2893
|
+
/** Validated transaction-list filters as query params (shared by listTransactions and exportTransactionsCsv). */
|
|
2894
|
+
buildTransactionFilterParams(filters) {
|
|
2895
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2896
|
+
this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
|
|
2897
|
+
}
|
|
2898
|
+
if (filters.country !== void 0 && !COUNTRY_REGEX.test(filters.country)) {
|
|
2899
|
+
throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
|
|
2900
|
+
}
|
|
2901
|
+
if (filters.search !== void 0 && filters.search.length > 100) {
|
|
2902
|
+
throw new TaxoraException("search must not exceed 100 characters.");
|
|
2903
|
+
}
|
|
2904
|
+
this.validateInternalReference(filters.internalReference);
|
|
2905
|
+
if (filters.providerState !== void 0 && !/^[a-z_]{1,30}$/.test(filters.providerState)) {
|
|
2906
|
+
throw new TaxoraException('providerState must be 1-30 lowercase letters or underscores (e.g. "refused").');
|
|
2907
|
+
}
|
|
2908
|
+
const params = new URLSearchParams();
|
|
2909
|
+
if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
|
|
2910
|
+
if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
|
|
2911
|
+
if (filters.state !== void 0) params.set("state", filters.state);
|
|
2912
|
+
if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
|
|
2913
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2914
|
+
params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
|
|
2915
|
+
}
|
|
2916
|
+
if (filters.country !== void 0) params.set("country", filters.country.toUpperCase());
|
|
2917
|
+
if (filters.search !== void 0) params.set("search", filters.search);
|
|
2918
|
+
if (filters.internalReference !== void 0) params.set("internal_reference", filters.internalReference);
|
|
2919
|
+
if (filters.providerState !== void 0) params.set("provider_state", filters.providerState);
|
|
2920
|
+
return params;
|
|
2921
|
+
}
|
|
1998
2922
|
validateEnrollmentInput(input) {
|
|
1999
2923
|
if (input.siret === void 0 && input.siren === void 0) {
|
|
2000
2924
|
throw new TaxoraException("Either siret or siren is required.");
|
|
@@ -2120,14 +3044,101 @@ var EReportingEndpoint = class {
|
|
|
2120
3044
|
if (line.taxes.length === 0) {
|
|
2121
3045
|
throw new TaxoraException(`invoiceLines[${index}].taxes must contain at least one tax.`);
|
|
2122
3046
|
}
|
|
3047
|
+
line.taxes.forEach((tax, taxIndex) => {
|
|
3048
|
+
const where = `invoiceLines[${index}].taxes[${taxIndex}]`;
|
|
3049
|
+
const category = parseComplianceTaxCategory(tax.category);
|
|
3050
|
+
if (category === null) {
|
|
3051
|
+
throw new TaxoraException(
|
|
3052
|
+
`${where}.category "${String(tax.category)}" is unknown. Valid: ${COMPLIANCE_TAX_CATEGORY_CODES.join(", ")}.`
|
|
3053
|
+
);
|
|
3054
|
+
}
|
|
3055
|
+
if (!Number.isFinite(tax.percent) || tax.percent < 0 || tax.percent > 100) {
|
|
3056
|
+
throw new TaxoraException(`${where}.percent must be between 0 and 100.`);
|
|
3057
|
+
}
|
|
3058
|
+
if (category === "E" && (tax.comment === void 0 || tax.comment.trim() === "")) {
|
|
3059
|
+
throw new TaxoraException(
|
|
3060
|
+
`${where}: an exempt (category E) tax needs the exemption code (VATEX) as comment.`
|
|
3061
|
+
);
|
|
3062
|
+
}
|
|
3063
|
+
if (tax.comment !== void 0 && tax.comment.length > 255) {
|
|
3064
|
+
throw new TaxoraException(`${where}.comment must not exceed 255 characters.`);
|
|
3065
|
+
}
|
|
3066
|
+
});
|
|
2123
3067
|
});
|
|
2124
3068
|
}
|
|
3069
|
+
validateInvoicePdfFilename(input) {
|
|
3070
|
+
if (input.invoicePdfFilename === void 0) return;
|
|
3071
|
+
if (input.invoicePdfFilename.length > 120) {
|
|
3072
|
+
throw new TaxoraException("invoicePdfFilename must not exceed 120 characters.");
|
|
3073
|
+
}
|
|
3074
|
+
if (input.invoicePdf === void 0) {
|
|
3075
|
+
throw new TaxoraException("invoicePdfFilename requires invoicePdf.");
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
validateInternalReference(internalReference) {
|
|
3079
|
+
if (internalReference !== void 0 && internalReference.length > 100) {
|
|
3080
|
+
throw new TaxoraException("internalReference must not exceed 100 characters.");
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
/**
|
|
3084
|
+
* Relations that cannot be right: a due date before the invoice date, a credit note without (or an
|
|
3085
|
+
* invoice with) a corrected invoice, a credit note that reuses the number it corrects, a corrected
|
|
3086
|
+
* invoice dated after the credit note.
|
|
3087
|
+
*/
|
|
3088
|
+
validateDocument(input) {
|
|
3089
|
+
for (const [field, value] of [
|
|
3090
|
+
["subtotal", input.subtotal],
|
|
3091
|
+
["taxAmount", input.taxAmount],
|
|
3092
|
+
["total", input.total]
|
|
3093
|
+
]) {
|
|
3094
|
+
if (value !== void 0 && !Number.isFinite(Number(value))) {
|
|
3095
|
+
throw new TaxoraException(`${field} must be a finite number.`);
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
for (const line of input.invoiceLines ?? []) {
|
|
3099
|
+
if (!Number.isFinite(line.quantity) || !Number.isFinite(line.price)) {
|
|
3100
|
+
throw new TaxoraException("Every invoice line needs a numeric quantity and price.");
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
const invoiceDate = this.formatDate(input.invoiceDate);
|
|
3104
|
+
if (input.dueDate !== void 0 && this.formatDate(input.dueDate) < invoiceDate) {
|
|
3105
|
+
throw new TaxoraException("dueDate must not be before invoiceDate.");
|
|
3106
|
+
}
|
|
3107
|
+
if (input.counterpartyVatNumber !== void 0 && input.counterpartyVatNumber.length > 50) {
|
|
3108
|
+
throw new TaxoraException("counterpartyVatNumber must not exceed 50 characters.");
|
|
3109
|
+
}
|
|
3110
|
+
const amended = input.amendedNumber?.trim() ?? "";
|
|
3111
|
+
if (input.isCreditNote === true && amended === "") {
|
|
3112
|
+
throw new TaxoraException("A credit note needs amendedNumber, the number of the invoice it corrects.");
|
|
3113
|
+
}
|
|
3114
|
+
if (input.isCreditNote !== true && amended !== "") {
|
|
3115
|
+
throw new TaxoraException("amendedNumber is only for credit notes; set isCreditNote to issue one.");
|
|
3116
|
+
}
|
|
3117
|
+
if (amended !== "" && amended === input.invoiceNumber.trim()) {
|
|
3118
|
+
throw new TaxoraException(
|
|
3119
|
+
"A credit note needs its own number; it cannot reuse the number of the invoice it corrects."
|
|
3120
|
+
);
|
|
3121
|
+
}
|
|
3122
|
+
if (input.amendedDate !== void 0 && this.formatDate(input.amendedDate) > invoiceDate) {
|
|
3123
|
+
throw new TaxoraException("amendedDate (the corrected invoice) must not be after the credit note date.");
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
/** A category read back from a stored invoice; one that is not a known code cannot be re-sent. */
|
|
3127
|
+
storedTaxCategory(category) {
|
|
3128
|
+
const parsed = parseComplianceTaxCategory(category);
|
|
3129
|
+
if (parsed === null) {
|
|
3130
|
+
throw new TaxoraException(
|
|
3131
|
+
`The original invoice has an unknown tax category "${String(category)}"; pass invoiceLines and the amounts explicitly.`
|
|
3132
|
+
);
|
|
3133
|
+
}
|
|
3134
|
+
return parsed;
|
|
3135
|
+
}
|
|
2125
3136
|
validateOptionalCodes(input) {
|
|
2126
|
-
if (input.currency !== void 0 && input.currency
|
|
2127
|
-
throw new TaxoraException("currency must be
|
|
3137
|
+
if (input.currency !== void 0 && !/^[A-Za-z]{3}$/.test(input.currency)) {
|
|
3138
|
+
throw new TaxoraException("currency must be an ISO 4217 code, three letters (e.g. NOK, EUR).");
|
|
2128
3139
|
}
|
|
2129
|
-
if (input.counterpartyCountry !== void 0 && input.counterpartyCountry
|
|
2130
|
-
throw new TaxoraException("counterpartyCountry must be
|
|
3140
|
+
if (input.counterpartyCountry !== void 0 && !/^[A-Za-z]{2}$/.test(input.counterpartyCountry)) {
|
|
3141
|
+
throw new TaxoraException("counterpartyCountry must be an ISO 3166-1 alpha-2 code, two letters (e.g. NO).");
|
|
2131
3142
|
}
|
|
2132
3143
|
}
|
|
2133
3144
|
/**
|
|
@@ -2170,7 +3181,7 @@ var EReportingEndpoint = class {
|
|
|
2170
3181
|
const taxBuilt = {
|
|
2171
3182
|
name: tax.name,
|
|
2172
3183
|
percent: tax.percent,
|
|
2173
|
-
category: tax.category
|
|
3184
|
+
category: parseComplianceTaxCategory(tax.category) ?? tax.category
|
|
2174
3185
|
};
|
|
2175
3186
|
if (tax.comment !== void 0) taxBuilt["comment"] = tax.comment;
|
|
2176
3187
|
return taxBuilt;
|
|
@@ -2183,10 +3194,10 @@ var EReportingEndpoint = class {
|
|
|
2183
3194
|
/** Applies the optional scalar fields shared by create and update. */
|
|
2184
3195
|
applyOptionalTransactionFields(body, input) {
|
|
2185
3196
|
if (input.dueDate !== void 0) body["due_date"] = this.formatDate(input.dueDate);
|
|
2186
|
-
if (input.currency !== void 0) body["currency"] = input.currency;
|
|
3197
|
+
if (input.currency !== void 0) body["currency"] = input.currency.toUpperCase();
|
|
2187
3198
|
if (input.taxAmount !== void 0) body["tax_amount"] = input.taxAmount;
|
|
2188
3199
|
if (input.counterpartyName !== void 0) body["counterparty_name"] = input.counterpartyName;
|
|
2189
|
-
if (input.counterpartyCountry !== void 0) body["counterparty_country"] = input.counterpartyCountry;
|
|
3200
|
+
if (input.counterpartyCountry !== void 0) body["counterparty_country"] = input.counterpartyCountry.toUpperCase();
|
|
2190
3201
|
if (input.counterpartyVatNumber !== void 0) body["counterparty_vat_number"] = input.counterpartyVatNumber;
|
|
2191
3202
|
if (input.providerContactId !== void 0) body["provider_contact_id"] = input.providerContactId;
|
|
2192
3203
|
if (input.paymentMethod !== void 0) body["payment_method"] = input.paymentMethod;
|
|
@@ -2203,6 +3214,29 @@ var EReportingEndpoint = class {
|
|
|
2203
3214
|
if (input.amendedNumber !== void 0) body["amended_number"] = input.amendedNumber;
|
|
2204
3215
|
if (input.amendedDate !== void 0) body["amended_date"] = this.formatDate(input.amendedDate);
|
|
2205
3216
|
if (input.extraInfo !== void 0) body["extra_info"] = input.extraInfo;
|
|
3217
|
+
if (input.internalReference !== void 0) body["internal_reference"] = input.internalReference;
|
|
3218
|
+
if (input.invoicePdf !== void 0) body["invoice_pdf"] = this.encodeInvoicePdf(input.invoicePdf);
|
|
3219
|
+
if (input.invoicePdfFilename !== void 0) body["invoice_pdf_filename"] = input.invoicePdfFilename;
|
|
3220
|
+
}
|
|
3221
|
+
/** Bytes → base64 (validated as a PDF of at most 10 MB); strings are taken as already base64. */
|
|
3222
|
+
encodeInvoicePdf(pdf) {
|
|
3223
|
+
if (typeof pdf === "string") {
|
|
3224
|
+
if (pdf.trim() === "") {
|
|
3225
|
+
throw new TaxoraException("invoicePdf must not be empty.");
|
|
3226
|
+
}
|
|
3227
|
+
return pdf;
|
|
3228
|
+
}
|
|
3229
|
+
const bytes = pdf instanceof Uint8Array ? pdf : new Uint8Array(pdf);
|
|
3230
|
+
if (bytes.byteLength === 0) {
|
|
3231
|
+
throw new TaxoraException("invoicePdf must not be empty.");
|
|
3232
|
+
}
|
|
3233
|
+
if (bytes.byteLength > INVOICE_PDF_MAX_BYTES) {
|
|
3234
|
+
throw new TaxoraException("invoicePdf must not exceed 10 MB.");
|
|
3235
|
+
}
|
|
3236
|
+
if (!INVOICE_PDF_MAGIC.every((byte, index) => bytes[index] === byte)) {
|
|
3237
|
+
throw new TaxoraException('invoicePdf must be a PDF file (content must start with "%PDF-").');
|
|
3238
|
+
}
|
|
3239
|
+
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
|
|
2206
3240
|
}
|
|
2207
3241
|
// ---------------------------------------------------------------------
|
|
2208
3242
|
// HTTP plumbing (kept in sync with SmartEnrichmentEndpoint)
|
|
@@ -2226,6 +3260,30 @@ var EReportingEndpoint = class {
|
|
|
2226
3260
|
const send = () => this.httpClient.request(method, url, options);
|
|
2227
3261
|
return method === "GET" ? withResponseRetries(this.retryPolicy, "e-reporting request", send) : send();
|
|
2228
3262
|
}
|
|
3263
|
+
/** GET for a non-JSON download (CSV / document): no JSON Content-Type, retried like other GETs. */
|
|
3264
|
+
async sendDownloadRequest(url, accept) {
|
|
3265
|
+
const headers = this.buildHeaders(false);
|
|
3266
|
+
headers["Accept"] = accept;
|
|
3267
|
+
return withResponseRetries(
|
|
3268
|
+
this.retryPolicy,
|
|
3269
|
+
"e-reporting download",
|
|
3270
|
+
() => this.httpClient.request("GET", url, { headers })
|
|
3271
|
+
);
|
|
3272
|
+
}
|
|
3273
|
+
/** `attachment; filename="x.xml"` (or RFC 5987 `filename*=UTF-8''x.xml`) → "x.xml". */
|
|
3274
|
+
filenameFromDisposition(header) {
|
|
3275
|
+
if (header === null) return null;
|
|
3276
|
+
const extended = /filename\*\s*=\s*(?:UTF-8|utf-8)''([^;]+)/.exec(header);
|
|
3277
|
+
if (extended?.[1] !== void 0) {
|
|
3278
|
+
try {
|
|
3279
|
+
return decodeURIComponent(extended[1].trim());
|
|
3280
|
+
} catch {
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
const plain = /filename\s*=\s*"([^"]*)"|filename\s*=\s*([^;]+)/.exec(header);
|
|
3284
|
+
const name = (plain?.[1] ?? plain?.[2])?.trim();
|
|
3285
|
+
return name !== void 0 && name !== "" ? name : null;
|
|
3286
|
+
}
|
|
2229
3287
|
/** Multipart upload: Content-Type is left to fetch so the boundary is set correctly. */
|
|
2230
3288
|
async sendMultipartRequest(method, url, form) {
|
|
2231
3289
|
return this.httpClient.request(method, url, { headers: this.buildHeaders(false), body: form });
|
|
@@ -2807,13 +3865,13 @@ var SmartEnrichmentHistoryPage = class _SmartEnrichmentHistoryPage {
|
|
|
2807
3865
|
};
|
|
2808
3866
|
|
|
2809
3867
|
// src/dto/SmartEnrichmentStatistics.ts
|
|
2810
|
-
function
|
|
3868
|
+
function asString12(value, fallback = "") {
|
|
2811
3869
|
return typeof value === "string" ? value : fallback;
|
|
2812
3870
|
}
|
|
2813
|
-
function
|
|
3871
|
+
function asNumber7(value) {
|
|
2814
3872
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
2815
3873
|
}
|
|
2816
|
-
function
|
|
3874
|
+
function asRecord5(value) {
|
|
2817
3875
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2818
3876
|
}
|
|
2819
3877
|
function asRecords2(value) {
|
|
@@ -2836,39 +3894,39 @@ var SmartEnrichmentStatistics = class _SmartEnrichmentStatistics {
|
|
|
2836
3894
|
byOutcome;
|
|
2837
3895
|
confidenceBuckets;
|
|
2838
3896
|
static fromArray(data) {
|
|
2839
|
-
const range =
|
|
2840
|
-
const totals =
|
|
3897
|
+
const range = asRecord5(data["range"]);
|
|
3898
|
+
const totals = asRecord5(data["totals"]);
|
|
2841
3899
|
const timeSeries = asRecords2(data["time_series"]).map((row) => ({
|
|
2842
|
-
bucket:
|
|
2843
|
-
jobs:
|
|
2844
|
-
items:
|
|
2845
|
-
found:
|
|
3900
|
+
bucket: asString12(row["bucket"]),
|
|
3901
|
+
jobs: asNumber7(row["jobs"]),
|
|
3902
|
+
items: asNumber7(row["items"]),
|
|
3903
|
+
found: asNumber7(row["found"])
|
|
2846
3904
|
}));
|
|
2847
3905
|
const bySource = asRecords2(data["by_source"]).map((row) => ({
|
|
2848
|
-
source:
|
|
2849
|
-
jobs:
|
|
2850
|
-
found:
|
|
3906
|
+
source: asString12(row["source"]),
|
|
3907
|
+
jobs: asNumber7(row["jobs"]),
|
|
3908
|
+
found: asNumber7(row["found"])
|
|
2851
3909
|
}));
|
|
2852
3910
|
const byOutcome = asRecords2(data["by_outcome"]).map((row) => ({
|
|
2853
|
-
outcome:
|
|
2854
|
-
count:
|
|
3911
|
+
outcome: asString12(row["outcome"]),
|
|
3912
|
+
count: asNumber7(row["count"])
|
|
2855
3913
|
}));
|
|
2856
3914
|
const confidenceBuckets = asRecords2(data["confidence_buckets"]).map((row) => ({
|
|
2857
|
-
bucket:
|
|
2858
|
-
count:
|
|
3915
|
+
bucket: asString12(row["bucket"]),
|
|
3916
|
+
count: asNumber7(row["count"])
|
|
2859
3917
|
}));
|
|
2860
3918
|
return new _SmartEnrichmentStatistics(
|
|
2861
3919
|
{
|
|
2862
|
-
dateFrom:
|
|
2863
|
-
dateTo:
|
|
2864
|
-
interval:
|
|
3920
|
+
dateFrom: asString12(range["date_from"]),
|
|
3921
|
+
dateTo: asString12(range["date_to"]),
|
|
3922
|
+
interval: asString12(range["interval"])
|
|
2865
3923
|
},
|
|
2866
3924
|
{
|
|
2867
|
-
jobs:
|
|
2868
|
-
items:
|
|
2869
|
-
found:
|
|
2870
|
-
foundRate:
|
|
2871
|
-
avgConfidence:
|
|
3925
|
+
jobs: asNumber7(totals["jobs"]),
|
|
3926
|
+
items: asNumber7(totals["items"]),
|
|
3927
|
+
found: asNumber7(totals["found"]),
|
|
3928
|
+
foundRate: asNumber7(totals["found_rate"]),
|
|
3929
|
+
avgConfidence: asNumber7(totals["avg_confidence"])
|
|
2872
3930
|
},
|
|
2873
3931
|
timeSeries,
|
|
2874
3932
|
bySource,
|
|
@@ -4148,7 +5206,7 @@ var AuthRetryHttpClient = class {
|
|
|
4148
5206
|
};
|
|
4149
5207
|
|
|
4150
5208
|
// src/version.ts
|
|
4151
|
-
var SDK_VERSION = "1.
|
|
5209
|
+
var SDK_VERSION = "1.9.0";
|
|
4152
5210
|
|
|
4153
5211
|
// src/http/SdkVersionHttpClient.ts
|
|
4154
5212
|
var SDK_VERSION_HEADER = "X-Taxora-SDK-Version";
|
|
@@ -4175,6 +5233,7 @@ var TaxoraClient = class {
|
|
|
4175
5233
|
company;
|
|
4176
5234
|
smartEnrichment;
|
|
4177
5235
|
eReporting;
|
|
5236
|
+
billing;
|
|
4178
5237
|
baseUrl;
|
|
4179
5238
|
constructor(options) {
|
|
4180
5239
|
const environment = options.environment ?? Environment.SANDBOX;
|
|
@@ -4195,6 +5254,7 @@ var TaxoraClient = class {
|
|
|
4195
5254
|
retryPolicy
|
|
4196
5255
|
);
|
|
4197
5256
|
this.eReporting = new EReportingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
|
|
5257
|
+
this.billing = new BillingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
|
|
4198
5258
|
}
|
|
4199
5259
|
};
|
|
4200
5260
|
|
|
@@ -4216,6 +5276,68 @@ var Language = {
|
|
|
4216
5276
|
GERMAN: "de"
|
|
4217
5277
|
};
|
|
4218
5278
|
|
|
5279
|
+
// src/support/calculateInvoiceTotals.ts
|
|
5280
|
+
function round2(value) {
|
|
5281
|
+
const magnitude = Math.round(Number((Math.abs(value) * 100).toPrecision(15)));
|
|
5282
|
+
return (value < 0 ? -magnitude : magnitude) / 100;
|
|
5283
|
+
}
|
|
5284
|
+
function calculateInvoiceTotals(lines, options = {}) {
|
|
5285
|
+
let net = 0;
|
|
5286
|
+
let tax = 0;
|
|
5287
|
+
for (const line of lines) {
|
|
5288
|
+
const lineNet = round2(line.quantity * line.price);
|
|
5289
|
+
const percent = line.taxes.reduce((sum, t) => sum + t.percent, 0);
|
|
5290
|
+
net += lineNet;
|
|
5291
|
+
tax += round2(lineNet * percent / 100);
|
|
5292
|
+
}
|
|
5293
|
+
const subtotal = round2(net);
|
|
5294
|
+
const taxAmount = round2(tax);
|
|
5295
|
+
const total = round2(subtotal + taxAmount);
|
|
5296
|
+
let roundingDifference = 0;
|
|
5297
|
+
if (options.expectedTotal !== void 0) {
|
|
5298
|
+
if (!Number.isFinite(options.expectedTotal)) {
|
|
5299
|
+
throw new Error("The expected total must be numeric.");
|
|
5300
|
+
}
|
|
5301
|
+
const tolerance = options.tolerance ?? 0.05;
|
|
5302
|
+
roundingDifference = round2(options.expectedTotal - total);
|
|
5303
|
+
if (Math.abs(roundingDifference) > tolerance) {
|
|
5304
|
+
throw new Error(
|
|
5305
|
+
`The expected total ${options.expectedTotal.toFixed(2)} differs from the invoice lines (${total.toFixed(2)}) by ${roundingDifference.toFixed(2)}, more than the rounding tolerance of ${tolerance.toFixed(2)}. Check quantities, prices and tax rates.`
|
|
5306
|
+
);
|
|
5307
|
+
}
|
|
5308
|
+
}
|
|
5309
|
+
return { subtotal, taxAmount, total, roundingDifference };
|
|
5310
|
+
}
|
|
5311
|
+
|
|
5312
|
+
// src/support/complianceTax.ts
|
|
5313
|
+
var ComplianceTax = {
|
|
5314
|
+
/**
|
|
5315
|
+
* A taxed rate above 0 %, category S (Norway: 25 / 15 / 12 % are all S). Where a country files reduced rates
|
|
5316
|
+
* under their own category (France: AA / AAA), pass it as `category`.
|
|
5317
|
+
*/
|
|
5318
|
+
rate(percent, name = "VAT", category = ComplianceTaxCategory.STANDARD) {
|
|
5319
|
+
if (!Number.isFinite(percent) || percent <= 0) {
|
|
5320
|
+
throw new Error("rate() needs a percent above 0; for 0 % use zeroRated(), exempt() or reverseCharge().");
|
|
5321
|
+
}
|
|
5322
|
+
return { name, percent, category };
|
|
5323
|
+
},
|
|
5324
|
+
/** 0 % but taxable (category Z). */
|
|
5325
|
+
zeroRated(name = "VAT") {
|
|
5326
|
+
return { name, percent: 0, category: ComplianceTaxCategory.ZERO };
|
|
5327
|
+
},
|
|
5328
|
+
/** Exempt from VAT (category E); the VATEX exemption code is mandatory. */
|
|
5329
|
+
exempt(exemptionCode, name = "VAT") {
|
|
5330
|
+
if (exemptionCode.trim() === "") {
|
|
5331
|
+
throw new Error("An exempt (category E) tax needs the exemption code (VATEX).");
|
|
5332
|
+
}
|
|
5333
|
+
return { name, percent: 0, category: ComplianceTaxCategory.EXEMPT, comment: exemptionCode };
|
|
5334
|
+
},
|
|
5335
|
+
/** Reverse charge (category AE); the mandatory legal text is added by Taxora. */
|
|
5336
|
+
reverseCharge(name = "VAT") {
|
|
5337
|
+
return { name, percent: 0, category: ComplianceTaxCategory.REVERSE_CHARGE };
|
|
5338
|
+
}
|
|
5339
|
+
};
|
|
5340
|
+
|
|
4219
5341
|
// src/webhooks/WebhookSignature.ts
|
|
4220
5342
|
var import_node_crypto = require("crypto");
|
|
4221
5343
|
var WEBHOOK_SIGNATURE_HEADER = "Taxora-Signature";
|
|
@@ -4334,19 +5456,30 @@ function toBytes(rawBody) {
|
|
|
4334
5456
|
"invalid_body"
|
|
4335
5457
|
);
|
|
4336
5458
|
}
|
|
5459
|
+
|
|
5460
|
+
// src/webhooks/WebhookEvent.ts
|
|
5461
|
+
function isTransactionBuyerRejected(object) {
|
|
5462
|
+
return object.buyer_rejected === true;
|
|
5463
|
+
}
|
|
4337
5464
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4338
5465
|
0 && (module.exports = {
|
|
4339
5466
|
ApiVersion,
|
|
4340
5467
|
AuthEndpoint,
|
|
4341
5468
|
AuthRetryHttpClient,
|
|
4342
5469
|
AuthenticationException,
|
|
5470
|
+
BillingEndpoint,
|
|
4343
5471
|
CompanyAddress,
|
|
4344
5472
|
CompanyEndpoint,
|
|
5473
|
+
ComplianceDeliveryAction,
|
|
5474
|
+
ComplianceDeliveryDecision,
|
|
4345
5475
|
ComplianceDocumentType,
|
|
4346
5476
|
ComplianceEnrollment,
|
|
4347
5477
|
ComplianceEnrollmentPage,
|
|
4348
5478
|
ComplianceEnrollmentStatus,
|
|
5479
|
+
ComplianceOfferings,
|
|
4349
5480
|
ComplianceService,
|
|
5481
|
+
ComplianceTax,
|
|
5482
|
+
ComplianceTaxCategory,
|
|
4350
5483
|
ComplianceTaxReport,
|
|
4351
5484
|
ComplianceTaxReportPage,
|
|
4352
5485
|
ComplianceTaxReportState,
|
|
@@ -4355,6 +5488,8 @@ function toBytes(rawBody) {
|
|
|
4355
5488
|
ComplianceTransactionState,
|
|
4356
5489
|
ComplianceTransactionType,
|
|
4357
5490
|
DEFAULT_WEBHOOK_TOLERANCE_SECONDS,
|
|
5491
|
+
EInvoicingQuota,
|
|
5492
|
+
EInvoicingQuotaPeriod,
|
|
4358
5493
|
EReportingEndpoint,
|
|
4359
5494
|
Environment,
|
|
4360
5495
|
FetchHttpClient,
|
|
@@ -4389,6 +5524,8 @@ function toBytes(rawBody) {
|
|
|
4389
5524
|
TaxoraClientFactory,
|
|
4390
5525
|
TaxoraException,
|
|
4391
5526
|
Token,
|
|
5527
|
+
UsageSummary,
|
|
5528
|
+
UsageSummaryPage,
|
|
4392
5529
|
ValidationException,
|
|
4393
5530
|
VatCertificateExport,
|
|
4394
5531
|
VatCollection,
|
|
@@ -4400,6 +5537,7 @@ function toBytes(rawBody) {
|
|
|
4400
5537
|
WEBHOOK_ID_HEADER,
|
|
4401
5538
|
WEBHOOK_SIGNATURE_HEADER,
|
|
4402
5539
|
WebhookSignatureVerificationError,
|
|
5540
|
+
calculateInvoiceTotals,
|
|
4403
5541
|
computeWebhookSignature,
|
|
4404
5542
|
constructWebhookEvent,
|
|
4405
5543
|
describeApiError,
|
|
@@ -4417,6 +5555,8 @@ function toBytes(rawBody) {
|
|
|
4417
5555
|
generateWebhookSignatureHeader,
|
|
4418
5556
|
getBaseUrl,
|
|
4419
5557
|
getFailedVatStates,
|
|
5558
|
+
isTransactionBuyerRejected,
|
|
5559
|
+
parseComplianceTaxCategory,
|
|
4420
5560
|
parseLocalTaxNumbersCsv,
|
|
4421
5561
|
parseWebhookSignatureHeader,
|
|
4422
5562
|
toComplianceDocumentType,
|