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