@taxora/sdk 1.9.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 +29 -0
- package/README.md +54 -0
- package/dist/index.cjs +692 -236
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +271 -50
- package/dist/index.d.ts +271 -50
- package/dist/index.js +684 -236
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -27,12 +27,16 @@ __export(index_exports, {
|
|
|
27
27
|
BillingEndpoint: () => BillingEndpoint,
|
|
28
28
|
CompanyAddress: () => CompanyAddress,
|
|
29
29
|
CompanyEndpoint: () => CompanyEndpoint,
|
|
30
|
+
ComplianceDeliveryAction: () => ComplianceDeliveryAction,
|
|
31
|
+
ComplianceDeliveryDecision: () => ComplianceDeliveryDecision,
|
|
30
32
|
ComplianceDocumentType: () => ComplianceDocumentType,
|
|
31
33
|
ComplianceEnrollment: () => ComplianceEnrollment,
|
|
32
34
|
ComplianceEnrollmentPage: () => ComplianceEnrollmentPage,
|
|
33
35
|
ComplianceEnrollmentStatus: () => ComplianceEnrollmentStatus,
|
|
34
36
|
ComplianceOfferings: () => ComplianceOfferings,
|
|
35
37
|
ComplianceService: () => ComplianceService,
|
|
38
|
+
ComplianceTax: () => ComplianceTax,
|
|
39
|
+
ComplianceTaxCategory: () => ComplianceTaxCategory,
|
|
36
40
|
ComplianceTaxReport: () => ComplianceTaxReport,
|
|
37
41
|
ComplianceTaxReportPage: () => ComplianceTaxReportPage,
|
|
38
42
|
ComplianceTaxReportState: () => ComplianceTaxReportState,
|
|
@@ -41,6 +45,8 @@ __export(index_exports, {
|
|
|
41
45
|
ComplianceTransactionState: () => ComplianceTransactionState,
|
|
42
46
|
ComplianceTransactionType: () => ComplianceTransactionType,
|
|
43
47
|
DEFAULT_WEBHOOK_TOLERANCE_SECONDS: () => DEFAULT_WEBHOOK_TOLERANCE_SECONDS,
|
|
48
|
+
EInvoicingQuota: () => EInvoicingQuota,
|
|
49
|
+
EInvoicingQuotaPeriod: () => EInvoicingQuotaPeriod,
|
|
44
50
|
EReportingEndpoint: () => EReportingEndpoint,
|
|
45
51
|
Environment: () => Environment,
|
|
46
52
|
FetchHttpClient: () => FetchHttpClient,
|
|
@@ -88,6 +94,7 @@ __export(index_exports, {
|
|
|
88
94
|
WEBHOOK_ID_HEADER: () => WEBHOOK_ID_HEADER,
|
|
89
95
|
WEBHOOK_SIGNATURE_HEADER: () => WEBHOOK_SIGNATURE_HEADER,
|
|
90
96
|
WebhookSignatureVerificationError: () => WebhookSignatureVerificationError,
|
|
97
|
+
calculateInvoiceTotals: () => calculateInvoiceTotals,
|
|
91
98
|
computeWebhookSignature: () => computeWebhookSignature,
|
|
92
99
|
constructWebhookEvent: () => constructWebhookEvent,
|
|
93
100
|
describeApiError: () => describeApiError,
|
|
@@ -106,6 +113,7 @@ __export(index_exports, {
|
|
|
106
113
|
getBaseUrl: () => getBaseUrl,
|
|
107
114
|
getFailedVatStates: () => getFailedVatStates,
|
|
108
115
|
isTransactionBuyerRejected: () => isTransactionBuyerRejected,
|
|
116
|
+
parseComplianceTaxCategory: () => parseComplianceTaxCategory,
|
|
109
117
|
parseLocalTaxNumbersCsv: () => parseLocalTaxNumbersCsv,
|
|
110
118
|
parseWebhookSignatureHeader: () => parseWebhookSignatureHeader,
|
|
111
119
|
toComplianceDocumentType: () => toComplianceDocumentType,
|
|
@@ -564,17 +572,144 @@ var AuthEndpoint = class {
|
|
|
564
572
|
}
|
|
565
573
|
};
|
|
566
574
|
|
|
567
|
-
// src/dto/
|
|
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
|
+
}
|
|
568
585
|
function asRecord(value) {
|
|
569
586
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
570
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
|
+
}
|
|
571
706
|
function asRecordList(value) {
|
|
572
|
-
return Array.isArray(value) ? value.filter((row) =>
|
|
707
|
+
return Array.isArray(value) ? value.filter((row) => asRecord2(row) !== null) : [];
|
|
573
708
|
}
|
|
574
|
-
function
|
|
709
|
+
function asString2(value, fallback = "") {
|
|
575
710
|
return typeof value === "string" ? value : fallback;
|
|
576
711
|
}
|
|
577
|
-
function
|
|
712
|
+
function asNullableString2(value) {
|
|
578
713
|
return typeof value === "string" ? value : null;
|
|
579
714
|
}
|
|
580
715
|
function asNumber(value) {
|
|
@@ -631,35 +766,35 @@ var UsageSummary = class _UsageSummary {
|
|
|
631
766
|
return new _UsageSummary(
|
|
632
767
|
asNumber(data["id"]),
|
|
633
768
|
asNumber(data["company_id"]),
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
769
|
+
asString2(data["feature"]),
|
|
770
|
+
asNullableString2(data["country"]),
|
|
771
|
+
asNullableString2(data["regime"]),
|
|
772
|
+
asString2(data["billing_period"]),
|
|
638
773
|
asNumber(data["event_count"]),
|
|
639
774
|
typeof amount === "string" ? amount : typeof amount === "number" ? String(amount) : null,
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
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"]),
|
|
648
783
|
asRecordList(data["events"]).map((event) => ({
|
|
649
784
|
id: asNumber(event["id"]),
|
|
650
785
|
usageSummaryId: asNullableNumber(event["usage_summary_id"]),
|
|
651
|
-
feature:
|
|
652
|
-
country:
|
|
653
|
-
regime:
|
|
654
|
-
billingPeriod:
|
|
655
|
-
resourceType:
|
|
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"]),
|
|
656
791
|
resourceId: asNullableNumber(event["resource_id"]),
|
|
657
|
-
stripeUsageRecordId:
|
|
658
|
-
stripeReportedAt:
|
|
659
|
-
createdAt:
|
|
792
|
+
stripeUsageRecordId: asNullableString2(event["stripe_usage_record_id"]),
|
|
793
|
+
stripeReportedAt: asNullableString2(event["stripe_reported_at"]),
|
|
794
|
+
createdAt: asNullableString2(event["created_at"])
|
|
660
795
|
})),
|
|
661
|
-
|
|
662
|
-
|
|
796
|
+
asNullableString2(data["created_at"]),
|
|
797
|
+
asNullableString2(data["updated_at"])
|
|
663
798
|
);
|
|
664
799
|
}
|
|
665
800
|
};
|
|
@@ -677,9 +812,9 @@ var UsageSummaryPage = class _UsageSummaryPage {
|
|
|
677
812
|
total;
|
|
678
813
|
lastPage;
|
|
679
814
|
static fromResponse(payload) {
|
|
680
|
-
const obj =
|
|
815
|
+
const obj = asRecord2(payload) ?? {};
|
|
681
816
|
const rows = asRecordList(obj["data"]).map(UsageSummary.fromArray);
|
|
682
|
-
const meta =
|
|
817
|
+
const meta = asRecord2(obj["meta"]) ?? {};
|
|
683
818
|
const num = (value, fallback) => typeof value === "number" ? value : fallback;
|
|
684
819
|
return new _UsageSummaryPage(
|
|
685
820
|
rows,
|
|
@@ -731,6 +866,22 @@ var BillingEndpoint = class {
|
|
|
731
866
|
const data = await this.getJson(`${this.baseUrl}/billing/usage-summaries/${period}`);
|
|
732
867
|
return Array.isArray(data) ? data.filter((row) => typeof row === "object" && row !== null).map(UsageSummary.fromArray) : [];
|
|
733
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
|
+
}
|
|
734
885
|
buildHeaders() {
|
|
735
886
|
const headers = {
|
|
736
887
|
"Content-Type": "application/json",
|
|
@@ -836,6 +987,67 @@ var CompanyEndpoint = class {
|
|
|
836
987
|
}
|
|
837
988
|
};
|
|
838
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
|
+
|
|
839
1051
|
// src/enums/ComplianceEnrollmentStatus.ts
|
|
840
1052
|
var ComplianceEnrollmentStatus = {
|
|
841
1053
|
PENDING_DATA: "pending_data",
|
|
@@ -847,17 +1059,17 @@ var ComplianceEnrollmentStatus = {
|
|
|
847
1059
|
ERROR_ACTIVATION: "error_activation",
|
|
848
1060
|
UNKNOWN: "unknown"
|
|
849
1061
|
};
|
|
850
|
-
var
|
|
1062
|
+
var VALUES2 = Object.values(ComplianceEnrollmentStatus);
|
|
851
1063
|
function toComplianceEnrollmentStatus(value) {
|
|
852
1064
|
if (typeof value === "string") {
|
|
853
1065
|
const normalized = value.trim().toLowerCase();
|
|
854
|
-
if (
|
|
1066
|
+
if (VALUES2.includes(normalized)) {
|
|
855
1067
|
return normalized;
|
|
856
1068
|
}
|
|
857
1069
|
}
|
|
858
1070
|
return ComplianceEnrollmentStatus.UNKNOWN;
|
|
859
1071
|
}
|
|
860
|
-
var
|
|
1072
|
+
var DESCRIPTIONS2 = {
|
|
861
1073
|
[ComplianceEnrollmentStatus.PENDING_DATA]: "Awaiting merchant data before provisioning can start.",
|
|
862
1074
|
[ComplianceEnrollmentStatus.ACCOUNT_CREATED]: "Provider account exists. Regime not yet activated.",
|
|
863
1075
|
[ComplianceEnrollmentStatus.REGIME_ACTIVATED]: "Compliance regime activated at provider. Ready to submit transactions.",
|
|
@@ -868,7 +1080,7 @@ var DESCRIPTIONS = {
|
|
|
868
1080
|
[ComplianceEnrollmentStatus.UNKNOWN]: "Unknown status."
|
|
869
1081
|
};
|
|
870
1082
|
function describeComplianceEnrollmentStatus(status) {
|
|
871
|
-
return
|
|
1083
|
+
return DESCRIPTIONS2[status];
|
|
872
1084
|
}
|
|
873
1085
|
|
|
874
1086
|
// src/enums/ComplianceService.ts
|
|
@@ -877,30 +1089,30 @@ var ComplianceService = {
|
|
|
877
1089
|
E_INVOICING: "e_invoicing",
|
|
878
1090
|
UNKNOWN: "unknown"
|
|
879
1091
|
};
|
|
880
|
-
var
|
|
1092
|
+
var VALUES3 = Object.values(ComplianceService);
|
|
881
1093
|
function toComplianceService(value) {
|
|
882
1094
|
if (typeof value === "string") {
|
|
883
1095
|
const normalized = value.trim().toLowerCase();
|
|
884
|
-
if (
|
|
1096
|
+
if (VALUES3.includes(normalized)) {
|
|
885
1097
|
return normalized;
|
|
886
1098
|
}
|
|
887
1099
|
}
|
|
888
1100
|
return ComplianceService.UNKNOWN;
|
|
889
1101
|
}
|
|
890
|
-
var
|
|
1102
|
+
var DESCRIPTIONS3 = {
|
|
891
1103
|
[ComplianceService.E_REPORTING]: "E-reporting of transaction data to the tax authority (France: DGFiP Flux 10).",
|
|
892
1104
|
[ComplianceService.E_INVOICING]: "Structured e-invoices delivered to the buyer (Norway: EHF / Peppol BIS 3.0).",
|
|
893
1105
|
[ComplianceService.UNKNOWN]: "Unknown service."
|
|
894
1106
|
};
|
|
895
1107
|
function describeComplianceService(service) {
|
|
896
|
-
return
|
|
1108
|
+
return DESCRIPTIONS3[service];
|
|
897
1109
|
}
|
|
898
1110
|
|
|
899
1111
|
// src/dto/ComplianceEnrollment.ts
|
|
900
|
-
function
|
|
1112
|
+
function asString3(value, fallback = "") {
|
|
901
1113
|
return typeof value === "string" ? value : fallback;
|
|
902
1114
|
}
|
|
903
|
-
function
|
|
1115
|
+
function asNullableString3(value) {
|
|
904
1116
|
return typeof value === "string" ? value : null;
|
|
905
1117
|
}
|
|
906
1118
|
function asNumber2(value) {
|
|
@@ -952,38 +1164,38 @@ var ComplianceEnrollment = class _ComplianceEnrollment {
|
|
|
952
1164
|
return new _ComplianceEnrollment(
|
|
953
1165
|
asNumber2(data["id"]),
|
|
954
1166
|
asNumber2(data["company_id"]),
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1167
|
+
asString3(data["country"]),
|
|
1168
|
+
asString3(data["regime"]),
|
|
1169
|
+
asString3(data["provider"]),
|
|
958
1170
|
toComplianceEnrollmentStatus(data["status"]),
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
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"]),
|
|
965
1177
|
regimeConfig,
|
|
966
|
-
|
|
967
|
-
|
|
1178
|
+
asNullableString3(data["reporting_start_date"]),
|
|
1179
|
+
asNullableString3(data["notification_email"]),
|
|
968
1180
|
data["auto_send"] === true,
|
|
969
|
-
|
|
970
|
-
|
|
1181
|
+
asNullableString3(data["created_at"]),
|
|
1182
|
+
asNullableString3(data["updated_at"]),
|
|
971
1183
|
toComplianceService(data["service"])
|
|
972
1184
|
);
|
|
973
1185
|
}
|
|
974
1186
|
};
|
|
975
1187
|
|
|
976
1188
|
// src/dto/ComplianceOfferings.ts
|
|
977
|
-
function
|
|
1189
|
+
function asRecord3(value) {
|
|
978
1190
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
979
1191
|
}
|
|
980
1192
|
function asRecordList2(value) {
|
|
981
|
-
return Array.isArray(value) ? value.filter((row) =>
|
|
1193
|
+
return Array.isArray(value) ? value.filter((row) => asRecord3(row) !== null) : [];
|
|
982
1194
|
}
|
|
983
|
-
function
|
|
1195
|
+
function asString4(value, fallback = "") {
|
|
984
1196
|
return typeof value === "string" ? value : fallback;
|
|
985
1197
|
}
|
|
986
|
-
function
|
|
1198
|
+
function asNullableString4(value) {
|
|
987
1199
|
return typeof value === "string" ? value : null;
|
|
988
1200
|
}
|
|
989
1201
|
function asNullableNumber2(value) {
|
|
@@ -995,8 +1207,8 @@ function asStringList(value) {
|
|
|
995
1207
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
996
1208
|
}
|
|
997
1209
|
function toRequires(value) {
|
|
998
|
-
const record =
|
|
999
|
-
return record === null ? null : { country:
|
|
1210
|
+
const record = asRecord3(value);
|
|
1211
|
+
return record === null ? null : { country: asString4(record["country"]), service: asString4(record["service"]) };
|
|
1000
1212
|
}
|
|
1001
1213
|
var ComplianceOfferings = class _ComplianceOfferings {
|
|
1002
1214
|
constructor(offerings, coverage, access, accessByCountry) {
|
|
@@ -1011,16 +1223,16 @@ var ComplianceOfferings = class _ComplianceOfferings {
|
|
|
1011
1223
|
accessByCountry;
|
|
1012
1224
|
static fromArray(data) {
|
|
1013
1225
|
const offerings = asRecordList2(data["data"]).map((row) => ({
|
|
1014
|
-
country:
|
|
1015
|
-
service:
|
|
1016
|
-
currency:
|
|
1226
|
+
country: asString4(row["country"]),
|
|
1227
|
+
service: asString4(row["service"]),
|
|
1228
|
+
currency: asString4(row["currency"], "EUR"),
|
|
1017
1229
|
active: row["active"] === true,
|
|
1018
1230
|
viaStripe: row["via_stripe"] === true,
|
|
1019
|
-
plan:
|
|
1020
|
-
status:
|
|
1231
|
+
plan: asNullableString4(row["plan"]),
|
|
1232
|
+
status: asNullableString4(row["status"]),
|
|
1021
1233
|
enrolled: row["enrolled"] === true,
|
|
1022
1234
|
plans: asRecordList2(row["plans"]).map((plan) => ({
|
|
1023
|
-
key:
|
|
1235
|
+
key: asString4(plan["key"]),
|
|
1024
1236
|
monthlyFee: asNullableNumber2(plan["monthly_fee"]),
|
|
1025
1237
|
setupFee: asNullableNumber2(plan["setup_fee"]),
|
|
1026
1238
|
usageTiers: Array.isArray(plan["usage_tiers"]) ? plan["usage_tiers"] : [],
|
|
@@ -1032,19 +1244,19 @@ var ComplianceOfferings = class _ComplianceOfferings {
|
|
|
1032
1244
|
}))
|
|
1033
1245
|
}));
|
|
1034
1246
|
const coverage = asRecordList2(data["coverage"]).map((row) => ({
|
|
1035
|
-
country:
|
|
1036
|
-
availability:
|
|
1247
|
+
country: asString4(row["country"]),
|
|
1248
|
+
availability: asString4(row["availability"]),
|
|
1037
1249
|
services: asStringList(row["services"]),
|
|
1038
|
-
network:
|
|
1039
|
-
format:
|
|
1040
|
-
mandate:
|
|
1041
|
-
support:
|
|
1250
|
+
network: asNullableString4(row["network"]),
|
|
1251
|
+
format: asNullableString4(row["format"]),
|
|
1252
|
+
mandate: asNullableString4(row["mandate"]),
|
|
1253
|
+
support: asString4(row["support"], "yes")
|
|
1042
1254
|
}));
|
|
1043
|
-
const access =
|
|
1255
|
+
const access = asRecord3(data["access"]) ?? {};
|
|
1044
1256
|
const accessByCountry = {};
|
|
1045
|
-
for (const [country, services] of Object.entries(
|
|
1257
|
+
for (const [country, services] of Object.entries(asRecord3(data["access_by_country"]) ?? {})) {
|
|
1046
1258
|
const flags = {};
|
|
1047
|
-
for (const [service, value] of Object.entries(
|
|
1259
|
+
for (const [service, value] of Object.entries(asRecord3(services) ?? {})) {
|
|
1048
1260
|
flags[service] = value === true;
|
|
1049
1261
|
}
|
|
1050
1262
|
accessByCountry[country] = flags;
|
|
@@ -1103,17 +1315,17 @@ var ComplianceTaxReportState = {
|
|
|
1103
1315
|
ERROR: "error",
|
|
1104
1316
|
UNKNOWN: "unknown"
|
|
1105
1317
|
};
|
|
1106
|
-
var
|
|
1318
|
+
var VALUES4 = Object.values(ComplianceTaxReportState);
|
|
1107
1319
|
function toComplianceTaxReportState(value) {
|
|
1108
1320
|
if (typeof value === "string") {
|
|
1109
1321
|
const normalized = value.trim().toLowerCase();
|
|
1110
|
-
if (
|
|
1322
|
+
if (VALUES4.includes(normalized)) {
|
|
1111
1323
|
return normalized;
|
|
1112
1324
|
}
|
|
1113
1325
|
}
|
|
1114
1326
|
return ComplianceTaxReportState.UNKNOWN;
|
|
1115
1327
|
}
|
|
1116
|
-
var
|
|
1328
|
+
var DESCRIPTIONS4 = {
|
|
1117
1329
|
[ComplianceTaxReportState.NEW]: "Created at provider, waiting for the daily ledger batch.",
|
|
1118
1330
|
[ComplianceTaxReportState.SENT]: "Ledger transmitted to PPF.",
|
|
1119
1331
|
[ComplianceTaxReportState.ACKNOWLEDGED]: "PPF acknowledged receipt of the ledger.",
|
|
@@ -1123,14 +1335,14 @@ var DESCRIPTIONS3 = {
|
|
|
1123
1335
|
[ComplianceTaxReportState.UNKNOWN]: "Unknown state."
|
|
1124
1336
|
};
|
|
1125
1337
|
function describeComplianceTaxReportState(state) {
|
|
1126
|
-
return
|
|
1338
|
+
return DESCRIPTIONS4[state];
|
|
1127
1339
|
}
|
|
1128
1340
|
|
|
1129
1341
|
// src/dto/ComplianceTaxReport.ts
|
|
1130
|
-
function
|
|
1342
|
+
function asString5(value, fallback = "") {
|
|
1131
1343
|
return typeof value === "string" ? value : fallback;
|
|
1132
1344
|
}
|
|
1133
|
-
function
|
|
1345
|
+
function asNullableString5(value) {
|
|
1134
1346
|
return typeof value === "string" ? value : null;
|
|
1135
1347
|
}
|
|
1136
1348
|
function asNumber3(value) {
|
|
@@ -1191,21 +1403,21 @@ var ComplianceTaxReport = class _ComplianceTaxReport {
|
|
|
1191
1403
|
asNumber3(data["company_id"]),
|
|
1192
1404
|
asNullableNumber3(data["compliance_transaction_id"]),
|
|
1193
1405
|
asNullableNumber3(data["ledger_id"]),
|
|
1194
|
-
|
|
1195
|
-
|
|
1406
|
+
asString5(data["country"]),
|
|
1407
|
+
asString5(data["regime"]),
|
|
1196
1408
|
toComplianceTaxReportState(data["state"]),
|
|
1197
|
-
|
|
1409
|
+
asString5(data["state_label"]),
|
|
1198
1410
|
data["is_terminal"] === true,
|
|
1199
|
-
|
|
1200
|
-
|
|
1411
|
+
asNullableString5(data["provider_tax_report_id"]),
|
|
1412
|
+
asNullableString5(data["provider_ledger_id"]),
|
|
1201
1413
|
asMoney(data["base_amount"]),
|
|
1202
1414
|
asMoney(data["tax_amount"]),
|
|
1203
1415
|
asMoney(data["total_amount"]),
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
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"])
|
|
1209
1421
|
);
|
|
1210
1422
|
}
|
|
1211
1423
|
};
|
|
@@ -1251,23 +1463,23 @@ var ComplianceDocumentType = {
|
|
|
1251
1463
|
CREDIT_NOTE: "credit_note",
|
|
1252
1464
|
UNKNOWN: "unknown"
|
|
1253
1465
|
};
|
|
1254
|
-
var
|
|
1466
|
+
var VALUES5 = Object.values(ComplianceDocumentType);
|
|
1255
1467
|
function toComplianceDocumentType(value) {
|
|
1256
1468
|
if (typeof value === "string") {
|
|
1257
1469
|
const normalized = value.trim().toLowerCase();
|
|
1258
|
-
if (
|
|
1470
|
+
if (VALUES5.includes(normalized)) {
|
|
1259
1471
|
return normalized;
|
|
1260
1472
|
}
|
|
1261
1473
|
}
|
|
1262
1474
|
return ComplianceDocumentType.UNKNOWN;
|
|
1263
1475
|
}
|
|
1264
|
-
var
|
|
1476
|
+
var DESCRIPTIONS5 = {
|
|
1265
1477
|
[ComplianceDocumentType.INVOICE]: "Commercial invoice.",
|
|
1266
1478
|
[ComplianceDocumentType.CREDIT_NOTE]: "Credit note correcting a previously issued invoice.",
|
|
1267
1479
|
[ComplianceDocumentType.UNKNOWN]: "Unknown document type."
|
|
1268
1480
|
};
|
|
1269
1481
|
function describeComplianceDocumentType(type) {
|
|
1270
|
-
return
|
|
1482
|
+
return DESCRIPTIONS5[type];
|
|
1271
1483
|
}
|
|
1272
1484
|
|
|
1273
1485
|
// src/enums/ComplianceTransactionState.ts
|
|
@@ -1280,17 +1492,17 @@ var ComplianceTransactionState = {
|
|
|
1280
1492
|
NOT_REQUIRED: "not_required",
|
|
1281
1493
|
UNKNOWN: "unknown"
|
|
1282
1494
|
};
|
|
1283
|
-
var
|
|
1495
|
+
var VALUES6 = Object.values(ComplianceTransactionState);
|
|
1284
1496
|
function toComplianceTransactionState(value) {
|
|
1285
1497
|
if (typeof value === "string") {
|
|
1286
1498
|
const normalized = value.trim().toLowerCase();
|
|
1287
|
-
if (
|
|
1499
|
+
if (VALUES6.includes(normalized)) {
|
|
1288
1500
|
return normalized;
|
|
1289
1501
|
}
|
|
1290
1502
|
}
|
|
1291
1503
|
return ComplianceTransactionState.UNKNOWN;
|
|
1292
1504
|
}
|
|
1293
|
-
var
|
|
1505
|
+
var DESCRIPTIONS6 = {
|
|
1294
1506
|
[ComplianceTransactionState.PENDING]: "Recorded locally, not yet submitted to the provider.",
|
|
1295
1507
|
[ComplianceTransactionState.SENDING]: "Submission to the provider in progress.",
|
|
1296
1508
|
[ComplianceTransactionState.SUBMITTED]: "Successfully submitted to the provider.",
|
|
@@ -1299,7 +1511,7 @@ var DESCRIPTIONS5 = {
|
|
|
1299
1511
|
[ComplianceTransactionState.UNKNOWN]: "Unknown state."
|
|
1300
1512
|
};
|
|
1301
1513
|
function describeComplianceTransactionState(state) {
|
|
1302
|
-
return
|
|
1514
|
+
return DESCRIPTIONS6[state];
|
|
1303
1515
|
}
|
|
1304
1516
|
|
|
1305
1517
|
// src/enums/ComplianceTransactionType.ts
|
|
@@ -1311,17 +1523,17 @@ var ComplianceTransactionType = {
|
|
|
1311
1523
|
CROSSBORDER_INBOUND: "crossborder_inbound",
|
|
1312
1524
|
UNKNOWN: "unknown"
|
|
1313
1525
|
};
|
|
1314
|
-
var
|
|
1526
|
+
var VALUES7 = Object.values(ComplianceTransactionType);
|
|
1315
1527
|
function toComplianceTransactionType(value) {
|
|
1316
1528
|
if (typeof value === "string") {
|
|
1317
1529
|
const normalized = value.trim().toLowerCase();
|
|
1318
|
-
if (
|
|
1530
|
+
if (VALUES7.includes(normalized)) {
|
|
1319
1531
|
return normalized;
|
|
1320
1532
|
}
|
|
1321
1533
|
}
|
|
1322
1534
|
return ComplianceTransactionType.UNKNOWN;
|
|
1323
1535
|
}
|
|
1324
|
-
var
|
|
1536
|
+
var DESCRIPTIONS7 = {
|
|
1325
1537
|
[ComplianceTransactionType.B2C_OUTBOUND]: "Sale to a private person in the merchant country (DGFiP Flux 10.3).",
|
|
1326
1538
|
[ComplianceTransactionType.B2B_DOMESTIC_OUTBOUND]: "Sale to a domestic business (DGFiP Flux 1, Phase 2).",
|
|
1327
1539
|
[ComplianceTransactionType.B2B_DOMESTIC_INBOUND]: "Purchase from a domestic business (DGFiP Flux 1, Phase 2).",
|
|
@@ -1330,14 +1542,14 @@ var DESCRIPTIONS6 = {
|
|
|
1330
1542
|
[ComplianceTransactionType.UNKNOWN]: "Unknown transaction type."
|
|
1331
1543
|
};
|
|
1332
1544
|
function describeComplianceTransactionType(type) {
|
|
1333
|
-
return
|
|
1545
|
+
return DESCRIPTIONS7[type];
|
|
1334
1546
|
}
|
|
1335
1547
|
|
|
1336
1548
|
// src/dto/ComplianceTransaction.ts
|
|
1337
|
-
function
|
|
1549
|
+
function asString6(value, fallback = "") {
|
|
1338
1550
|
return typeof value === "string" ? value : fallback;
|
|
1339
1551
|
}
|
|
1340
|
-
function
|
|
1552
|
+
function asNullableString6(value) {
|
|
1341
1553
|
return typeof value === "string" ? value : null;
|
|
1342
1554
|
}
|
|
1343
1555
|
function asNumber4(value) {
|
|
@@ -1353,45 +1565,45 @@ function asNullableNumber4(value) {
|
|
|
1353
1565
|
if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
|
|
1354
1566
|
return null;
|
|
1355
1567
|
}
|
|
1356
|
-
function
|
|
1568
|
+
function asRecord4(value) {
|
|
1357
1569
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
1358
1570
|
}
|
|
1359
1571
|
function asRecordList3(value) {
|
|
1360
|
-
return Array.isArray(value) ? value.filter((row) =>
|
|
1572
|
+
return Array.isArray(value) ? value.filter((row) => asRecord4(row) !== null) : [];
|
|
1361
1573
|
}
|
|
1362
1574
|
function toInvoicePdf(value) {
|
|
1363
|
-
const pdf =
|
|
1575
|
+
const pdf = asRecord4(value);
|
|
1364
1576
|
if (pdf === null) return null;
|
|
1365
1577
|
return {
|
|
1366
|
-
filename:
|
|
1578
|
+
filename: asNullableString6(pdf["filename"]),
|
|
1367
1579
|
size: asNullableNumber4(pdf["size"]),
|
|
1368
1580
|
attached: pdf["attached"] === true
|
|
1369
1581
|
};
|
|
1370
1582
|
}
|
|
1371
1583
|
function toEurEquivalent(value) {
|
|
1372
|
-
const eur =
|
|
1584
|
+
const eur = asRecord4(value);
|
|
1373
1585
|
if (eur === null) return null;
|
|
1374
1586
|
return {
|
|
1375
1587
|
subtotal: asMoney2(eur["subtotal"]),
|
|
1376
1588
|
taxAmount: asMoney2(eur["tax_amount"]),
|
|
1377
1589
|
total: asMoney2(eur["total"]),
|
|
1378
|
-
rate:
|
|
1379
|
-
rateDate:
|
|
1380
|
-
source:
|
|
1590
|
+
rate: asString6(eur["rate"]),
|
|
1591
|
+
rateDate: asNullableString6(eur["rate_date"]),
|
|
1592
|
+
source: asString6(eur["source"], "ECB")
|
|
1381
1593
|
};
|
|
1382
1594
|
}
|
|
1383
1595
|
function toInvoiceLines(providerPayload) {
|
|
1384
|
-
const invoice =
|
|
1596
|
+
const invoice = asRecord4(asRecord4(providerPayload)?.["invoice"]);
|
|
1385
1597
|
return asRecordList3(invoice?.["invoice_lines_attributes"]).map((line) => ({
|
|
1386
|
-
description:
|
|
1598
|
+
description: asString6(line["description"]),
|
|
1387
1599
|
quantity: asNullableNumber4(line["quantity"]),
|
|
1388
1600
|
price: asMoney2(line["price"]),
|
|
1389
1601
|
unit: asNullableNumber4(line["unit"]),
|
|
1390
1602
|
taxes: asRecordList3(line["taxes_attributes"]).map((tax) => ({
|
|
1391
|
-
name:
|
|
1603
|
+
name: asString6(tax["name"]),
|
|
1392
1604
|
percent: asNullableNumber4(tax["percent"]),
|
|
1393
|
-
category:
|
|
1394
|
-
comment:
|
|
1605
|
+
category: asNullableString6(tax["category"]),
|
|
1606
|
+
comment: asNullableString6(tax["comment"])
|
|
1395
1607
|
}))
|
|
1396
1608
|
}));
|
|
1397
1609
|
}
|
|
@@ -1499,48 +1711,48 @@ var ComplianceTransaction = class _ComplianceTransaction {
|
|
|
1499
1711
|
asNumber4(data["id"]),
|
|
1500
1712
|
asNumber4(data["company_id"]),
|
|
1501
1713
|
asNumber4(data["compliance_enrollment_id"]),
|
|
1502
|
-
|
|
1503
|
-
|
|
1714
|
+
asString6(data["country"]),
|
|
1715
|
+
asString6(data["regime"]),
|
|
1504
1716
|
toComplianceTransactionType(data["transaction_type"]),
|
|
1505
|
-
|
|
1717
|
+
asString6(data["transaction_type_label"]),
|
|
1506
1718
|
toComplianceTransactionState(data["state"]),
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
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"),
|
|
1512
1724
|
asMoney2(data["subtotal"]),
|
|
1513
1725
|
asMoney2(data["tax_amount"]),
|
|
1514
1726
|
asMoney2(data["total"]),
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1727
|
+
asNullableString6(data["counterparty_name"]),
|
|
1728
|
+
asNullableString6(data["counterparty_country"]),
|
|
1729
|
+
asNullableString6(data["counterparty_vat_number"]),
|
|
1518
1730
|
data["is_paid"] === true,
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1731
|
+
asNullableString6(data["paid_at"]),
|
|
1732
|
+
asNullableString6(data["provider_invoice_id"]),
|
|
1733
|
+
asNullableString6(data["submission_error"]),
|
|
1734
|
+
asNullableString6(data["reported_at"]),
|
|
1523
1735
|
taxReport,
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
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"]),
|
|
1532
1744
|
data["document_type"] === void 0 || data["document_type"] === null ? ComplianceDocumentType.INVOICE : toComplianceDocumentType(data["document_type"]),
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1745
|
+
asNullableString6(data["amended_number"]),
|
|
1746
|
+
asNullableString6(data["amended_date"]),
|
|
1747
|
+
asNullableString6(data["provider_state"]),
|
|
1536
1748
|
toInvoiceLines(data["provider_payload"]),
|
|
1537
|
-
|
|
1538
|
-
|
|
1749
|
+
asNullableString6(data["document_url"]),
|
|
1750
|
+
asRecord4(data["delivery_check"]),
|
|
1539
1751
|
toInvoicePdf(data["invoice_pdf"]),
|
|
1540
|
-
|
|
1541
|
-
|
|
1752
|
+
asNullableString6(data["internal_reference"]),
|
|
1753
|
+
asNullableString6(data["pdf_url"]),
|
|
1542
1754
|
data["buyer_rejected"] === true,
|
|
1543
|
-
|
|
1755
|
+
asNullableString6(data["rejection_reason"]),
|
|
1544
1756
|
toEurEquivalent(data["eur_equivalent"])
|
|
1545
1757
|
);
|
|
1546
1758
|
}
|
|
@@ -1600,38 +1812,11 @@ var ImportResult = class _ImportResult {
|
|
|
1600
1812
|
}
|
|
1601
1813
|
};
|
|
1602
1814
|
|
|
1603
|
-
// src/enums/PeppolLookupStatus.ts
|
|
1604
|
-
var PeppolLookupStatus = {
|
|
1605
|
-
REACHABLE: "reachable",
|
|
1606
|
-
NOT_REACHABLE: "not_reachable",
|
|
1607
|
-
PENDING: "pending",
|
|
1608
|
-
UNKNOWN: "unknown"
|
|
1609
|
-
};
|
|
1610
|
-
var VALUES7 = Object.values(PeppolLookupStatus);
|
|
1611
|
-
function toPeppolLookupStatus(value) {
|
|
1612
|
-
if (typeof value === "string") {
|
|
1613
|
-
const normalized = value.trim().toLowerCase();
|
|
1614
|
-
if (VALUES7.includes(normalized)) {
|
|
1615
|
-
return normalized;
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
return PeppolLookupStatus.UNKNOWN;
|
|
1619
|
-
}
|
|
1620
|
-
var DESCRIPTIONS7 = {
|
|
1621
|
-
[PeppolLookupStatus.REACHABLE]: "The participant is registered on Peppol and can receive e-invoices.",
|
|
1622
|
-
[PeppolLookupStatus.NOT_REACHABLE]: "The participant is not registered on Peppol.",
|
|
1623
|
-
[PeppolLookupStatus.PENDING]: "The directory lookup is still resolving \u2014 ask again in a few seconds.",
|
|
1624
|
-
[PeppolLookupStatus.UNKNOWN]: "Unknown lookup status."
|
|
1625
|
-
};
|
|
1626
|
-
function describePeppolLookupStatus(status) {
|
|
1627
|
-
return DESCRIPTIONS7[status];
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
1815
|
// src/dto/PeppolLookupResult.ts
|
|
1631
|
-
function
|
|
1816
|
+
function asString7(value, fallback = "") {
|
|
1632
1817
|
return typeof value === "string" ? value : fallback;
|
|
1633
1818
|
}
|
|
1634
|
-
function
|
|
1819
|
+
function asNullableString7(value) {
|
|
1635
1820
|
return typeof value === "string" ? value : null;
|
|
1636
1821
|
}
|
|
1637
1822
|
var PeppolLookupResult = class _PeppolLookupResult {
|
|
@@ -1662,21 +1847,21 @@ var PeppolLookupResult = class _PeppolLookupResult {
|
|
|
1662
1847
|
return new _PeppolLookupResult(
|
|
1663
1848
|
toPeppolLookupStatus(data["status"]),
|
|
1664
1849
|
typeof data["reachable"] === "boolean" ? data["reachable"] : null,
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1850
|
+
asString7(data["country"]),
|
|
1851
|
+
asString7(data["scheme"]),
|
|
1852
|
+
asString7(data["id"]),
|
|
1668
1853
|
documentTypes,
|
|
1669
|
-
|
|
1670
|
-
|
|
1854
|
+
asNullableString7(data["transport_type_code"]),
|
|
1855
|
+
asNullableString7(data["checked_at"])
|
|
1671
1856
|
);
|
|
1672
1857
|
}
|
|
1673
1858
|
};
|
|
1674
1859
|
|
|
1675
1860
|
// src/dto/RegistryCompany.ts
|
|
1676
|
-
function
|
|
1861
|
+
function asString8(value, fallback = "") {
|
|
1677
1862
|
return typeof value === "string" ? value : fallback;
|
|
1678
1863
|
}
|
|
1679
|
-
function
|
|
1864
|
+
function asNullableString8(value) {
|
|
1680
1865
|
return typeof value === "string" ? value : null;
|
|
1681
1866
|
}
|
|
1682
1867
|
var RegistryCompany = class _RegistryCompany {
|
|
@@ -1708,24 +1893,24 @@ var RegistryCompany = class _RegistryCompany {
|
|
|
1708
1893
|
country;
|
|
1709
1894
|
static fromArray(data) {
|
|
1710
1895
|
return new _RegistryCompany(
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1896
|
+
asString8(data["org_number"]),
|
|
1897
|
+
asString8(data["company_name"]),
|
|
1898
|
+
asNullableString8(data["organisation_form"]),
|
|
1714
1899
|
data["vat_registered"] === true,
|
|
1715
|
-
|
|
1900
|
+
asNullableString8(data["vat_number"]),
|
|
1716
1901
|
data["enterprise_register"] === true,
|
|
1717
1902
|
data["bankrupt"] === true,
|
|
1718
1903
|
data["under_liquidation"] === true,
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1904
|
+
asNullableString8(data["address"]),
|
|
1905
|
+
asNullableString8(data["postalcode"]),
|
|
1906
|
+
asNullableString8(data["city"]),
|
|
1907
|
+
asString8(data["country"])
|
|
1723
1908
|
);
|
|
1724
1909
|
}
|
|
1725
1910
|
};
|
|
1726
1911
|
|
|
1727
1912
|
// src/dto/RevenueStatistics.ts
|
|
1728
|
-
function
|
|
1913
|
+
function asString9(value, fallback = "") {
|
|
1729
1914
|
return typeof value === "string" ? value : fallback;
|
|
1730
1915
|
}
|
|
1731
1916
|
function asNumber5(value) {
|
|
@@ -1777,35 +1962,35 @@ var RevenueStatistics = class _RevenueStatistics {
|
|
|
1777
1962
|
typeof data["totals"] === "object" && data["totals"] !== null ? data["totals"] : {}
|
|
1778
1963
|
);
|
|
1779
1964
|
const timeSeries = asRecords(data["time_series"]).map((row) => ({
|
|
1780
|
-
bucket:
|
|
1965
|
+
bucket: asString9(row["bucket"]),
|
|
1781
1966
|
...toTotals(row)
|
|
1782
1967
|
}));
|
|
1783
1968
|
const byType = asRecords(data["by_type"]).map((row) => ({
|
|
1784
|
-
type:
|
|
1785
|
-
typeLabel:
|
|
1969
|
+
type: asString9(row["type"]),
|
|
1970
|
+
typeLabel: asString9(row["type_label"]),
|
|
1786
1971
|
count: asNumber5(row["count"]),
|
|
1787
1972
|
total: asMoney3(row["total"])
|
|
1788
1973
|
}));
|
|
1789
1974
|
const byCountry = asRecords(data["by_country"]).map((row) => ({
|
|
1790
|
-
country:
|
|
1975
|
+
country: asString9(row["country"], "unknown"),
|
|
1791
1976
|
count: asNumber5(row["count"]),
|
|
1792
1977
|
total: asMoney3(row["total"])
|
|
1793
1978
|
}));
|
|
1794
1979
|
const byState = asRecords(data["by_state"]).map((row) => ({
|
|
1795
|
-
state:
|
|
1796
|
-
stateLabel:
|
|
1980
|
+
state: asString9(row["state"]),
|
|
1981
|
+
stateLabel: asString9(row["state_label"]),
|
|
1797
1982
|
count: asNumber5(row["count"]),
|
|
1798
1983
|
total: asMoney3(row["total"])
|
|
1799
1984
|
}));
|
|
1800
1985
|
const byCurrency = asRecords(data["by_currency"]).map((row) => ({
|
|
1801
|
-
currency:
|
|
1986
|
+
currency: asString9(row["currency"]),
|
|
1802
1987
|
...toTotals(row)
|
|
1803
1988
|
}));
|
|
1804
1989
|
return new _RevenueStatistics(
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
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"),
|
|
1809
1994
|
data["is_multi_currency"] === true,
|
|
1810
1995
|
totals,
|
|
1811
1996
|
timeSeries,
|
|
@@ -1818,10 +2003,10 @@ var RevenueStatistics = class _RevenueStatistics {
|
|
|
1818
2003
|
};
|
|
1819
2004
|
|
|
1820
2005
|
// src/dto/SireneLookupResult.ts
|
|
1821
|
-
function
|
|
2006
|
+
function asString10(value, fallback = "") {
|
|
1822
2007
|
return typeof value === "string" ? value : fallback;
|
|
1823
2008
|
}
|
|
1824
|
-
function
|
|
2009
|
+
function asNullableString9(value) {
|
|
1825
2010
|
return typeof value === "string" ? value : null;
|
|
1826
2011
|
}
|
|
1827
2012
|
var SireneLookupResult = class _SireneLookupResult {
|
|
@@ -1847,21 +2032,21 @@ var SireneLookupResult = class _SireneLookupResult {
|
|
|
1847
2032
|
postalcode;
|
|
1848
2033
|
static fromArray(data) {
|
|
1849
2034
|
return new _SireneLookupResult(
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
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"])
|
|
1859
2044
|
);
|
|
1860
2045
|
}
|
|
1861
2046
|
};
|
|
1862
2047
|
|
|
1863
2048
|
// src/dto/VatRates.ts
|
|
1864
|
-
function
|
|
2049
|
+
function asString11(value, fallback = "") {
|
|
1865
2050
|
return typeof value === "string" ? value : fallback;
|
|
1866
2051
|
}
|
|
1867
2052
|
function asNumber6(value) {
|
|
@@ -1879,12 +2064,12 @@ var VatRates = class _VatRates {
|
|
|
1879
2064
|
static fromArray(data) {
|
|
1880
2065
|
const rates = (Array.isArray(data["rates"]) ? data["rates"] : []).filter((row) => typeof row === "object" && row !== null).map((row) => ({
|
|
1881
2066
|
percent: asNumber6(row["percent"]),
|
|
1882
|
-
category:
|
|
1883
|
-
labelKey:
|
|
1884
|
-
taxName:
|
|
2067
|
+
category: asString11(row["category"]),
|
|
2068
|
+
labelKey: asString11(row["label_key"]),
|
|
2069
|
+
taxName: asString11(row["tax_name"]),
|
|
1885
2070
|
requiresVatex: row["requires_vatex"] === true
|
|
1886
2071
|
}));
|
|
1887
|
-
return new _VatRates(
|
|
2072
|
+
return new _VatRates(asString11(data["country"]), asString11(data["currency"], "EUR"), rates);
|
|
1888
2073
|
}
|
|
1889
2074
|
};
|
|
1890
2075
|
|
|
@@ -1938,6 +2123,25 @@ var NorwegianOrgNumber = {
|
|
|
1938
2123
|
}
|
|
1939
2124
|
};
|
|
1940
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
|
+
|
|
1941
2145
|
// src/endpoints/EReportingEndpoint.ts
|
|
1942
2146
|
var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
1943
2147
|
var NAF_CODE_REGEX = /^\d{2}$/;
|
|
@@ -2122,6 +2326,19 @@ var EReportingEndpoint = class {
|
|
|
2122
2326
|
const data = await this.parseJsonResponse(response);
|
|
2123
2327
|
return PeppolLookupResult.fromArray(data);
|
|
2124
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
|
+
}
|
|
2125
2342
|
// ---------------------------------------------------------------------
|
|
2126
2343
|
// Transactions
|
|
2127
2344
|
// ---------------------------------------------------------------------
|
|
@@ -2148,6 +2365,7 @@ var EReportingEndpoint = class {
|
|
|
2148
2365
|
this.validateBuyerFields(input);
|
|
2149
2366
|
this.validateInternalReference(input.internalReference);
|
|
2150
2367
|
this.validateInvoicePdfFilename(input);
|
|
2368
|
+
this.validateDocument(input);
|
|
2151
2369
|
const body = {
|
|
2152
2370
|
compliance_enrollment_id: input.complianceEnrollmentId,
|
|
2153
2371
|
transaction_type: input.transactionType,
|
|
@@ -2163,6 +2381,101 @@ var EReportingEndpoint = class {
|
|
|
2163
2381
|
const data = await this.parseJsonResponse(response);
|
|
2164
2382
|
return ComplianceTransaction.fromArray(data);
|
|
2165
2383
|
}
|
|
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.");
|
|
2401
|
+
}
|
|
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
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
if (original.invoiceNumber === "") {
|
|
2408
|
+
throw new TaxoraException("The original transaction has no invoice number.");
|
|
2409
|
+
}
|
|
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
|
+
}
|
|
2166
2479
|
/** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type / enrollment / country / search / internal reference / provider state. */
|
|
2167
2480
|
async listTransactions(filters = {}) {
|
|
2168
2481
|
const params = this.buildTransactionFilterParams(filters);
|
|
@@ -2731,6 +3044,26 @@ var EReportingEndpoint = class {
|
|
|
2731
3044
|
if (line.taxes.length === 0) {
|
|
2732
3045
|
throw new TaxoraException(`invoiceLines[${index}].taxes must contain at least one tax.`);
|
|
2733
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
|
+
});
|
|
2734
3067
|
});
|
|
2735
3068
|
}
|
|
2736
3069
|
validateInvoicePdfFilename(input) {
|
|
@@ -2747,12 +3080,65 @@ var EReportingEndpoint = class {
|
|
|
2747
3080
|
throw new TaxoraException("internalReference must not exceed 100 characters.");
|
|
2748
3081
|
}
|
|
2749
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
|
+
}
|
|
2750
3136
|
validateOptionalCodes(input) {
|
|
2751
|
-
if (input.currency !== void 0 && input.currency
|
|
2752
|
-
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).");
|
|
2753
3139
|
}
|
|
2754
|
-
if (input.counterpartyCountry !== void 0 && input.counterpartyCountry
|
|
2755
|
-
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).");
|
|
2756
3142
|
}
|
|
2757
3143
|
}
|
|
2758
3144
|
/**
|
|
@@ -2795,7 +3181,7 @@ var EReportingEndpoint = class {
|
|
|
2795
3181
|
const taxBuilt = {
|
|
2796
3182
|
name: tax.name,
|
|
2797
3183
|
percent: tax.percent,
|
|
2798
|
-
category: tax.category
|
|
3184
|
+
category: parseComplianceTaxCategory(tax.category) ?? tax.category
|
|
2799
3185
|
};
|
|
2800
3186
|
if (tax.comment !== void 0) taxBuilt["comment"] = tax.comment;
|
|
2801
3187
|
return taxBuilt;
|
|
@@ -2808,10 +3194,10 @@ var EReportingEndpoint = class {
|
|
|
2808
3194
|
/** Applies the optional scalar fields shared by create and update. */
|
|
2809
3195
|
applyOptionalTransactionFields(body, input) {
|
|
2810
3196
|
if (input.dueDate !== void 0) body["due_date"] = this.formatDate(input.dueDate);
|
|
2811
|
-
if (input.currency !== void 0) body["currency"] = input.currency;
|
|
3197
|
+
if (input.currency !== void 0) body["currency"] = input.currency.toUpperCase();
|
|
2812
3198
|
if (input.taxAmount !== void 0) body["tax_amount"] = input.taxAmount;
|
|
2813
3199
|
if (input.counterpartyName !== void 0) body["counterparty_name"] = input.counterpartyName;
|
|
2814
|
-
if (input.counterpartyCountry !== void 0) body["counterparty_country"] = input.counterpartyCountry;
|
|
3200
|
+
if (input.counterpartyCountry !== void 0) body["counterparty_country"] = input.counterpartyCountry.toUpperCase();
|
|
2815
3201
|
if (input.counterpartyVatNumber !== void 0) body["counterparty_vat_number"] = input.counterpartyVatNumber;
|
|
2816
3202
|
if (input.providerContactId !== void 0) body["provider_contact_id"] = input.providerContactId;
|
|
2817
3203
|
if (input.paymentMethod !== void 0) body["payment_method"] = input.paymentMethod;
|
|
@@ -3479,13 +3865,13 @@ var SmartEnrichmentHistoryPage = class _SmartEnrichmentHistoryPage {
|
|
|
3479
3865
|
};
|
|
3480
3866
|
|
|
3481
3867
|
// src/dto/SmartEnrichmentStatistics.ts
|
|
3482
|
-
function
|
|
3868
|
+
function asString12(value, fallback = "") {
|
|
3483
3869
|
return typeof value === "string" ? value : fallback;
|
|
3484
3870
|
}
|
|
3485
3871
|
function asNumber7(value) {
|
|
3486
3872
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
3487
3873
|
}
|
|
3488
|
-
function
|
|
3874
|
+
function asRecord5(value) {
|
|
3489
3875
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
3490
3876
|
}
|
|
3491
3877
|
function asRecords2(value) {
|
|
@@ -3508,32 +3894,32 @@ var SmartEnrichmentStatistics = class _SmartEnrichmentStatistics {
|
|
|
3508
3894
|
byOutcome;
|
|
3509
3895
|
confidenceBuckets;
|
|
3510
3896
|
static fromArray(data) {
|
|
3511
|
-
const range =
|
|
3512
|
-
const totals =
|
|
3897
|
+
const range = asRecord5(data["range"]);
|
|
3898
|
+
const totals = asRecord5(data["totals"]);
|
|
3513
3899
|
const timeSeries = asRecords2(data["time_series"]).map((row) => ({
|
|
3514
|
-
bucket:
|
|
3900
|
+
bucket: asString12(row["bucket"]),
|
|
3515
3901
|
jobs: asNumber7(row["jobs"]),
|
|
3516
3902
|
items: asNumber7(row["items"]),
|
|
3517
3903
|
found: asNumber7(row["found"])
|
|
3518
3904
|
}));
|
|
3519
3905
|
const bySource = asRecords2(data["by_source"]).map((row) => ({
|
|
3520
|
-
source:
|
|
3906
|
+
source: asString12(row["source"]),
|
|
3521
3907
|
jobs: asNumber7(row["jobs"]),
|
|
3522
3908
|
found: asNumber7(row["found"])
|
|
3523
3909
|
}));
|
|
3524
3910
|
const byOutcome = asRecords2(data["by_outcome"]).map((row) => ({
|
|
3525
|
-
outcome:
|
|
3911
|
+
outcome: asString12(row["outcome"]),
|
|
3526
3912
|
count: asNumber7(row["count"])
|
|
3527
3913
|
}));
|
|
3528
3914
|
const confidenceBuckets = asRecords2(data["confidence_buckets"]).map((row) => ({
|
|
3529
|
-
bucket:
|
|
3915
|
+
bucket: asString12(row["bucket"]),
|
|
3530
3916
|
count: asNumber7(row["count"])
|
|
3531
3917
|
}));
|
|
3532
3918
|
return new _SmartEnrichmentStatistics(
|
|
3533
3919
|
{
|
|
3534
|
-
dateFrom:
|
|
3535
|
-
dateTo:
|
|
3536
|
-
interval:
|
|
3920
|
+
dateFrom: asString12(range["date_from"]),
|
|
3921
|
+
dateTo: asString12(range["date_to"]),
|
|
3922
|
+
interval: asString12(range["interval"])
|
|
3537
3923
|
},
|
|
3538
3924
|
{
|
|
3539
3925
|
jobs: asNumber7(totals["jobs"]),
|
|
@@ -4890,6 +5276,68 @@ var Language = {
|
|
|
4890
5276
|
GERMAN: "de"
|
|
4891
5277
|
};
|
|
4892
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
|
+
|
|
4893
5341
|
// src/webhooks/WebhookSignature.ts
|
|
4894
5342
|
var import_node_crypto = require("crypto");
|
|
4895
5343
|
var WEBHOOK_SIGNATURE_HEADER = "Taxora-Signature";
|
|
@@ -5022,12 +5470,16 @@ function isTransactionBuyerRejected(object) {
|
|
|
5022
5470
|
BillingEndpoint,
|
|
5023
5471
|
CompanyAddress,
|
|
5024
5472
|
CompanyEndpoint,
|
|
5473
|
+
ComplianceDeliveryAction,
|
|
5474
|
+
ComplianceDeliveryDecision,
|
|
5025
5475
|
ComplianceDocumentType,
|
|
5026
5476
|
ComplianceEnrollment,
|
|
5027
5477
|
ComplianceEnrollmentPage,
|
|
5028
5478
|
ComplianceEnrollmentStatus,
|
|
5029
5479
|
ComplianceOfferings,
|
|
5030
5480
|
ComplianceService,
|
|
5481
|
+
ComplianceTax,
|
|
5482
|
+
ComplianceTaxCategory,
|
|
5031
5483
|
ComplianceTaxReport,
|
|
5032
5484
|
ComplianceTaxReportPage,
|
|
5033
5485
|
ComplianceTaxReportState,
|
|
@@ -5036,6 +5488,8 @@ function isTransactionBuyerRejected(object) {
|
|
|
5036
5488
|
ComplianceTransactionState,
|
|
5037
5489
|
ComplianceTransactionType,
|
|
5038
5490
|
DEFAULT_WEBHOOK_TOLERANCE_SECONDS,
|
|
5491
|
+
EInvoicingQuota,
|
|
5492
|
+
EInvoicingQuotaPeriod,
|
|
5039
5493
|
EReportingEndpoint,
|
|
5040
5494
|
Environment,
|
|
5041
5495
|
FetchHttpClient,
|
|
@@ -5083,6 +5537,7 @@ function isTransactionBuyerRejected(object) {
|
|
|
5083
5537
|
WEBHOOK_ID_HEADER,
|
|
5084
5538
|
WEBHOOK_SIGNATURE_HEADER,
|
|
5085
5539
|
WebhookSignatureVerificationError,
|
|
5540
|
+
calculateInvoiceTotals,
|
|
5086
5541
|
computeWebhookSignature,
|
|
5087
5542
|
constructWebhookEvent,
|
|
5088
5543
|
describeApiError,
|
|
@@ -5101,6 +5556,7 @@ function isTransactionBuyerRejected(object) {
|
|
|
5101
5556
|
getBaseUrl,
|
|
5102
5557
|
getFailedVatStates,
|
|
5103
5558
|
isTransactionBuyerRejected,
|
|
5559
|
+
parseComplianceTaxCategory,
|
|
5104
5560
|
parseLocalTaxNumbersCsv,
|
|
5105
5561
|
parseWebhookSignatureHeader,
|
|
5106
5562
|
toComplianceDocumentType,
|