@taxora/sdk 1.8.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +97 -0
- package/README.md +239 -4
- package/dist/index.cjs +1382 -242
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +748 -63
- package/dist/index.d.ts +748 -63
- package/dist/index.js +1369 -242
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -440,6 +440,356 @@ var AuthEndpoint = class {
|
|
|
440
440
|
}
|
|
441
441
|
};
|
|
442
442
|
|
|
443
|
+
// src/dto/EInvoicingQuota.ts
|
|
444
|
+
function asString(value, fallback = "") {
|
|
445
|
+
return typeof value === "string" ? value : fallback;
|
|
446
|
+
}
|
|
447
|
+
function asNullableString(value) {
|
|
448
|
+
return typeof value === "string" ? value : null;
|
|
449
|
+
}
|
|
450
|
+
function asInt(value) {
|
|
451
|
+
return typeof value === "number" ? Math.trunc(value) : Number(value ?? 0) || 0;
|
|
452
|
+
}
|
|
453
|
+
function asRecord(value) {
|
|
454
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
455
|
+
}
|
|
456
|
+
var EInvoicingQuotaPeriod = class _EInvoicingQuotaPeriod {
|
|
457
|
+
constructor(period, used, included, freeUsed, freeRemaining, billableUnits, amount, monthlyFee, total, state, byCountry, billedAt, manualReference, monthlyFeeReference) {
|
|
458
|
+
this.period = period;
|
|
459
|
+
this.used = used;
|
|
460
|
+
this.included = included;
|
|
461
|
+
this.freeUsed = freeUsed;
|
|
462
|
+
this.freeRemaining = freeRemaining;
|
|
463
|
+
this.billableUnits = billableUnits;
|
|
464
|
+
this.amount = amount;
|
|
465
|
+
this.monthlyFee = monthlyFee;
|
|
466
|
+
this.total = total;
|
|
467
|
+
this.state = state;
|
|
468
|
+
this.byCountry = byCountry;
|
|
469
|
+
this.billedAt = billedAt;
|
|
470
|
+
this.manualReference = manualReference;
|
|
471
|
+
this.monthlyFeeReference = monthlyFeeReference;
|
|
472
|
+
}
|
|
473
|
+
period;
|
|
474
|
+
used;
|
|
475
|
+
included;
|
|
476
|
+
freeUsed;
|
|
477
|
+
freeRemaining;
|
|
478
|
+
billableUnits;
|
|
479
|
+
amount;
|
|
480
|
+
monthlyFee;
|
|
481
|
+
total;
|
|
482
|
+
state;
|
|
483
|
+
byCountry;
|
|
484
|
+
billedAt;
|
|
485
|
+
manualReference;
|
|
486
|
+
monthlyFeeReference;
|
|
487
|
+
static fromArray(data) {
|
|
488
|
+
const byCountry = {};
|
|
489
|
+
if (Array.isArray(data["by_country"])) {
|
|
490
|
+
for (const row of data["by_country"]) {
|
|
491
|
+
const entry = asRecord(row);
|
|
492
|
+
if (entry && typeof entry["country"] === "string") {
|
|
493
|
+
byCountry[entry["country"]] = asInt(entry["used"]);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return new _EInvoicingQuotaPeriod(
|
|
498
|
+
asString(data["period"]),
|
|
499
|
+
asInt(data["used"]),
|
|
500
|
+
asInt(data["included"]),
|
|
501
|
+
asInt(data["free_used"]),
|
|
502
|
+
asInt(data["free_remaining"]),
|
|
503
|
+
asInt(data["billable_units"]),
|
|
504
|
+
asString(data["amount"], "0.00"),
|
|
505
|
+
asNullableString(data["monthly_fee"]),
|
|
506
|
+
asString(data["total"], "0.00"),
|
|
507
|
+
asString(data["state"], "open"),
|
|
508
|
+
byCountry,
|
|
509
|
+
asNullableString(data["billed_at"]),
|
|
510
|
+
asNullableString(data["manual_reference"]),
|
|
511
|
+
asNullableString(data["monthly_fee_reference"])
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
var EInvoicingQuota = class _EInvoicingQuota {
|
|
516
|
+
constructor(billing, plan, currency, included, tiers, countries, monthlyFee, current, history) {
|
|
517
|
+
this.billing = billing;
|
|
518
|
+
this.plan = plan;
|
|
519
|
+
this.currency = currency;
|
|
520
|
+
this.included = included;
|
|
521
|
+
this.tiers = tiers;
|
|
522
|
+
this.countries = countries;
|
|
523
|
+
this.monthlyFee = monthlyFee;
|
|
524
|
+
this.current = current;
|
|
525
|
+
this.history = history;
|
|
526
|
+
}
|
|
527
|
+
billing;
|
|
528
|
+
plan;
|
|
529
|
+
currency;
|
|
530
|
+
included;
|
|
531
|
+
tiers;
|
|
532
|
+
countries;
|
|
533
|
+
monthlyFee;
|
|
534
|
+
current;
|
|
535
|
+
history;
|
|
536
|
+
static fromArray(data) {
|
|
537
|
+
const tiers = [];
|
|
538
|
+
if (Array.isArray(data["tiers"])) {
|
|
539
|
+
for (const row of data["tiers"]) {
|
|
540
|
+
const tier = asRecord(row);
|
|
541
|
+
if (tier) {
|
|
542
|
+
tiers.push({
|
|
543
|
+
upTo: tier["up_to"] === null || tier["up_to"] === void 0 ? null : asInt(tier["up_to"]),
|
|
544
|
+
unitPrice: asString(tier["unit_price"], "0")
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
const history = [];
|
|
550
|
+
if (Array.isArray(data["history"])) {
|
|
551
|
+
for (const row of data["history"]) {
|
|
552
|
+
const period = asRecord(row);
|
|
553
|
+
if (period) history.push(EInvoicingQuotaPeriod.fromArray(period));
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return new _EInvoicingQuota(
|
|
557
|
+
asString(data["billing"], "invoice"),
|
|
558
|
+
asNullableString(data["plan"]),
|
|
559
|
+
asString(data["currency"], "EUR"),
|
|
560
|
+
asInt(data["included"]),
|
|
561
|
+
tiers,
|
|
562
|
+
Array.isArray(data["countries"]) ? data["countries"].map(String) : [],
|
|
563
|
+
asNullableString(data["monthly_fee"]),
|
|
564
|
+
EInvoicingQuotaPeriod.fromArray(asRecord(data["current"]) ?? {}),
|
|
565
|
+
history
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
// src/dto/UsageSummary.ts
|
|
571
|
+
function asRecord2(value) {
|
|
572
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
573
|
+
}
|
|
574
|
+
function asRecordList(value) {
|
|
575
|
+
return Array.isArray(value) ? value.filter((row) => asRecord2(row) !== null) : [];
|
|
576
|
+
}
|
|
577
|
+
function asString2(value, fallback = "") {
|
|
578
|
+
return typeof value === "string" ? value : fallback;
|
|
579
|
+
}
|
|
580
|
+
function asNullableString2(value) {
|
|
581
|
+
return typeof value === "string" ? value : null;
|
|
582
|
+
}
|
|
583
|
+
function asNumber(value) {
|
|
584
|
+
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
585
|
+
}
|
|
586
|
+
function asNullableNumber(value) {
|
|
587
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
588
|
+
const num = Number(value);
|
|
589
|
+
return Number.isFinite(num) ? num : null;
|
|
590
|
+
}
|
|
591
|
+
var UsageSummary = class _UsageSummary {
|
|
592
|
+
constructor(id, companyId, feature, country, regime, billingPeriod, eventCount, billableAmount, state, stateLabel, periodClosedAt, billingTriggeredAt, billedAt, stripeInvoiceId, stripeSubscriptionItemId, billingNotes, events, createdAt, updatedAt) {
|
|
593
|
+
this.id = id;
|
|
594
|
+
this.companyId = companyId;
|
|
595
|
+
this.feature = feature;
|
|
596
|
+
this.country = country;
|
|
597
|
+
this.regime = regime;
|
|
598
|
+
this.billingPeriod = billingPeriod;
|
|
599
|
+
this.eventCount = eventCount;
|
|
600
|
+
this.billableAmount = billableAmount;
|
|
601
|
+
this.state = state;
|
|
602
|
+
this.stateLabel = stateLabel;
|
|
603
|
+
this.periodClosedAt = periodClosedAt;
|
|
604
|
+
this.billingTriggeredAt = billingTriggeredAt;
|
|
605
|
+
this.billedAt = billedAt;
|
|
606
|
+
this.stripeInvoiceId = stripeInvoiceId;
|
|
607
|
+
this.stripeSubscriptionItemId = stripeSubscriptionItemId;
|
|
608
|
+
this.billingNotes = billingNotes;
|
|
609
|
+
this.events = events;
|
|
610
|
+
this.createdAt = createdAt;
|
|
611
|
+
this.updatedAt = updatedAt;
|
|
612
|
+
}
|
|
613
|
+
id;
|
|
614
|
+
companyId;
|
|
615
|
+
feature;
|
|
616
|
+
country;
|
|
617
|
+
regime;
|
|
618
|
+
billingPeriod;
|
|
619
|
+
eventCount;
|
|
620
|
+
billableAmount;
|
|
621
|
+
state;
|
|
622
|
+
stateLabel;
|
|
623
|
+
periodClosedAt;
|
|
624
|
+
billingTriggeredAt;
|
|
625
|
+
billedAt;
|
|
626
|
+
stripeInvoiceId;
|
|
627
|
+
stripeSubscriptionItemId;
|
|
628
|
+
billingNotes;
|
|
629
|
+
events;
|
|
630
|
+
createdAt;
|
|
631
|
+
updatedAt;
|
|
632
|
+
static fromArray(data) {
|
|
633
|
+
const amount = data["billable_amount"];
|
|
634
|
+
return new _UsageSummary(
|
|
635
|
+
asNumber(data["id"]),
|
|
636
|
+
asNumber(data["company_id"]),
|
|
637
|
+
asString2(data["feature"]),
|
|
638
|
+
asNullableString2(data["country"]),
|
|
639
|
+
asNullableString2(data["regime"]),
|
|
640
|
+
asString2(data["billing_period"]),
|
|
641
|
+
asNumber(data["event_count"]),
|
|
642
|
+
typeof amount === "string" ? amount : typeof amount === "number" ? String(amount) : null,
|
|
643
|
+
asNullableString2(data["state"]),
|
|
644
|
+
asNullableString2(data["state_label"]),
|
|
645
|
+
asNullableString2(data["period_closed_at"]),
|
|
646
|
+
asNullableString2(data["billing_triggered_at"]),
|
|
647
|
+
asNullableString2(data["billed_at"]),
|
|
648
|
+
asNullableString2(data["stripe_invoice_id"]),
|
|
649
|
+
asNullableString2(data["stripe_subscription_item_id"]),
|
|
650
|
+
asNullableString2(data["billing_notes"]),
|
|
651
|
+
asRecordList(data["events"]).map((event) => ({
|
|
652
|
+
id: asNumber(event["id"]),
|
|
653
|
+
usageSummaryId: asNullableNumber(event["usage_summary_id"]),
|
|
654
|
+
feature: asString2(event["feature"]),
|
|
655
|
+
country: asNullableString2(event["country"]),
|
|
656
|
+
regime: asNullableString2(event["regime"]),
|
|
657
|
+
billingPeriod: asString2(event["billing_period"]),
|
|
658
|
+
resourceType: asNullableString2(event["resource_type"]),
|
|
659
|
+
resourceId: asNullableNumber(event["resource_id"]),
|
|
660
|
+
stripeUsageRecordId: asNullableString2(event["stripe_usage_record_id"]),
|
|
661
|
+
stripeReportedAt: asNullableString2(event["stripe_reported_at"]),
|
|
662
|
+
createdAt: asNullableString2(event["created_at"])
|
|
663
|
+
})),
|
|
664
|
+
asNullableString2(data["created_at"]),
|
|
665
|
+
asNullableString2(data["updated_at"])
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
var UsageSummaryPage = class _UsageSummaryPage {
|
|
670
|
+
constructor(rows, currentPage, perPage, total, lastPage) {
|
|
671
|
+
this.rows = rows;
|
|
672
|
+
this.currentPage = currentPage;
|
|
673
|
+
this.perPage = perPage;
|
|
674
|
+
this.total = total;
|
|
675
|
+
this.lastPage = lastPage;
|
|
676
|
+
}
|
|
677
|
+
rows;
|
|
678
|
+
currentPage;
|
|
679
|
+
perPage;
|
|
680
|
+
total;
|
|
681
|
+
lastPage;
|
|
682
|
+
static fromResponse(payload) {
|
|
683
|
+
const obj = asRecord2(payload) ?? {};
|
|
684
|
+
const rows = asRecordList(obj["data"]).map(UsageSummary.fromArray);
|
|
685
|
+
const meta = asRecord2(obj["meta"]) ?? {};
|
|
686
|
+
const num = (value, fallback) => typeof value === "number" ? value : fallback;
|
|
687
|
+
return new _UsageSummaryPage(
|
|
688
|
+
rows,
|
|
689
|
+
num(meta["current_page"], 1),
|
|
690
|
+
num(meta["per_page"], rows.length),
|
|
691
|
+
num(meta["total"], rows.length),
|
|
692
|
+
num(meta["last_page"], 1)
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
[Symbol.iterator]() {
|
|
696
|
+
return this.rows[Symbol.iterator]();
|
|
697
|
+
}
|
|
698
|
+
get length() {
|
|
699
|
+
return this.rows.length;
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
// src/endpoints/BillingEndpoint.ts
|
|
704
|
+
var PERIOD_REGEX = /^\d{4}-(0[1-9]|1[0-2])$/;
|
|
705
|
+
var BillingEndpoint = class {
|
|
706
|
+
constructor(baseUrl, apiKey, tokenStorage, httpClient, retryPolicy = new RetryPolicy()) {
|
|
707
|
+
this.baseUrl = baseUrl;
|
|
708
|
+
this.apiKey = apiKey;
|
|
709
|
+
this.tokenStorage = tokenStorage;
|
|
710
|
+
this.httpClient = httpClient;
|
|
711
|
+
this.retryPolicy = retryPolicy;
|
|
712
|
+
}
|
|
713
|
+
baseUrl;
|
|
714
|
+
apiKey;
|
|
715
|
+
tokenStorage;
|
|
716
|
+
httpClient;
|
|
717
|
+
retryPolicy;
|
|
718
|
+
/** Paginated usage summaries, newest billing period first. `perPage` is clamped to 1-100 server-side. */
|
|
719
|
+
async listUsageSummaries(page = 1, perPage = 25) {
|
|
720
|
+
const params = new URLSearchParams();
|
|
721
|
+
params.set("page", String(page));
|
|
722
|
+
params.set("per_page", String(perPage));
|
|
723
|
+
const data = await this.getJson(`${this.baseUrl}/billing/usage-summaries?${params.toString()}`);
|
|
724
|
+
return UsageSummaryPage.fromResponse(data);
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* The usage summaries of one billing period (YYYY-MM), one per feature,
|
|
728
|
+
* each with its usage events. A period without usage yields a 404 (HttpException).
|
|
729
|
+
*/
|
|
730
|
+
async getUsageSummaries(period) {
|
|
731
|
+
if (!PERIOD_REGEX.test(period)) {
|
|
732
|
+
throw new TaxoraException(`Invalid billing period "${period}". Expected YYYY-MM, e.g. "2026-09".`);
|
|
733
|
+
}
|
|
734
|
+
const data = await this.getJson(`${this.baseUrl}/billing/usage-summaries/${period}`);
|
|
735
|
+
return Array.isArray(data) ? data.filter((row) => typeof row === "object" && row !== null).map(UsageSummary.fromArray) : [];
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* The e-invoicing allowance: documents included per month, used, still free and
|
|
739
|
+
* what is due for the documents beyond it — one pool over all e-invoicing
|
|
740
|
+
* countries, current month first, then up to `months - 1` earlier months (1-24).
|
|
741
|
+
* Resolves to null when the company has no e-invoicing.
|
|
742
|
+
*/
|
|
743
|
+
async getEInvoicingQuota(months = 6) {
|
|
744
|
+
if (!Number.isInteger(months) || months < 1 || months > 24) {
|
|
745
|
+
throw new TaxoraException("months must be an integer between 1 and 24.");
|
|
746
|
+
}
|
|
747
|
+
const data = await this.getJson(`${this.baseUrl}/billing/einvoicing-quota?months=${months}`);
|
|
748
|
+
if (typeof data !== "object" || data === null || Array.isArray(data) || !("billing" in data)) {
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
751
|
+
return EInvoicingQuota.fromArray(data);
|
|
752
|
+
}
|
|
753
|
+
buildHeaders() {
|
|
754
|
+
const headers = {
|
|
755
|
+
"Content-Type": "application/json",
|
|
756
|
+
"x-api-key": this.apiKey
|
|
757
|
+
};
|
|
758
|
+
const token = this.tokenStorage.get();
|
|
759
|
+
if (token) {
|
|
760
|
+
headers["Authorization"] = `Bearer ${token.accessToken}`;
|
|
761
|
+
}
|
|
762
|
+
return headers;
|
|
763
|
+
}
|
|
764
|
+
/** GET + envelope unwrap (`{success, data}` → data, which may be an object or an array). */
|
|
765
|
+
async getJson(url) {
|
|
766
|
+
return withRetries(this.retryPolicy, "billing request", async () => {
|
|
767
|
+
const response = await this.httpClient.request("GET", url, { headers: this.buildHeaders() });
|
|
768
|
+
const responseText = await response.text();
|
|
769
|
+
if (!response.ok) {
|
|
770
|
+
throw new HttpException(
|
|
771
|
+
describeApiError(responseText, response.status),
|
|
772
|
+
response.status,
|
|
773
|
+
responseText,
|
|
774
|
+
{},
|
|
775
|
+
response.headers.get("retry-after")
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
let parsed;
|
|
779
|
+
try {
|
|
780
|
+
parsed = JSON.parse(responseText);
|
|
781
|
+
} catch {
|
|
782
|
+
throw new HttpException("Invalid JSON response", response.status, responseText);
|
|
783
|
+
}
|
|
784
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
785
|
+
throw new HttpException("Unexpected response format", response.status, responseText);
|
|
786
|
+
}
|
|
787
|
+
const obj = parsed;
|
|
788
|
+
return obj["data"] !== void 0 && obj["data"] !== null ? obj["data"] : obj;
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
|
|
443
793
|
// src/dto/CompanyResource.ts
|
|
444
794
|
function normalizeCompanyResource(data) {
|
|
445
795
|
const apiRateLimit = typeof data["api_rate_limit"] === "number" ? data["api_rate_limit"] : void 0;
|
|
@@ -505,6 +855,67 @@ var CompanyEndpoint = class {
|
|
|
505
855
|
}
|
|
506
856
|
};
|
|
507
857
|
|
|
858
|
+
// src/enums/ComplianceDeliveryAction.ts
|
|
859
|
+
var ComplianceDeliveryAction = {
|
|
860
|
+
/** The buyer can receive e-invoices: create the transaction, Taxora sends it. */
|
|
861
|
+
EINVOICE: "einvoice",
|
|
862
|
+
/** The buyer is not on the network: send your regular invoice yourself (a transaction would end as not_required and is not billed). */
|
|
863
|
+
REGULAR_INVOICE: "regular_invoice",
|
|
864
|
+
/** No definite directory answer yet: create the transaction anyway, Taxora asks again before sending. */
|
|
865
|
+
EINVOICE_PENDING: "einvoice_pending"
|
|
866
|
+
};
|
|
867
|
+
|
|
868
|
+
// src/enums/PeppolLookupStatus.ts
|
|
869
|
+
var PeppolLookupStatus = {
|
|
870
|
+
REACHABLE: "reachable",
|
|
871
|
+
NOT_REACHABLE: "not_reachable",
|
|
872
|
+
PENDING: "pending",
|
|
873
|
+
UNKNOWN: "unknown"
|
|
874
|
+
};
|
|
875
|
+
var VALUES = Object.values(PeppolLookupStatus);
|
|
876
|
+
function toPeppolLookupStatus(value) {
|
|
877
|
+
if (typeof value === "string") {
|
|
878
|
+
const normalized = value.trim().toLowerCase();
|
|
879
|
+
if (VALUES.includes(normalized)) {
|
|
880
|
+
return normalized;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return PeppolLookupStatus.UNKNOWN;
|
|
884
|
+
}
|
|
885
|
+
var DESCRIPTIONS = {
|
|
886
|
+
[PeppolLookupStatus.REACHABLE]: "The participant is registered on Peppol and can receive e-invoices.",
|
|
887
|
+
[PeppolLookupStatus.NOT_REACHABLE]: "The participant is not registered on Peppol.",
|
|
888
|
+
[PeppolLookupStatus.PENDING]: "The directory lookup is still resolving \u2014 ask again in a few seconds.",
|
|
889
|
+
[PeppolLookupStatus.UNKNOWN]: "Unknown lookup status."
|
|
890
|
+
};
|
|
891
|
+
function describePeppolLookupStatus(status) {
|
|
892
|
+
return DESCRIPTIONS[status];
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// src/dto/ComplianceDeliveryDecision.ts
|
|
896
|
+
var ComplianceDeliveryDecision = class _ComplianceDeliveryDecision {
|
|
897
|
+
constructor(action, lookup) {
|
|
898
|
+
this.action = action;
|
|
899
|
+
this.lookup = lookup;
|
|
900
|
+
}
|
|
901
|
+
action;
|
|
902
|
+
lookup;
|
|
903
|
+
/** True unless the buyer is not on the network. */
|
|
904
|
+
get sendsEInvoice() {
|
|
905
|
+
return this.action !== ComplianceDeliveryAction.REGULAR_INVOICE;
|
|
906
|
+
}
|
|
907
|
+
static fromLookup(lookup) {
|
|
908
|
+
switch (lookup.status) {
|
|
909
|
+
case PeppolLookupStatus.REACHABLE:
|
|
910
|
+
return new _ComplianceDeliveryDecision(ComplianceDeliveryAction.EINVOICE, lookup);
|
|
911
|
+
case PeppolLookupStatus.NOT_REACHABLE:
|
|
912
|
+
return new _ComplianceDeliveryDecision(ComplianceDeliveryAction.REGULAR_INVOICE, lookup);
|
|
913
|
+
default:
|
|
914
|
+
return new _ComplianceDeliveryDecision(ComplianceDeliveryAction.EINVOICE_PENDING, lookup);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
|
|
508
919
|
// src/enums/ComplianceEnrollmentStatus.ts
|
|
509
920
|
var ComplianceEnrollmentStatus = {
|
|
510
921
|
PENDING_DATA: "pending_data",
|
|
@@ -516,17 +927,17 @@ var ComplianceEnrollmentStatus = {
|
|
|
516
927
|
ERROR_ACTIVATION: "error_activation",
|
|
517
928
|
UNKNOWN: "unknown"
|
|
518
929
|
};
|
|
519
|
-
var
|
|
930
|
+
var VALUES2 = Object.values(ComplianceEnrollmentStatus);
|
|
520
931
|
function toComplianceEnrollmentStatus(value) {
|
|
521
932
|
if (typeof value === "string") {
|
|
522
933
|
const normalized = value.trim().toLowerCase();
|
|
523
|
-
if (
|
|
934
|
+
if (VALUES2.includes(normalized)) {
|
|
524
935
|
return normalized;
|
|
525
936
|
}
|
|
526
937
|
}
|
|
527
938
|
return ComplianceEnrollmentStatus.UNKNOWN;
|
|
528
939
|
}
|
|
529
|
-
var
|
|
940
|
+
var DESCRIPTIONS2 = {
|
|
530
941
|
[ComplianceEnrollmentStatus.PENDING_DATA]: "Awaiting merchant data before provisioning can start.",
|
|
531
942
|
[ComplianceEnrollmentStatus.ACCOUNT_CREATED]: "Provider account exists. Regime not yet activated.",
|
|
532
943
|
[ComplianceEnrollmentStatus.REGIME_ACTIVATED]: "Compliance regime activated at provider. Ready to submit transactions.",
|
|
@@ -537,7 +948,7 @@ var DESCRIPTIONS = {
|
|
|
537
948
|
[ComplianceEnrollmentStatus.UNKNOWN]: "Unknown status."
|
|
538
949
|
};
|
|
539
950
|
function describeComplianceEnrollmentStatus(status) {
|
|
540
|
-
return
|
|
951
|
+
return DESCRIPTIONS2[status];
|
|
541
952
|
}
|
|
542
953
|
|
|
543
954
|
// src/enums/ComplianceService.ts
|
|
@@ -546,33 +957,33 @@ var ComplianceService = {
|
|
|
546
957
|
E_INVOICING: "e_invoicing",
|
|
547
958
|
UNKNOWN: "unknown"
|
|
548
959
|
};
|
|
549
|
-
var
|
|
960
|
+
var VALUES3 = Object.values(ComplianceService);
|
|
550
961
|
function toComplianceService(value) {
|
|
551
962
|
if (typeof value === "string") {
|
|
552
963
|
const normalized = value.trim().toLowerCase();
|
|
553
|
-
if (
|
|
964
|
+
if (VALUES3.includes(normalized)) {
|
|
554
965
|
return normalized;
|
|
555
966
|
}
|
|
556
967
|
}
|
|
557
968
|
return ComplianceService.UNKNOWN;
|
|
558
969
|
}
|
|
559
|
-
var
|
|
970
|
+
var DESCRIPTIONS3 = {
|
|
560
971
|
[ComplianceService.E_REPORTING]: "E-reporting of transaction data to the tax authority (France: DGFiP Flux 10).",
|
|
561
972
|
[ComplianceService.E_INVOICING]: "Structured e-invoices delivered to the buyer (Norway: EHF / Peppol BIS 3.0).",
|
|
562
973
|
[ComplianceService.UNKNOWN]: "Unknown service."
|
|
563
974
|
};
|
|
564
975
|
function describeComplianceService(service) {
|
|
565
|
-
return
|
|
976
|
+
return DESCRIPTIONS3[service];
|
|
566
977
|
}
|
|
567
978
|
|
|
568
979
|
// src/dto/ComplianceEnrollment.ts
|
|
569
|
-
function
|
|
980
|
+
function asString3(value, fallback = "") {
|
|
570
981
|
return typeof value === "string" ? value : fallback;
|
|
571
982
|
}
|
|
572
|
-
function
|
|
983
|
+
function asNullableString3(value) {
|
|
573
984
|
return typeof value === "string" ? value : null;
|
|
574
985
|
}
|
|
575
|
-
function
|
|
986
|
+
function asNumber2(value) {
|
|
576
987
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
577
988
|
}
|
|
578
989
|
var ComplianceEnrollment = class _ComplianceEnrollment {
|
|
@@ -619,29 +1030,114 @@ var ComplianceEnrollment = class _ComplianceEnrollment {
|
|
|
619
1030
|
static fromArray(data) {
|
|
620
1031
|
const regimeConfig = typeof data["regime_config"] === "object" && data["regime_config"] !== null && !Array.isArray(data["regime_config"]) ? data["regime_config"] : null;
|
|
621
1032
|
return new _ComplianceEnrollment(
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
1033
|
+
asNumber2(data["id"]),
|
|
1034
|
+
asNumber2(data["company_id"]),
|
|
1035
|
+
asString3(data["country"]),
|
|
1036
|
+
asString3(data["regime"]),
|
|
1037
|
+
asString3(data["provider"]),
|
|
627
1038
|
toComplianceEnrollmentStatus(data["status"]),
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
1039
|
+
asString3(data["status_label"]),
|
|
1040
|
+
asNullableString3(data["status_error"]),
|
|
1041
|
+
asNullableString3(data["provider_account_id"]),
|
|
1042
|
+
asNullableString3(data["tax_id"]),
|
|
1043
|
+
asNullableString3(data["company_register_id"]),
|
|
1044
|
+
asNullableString3(data["company_register_scheme"]),
|
|
634
1045
|
regimeConfig,
|
|
635
|
-
|
|
636
|
-
|
|
1046
|
+
asNullableString3(data["reporting_start_date"]),
|
|
1047
|
+
asNullableString3(data["notification_email"]),
|
|
637
1048
|
data["auto_send"] === true,
|
|
638
|
-
|
|
639
|
-
|
|
1049
|
+
asNullableString3(data["created_at"]),
|
|
1050
|
+
asNullableString3(data["updated_at"]),
|
|
640
1051
|
toComplianceService(data["service"])
|
|
641
1052
|
);
|
|
642
1053
|
}
|
|
643
1054
|
};
|
|
644
1055
|
|
|
1056
|
+
// src/dto/ComplianceOfferings.ts
|
|
1057
|
+
function asRecord3(value) {
|
|
1058
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
1059
|
+
}
|
|
1060
|
+
function asRecordList2(value) {
|
|
1061
|
+
return Array.isArray(value) ? value.filter((row) => asRecord3(row) !== null) : [];
|
|
1062
|
+
}
|
|
1063
|
+
function asString4(value, fallback = "") {
|
|
1064
|
+
return typeof value === "string" ? value : fallback;
|
|
1065
|
+
}
|
|
1066
|
+
function asNullableString4(value) {
|
|
1067
|
+
return typeof value === "string" ? value : null;
|
|
1068
|
+
}
|
|
1069
|
+
function asNullableNumber2(value) {
|
|
1070
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
1071
|
+
if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
|
|
1072
|
+
return null;
|
|
1073
|
+
}
|
|
1074
|
+
function asStringList(value) {
|
|
1075
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
1076
|
+
}
|
|
1077
|
+
function toRequires(value) {
|
|
1078
|
+
const record = asRecord3(value);
|
|
1079
|
+
return record === null ? null : { country: asString4(record["country"]), service: asString4(record["service"]) };
|
|
1080
|
+
}
|
|
1081
|
+
var ComplianceOfferings = class _ComplianceOfferings {
|
|
1082
|
+
constructor(offerings, coverage, access, accessByCountry) {
|
|
1083
|
+
this.offerings = offerings;
|
|
1084
|
+
this.coverage = coverage;
|
|
1085
|
+
this.access = access;
|
|
1086
|
+
this.accessByCountry = accessByCountry;
|
|
1087
|
+
}
|
|
1088
|
+
offerings;
|
|
1089
|
+
coverage;
|
|
1090
|
+
access;
|
|
1091
|
+
accessByCountry;
|
|
1092
|
+
static fromArray(data) {
|
|
1093
|
+
const offerings = asRecordList2(data["data"]).map((row) => ({
|
|
1094
|
+
country: asString4(row["country"]),
|
|
1095
|
+
service: asString4(row["service"]),
|
|
1096
|
+
currency: asString4(row["currency"], "EUR"),
|
|
1097
|
+
active: row["active"] === true,
|
|
1098
|
+
viaStripe: row["via_stripe"] === true,
|
|
1099
|
+
plan: asNullableString4(row["plan"]),
|
|
1100
|
+
status: asNullableString4(row["status"]),
|
|
1101
|
+
enrolled: row["enrolled"] === true,
|
|
1102
|
+
plans: asRecordList2(row["plans"]).map((plan) => ({
|
|
1103
|
+
key: asString4(plan["key"]),
|
|
1104
|
+
monthlyFee: asNullableNumber2(plan["monthly_fee"]),
|
|
1105
|
+
setupFee: asNullableNumber2(plan["setup_fee"]),
|
|
1106
|
+
usageTiers: Array.isArray(plan["usage_tiers"]) ? plan["usage_tiers"] : [],
|
|
1107
|
+
volumeLimit: plan["volume_limit"] ?? null,
|
|
1108
|
+
contactOnly: plan["contact_only"] === true,
|
|
1109
|
+
checkoutAvailable: plan["checkout_available"] === true,
|
|
1110
|
+
requires: toRequires(plan["requires"]),
|
|
1111
|
+
eligible: plan["eligible"] === true
|
|
1112
|
+
}))
|
|
1113
|
+
}));
|
|
1114
|
+
const coverage = asRecordList2(data["coverage"]).map((row) => ({
|
|
1115
|
+
country: asString4(row["country"]),
|
|
1116
|
+
availability: asString4(row["availability"]),
|
|
1117
|
+
services: asStringList(row["services"]),
|
|
1118
|
+
network: asNullableString4(row["network"]),
|
|
1119
|
+
format: asNullableString4(row["format"]),
|
|
1120
|
+
mandate: asNullableString4(row["mandate"]),
|
|
1121
|
+
support: asString4(row["support"], "yes")
|
|
1122
|
+
}));
|
|
1123
|
+
const access = asRecord3(data["access"]) ?? {};
|
|
1124
|
+
const accessByCountry = {};
|
|
1125
|
+
for (const [country, services] of Object.entries(asRecord3(data["access_by_country"]) ?? {})) {
|
|
1126
|
+
const flags = {};
|
|
1127
|
+
for (const [service, value] of Object.entries(asRecord3(services) ?? {})) {
|
|
1128
|
+
flags[service] = value === true;
|
|
1129
|
+
}
|
|
1130
|
+
accessByCountry[country] = flags;
|
|
1131
|
+
}
|
|
1132
|
+
return new _ComplianceOfferings(
|
|
1133
|
+
offerings,
|
|
1134
|
+
coverage,
|
|
1135
|
+
{ eReporting: access["e_reporting"] === true, eInvoicing: access["e_invoicing"] === true },
|
|
1136
|
+
accessByCountry
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
|
|
645
1141
|
// src/dto/ComplianceEnrollmentPage.ts
|
|
646
1142
|
var ComplianceEnrollmentPage = class _ComplianceEnrollmentPage {
|
|
647
1143
|
constructor(rows, currentPage, perPage, total, lastPage) {
|
|
@@ -687,17 +1183,17 @@ var ComplianceTaxReportState = {
|
|
|
687
1183
|
ERROR: "error",
|
|
688
1184
|
UNKNOWN: "unknown"
|
|
689
1185
|
};
|
|
690
|
-
var
|
|
1186
|
+
var VALUES4 = Object.values(ComplianceTaxReportState);
|
|
691
1187
|
function toComplianceTaxReportState(value) {
|
|
692
1188
|
if (typeof value === "string") {
|
|
693
1189
|
const normalized = value.trim().toLowerCase();
|
|
694
|
-
if (
|
|
1190
|
+
if (VALUES4.includes(normalized)) {
|
|
695
1191
|
return normalized;
|
|
696
1192
|
}
|
|
697
1193
|
}
|
|
698
1194
|
return ComplianceTaxReportState.UNKNOWN;
|
|
699
1195
|
}
|
|
700
|
-
var
|
|
1196
|
+
var DESCRIPTIONS4 = {
|
|
701
1197
|
[ComplianceTaxReportState.NEW]: "Created at provider, waiting for the daily ledger batch.",
|
|
702
1198
|
[ComplianceTaxReportState.SENT]: "Ledger transmitted to PPF.",
|
|
703
1199
|
[ComplianceTaxReportState.ACKNOWLEDGED]: "PPF acknowledged receipt of the ledger.",
|
|
@@ -707,20 +1203,20 @@ var DESCRIPTIONS3 = {
|
|
|
707
1203
|
[ComplianceTaxReportState.UNKNOWN]: "Unknown state."
|
|
708
1204
|
};
|
|
709
1205
|
function describeComplianceTaxReportState(state) {
|
|
710
|
-
return
|
|
1206
|
+
return DESCRIPTIONS4[state];
|
|
711
1207
|
}
|
|
712
1208
|
|
|
713
1209
|
// src/dto/ComplianceTaxReport.ts
|
|
714
|
-
function
|
|
1210
|
+
function asString5(value, fallback = "") {
|
|
715
1211
|
return typeof value === "string" ? value : fallback;
|
|
716
1212
|
}
|
|
717
|
-
function
|
|
1213
|
+
function asNullableString5(value) {
|
|
718
1214
|
return typeof value === "string" ? value : null;
|
|
719
1215
|
}
|
|
720
|
-
function
|
|
1216
|
+
function asNumber3(value) {
|
|
721
1217
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
722
1218
|
}
|
|
723
|
-
function
|
|
1219
|
+
function asNullableNumber3(value) {
|
|
724
1220
|
return typeof value === "number" ? value : null;
|
|
725
1221
|
}
|
|
726
1222
|
function asMoney(value) {
|
|
@@ -771,25 +1267,25 @@ var ComplianceTaxReport = class _ComplianceTaxReport {
|
|
|
771
1267
|
updatedAt;
|
|
772
1268
|
static fromArray(data) {
|
|
773
1269
|
return new _ComplianceTaxReport(
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
1270
|
+
asNumber3(data["id"]),
|
|
1271
|
+
asNumber3(data["company_id"]),
|
|
1272
|
+
asNullableNumber3(data["compliance_transaction_id"]),
|
|
1273
|
+
asNullableNumber3(data["ledger_id"]),
|
|
1274
|
+
asString5(data["country"]),
|
|
1275
|
+
asString5(data["regime"]),
|
|
780
1276
|
toComplianceTaxReportState(data["state"]),
|
|
781
|
-
|
|
1277
|
+
asString5(data["state_label"]),
|
|
782
1278
|
data["is_terminal"] === true,
|
|
783
|
-
|
|
784
|
-
|
|
1279
|
+
asNullableString5(data["provider_tax_report_id"]),
|
|
1280
|
+
asNullableString5(data["provider_ledger_id"]),
|
|
785
1281
|
asMoney(data["base_amount"]),
|
|
786
1282
|
asMoney(data["tax_amount"]),
|
|
787
1283
|
asMoney(data["total_amount"]),
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
1284
|
+
asNullableString5(data["refusal_reason"]),
|
|
1285
|
+
asNullableString5(data["registered_at"]),
|
|
1286
|
+
asNullableString5(data["refused_at"]),
|
|
1287
|
+
asNullableString5(data["created_at"]),
|
|
1288
|
+
asNullableString5(data["updated_at"])
|
|
793
1289
|
);
|
|
794
1290
|
}
|
|
795
1291
|
};
|
|
@@ -835,23 +1331,23 @@ var ComplianceDocumentType = {
|
|
|
835
1331
|
CREDIT_NOTE: "credit_note",
|
|
836
1332
|
UNKNOWN: "unknown"
|
|
837
1333
|
};
|
|
838
|
-
var
|
|
1334
|
+
var VALUES5 = Object.values(ComplianceDocumentType);
|
|
839
1335
|
function toComplianceDocumentType(value) {
|
|
840
1336
|
if (typeof value === "string") {
|
|
841
1337
|
const normalized = value.trim().toLowerCase();
|
|
842
|
-
if (
|
|
1338
|
+
if (VALUES5.includes(normalized)) {
|
|
843
1339
|
return normalized;
|
|
844
1340
|
}
|
|
845
1341
|
}
|
|
846
1342
|
return ComplianceDocumentType.UNKNOWN;
|
|
847
1343
|
}
|
|
848
|
-
var
|
|
1344
|
+
var DESCRIPTIONS5 = {
|
|
849
1345
|
[ComplianceDocumentType.INVOICE]: "Commercial invoice.",
|
|
850
1346
|
[ComplianceDocumentType.CREDIT_NOTE]: "Credit note correcting a previously issued invoice.",
|
|
851
1347
|
[ComplianceDocumentType.UNKNOWN]: "Unknown document type."
|
|
852
1348
|
};
|
|
853
1349
|
function describeComplianceDocumentType(type) {
|
|
854
|
-
return
|
|
1350
|
+
return DESCRIPTIONS5[type];
|
|
855
1351
|
}
|
|
856
1352
|
|
|
857
1353
|
// src/enums/ComplianceTransactionState.ts
|
|
@@ -860,27 +1356,30 @@ var ComplianceTransactionState = {
|
|
|
860
1356
|
SENDING: "sending",
|
|
861
1357
|
SUBMITTED: "submitted",
|
|
862
1358
|
ERROR: "error",
|
|
1359
|
+
/** E-invoicing only: the buyer cannot receive e-invoices — nothing was sent or billed; send your regular invoice. */
|
|
1360
|
+
NOT_REQUIRED: "not_required",
|
|
863
1361
|
UNKNOWN: "unknown"
|
|
864
1362
|
};
|
|
865
|
-
var
|
|
1363
|
+
var VALUES6 = Object.values(ComplianceTransactionState);
|
|
866
1364
|
function toComplianceTransactionState(value) {
|
|
867
1365
|
if (typeof value === "string") {
|
|
868
1366
|
const normalized = value.trim().toLowerCase();
|
|
869
|
-
if (
|
|
1367
|
+
if (VALUES6.includes(normalized)) {
|
|
870
1368
|
return normalized;
|
|
871
1369
|
}
|
|
872
1370
|
}
|
|
873
1371
|
return ComplianceTransactionState.UNKNOWN;
|
|
874
1372
|
}
|
|
875
|
-
var
|
|
1373
|
+
var DESCRIPTIONS6 = {
|
|
876
1374
|
[ComplianceTransactionState.PENDING]: "Recorded locally, not yet submitted to the provider.",
|
|
877
1375
|
[ComplianceTransactionState.SENDING]: "Submission to the provider in progress.",
|
|
878
1376
|
[ComplianceTransactionState.SUBMITTED]: "Successfully submitted to the provider.",
|
|
879
1377
|
[ComplianceTransactionState.ERROR]: "Submission failed. See submissionError.",
|
|
1378
|
+
[ComplianceTransactionState.NOT_REQUIRED]: "The buyer cannot receive e-invoices, so none was sent or billed. Send your regular invoice instead.",
|
|
880
1379
|
[ComplianceTransactionState.UNKNOWN]: "Unknown state."
|
|
881
1380
|
};
|
|
882
1381
|
function describeComplianceTransactionState(state) {
|
|
883
|
-
return
|
|
1382
|
+
return DESCRIPTIONS6[state];
|
|
884
1383
|
}
|
|
885
1384
|
|
|
886
1385
|
// src/enums/ComplianceTransactionType.ts
|
|
@@ -892,17 +1391,17 @@ var ComplianceTransactionType = {
|
|
|
892
1391
|
CROSSBORDER_INBOUND: "crossborder_inbound",
|
|
893
1392
|
UNKNOWN: "unknown"
|
|
894
1393
|
};
|
|
895
|
-
var
|
|
1394
|
+
var VALUES7 = Object.values(ComplianceTransactionType);
|
|
896
1395
|
function toComplianceTransactionType(value) {
|
|
897
1396
|
if (typeof value === "string") {
|
|
898
1397
|
const normalized = value.trim().toLowerCase();
|
|
899
|
-
if (
|
|
1398
|
+
if (VALUES7.includes(normalized)) {
|
|
900
1399
|
return normalized;
|
|
901
1400
|
}
|
|
902
1401
|
}
|
|
903
1402
|
return ComplianceTransactionType.UNKNOWN;
|
|
904
1403
|
}
|
|
905
|
-
var
|
|
1404
|
+
var DESCRIPTIONS7 = {
|
|
906
1405
|
[ComplianceTransactionType.B2C_OUTBOUND]: "Sale to a private person in the merchant country (DGFiP Flux 10.3).",
|
|
907
1406
|
[ComplianceTransactionType.B2B_DOMESTIC_OUTBOUND]: "Sale to a domestic business (DGFiP Flux 1, Phase 2).",
|
|
908
1407
|
[ComplianceTransactionType.B2B_DOMESTIC_INBOUND]: "Purchase from a domestic business (DGFiP Flux 1, Phase 2).",
|
|
@@ -911,17 +1410,17 @@ var DESCRIPTIONS6 = {
|
|
|
911
1410
|
[ComplianceTransactionType.UNKNOWN]: "Unknown transaction type."
|
|
912
1411
|
};
|
|
913
1412
|
function describeComplianceTransactionType(type) {
|
|
914
|
-
return
|
|
1413
|
+
return DESCRIPTIONS7[type];
|
|
915
1414
|
}
|
|
916
1415
|
|
|
917
1416
|
// src/dto/ComplianceTransaction.ts
|
|
918
|
-
function
|
|
1417
|
+
function asString6(value, fallback = "") {
|
|
919
1418
|
return typeof value === "string" ? value : fallback;
|
|
920
1419
|
}
|
|
921
|
-
function
|
|
1420
|
+
function asNullableString6(value) {
|
|
922
1421
|
return typeof value === "string" ? value : null;
|
|
923
1422
|
}
|
|
924
|
-
function
|
|
1423
|
+
function asNumber4(value) {
|
|
925
1424
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
926
1425
|
}
|
|
927
1426
|
function asMoney2(value) {
|
|
@@ -929,34 +1428,55 @@ function asMoney2(value) {
|
|
|
929
1428
|
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
930
1429
|
return "0.00";
|
|
931
1430
|
}
|
|
932
|
-
function
|
|
1431
|
+
function asNullableNumber4(value) {
|
|
933
1432
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
934
1433
|
if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
|
|
935
1434
|
return null;
|
|
936
1435
|
}
|
|
937
|
-
function
|
|
1436
|
+
function asRecord4(value) {
|
|
938
1437
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
939
1438
|
}
|
|
940
|
-
function
|
|
941
|
-
return Array.isArray(value) ? value.filter((row) =>
|
|
1439
|
+
function asRecordList3(value) {
|
|
1440
|
+
return Array.isArray(value) ? value.filter((row) => asRecord4(row) !== null) : [];
|
|
1441
|
+
}
|
|
1442
|
+
function toInvoicePdf(value) {
|
|
1443
|
+
const pdf = asRecord4(value);
|
|
1444
|
+
if (pdf === null) return null;
|
|
1445
|
+
return {
|
|
1446
|
+
filename: asNullableString6(pdf["filename"]),
|
|
1447
|
+
size: asNullableNumber4(pdf["size"]),
|
|
1448
|
+
attached: pdf["attached"] === true
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
function toEurEquivalent(value) {
|
|
1452
|
+
const eur = asRecord4(value);
|
|
1453
|
+
if (eur === null) return null;
|
|
1454
|
+
return {
|
|
1455
|
+
subtotal: asMoney2(eur["subtotal"]),
|
|
1456
|
+
taxAmount: asMoney2(eur["tax_amount"]),
|
|
1457
|
+
total: asMoney2(eur["total"]),
|
|
1458
|
+
rate: asString6(eur["rate"]),
|
|
1459
|
+
rateDate: asNullableString6(eur["rate_date"]),
|
|
1460
|
+
source: asString6(eur["source"], "ECB")
|
|
1461
|
+
};
|
|
942
1462
|
}
|
|
943
1463
|
function toInvoiceLines(providerPayload) {
|
|
944
|
-
const invoice =
|
|
945
|
-
return
|
|
946
|
-
description:
|
|
947
|
-
quantity:
|
|
1464
|
+
const invoice = asRecord4(asRecord4(providerPayload)?.["invoice"]);
|
|
1465
|
+
return asRecordList3(invoice?.["invoice_lines_attributes"]).map((line) => ({
|
|
1466
|
+
description: asString6(line["description"]),
|
|
1467
|
+
quantity: asNullableNumber4(line["quantity"]),
|
|
948
1468
|
price: asMoney2(line["price"]),
|
|
949
|
-
unit:
|
|
950
|
-
taxes:
|
|
951
|
-
name:
|
|
952
|
-
percent:
|
|
953
|
-
category:
|
|
954
|
-
comment:
|
|
1469
|
+
unit: asNullableNumber4(line["unit"]),
|
|
1470
|
+
taxes: asRecordList3(line["taxes_attributes"]).map((tax) => ({
|
|
1471
|
+
name: asString6(tax["name"]),
|
|
1472
|
+
percent: asNullableNumber4(tax["percent"]),
|
|
1473
|
+
category: asNullableString6(tax["category"]),
|
|
1474
|
+
comment: asNullableString6(tax["comment"])
|
|
955
1475
|
}))
|
|
956
1476
|
}));
|
|
957
1477
|
}
|
|
958
1478
|
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 = []) {
|
|
1479
|
+
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
1480
|
this.id = id;
|
|
961
1481
|
this.companyId = companyId;
|
|
962
1482
|
this.complianceEnrollmentId = complianceEnrollmentId;
|
|
@@ -995,6 +1515,14 @@ var ComplianceTransaction = class _ComplianceTransaction {
|
|
|
995
1515
|
this.amendedDate = amendedDate;
|
|
996
1516
|
this.providerState = providerState;
|
|
997
1517
|
this.invoiceLines = invoiceLines;
|
|
1518
|
+
this.documentUrl = documentUrl;
|
|
1519
|
+
this.deliveryCheck = deliveryCheck;
|
|
1520
|
+
this.invoicePdf = invoicePdf;
|
|
1521
|
+
this.internalReference = internalReference;
|
|
1522
|
+
this.pdfUrl = pdfUrl;
|
|
1523
|
+
this.buyerRejected = buyerRejected;
|
|
1524
|
+
this.rejectionReason = rejectionReason;
|
|
1525
|
+
this.eurEquivalent = eurEquivalent;
|
|
998
1526
|
}
|
|
999
1527
|
id;
|
|
1000
1528
|
companyId;
|
|
@@ -1034,50 +1562,66 @@ var ComplianceTransaction = class _ComplianceTransaction {
|
|
|
1034
1562
|
amendedDate;
|
|
1035
1563
|
providerState;
|
|
1036
1564
|
invoiceLines;
|
|
1565
|
+
documentUrl;
|
|
1566
|
+
deliveryCheck;
|
|
1567
|
+
invoicePdf;
|
|
1568
|
+
internalReference;
|
|
1569
|
+
pdfUrl;
|
|
1570
|
+
buyerRejected;
|
|
1571
|
+
rejectionReason;
|
|
1572
|
+
eurEquivalent;
|
|
1037
1573
|
get isCreditNote() {
|
|
1038
1574
|
return this.documentType === ComplianceDocumentType.CREDIT_NOTE;
|
|
1039
1575
|
}
|
|
1040
1576
|
static fromArray(data) {
|
|
1041
1577
|
const taxReport = typeof data["tax_report"] === "object" && data["tax_report"] !== null && !Array.isArray(data["tax_report"]) ? ComplianceTaxReport.fromArray(data["tax_report"]) : void 0;
|
|
1042
1578
|
return new _ComplianceTransaction(
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1579
|
+
asNumber4(data["id"]),
|
|
1580
|
+
asNumber4(data["company_id"]),
|
|
1581
|
+
asNumber4(data["compliance_enrollment_id"]),
|
|
1582
|
+
asString6(data["country"]),
|
|
1583
|
+
asString6(data["regime"]),
|
|
1048
1584
|
toComplianceTransactionType(data["transaction_type"]),
|
|
1049
|
-
|
|
1585
|
+
asString6(data["transaction_type_label"]),
|
|
1050
1586
|
toComplianceTransactionState(data["state"]),
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1587
|
+
asString6(data["state_label"]),
|
|
1588
|
+
asString6(data["invoice_number"]),
|
|
1589
|
+
asNullableString6(data["invoice_date"]),
|
|
1590
|
+
asNullableString6(data["due_date"]),
|
|
1591
|
+
asString6(data["currency"], "EUR"),
|
|
1056
1592
|
asMoney2(data["subtotal"]),
|
|
1057
1593
|
asMoney2(data["tax_amount"]),
|
|
1058
1594
|
asMoney2(data["total"]),
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1595
|
+
asNullableString6(data["counterparty_name"]),
|
|
1596
|
+
asNullableString6(data["counterparty_country"]),
|
|
1597
|
+
asNullableString6(data["counterparty_vat_number"]),
|
|
1062
1598
|
data["is_paid"] === true,
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1599
|
+
asNullableString6(data["paid_at"]),
|
|
1600
|
+
asNullableString6(data["provider_invoice_id"]),
|
|
1601
|
+
asNullableString6(data["submission_error"]),
|
|
1602
|
+
asNullableString6(data["reported_at"]),
|
|
1067
1603
|
taxReport,
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1604
|
+
asNullableString6(data["created_at"]),
|
|
1605
|
+
asNullableString6(data["updated_at"]),
|
|
1606
|
+
asNullableString6(data["counterparty_register_id"]),
|
|
1607
|
+
asNullableString6(data["counterparty_address"]),
|
|
1608
|
+
asNullableString6(data["counterparty_city"]),
|
|
1609
|
+
asNullableString6(data["counterparty_postalcode"]),
|
|
1610
|
+
asNullableString6(data["counterparty_email"]),
|
|
1611
|
+
asNullableString6(data["buyer_reference"]),
|
|
1076
1612
|
data["document_type"] === void 0 || data["document_type"] === null ? ComplianceDocumentType.INVOICE : toComplianceDocumentType(data["document_type"]),
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
toInvoiceLines(data["provider_payload"])
|
|
1613
|
+
asNullableString6(data["amended_number"]),
|
|
1614
|
+
asNullableString6(data["amended_date"]),
|
|
1615
|
+
asNullableString6(data["provider_state"]),
|
|
1616
|
+
toInvoiceLines(data["provider_payload"]),
|
|
1617
|
+
asNullableString6(data["document_url"]),
|
|
1618
|
+
asRecord4(data["delivery_check"]),
|
|
1619
|
+
toInvoicePdf(data["invoice_pdf"]),
|
|
1620
|
+
asNullableString6(data["internal_reference"]),
|
|
1621
|
+
asNullableString6(data["pdf_url"]),
|
|
1622
|
+
data["buyer_rejected"] === true,
|
|
1623
|
+
asNullableString6(data["rejection_reason"]),
|
|
1624
|
+
toEurEquivalent(data["eur_equivalent"])
|
|
1081
1625
|
);
|
|
1082
1626
|
}
|
|
1083
1627
|
};
|
|
@@ -1126,48 +1670,21 @@ var ImportResult = class _ImportResult {
|
|
|
1126
1670
|
}
|
|
1127
1671
|
created;
|
|
1128
1672
|
skippedDuplicates;
|
|
1129
|
-
errors;
|
|
1130
|
-
static fromArray(data) {
|
|
1131
|
-
const errors = (Array.isArray(data["errors"]) ? data["errors"] : []).filter(
|
|
1132
|
-
(row) => typeof row === "string"
|
|
1133
|
-
);
|
|
1134
|
-
const num = (value) => typeof value === "number" ? value : 0;
|
|
1135
|
-
return new _ImportResult(num(data["created"]), num(data["skipped_duplicates"]), errors);
|
|
1136
|
-
}
|
|
1137
|
-
};
|
|
1138
|
-
|
|
1139
|
-
// src/enums/PeppolLookupStatus.ts
|
|
1140
|
-
var PeppolLookupStatus = {
|
|
1141
|
-
REACHABLE: "reachable",
|
|
1142
|
-
NOT_REACHABLE: "not_reachable",
|
|
1143
|
-
PENDING: "pending",
|
|
1144
|
-
UNKNOWN: "unknown"
|
|
1145
|
-
};
|
|
1146
|
-
var VALUES7 = Object.values(PeppolLookupStatus);
|
|
1147
|
-
function toPeppolLookupStatus(value) {
|
|
1148
|
-
if (typeof value === "string") {
|
|
1149
|
-
const normalized = value.trim().toLowerCase();
|
|
1150
|
-
if (VALUES7.includes(normalized)) {
|
|
1151
|
-
return normalized;
|
|
1152
|
-
}
|
|
1673
|
+
errors;
|
|
1674
|
+
static fromArray(data) {
|
|
1675
|
+
const errors = (Array.isArray(data["errors"]) ? data["errors"] : []).filter(
|
|
1676
|
+
(row) => typeof row === "string"
|
|
1677
|
+
);
|
|
1678
|
+
const num = (value) => typeof value === "number" ? value : 0;
|
|
1679
|
+
return new _ImportResult(num(data["created"]), num(data["skipped_duplicates"]), errors);
|
|
1153
1680
|
}
|
|
1154
|
-
return PeppolLookupStatus.UNKNOWN;
|
|
1155
|
-
}
|
|
1156
|
-
var DESCRIPTIONS7 = {
|
|
1157
|
-
[PeppolLookupStatus.REACHABLE]: "The participant is registered on Peppol and can receive e-invoices.",
|
|
1158
|
-
[PeppolLookupStatus.NOT_REACHABLE]: "The participant is not registered on Peppol.",
|
|
1159
|
-
[PeppolLookupStatus.PENDING]: "The directory lookup is still resolving \u2014 ask again in a few seconds.",
|
|
1160
|
-
[PeppolLookupStatus.UNKNOWN]: "Unknown lookup status."
|
|
1161
1681
|
};
|
|
1162
|
-
function describePeppolLookupStatus(status) {
|
|
1163
|
-
return DESCRIPTIONS7[status];
|
|
1164
|
-
}
|
|
1165
1682
|
|
|
1166
1683
|
// src/dto/PeppolLookupResult.ts
|
|
1167
|
-
function
|
|
1684
|
+
function asString7(value, fallback = "") {
|
|
1168
1685
|
return typeof value === "string" ? value : fallback;
|
|
1169
1686
|
}
|
|
1170
|
-
function
|
|
1687
|
+
function asNullableString7(value) {
|
|
1171
1688
|
return typeof value === "string" ? value : null;
|
|
1172
1689
|
}
|
|
1173
1690
|
var PeppolLookupResult = class _PeppolLookupResult {
|
|
@@ -1198,21 +1715,21 @@ var PeppolLookupResult = class _PeppolLookupResult {
|
|
|
1198
1715
|
return new _PeppolLookupResult(
|
|
1199
1716
|
toPeppolLookupStatus(data["status"]),
|
|
1200
1717
|
typeof data["reachable"] === "boolean" ? data["reachable"] : null,
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1718
|
+
asString7(data["country"]),
|
|
1719
|
+
asString7(data["scheme"]),
|
|
1720
|
+
asString7(data["id"]),
|
|
1204
1721
|
documentTypes,
|
|
1205
|
-
|
|
1206
|
-
|
|
1722
|
+
asNullableString7(data["transport_type_code"]),
|
|
1723
|
+
asNullableString7(data["checked_at"])
|
|
1207
1724
|
);
|
|
1208
1725
|
}
|
|
1209
1726
|
};
|
|
1210
1727
|
|
|
1211
1728
|
// src/dto/RegistryCompany.ts
|
|
1212
|
-
function
|
|
1729
|
+
function asString8(value, fallback = "") {
|
|
1213
1730
|
return typeof value === "string" ? value : fallback;
|
|
1214
1731
|
}
|
|
1215
|
-
function
|
|
1732
|
+
function asNullableString8(value) {
|
|
1216
1733
|
return typeof value === "string" ? value : null;
|
|
1217
1734
|
}
|
|
1218
1735
|
var RegistryCompany = class _RegistryCompany {
|
|
@@ -1244,27 +1761,27 @@ var RegistryCompany = class _RegistryCompany {
|
|
|
1244
1761
|
country;
|
|
1245
1762
|
static fromArray(data) {
|
|
1246
1763
|
return new _RegistryCompany(
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1764
|
+
asString8(data["org_number"]),
|
|
1765
|
+
asString8(data["company_name"]),
|
|
1766
|
+
asNullableString8(data["organisation_form"]),
|
|
1250
1767
|
data["vat_registered"] === true,
|
|
1251
|
-
|
|
1768
|
+
asNullableString8(data["vat_number"]),
|
|
1252
1769
|
data["enterprise_register"] === true,
|
|
1253
1770
|
data["bankrupt"] === true,
|
|
1254
1771
|
data["under_liquidation"] === true,
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1772
|
+
asNullableString8(data["address"]),
|
|
1773
|
+
asNullableString8(data["postalcode"]),
|
|
1774
|
+
asNullableString8(data["city"]),
|
|
1775
|
+
asString8(data["country"])
|
|
1259
1776
|
);
|
|
1260
1777
|
}
|
|
1261
1778
|
};
|
|
1262
1779
|
|
|
1263
1780
|
// src/dto/RevenueStatistics.ts
|
|
1264
|
-
function
|
|
1781
|
+
function asString9(value, fallback = "") {
|
|
1265
1782
|
return typeof value === "string" ? value : fallback;
|
|
1266
1783
|
}
|
|
1267
|
-
function
|
|
1784
|
+
function asNumber5(value) {
|
|
1268
1785
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
1269
1786
|
}
|
|
1270
1787
|
function asMoney3(value) {
|
|
@@ -1276,7 +1793,7 @@ function asRecords(value) {
|
|
|
1276
1793
|
}
|
|
1277
1794
|
function toTotals(data) {
|
|
1278
1795
|
return {
|
|
1279
|
-
count:
|
|
1796
|
+
count: asNumber5(data["count"]),
|
|
1280
1797
|
subtotal: asMoney3(data["subtotal"]),
|
|
1281
1798
|
taxAmount: asMoney3(data["tax_amount"]),
|
|
1282
1799
|
total: asMoney3(data["total"])
|
|
@@ -1313,35 +1830,35 @@ var RevenueStatistics = class _RevenueStatistics {
|
|
|
1313
1830
|
typeof data["totals"] === "object" && data["totals"] !== null ? data["totals"] : {}
|
|
1314
1831
|
);
|
|
1315
1832
|
const timeSeries = asRecords(data["time_series"]).map((row) => ({
|
|
1316
|
-
bucket:
|
|
1833
|
+
bucket: asString9(row["bucket"]),
|
|
1317
1834
|
...toTotals(row)
|
|
1318
1835
|
}));
|
|
1319
1836
|
const byType = asRecords(data["by_type"]).map((row) => ({
|
|
1320
|
-
type:
|
|
1321
|
-
typeLabel:
|
|
1322
|
-
count:
|
|
1837
|
+
type: asString9(row["type"]),
|
|
1838
|
+
typeLabel: asString9(row["type_label"]),
|
|
1839
|
+
count: asNumber5(row["count"]),
|
|
1323
1840
|
total: asMoney3(row["total"])
|
|
1324
1841
|
}));
|
|
1325
1842
|
const byCountry = asRecords(data["by_country"]).map((row) => ({
|
|
1326
|
-
country:
|
|
1327
|
-
count:
|
|
1843
|
+
country: asString9(row["country"], "unknown"),
|
|
1844
|
+
count: asNumber5(row["count"]),
|
|
1328
1845
|
total: asMoney3(row["total"])
|
|
1329
1846
|
}));
|
|
1330
1847
|
const byState = asRecords(data["by_state"]).map((row) => ({
|
|
1331
|
-
state:
|
|
1332
|
-
stateLabel:
|
|
1333
|
-
count:
|
|
1848
|
+
state: asString9(row["state"]),
|
|
1849
|
+
stateLabel: asString9(row["state_label"]),
|
|
1850
|
+
count: asNumber5(row["count"]),
|
|
1334
1851
|
total: asMoney3(row["total"])
|
|
1335
1852
|
}));
|
|
1336
1853
|
const byCurrency = asRecords(data["by_currency"]).map((row) => ({
|
|
1337
|
-
currency:
|
|
1854
|
+
currency: asString9(row["currency"]),
|
|
1338
1855
|
...toTotals(row)
|
|
1339
1856
|
}));
|
|
1340
1857
|
return new _RevenueStatistics(
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1858
|
+
asString9(range["date_from"]) || void 0,
|
|
1859
|
+
asString9(range["date_to"]) || void 0,
|
|
1860
|
+
asString9(range["interval"]) || void 0,
|
|
1861
|
+
asString9(data["primary_currency"], "EUR"),
|
|
1345
1862
|
data["is_multi_currency"] === true,
|
|
1346
1863
|
totals,
|
|
1347
1864
|
timeSeries,
|
|
@@ -1354,10 +1871,10 @@ var RevenueStatistics = class _RevenueStatistics {
|
|
|
1354
1871
|
};
|
|
1355
1872
|
|
|
1356
1873
|
// src/dto/SireneLookupResult.ts
|
|
1357
|
-
function
|
|
1874
|
+
function asString10(value, fallback = "") {
|
|
1358
1875
|
return typeof value === "string" ? value : fallback;
|
|
1359
1876
|
}
|
|
1360
|
-
function
|
|
1877
|
+
function asNullableString9(value) {
|
|
1361
1878
|
return typeof value === "string" ? value : null;
|
|
1362
1879
|
}
|
|
1363
1880
|
var SireneLookupResult = class _SireneLookupResult {
|
|
@@ -1383,24 +1900,24 @@ var SireneLookupResult = class _SireneLookupResult {
|
|
|
1383
1900
|
postalcode;
|
|
1384
1901
|
static fromArray(data) {
|
|
1385
1902
|
return new _SireneLookupResult(
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1903
|
+
asString10(data["company_name"]),
|
|
1904
|
+
asString10(data["siren"]),
|
|
1905
|
+
asNullableString9(data["siret"]),
|
|
1906
|
+
asNullableString9(data["naf_code"]),
|
|
1907
|
+
asNullableString9(data["naf_full"]),
|
|
1908
|
+
asNullableString9(data["enterprise_size"]),
|
|
1909
|
+
asNullableString9(data["address"]),
|
|
1910
|
+
asNullableString9(data["city"]),
|
|
1911
|
+
asNullableString9(data["postalcode"])
|
|
1395
1912
|
);
|
|
1396
1913
|
}
|
|
1397
1914
|
};
|
|
1398
1915
|
|
|
1399
1916
|
// src/dto/VatRates.ts
|
|
1400
|
-
function
|
|
1917
|
+
function asString11(value, fallback = "") {
|
|
1401
1918
|
return typeof value === "string" ? value : fallback;
|
|
1402
1919
|
}
|
|
1403
|
-
function
|
|
1920
|
+
function asNumber6(value) {
|
|
1404
1921
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
1405
1922
|
}
|
|
1406
1923
|
var VatRates = class _VatRates {
|
|
@@ -1414,13 +1931,13 @@ var VatRates = class _VatRates {
|
|
|
1414
1931
|
rates;
|
|
1415
1932
|
static fromArray(data) {
|
|
1416
1933
|
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:
|
|
1934
|
+
percent: asNumber6(row["percent"]),
|
|
1935
|
+
category: asString11(row["category"]),
|
|
1936
|
+
labelKey: asString11(row["label_key"]),
|
|
1937
|
+
taxName: asString11(row["tax_name"]),
|
|
1421
1938
|
requiresVatex: row["requires_vatex"] === true
|
|
1422
1939
|
}));
|
|
1423
|
-
return new _VatRates(
|
|
1940
|
+
return new _VatRates(asString11(data["country"]), asString11(data["currency"], "EUR"), rates);
|
|
1424
1941
|
}
|
|
1425
1942
|
};
|
|
1426
1943
|
|
|
@@ -1474,15 +1991,39 @@ var NorwegianOrgNumber = {
|
|
|
1474
1991
|
}
|
|
1475
1992
|
};
|
|
1476
1993
|
|
|
1994
|
+
// src/enums/ComplianceTaxCategory.ts
|
|
1995
|
+
var ComplianceTaxCategory = {
|
|
1996
|
+
STANDARD: "S",
|
|
1997
|
+
REDUCED: "AA",
|
|
1998
|
+
SUPER_REDUCED: "AAA",
|
|
1999
|
+
INTRA_COMMUNITY: "K",
|
|
2000
|
+
EXPORT: "G",
|
|
2001
|
+
REVERSE_CHARGE: "AE",
|
|
2002
|
+
EXEMPT: "E",
|
|
2003
|
+
ZERO: "Z"
|
|
2004
|
+
};
|
|
2005
|
+
var CODES = Object.values(ComplianceTaxCategory);
|
|
2006
|
+
function parseComplianceTaxCategory(value) {
|
|
2007
|
+
if (typeof value !== "string") return null;
|
|
2008
|
+
const code = value.trim().toUpperCase();
|
|
2009
|
+
return CODES.includes(code) ? code : null;
|
|
2010
|
+
}
|
|
2011
|
+
var COMPLIANCE_TAX_CATEGORY_CODES = CODES;
|
|
2012
|
+
|
|
1477
2013
|
// src/endpoints/EReportingEndpoint.ts
|
|
1478
2014
|
var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
1479
2015
|
var NAF_CODE_REGEX = /^\d{2}$/;
|
|
1480
2016
|
var COUNTRY_REGEX = /^[A-Za-z]{2}$/;
|
|
1481
2017
|
var PEPPOL_SCHEME_REGEX = /^\d{4}$/;
|
|
2018
|
+
var INVOICE_PDF_MAX_BYTES = 10 * 1024 * 1024;
|
|
2019
|
+
var INVOICE_PDF_MAGIC = [37, 80, 68, 70, 45];
|
|
1482
2020
|
var INTERVALS = ["day", "week", "month"];
|
|
1483
2021
|
var SERVICES = ["e_reporting", "e_invoicing"];
|
|
1484
2022
|
var ENTERPRISE_SIZES = ["micro", "pme", "eti", "ge"];
|
|
1485
2023
|
var TYPE_OPERATIONS = ["services", "goods", "mixed"];
|
|
2024
|
+
var NOTIFICATION_LANGUAGES = ["de", "en"];
|
|
2025
|
+
var NOTIFICATION_DIGESTS = ["off", "daily", "weekly"];
|
|
2026
|
+
var NOTIFICATION_MAX_RECIPIENTS = 5;
|
|
1486
2027
|
var TRANSACTION_TYPES = [
|
|
1487
2028
|
"b2c_outbound",
|
|
1488
2029
|
"b2b_domestic_outbound",
|
|
@@ -1653,6 +2194,19 @@ var EReportingEndpoint = class {
|
|
|
1653
2194
|
const data = await this.parseJsonResponse(response);
|
|
1654
2195
|
return PeppolLookupResult.fromArray(data);
|
|
1655
2196
|
}
|
|
2197
|
+
/**
|
|
2198
|
+
* What to do with an invoice to this buyer: peppolLookup() turned into an action,
|
|
2199
|
+
* e.g. in a shop checkout when a business buyer enters its number.
|
|
2200
|
+
*
|
|
2201
|
+
* - `einvoice`: create the transaction, Taxora sends the e-invoice;
|
|
2202
|
+
* - `regular_invoice`: the buyer is not on the network — send your regular invoice
|
|
2203
|
+
* yourself (a transaction would end as not_required and is not billed);
|
|
2204
|
+
* - `einvoice_pending`: no definite answer yet — create the transaction anyway,
|
|
2205
|
+
* Taxora asks the directory again before sending.
|
|
2206
|
+
*/
|
|
2207
|
+
async decideDelivery(country, id, scheme) {
|
|
2208
|
+
return ComplianceDeliveryDecision.fromLookup(await this.peppolLookup(country, id, scheme));
|
|
2209
|
+
}
|
|
1656
2210
|
// ---------------------------------------------------------------------
|
|
1657
2211
|
// Transactions
|
|
1658
2212
|
// ---------------------------------------------------------------------
|
|
@@ -1677,6 +2231,9 @@ var EReportingEndpoint = class {
|
|
|
1677
2231
|
this.validateInvoiceLines(input.invoiceLines);
|
|
1678
2232
|
this.validateOptionalCodes(input);
|
|
1679
2233
|
this.validateBuyerFields(input);
|
|
2234
|
+
this.validateInternalReference(input.internalReference);
|
|
2235
|
+
this.validateInvoicePdfFilename(input);
|
|
2236
|
+
this.validateDocument(input);
|
|
1680
2237
|
const body = {
|
|
1681
2238
|
compliance_enrollment_id: input.complianceEnrollmentId,
|
|
1682
2239
|
transaction_type: input.transactionType,
|
|
@@ -1692,23 +2249,104 @@ var EReportingEndpoint = class {
|
|
|
1692
2249
|
const data = await this.parseJsonResponse(response);
|
|
1693
2250
|
return ComplianceTransaction.fromArray(data);
|
|
1694
2251
|
}
|
|
1695
|
-
/**
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
2252
|
+
/**
|
|
2253
|
+
* Issue a credit note for an invoice you sent through Taxora. Everything that must
|
|
2254
|
+
* match the corrected invoice is taken from `original`: enrollment, transaction type,
|
|
2255
|
+
* currency, buyer (name, register id, address, e-mail, VAT number, buyer reference) —
|
|
2256
|
+
* and the reference to the corrected invoice (number and date) is set for you.
|
|
2257
|
+
*
|
|
2258
|
+
* Without invoiceLines and amounts it credits the whole invoice: lines and amounts are
|
|
2259
|
+
* taken over as they are (the document type, not the sign, makes it a credit note).
|
|
2260
|
+
* For a partial credit pass invoiceLines, subtotal, taxAmount and total together.
|
|
2261
|
+
*
|
|
2262
|
+
* Not possible for an invoice that was never sent to the buyer (state not_required)
|
|
2263
|
+
* or for a credit note. A buyer rejected the invoice? Correct it with a credit note and
|
|
2264
|
+
* send a new invoice (`original.buyerRejected`).
|
|
2265
|
+
*/
|
|
2266
|
+
async createCreditNote(original, input) {
|
|
2267
|
+
if (original.documentType === ComplianceDocumentType.CREDIT_NOTE) {
|
|
2268
|
+
throw new TaxoraException("A credit note cannot be credited again; issue a new invoice instead.");
|
|
1699
2269
|
}
|
|
1700
|
-
if (
|
|
1701
|
-
throw new TaxoraException(
|
|
2270
|
+
if (original.state === ComplianceTransactionState.NOT_REQUIRED) {
|
|
2271
|
+
throw new TaxoraException(
|
|
2272
|
+
"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."
|
|
2273
|
+
);
|
|
1702
2274
|
}
|
|
1703
|
-
|
|
1704
|
-
|
|
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));
|
|
2275
|
+
if (original.invoiceNumber === "") {
|
|
2276
|
+
throw new TaxoraException("The original transaction has no invoice number.");
|
|
1710
2277
|
}
|
|
1711
|
-
if (
|
|
2278
|
+
if (original.transactionType === "unknown") {
|
|
2279
|
+
throw new TaxoraException("The original transaction has an unknown transaction type.");
|
|
2280
|
+
}
|
|
2281
|
+
const partial = [input.invoiceLines, input.subtotal, input.taxAmount, input.total];
|
|
2282
|
+
if (![0, 4].includes(partial.filter((value) => value !== void 0).length)) {
|
|
2283
|
+
throw new TaxoraException("For a partial credit pass invoiceLines, subtotal, taxAmount and total together.");
|
|
2284
|
+
}
|
|
2285
|
+
let invoiceLines;
|
|
2286
|
+
let subtotal = input.subtotal;
|
|
2287
|
+
let taxAmount = input.taxAmount;
|
|
2288
|
+
let total = input.total;
|
|
2289
|
+
if (input.invoiceLines !== void 0) {
|
|
2290
|
+
invoiceLines = input.invoiceLines;
|
|
2291
|
+
} else {
|
|
2292
|
+
invoiceLines = original.invoiceLines.map((line) => ({
|
|
2293
|
+
description: line.description,
|
|
2294
|
+
quantity: line.quantity ?? 0,
|
|
2295
|
+
price: Number(line.price),
|
|
2296
|
+
...line.unit !== null ? { unit: line.unit } : {},
|
|
2297
|
+
taxes: line.taxes.map((tax) => ({
|
|
2298
|
+
name: tax.name,
|
|
2299
|
+
percent: tax.percent ?? 0,
|
|
2300
|
+
category: this.storedTaxCategory(tax.category),
|
|
2301
|
+
...tax.comment !== null ? { comment: tax.comment } : {}
|
|
2302
|
+
}))
|
|
2303
|
+
}));
|
|
2304
|
+
if (invoiceLines.length === 0) {
|
|
2305
|
+
throw new TaxoraException(
|
|
2306
|
+
"The original transaction carries no invoice lines; pass invoiceLines and the amounts explicitly."
|
|
2307
|
+
);
|
|
2308
|
+
}
|
|
2309
|
+
subtotal = Number(original.subtotal);
|
|
2310
|
+
taxAmount = Number(original.taxAmount);
|
|
2311
|
+
total = Number(original.total);
|
|
2312
|
+
}
|
|
2313
|
+
const buyer = Object.fromEntries(
|
|
2314
|
+
Object.entries({
|
|
2315
|
+
counterpartyName: original.counterpartyName,
|
|
2316
|
+
counterpartyCountry: original.counterpartyCountry,
|
|
2317
|
+
counterpartyVatNumber: original.counterpartyVatNumber,
|
|
2318
|
+
counterpartyRegisterId: original.counterpartyRegisterId,
|
|
2319
|
+
counterpartyAddress: original.counterpartyAddress,
|
|
2320
|
+
counterpartyCity: original.counterpartyCity,
|
|
2321
|
+
counterpartyPostalcode: original.counterpartyPostalcode,
|
|
2322
|
+
counterpartyEmail: original.counterpartyEmail,
|
|
2323
|
+
buyerReference: original.buyerReference
|
|
2324
|
+
}).filter(([, value]) => value !== null && value !== "")
|
|
2325
|
+
);
|
|
2326
|
+
return this.createTransaction({
|
|
2327
|
+
complianceEnrollmentId: original.complianceEnrollmentId,
|
|
2328
|
+
transactionType: original.transactionType,
|
|
2329
|
+
invoiceNumber: input.creditNoteNumber,
|
|
2330
|
+
invoiceDate: input.invoiceDate ?? /* @__PURE__ */ new Date(),
|
|
2331
|
+
subtotal: subtotal ?? 0,
|
|
2332
|
+
total: total ?? 0,
|
|
2333
|
+
invoiceLines,
|
|
2334
|
+
currency: original.currency,
|
|
2335
|
+
...taxAmount !== void 0 ? { taxAmount } : {},
|
|
2336
|
+
...buyer,
|
|
2337
|
+
isCreditNote: true,
|
|
2338
|
+
amendedNumber: original.invoiceNumber,
|
|
2339
|
+
...original.invoiceDate !== null ? { amendedDate: original.invoiceDate } : {},
|
|
2340
|
+
...input.extraInfo !== void 0 ? { extraInfo: input.extraInfo } : {},
|
|
2341
|
+
...input.internalReference !== void 0 ? { internalReference: input.internalReference } : {},
|
|
2342
|
+
...input.submitNow !== void 0 ? { submitNow: input.submitNow } : {},
|
|
2343
|
+
...input.invoicePdf !== void 0 ? { invoicePdf: input.invoicePdf } : {},
|
|
2344
|
+
...input.invoicePdfFilename !== void 0 ? { invoicePdfFilename: input.invoicePdfFilename } : {}
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
/** Paginated list of the company's transactions (newest invoice date first), filterable by date / state / type / enrollment / country / search / internal reference / provider state. */
|
|
2348
|
+
async listTransactions(filters = {}) {
|
|
2349
|
+
const params = this.buildTransactionFilterParams(filters);
|
|
1712
2350
|
if (filters.page !== void 0) params.set("page", String(filters.page));
|
|
1713
2351
|
if (filters.perPage !== void 0) params.set("per_page", String(filters.perPage));
|
|
1714
2352
|
const queryString = params.toString();
|
|
@@ -1716,6 +2354,27 @@ var EReportingEndpoint = class {
|
|
|
1716
2354
|
const response = await this.sendRequest("GET", url);
|
|
1717
2355
|
return ComplianceTransactionPage.fromResponse(await this.parseJsonResponse(response));
|
|
1718
2356
|
}
|
|
2357
|
+
/**
|
|
2358
|
+
* All transactions matching the filters as raw CSV for bookkeeping
|
|
2359
|
+
* (semicolon-separated) — the same filters as listTransactions() without
|
|
2360
|
+
* pagination. Columns: invoice_number, internal_reference, invoice_date,
|
|
2361
|
+
* due_date, country, transaction_type, document_type, state, delivery_state,
|
|
2362
|
+
* buyer_rejected, rejection_reason, counterparty_name, counterparty_country,
|
|
2363
|
+
* counterparty_vat_number, currency, subtotal, tax_amount, total, eur_rate,
|
|
2364
|
+
* eur_rate_date, eur_subtotal, eur_tax_amount, eur_total, provider_invoice_id,
|
|
2365
|
+
* created_at. The eur_* columns hold the ECB reference conversion for non-EUR
|
|
2366
|
+
* invoices (empty for EUR). Max 50,000 rows server-side. The body is returned
|
|
2367
|
+
* verbatim, a UTF-8 BOM included when the API sends one.
|
|
2368
|
+
*/
|
|
2369
|
+
async exportTransactionsCsv(filters = {}) {
|
|
2370
|
+
const queryString = this.buildTransactionFilterParams(filters).toString();
|
|
2371
|
+
const url = `${this.baseUrl}/compliance/transactions/export.csv${queryString ? `?${queryString}` : ""}`;
|
|
2372
|
+
const response = await this.sendDownloadRequest(url, "text/csv, application/json");
|
|
2373
|
+
if (!response.ok) {
|
|
2374
|
+
await this.parseJsonResponse(response);
|
|
2375
|
+
}
|
|
2376
|
+
return new TextDecoder("utf-8", { ignoreBOM: true }).decode(await response.arrayBuffer());
|
|
2377
|
+
}
|
|
1719
2378
|
/** Get a single transaction by id (includes its tax report when one exists). */
|
|
1720
2379
|
async getTransaction(id) {
|
|
1721
2380
|
this.validateId(id, "id");
|
|
@@ -1731,6 +2390,8 @@ var EReportingEndpoint = class {
|
|
|
1731
2390
|
if (input.invoiceLines !== void 0) this.validateInvoiceLines(input.invoiceLines);
|
|
1732
2391
|
this.validateOptionalCodes(input);
|
|
1733
2392
|
this.validateBuyerFields(input);
|
|
2393
|
+
this.validateInternalReference(input.internalReference);
|
|
2394
|
+
this.validateInvoicePdfFilename(input);
|
|
1734
2395
|
const body = {};
|
|
1735
2396
|
if (input.transactionType !== void 0) body["transaction_type"] = input.transactionType;
|
|
1736
2397
|
if (input.invoiceNumber !== void 0) body["invoice_number"] = input.invoiceNumber;
|
|
@@ -1765,6 +2426,47 @@ var EReportingEndpoint = class {
|
|
|
1765
2426
|
const data = await this.parseJsonResponse(response);
|
|
1766
2427
|
return ComplianceTransaction.fromArray(data);
|
|
1767
2428
|
}
|
|
2429
|
+
/**
|
|
2430
|
+
* Download the legal e-invoice document of a sent transaction (e-invoicing,
|
|
2431
|
+
* e.g. the EHF/UBL XML for Norway). Available once `documentUrl` on the
|
|
2432
|
+
* transaction is set.
|
|
2433
|
+
*
|
|
2434
|
+
* Errors: 404 (HttpException) for an unknown transaction; 409 (HttpException)
|
|
2435
|
+
* when no document exists (yet) — e.g. e-reporting, not sent, or the network
|
|
2436
|
+
* has not produced it yet (try again in a few minutes); 502 on provider failure.
|
|
2437
|
+
*/
|
|
2438
|
+
async getTransactionDocument(id) {
|
|
2439
|
+
this.validateId(id, "id");
|
|
2440
|
+
const response = await this.sendDownloadRequest(`${this.baseUrl}/compliance/transactions/${id}/document`, "*/*");
|
|
2441
|
+
if (!response.ok) {
|
|
2442
|
+
await this.parseJsonResponse(response);
|
|
2443
|
+
}
|
|
2444
|
+
return {
|
|
2445
|
+
content: new Uint8Array(await response.arrayBuffer()),
|
|
2446
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
2447
|
+
filename: this.filenameFromDisposition(response.headers.get("content-disposition"))
|
|
2448
|
+
};
|
|
2449
|
+
}
|
|
2450
|
+
/**
|
|
2451
|
+
* Download the invoice as a readable PDF — the seller's attached PDF, or the
|
|
2452
|
+
* provider's rendering of the e-invoice. Available once `pdfUrl` on the
|
|
2453
|
+
* transaction is set.
|
|
2454
|
+
*
|
|
2455
|
+
* Errors: 404 (HttpException) for an unknown transaction; 409 (HttpException)
|
|
2456
|
+
* when no PDF exists (yet); 502 on provider failure.
|
|
2457
|
+
*/
|
|
2458
|
+
async getTransactionPdf(id) {
|
|
2459
|
+
this.validateId(id, "id");
|
|
2460
|
+
const response = await this.sendDownloadRequest(`${this.baseUrl}/compliance/transactions/${id}/pdf`, "*/*");
|
|
2461
|
+
if (!response.ok) {
|
|
2462
|
+
await this.parseJsonResponse(response);
|
|
2463
|
+
}
|
|
2464
|
+
return {
|
|
2465
|
+
content: new Uint8Array(await response.arrayBuffer()),
|
|
2466
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
2467
|
+
filename: this.filenameFromDisposition(response.headers.get("content-disposition"))
|
|
2468
|
+
};
|
|
2469
|
+
}
|
|
1768
2470
|
/**
|
|
1769
2471
|
* Bulk-import transactions from a semicolon-separated CSV (one row per invoice
|
|
1770
2472
|
* line; rows sharing an invoice_number are grouped into one transaction).
|
|
@@ -1796,6 +2498,9 @@ var EReportingEndpoint = class {
|
|
|
1796
2498
|
* months and a monthly interval.
|
|
1797
2499
|
*/
|
|
1798
2500
|
async getRevenueStatistics(filters = {}) {
|
|
2501
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2502
|
+
this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
|
|
2503
|
+
}
|
|
1799
2504
|
if (filters.interval !== void 0 && !INTERVALS.includes(filters.interval)) {
|
|
1800
2505
|
throw new TaxoraException(
|
|
1801
2506
|
`Invalid statistics interval "${String(filters.interval)}". Expected one of: ${INTERVALS.join(", ")}.`
|
|
@@ -1807,12 +2512,42 @@ var EReportingEndpoint = class {
|
|
|
1807
2512
|
if (filters.interval !== void 0) params.set("interval", filters.interval);
|
|
1808
2513
|
if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
|
|
1809
2514
|
if (filters.state !== void 0) params.set("state", filters.state);
|
|
2515
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2516
|
+
params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
|
|
2517
|
+
}
|
|
1810
2518
|
const queryString = params.toString();
|
|
1811
2519
|
const url = `${this.baseUrl}/compliance/revenue-statistics${queryString ? `?${queryString}` : ""}`;
|
|
1812
2520
|
const response = await this.sendRequest("GET", url);
|
|
1813
2521
|
const data = await this.parseJsonResponse(response);
|
|
1814
2522
|
return RevenueStatistics.fromArray(data);
|
|
1815
2523
|
}
|
|
2524
|
+
/**
|
|
2525
|
+
* Net and VAT per month, currency and tax rate as raw CSV (semicolon-separated,
|
|
2526
|
+
* computed from the invoice lines) — for reconciling the reported figures with
|
|
2527
|
+
* your VAT return. Same filters as getRevenueStatistics() (without
|
|
2528
|
+
* `interval`); defaults to the last 12 months. The body is returned verbatim,
|
|
2529
|
+
* a UTF-8 BOM included when the API sends one.
|
|
2530
|
+
*/
|
|
2531
|
+
async exportRevenueTaxRatesCsv(filters = {}) {
|
|
2532
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2533
|
+
this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
|
|
2534
|
+
}
|
|
2535
|
+
const params = new URLSearchParams();
|
|
2536
|
+
if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
|
|
2537
|
+
if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
|
|
2538
|
+
if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
|
|
2539
|
+
if (filters.state !== void 0) params.set("state", filters.state);
|
|
2540
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2541
|
+
params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
|
|
2542
|
+
}
|
|
2543
|
+
const queryString = params.toString();
|
|
2544
|
+
const url = `${this.baseUrl}/compliance/revenue-statistics/tax-rates.csv${queryString ? `?${queryString}` : ""}`;
|
|
2545
|
+
const response = await this.sendDownloadRequest(url, "text/csv, application/json");
|
|
2546
|
+
if (!response.ok) {
|
|
2547
|
+
await this.parseJsonResponse(response);
|
|
2548
|
+
}
|
|
2549
|
+
return new TextDecoder("utf-8", { ignoreBOM: true }).decode(await response.arrayBuffer());
|
|
2550
|
+
}
|
|
1816
2551
|
/**
|
|
1817
2552
|
* The canonical VAT rates & DGFiP tax categories for a reporting country
|
|
1818
2553
|
* (the seller's reporting-country rates, not the counterparty's). Falls back
|
|
@@ -1827,12 +2562,19 @@ var EReportingEndpoint = class {
|
|
|
1827
2562
|
const data = await this.parseJsonResponse(response);
|
|
1828
2563
|
return VatRates.fromArray(data);
|
|
1829
2564
|
}
|
|
1830
|
-
/**
|
|
1831
|
-
|
|
2565
|
+
/**
|
|
2566
|
+
* Paginated list of the company's tax reports (newest first), optionally
|
|
2567
|
+
* filtered by state and by country (2-letter ISO code, case-insensitive).
|
|
2568
|
+
*/
|
|
2569
|
+
async listTaxReports(page = 1, perPage = 25, state, country) {
|
|
2570
|
+
if (country !== void 0 && !COUNTRY_REGEX.test(country)) {
|
|
2571
|
+
throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
|
|
2572
|
+
}
|
|
1832
2573
|
const params = new URLSearchParams();
|
|
1833
2574
|
params.set("page", String(page));
|
|
1834
2575
|
params.set("per_page", String(perPage));
|
|
1835
2576
|
if (state !== void 0) params.set("state", state);
|
|
2577
|
+
if (country !== void 0) params.set("country", country.toUpperCase());
|
|
1836
2578
|
const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/tax-reports?${params.toString()}`);
|
|
1837
2579
|
return ComplianceTaxReportPage.fromResponse(await this.parseJsonResponse(response));
|
|
1838
2580
|
}
|
|
@@ -1874,8 +2616,177 @@ var EReportingEndpoint = class {
|
|
|
1874
2616
|
await this.parseJsonResponse(response);
|
|
1875
2617
|
}
|
|
1876
2618
|
// ---------------------------------------------------------------------
|
|
2619
|
+
// Self-service plans (Stripe) — not gated by compliance access
|
|
2620
|
+
// ---------------------------------------------------------------------
|
|
2621
|
+
/**
|
|
2622
|
+
* Bookable compliance plans per country (net EUR prices), every country
|
|
2623
|
+
* Taxora reaches, and which services the company has unlocked. Works for
|
|
2624
|
+
* companies without compliance access.
|
|
2625
|
+
*/
|
|
2626
|
+
async getOfferings() {
|
|
2627
|
+
const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/offerings`);
|
|
2628
|
+
return ComplianceOfferings.fromArray(await this.parseJsonResponse(response));
|
|
2629
|
+
}
|
|
2630
|
+
/**
|
|
2631
|
+
* Start a Stripe Checkout for a plan (see getOfferings() — `checkoutAvailable`).
|
|
2632
|
+
* Open the returned `url` in the browser. After payment Stripe redirects to
|
|
2633
|
+
* the Taxora portal, which activates the plan; call confirmCheckout() with the
|
|
2634
|
+
* session id to activate it from your side (idempotent).
|
|
2635
|
+
*
|
|
2636
|
+
* Errors: 422 (ValidationException) for an unknown or contact-only plan or a
|
|
2637
|
+
* plan that is already active; 502 (HttpException) on a Stripe error.
|
|
2638
|
+
*/
|
|
2639
|
+
async createCheckout(country, plan) {
|
|
2640
|
+
if (!COUNTRY_REGEX.test(country)) {
|
|
2641
|
+
throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
|
|
2642
|
+
}
|
|
2643
|
+
if (plan.trim() === "" || plan.length > 30) {
|
|
2644
|
+
throw new TaxoraException("plan must be a non-empty plan key of at most 30 characters.");
|
|
2645
|
+
}
|
|
2646
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/checkout`, {
|
|
2647
|
+
country: country.toUpperCase(),
|
|
2648
|
+
plan
|
|
2649
|
+
});
|
|
2650
|
+
const data = await this.parseJsonResponse(response);
|
|
2651
|
+
return { id: String(data["id"] ?? ""), url: String(data["url"] ?? "") };
|
|
2652
|
+
}
|
|
2653
|
+
/** Confirm a finished Stripe Checkout and activate its plan. Idempotent. */
|
|
2654
|
+
async confirmCheckout(sessionId) {
|
|
2655
|
+
if (sessionId.trim() === "" || sessionId.length > 255) {
|
|
2656
|
+
throw new TaxoraException("sessionId must be a non-empty string of at most 255 characters.");
|
|
2657
|
+
}
|
|
2658
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/checkout/confirm`, {
|
|
2659
|
+
session_id: sessionId
|
|
2660
|
+
});
|
|
2661
|
+
const data = await this.parseJsonResponse(response);
|
|
2662
|
+
const str = (value) => typeof value === "string" ? value : null;
|
|
2663
|
+
return {
|
|
2664
|
+
active: data["active"] === true,
|
|
2665
|
+
country: str(data["country"]),
|
|
2666
|
+
service: str(data["service"]),
|
|
2667
|
+
plan: str(data["plan"]),
|
|
2668
|
+
status: str(data["status"])
|
|
2669
|
+
};
|
|
2670
|
+
}
|
|
2671
|
+
/**
|
|
2672
|
+
* URL of the Stripe billing portal for the company's compliance plans
|
|
2673
|
+
* (invoices, payment method, cancellation). The portal returns to the Taxora
|
|
2674
|
+
* portal. 404 (HttpException) when no plan was bought via Stripe.
|
|
2675
|
+
*/
|
|
2676
|
+
async createBillingPortalSession() {
|
|
2677
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/compliance/billing-portal`);
|
|
2678
|
+
const data = await this.parseJsonResponse(response);
|
|
2679
|
+
return String(data["url"] ?? "");
|
|
2680
|
+
}
|
|
2681
|
+
// ---------------------------------------------------------------------
|
|
2682
|
+
// Notification emails
|
|
2683
|
+
// ---------------------------------------------------------------------
|
|
2684
|
+
/**
|
|
2685
|
+
* The company's opt-in e-reporting / e-invoicing emails: recipients, language,
|
|
2686
|
+
* the "buyer not on Peppol" email and the digest. Readable by every member.
|
|
2687
|
+
*/
|
|
2688
|
+
async getNotificationSettings() {
|
|
2689
|
+
const response = await this.sendRequest("GET", `${this.baseUrl}/compliance/notification-settings`);
|
|
2690
|
+
return this.toNotificationSettings(await this.parseJsonResponse(response));
|
|
2691
|
+
}
|
|
2692
|
+
/**
|
|
2693
|
+
* Change the company's e-reporting / e-invoicing emails; only the provided
|
|
2694
|
+
* fields change. `notOnPeppol` only reports invoices held back after it was
|
|
2695
|
+
* switched on.
|
|
2696
|
+
*
|
|
2697
|
+
* Errors: 422 (ValidationException) when an email is switched on without any
|
|
2698
|
+
* recipient; 403 (HttpException, body code `admin_only` or `read_only_role`)
|
|
2699
|
+
* for users who are not company admins.
|
|
2700
|
+
*/
|
|
2701
|
+
async updateNotificationSettings(input) {
|
|
2702
|
+
const body = {};
|
|
2703
|
+
if (input.recipients !== void 0) {
|
|
2704
|
+
if (!Array.isArray(input.recipients)) {
|
|
2705
|
+
throw new TaxoraException("recipients must be an array of email addresses.");
|
|
2706
|
+
}
|
|
2707
|
+
if (input.recipients.length > NOTIFICATION_MAX_RECIPIENTS) {
|
|
2708
|
+
throw new TaxoraException(`recipients must not contain more than ${NOTIFICATION_MAX_RECIPIENTS} entries.`);
|
|
2709
|
+
}
|
|
2710
|
+
body["recipients"] = input.recipients.map((recipient, index) => {
|
|
2711
|
+
const email = typeof recipient === "string" ? recipient.trim() : recipient;
|
|
2712
|
+
this.validateEmail(email, `recipients[${index}]`);
|
|
2713
|
+
if (email.length > 191) {
|
|
2714
|
+
throw new TaxoraException(`recipients[${index}] must not exceed 191 characters.`);
|
|
2715
|
+
}
|
|
2716
|
+
return email;
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
if (input.language !== void 0) {
|
|
2720
|
+
if (!NOTIFICATION_LANGUAGES.includes(input.language)) {
|
|
2721
|
+
throw new TaxoraException(
|
|
2722
|
+
`Invalid language "${String(input.language)}". Expected one of: ${NOTIFICATION_LANGUAGES.join(", ")}.`
|
|
2723
|
+
);
|
|
2724
|
+
}
|
|
2725
|
+
body["language"] = input.language;
|
|
2726
|
+
}
|
|
2727
|
+
if (input.notOnPeppol !== void 0) {
|
|
2728
|
+
if (typeof input.notOnPeppol !== "boolean") {
|
|
2729
|
+
throw new TaxoraException("notOnPeppol must be a boolean.");
|
|
2730
|
+
}
|
|
2731
|
+
body["not_on_peppol"] = input.notOnPeppol;
|
|
2732
|
+
}
|
|
2733
|
+
if (input.digest !== void 0) {
|
|
2734
|
+
if (!NOTIFICATION_DIGESTS.includes(input.digest)) {
|
|
2735
|
+
throw new TaxoraException(
|
|
2736
|
+
`Invalid digest "${String(input.digest)}". Expected one of: ${NOTIFICATION_DIGESTS.join(", ")}.`
|
|
2737
|
+
);
|
|
2738
|
+
}
|
|
2739
|
+
body["digest"] = input.digest;
|
|
2740
|
+
}
|
|
2741
|
+
if (Object.keys(body).length === 0) {
|
|
2742
|
+
throw new TaxoraException("updateNotificationSettings requires at least one field to update.");
|
|
2743
|
+
}
|
|
2744
|
+
const response = await this.sendRequest("PUT", `${this.baseUrl}/compliance/notification-settings`, body);
|
|
2745
|
+
return this.toNotificationSettings(await this.parseJsonResponse(response));
|
|
2746
|
+
}
|
|
2747
|
+
toNotificationSettings(data) {
|
|
2748
|
+
const recipients = Array.isArray(data["recipients"]) ? data["recipients"].filter((r) => typeof r === "string" && r !== "") : [];
|
|
2749
|
+
const digest = data["digest"];
|
|
2750
|
+
return {
|
|
2751
|
+
recipients,
|
|
2752
|
+
language: data["language"] === "en" ? "en" : "de",
|
|
2753
|
+
notOnPeppol: data["not_on_peppol"] === true,
|
|
2754
|
+
digest: digest === "daily" || digest === "weekly" ? digest : "off",
|
|
2755
|
+
digestSentAt: typeof data["digest_sent_at"] === "string" ? data["digest_sent_at"] : null
|
|
2756
|
+
};
|
|
2757
|
+
}
|
|
2758
|
+
// ---------------------------------------------------------------------
|
|
1877
2759
|
// Input validation (fail fast, before any request is sent)
|
|
1878
2760
|
// ---------------------------------------------------------------------
|
|
2761
|
+
/** Validated transaction-list filters as query params (shared by listTransactions and exportTransactionsCsv). */
|
|
2762
|
+
buildTransactionFilterParams(filters) {
|
|
2763
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2764
|
+
this.validateId(filters.complianceEnrollmentId, "complianceEnrollmentId");
|
|
2765
|
+
}
|
|
2766
|
+
if (filters.country !== void 0 && !COUNTRY_REGEX.test(filters.country)) {
|
|
2767
|
+
throw new TaxoraException("country must be a 2-character ISO 3166-1 alpha-2 code.");
|
|
2768
|
+
}
|
|
2769
|
+
if (filters.search !== void 0 && filters.search.length > 100) {
|
|
2770
|
+
throw new TaxoraException("search must not exceed 100 characters.");
|
|
2771
|
+
}
|
|
2772
|
+
this.validateInternalReference(filters.internalReference);
|
|
2773
|
+
if (filters.providerState !== void 0 && !/^[a-z_]{1,30}$/.test(filters.providerState)) {
|
|
2774
|
+
throw new TaxoraException('providerState must be 1-30 lowercase letters or underscores (e.g. "refused").');
|
|
2775
|
+
}
|
|
2776
|
+
const params = new URLSearchParams();
|
|
2777
|
+
if (filters.dateFrom !== void 0) params.set("date_from", this.formatDate(filters.dateFrom));
|
|
2778
|
+
if (filters.dateTo !== void 0) params.set("date_to", this.formatDate(filters.dateTo));
|
|
2779
|
+
if (filters.state !== void 0) params.set("state", filters.state);
|
|
2780
|
+
if (filters.transactionType !== void 0) params.set("transaction_type", filters.transactionType);
|
|
2781
|
+
if (filters.complianceEnrollmentId !== void 0) {
|
|
2782
|
+
params.set("compliance_enrollment_id", String(filters.complianceEnrollmentId));
|
|
2783
|
+
}
|
|
2784
|
+
if (filters.country !== void 0) params.set("country", filters.country.toUpperCase());
|
|
2785
|
+
if (filters.search !== void 0) params.set("search", filters.search);
|
|
2786
|
+
if (filters.internalReference !== void 0) params.set("internal_reference", filters.internalReference);
|
|
2787
|
+
if (filters.providerState !== void 0) params.set("provider_state", filters.providerState);
|
|
2788
|
+
return params;
|
|
2789
|
+
}
|
|
1879
2790
|
validateEnrollmentInput(input) {
|
|
1880
2791
|
if (input.siret === void 0 && input.siren === void 0) {
|
|
1881
2792
|
throw new TaxoraException("Either siret or siren is required.");
|
|
@@ -2001,14 +2912,101 @@ var EReportingEndpoint = class {
|
|
|
2001
2912
|
if (line.taxes.length === 0) {
|
|
2002
2913
|
throw new TaxoraException(`invoiceLines[${index}].taxes must contain at least one tax.`);
|
|
2003
2914
|
}
|
|
2915
|
+
line.taxes.forEach((tax, taxIndex) => {
|
|
2916
|
+
const where = `invoiceLines[${index}].taxes[${taxIndex}]`;
|
|
2917
|
+
const category = parseComplianceTaxCategory(tax.category);
|
|
2918
|
+
if (category === null) {
|
|
2919
|
+
throw new TaxoraException(
|
|
2920
|
+
`${where}.category "${String(tax.category)}" is unknown. Valid: ${COMPLIANCE_TAX_CATEGORY_CODES.join(", ")}.`
|
|
2921
|
+
);
|
|
2922
|
+
}
|
|
2923
|
+
if (!Number.isFinite(tax.percent) || tax.percent < 0 || tax.percent > 100) {
|
|
2924
|
+
throw new TaxoraException(`${where}.percent must be between 0 and 100.`);
|
|
2925
|
+
}
|
|
2926
|
+
if (category === "E" && (tax.comment === void 0 || tax.comment.trim() === "")) {
|
|
2927
|
+
throw new TaxoraException(
|
|
2928
|
+
`${where}: an exempt (category E) tax needs the exemption code (VATEX) as comment.`
|
|
2929
|
+
);
|
|
2930
|
+
}
|
|
2931
|
+
if (tax.comment !== void 0 && tax.comment.length > 255) {
|
|
2932
|
+
throw new TaxoraException(`${where}.comment must not exceed 255 characters.`);
|
|
2933
|
+
}
|
|
2934
|
+
});
|
|
2004
2935
|
});
|
|
2005
2936
|
}
|
|
2937
|
+
validateInvoicePdfFilename(input) {
|
|
2938
|
+
if (input.invoicePdfFilename === void 0) return;
|
|
2939
|
+
if (input.invoicePdfFilename.length > 120) {
|
|
2940
|
+
throw new TaxoraException("invoicePdfFilename must not exceed 120 characters.");
|
|
2941
|
+
}
|
|
2942
|
+
if (input.invoicePdf === void 0) {
|
|
2943
|
+
throw new TaxoraException("invoicePdfFilename requires invoicePdf.");
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
validateInternalReference(internalReference) {
|
|
2947
|
+
if (internalReference !== void 0 && internalReference.length > 100) {
|
|
2948
|
+
throw new TaxoraException("internalReference must not exceed 100 characters.");
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
/**
|
|
2952
|
+
* Relations that cannot be right: a due date before the invoice date, a credit note without (or an
|
|
2953
|
+
* invoice with) a corrected invoice, a credit note that reuses the number it corrects, a corrected
|
|
2954
|
+
* invoice dated after the credit note.
|
|
2955
|
+
*/
|
|
2956
|
+
validateDocument(input) {
|
|
2957
|
+
for (const [field, value] of [
|
|
2958
|
+
["subtotal", input.subtotal],
|
|
2959
|
+
["taxAmount", input.taxAmount],
|
|
2960
|
+
["total", input.total]
|
|
2961
|
+
]) {
|
|
2962
|
+
if (value !== void 0 && !Number.isFinite(Number(value))) {
|
|
2963
|
+
throw new TaxoraException(`${field} must be a finite number.`);
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
for (const line of input.invoiceLines ?? []) {
|
|
2967
|
+
if (!Number.isFinite(line.quantity) || !Number.isFinite(line.price)) {
|
|
2968
|
+
throw new TaxoraException("Every invoice line needs a numeric quantity and price.");
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
const invoiceDate = this.formatDate(input.invoiceDate);
|
|
2972
|
+
if (input.dueDate !== void 0 && this.formatDate(input.dueDate) < invoiceDate) {
|
|
2973
|
+
throw new TaxoraException("dueDate must not be before invoiceDate.");
|
|
2974
|
+
}
|
|
2975
|
+
if (input.counterpartyVatNumber !== void 0 && input.counterpartyVatNumber.length > 50) {
|
|
2976
|
+
throw new TaxoraException("counterpartyVatNumber must not exceed 50 characters.");
|
|
2977
|
+
}
|
|
2978
|
+
const amended = input.amendedNumber?.trim() ?? "";
|
|
2979
|
+
if (input.isCreditNote === true && amended === "") {
|
|
2980
|
+
throw new TaxoraException("A credit note needs amendedNumber, the number of the invoice it corrects.");
|
|
2981
|
+
}
|
|
2982
|
+
if (input.isCreditNote !== true && amended !== "") {
|
|
2983
|
+
throw new TaxoraException("amendedNumber is only for credit notes; set isCreditNote to issue one.");
|
|
2984
|
+
}
|
|
2985
|
+
if (amended !== "" && amended === input.invoiceNumber.trim()) {
|
|
2986
|
+
throw new TaxoraException(
|
|
2987
|
+
"A credit note needs its own number; it cannot reuse the number of the invoice it corrects."
|
|
2988
|
+
);
|
|
2989
|
+
}
|
|
2990
|
+
if (input.amendedDate !== void 0 && this.formatDate(input.amendedDate) > invoiceDate) {
|
|
2991
|
+
throw new TaxoraException("amendedDate (the corrected invoice) must not be after the credit note date.");
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
/** A category read back from a stored invoice; one that is not a known code cannot be re-sent. */
|
|
2995
|
+
storedTaxCategory(category) {
|
|
2996
|
+
const parsed = parseComplianceTaxCategory(category);
|
|
2997
|
+
if (parsed === null) {
|
|
2998
|
+
throw new TaxoraException(
|
|
2999
|
+
`The original invoice has an unknown tax category "${String(category)}"; pass invoiceLines and the amounts explicitly.`
|
|
3000
|
+
);
|
|
3001
|
+
}
|
|
3002
|
+
return parsed;
|
|
3003
|
+
}
|
|
2006
3004
|
validateOptionalCodes(input) {
|
|
2007
|
-
if (input.currency !== void 0 && input.currency
|
|
2008
|
-
throw new TaxoraException("currency must be
|
|
3005
|
+
if (input.currency !== void 0 && !/^[A-Za-z]{3}$/.test(input.currency)) {
|
|
3006
|
+
throw new TaxoraException("currency must be an ISO 4217 code, three letters (e.g. NOK, EUR).");
|
|
2009
3007
|
}
|
|
2010
|
-
if (input.counterpartyCountry !== void 0 && input.counterpartyCountry
|
|
2011
|
-
throw new TaxoraException("counterpartyCountry must be
|
|
3008
|
+
if (input.counterpartyCountry !== void 0 && !/^[A-Za-z]{2}$/.test(input.counterpartyCountry)) {
|
|
3009
|
+
throw new TaxoraException("counterpartyCountry must be an ISO 3166-1 alpha-2 code, two letters (e.g. NO).");
|
|
2012
3010
|
}
|
|
2013
3011
|
}
|
|
2014
3012
|
/**
|
|
@@ -2051,7 +3049,7 @@ var EReportingEndpoint = class {
|
|
|
2051
3049
|
const taxBuilt = {
|
|
2052
3050
|
name: tax.name,
|
|
2053
3051
|
percent: tax.percent,
|
|
2054
|
-
category: tax.category
|
|
3052
|
+
category: parseComplianceTaxCategory(tax.category) ?? tax.category
|
|
2055
3053
|
};
|
|
2056
3054
|
if (tax.comment !== void 0) taxBuilt["comment"] = tax.comment;
|
|
2057
3055
|
return taxBuilt;
|
|
@@ -2064,10 +3062,10 @@ var EReportingEndpoint = class {
|
|
|
2064
3062
|
/** Applies the optional scalar fields shared by create and update. */
|
|
2065
3063
|
applyOptionalTransactionFields(body, input) {
|
|
2066
3064
|
if (input.dueDate !== void 0) body["due_date"] = this.formatDate(input.dueDate);
|
|
2067
|
-
if (input.currency !== void 0) body["currency"] = input.currency;
|
|
3065
|
+
if (input.currency !== void 0) body["currency"] = input.currency.toUpperCase();
|
|
2068
3066
|
if (input.taxAmount !== void 0) body["tax_amount"] = input.taxAmount;
|
|
2069
3067
|
if (input.counterpartyName !== void 0) body["counterparty_name"] = input.counterpartyName;
|
|
2070
|
-
if (input.counterpartyCountry !== void 0) body["counterparty_country"] = input.counterpartyCountry;
|
|
3068
|
+
if (input.counterpartyCountry !== void 0) body["counterparty_country"] = input.counterpartyCountry.toUpperCase();
|
|
2071
3069
|
if (input.counterpartyVatNumber !== void 0) body["counterparty_vat_number"] = input.counterpartyVatNumber;
|
|
2072
3070
|
if (input.providerContactId !== void 0) body["provider_contact_id"] = input.providerContactId;
|
|
2073
3071
|
if (input.paymentMethod !== void 0) body["payment_method"] = input.paymentMethod;
|
|
@@ -2084,6 +3082,29 @@ var EReportingEndpoint = class {
|
|
|
2084
3082
|
if (input.amendedNumber !== void 0) body["amended_number"] = input.amendedNumber;
|
|
2085
3083
|
if (input.amendedDate !== void 0) body["amended_date"] = this.formatDate(input.amendedDate);
|
|
2086
3084
|
if (input.extraInfo !== void 0) body["extra_info"] = input.extraInfo;
|
|
3085
|
+
if (input.internalReference !== void 0) body["internal_reference"] = input.internalReference;
|
|
3086
|
+
if (input.invoicePdf !== void 0) body["invoice_pdf"] = this.encodeInvoicePdf(input.invoicePdf);
|
|
3087
|
+
if (input.invoicePdfFilename !== void 0) body["invoice_pdf_filename"] = input.invoicePdfFilename;
|
|
3088
|
+
}
|
|
3089
|
+
/** Bytes → base64 (validated as a PDF of at most 10 MB); strings are taken as already base64. */
|
|
3090
|
+
encodeInvoicePdf(pdf) {
|
|
3091
|
+
if (typeof pdf === "string") {
|
|
3092
|
+
if (pdf.trim() === "") {
|
|
3093
|
+
throw new TaxoraException("invoicePdf must not be empty.");
|
|
3094
|
+
}
|
|
3095
|
+
return pdf;
|
|
3096
|
+
}
|
|
3097
|
+
const bytes = pdf instanceof Uint8Array ? pdf : new Uint8Array(pdf);
|
|
3098
|
+
if (bytes.byteLength === 0) {
|
|
3099
|
+
throw new TaxoraException("invoicePdf must not be empty.");
|
|
3100
|
+
}
|
|
3101
|
+
if (bytes.byteLength > INVOICE_PDF_MAX_BYTES) {
|
|
3102
|
+
throw new TaxoraException("invoicePdf must not exceed 10 MB.");
|
|
3103
|
+
}
|
|
3104
|
+
if (!INVOICE_PDF_MAGIC.every((byte, index) => bytes[index] === byte)) {
|
|
3105
|
+
throw new TaxoraException('invoicePdf must be a PDF file (content must start with "%PDF-").');
|
|
3106
|
+
}
|
|
3107
|
+
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
|
|
2087
3108
|
}
|
|
2088
3109
|
// ---------------------------------------------------------------------
|
|
2089
3110
|
// HTTP plumbing (kept in sync with SmartEnrichmentEndpoint)
|
|
@@ -2107,6 +3128,30 @@ var EReportingEndpoint = class {
|
|
|
2107
3128
|
const send = () => this.httpClient.request(method, url, options);
|
|
2108
3129
|
return method === "GET" ? withResponseRetries(this.retryPolicy, "e-reporting request", send) : send();
|
|
2109
3130
|
}
|
|
3131
|
+
/** GET for a non-JSON download (CSV / document): no JSON Content-Type, retried like other GETs. */
|
|
3132
|
+
async sendDownloadRequest(url, accept) {
|
|
3133
|
+
const headers = this.buildHeaders(false);
|
|
3134
|
+
headers["Accept"] = accept;
|
|
3135
|
+
return withResponseRetries(
|
|
3136
|
+
this.retryPolicy,
|
|
3137
|
+
"e-reporting download",
|
|
3138
|
+
() => this.httpClient.request("GET", url, { headers })
|
|
3139
|
+
);
|
|
3140
|
+
}
|
|
3141
|
+
/** `attachment; filename="x.xml"` (or RFC 5987 `filename*=UTF-8''x.xml`) → "x.xml". */
|
|
3142
|
+
filenameFromDisposition(header) {
|
|
3143
|
+
if (header === null) return null;
|
|
3144
|
+
const extended = /filename\*\s*=\s*(?:UTF-8|utf-8)''([^;]+)/.exec(header);
|
|
3145
|
+
if (extended?.[1] !== void 0) {
|
|
3146
|
+
try {
|
|
3147
|
+
return decodeURIComponent(extended[1].trim());
|
|
3148
|
+
} catch {
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
const plain = /filename\s*=\s*"([^"]*)"|filename\s*=\s*([^;]+)/.exec(header);
|
|
3152
|
+
const name = (plain?.[1] ?? plain?.[2])?.trim();
|
|
3153
|
+
return name !== void 0 && name !== "" ? name : null;
|
|
3154
|
+
}
|
|
2110
3155
|
/** Multipart upload: Content-Type is left to fetch so the boundary is set correctly. */
|
|
2111
3156
|
async sendMultipartRequest(method, url, form) {
|
|
2112
3157
|
return this.httpClient.request(method, url, { headers: this.buildHeaders(false), body: form });
|
|
@@ -2688,13 +3733,13 @@ var SmartEnrichmentHistoryPage = class _SmartEnrichmentHistoryPage {
|
|
|
2688
3733
|
};
|
|
2689
3734
|
|
|
2690
3735
|
// src/dto/SmartEnrichmentStatistics.ts
|
|
2691
|
-
function
|
|
3736
|
+
function asString12(value, fallback = "") {
|
|
2692
3737
|
return typeof value === "string" ? value : fallback;
|
|
2693
3738
|
}
|
|
2694
|
-
function
|
|
3739
|
+
function asNumber7(value) {
|
|
2695
3740
|
return typeof value === "number" ? value : Number(value ?? 0) || 0;
|
|
2696
3741
|
}
|
|
2697
|
-
function
|
|
3742
|
+
function asRecord5(value) {
|
|
2698
3743
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2699
3744
|
}
|
|
2700
3745
|
function asRecords2(value) {
|
|
@@ -2717,39 +3762,39 @@ var SmartEnrichmentStatistics = class _SmartEnrichmentStatistics {
|
|
|
2717
3762
|
byOutcome;
|
|
2718
3763
|
confidenceBuckets;
|
|
2719
3764
|
static fromArray(data) {
|
|
2720
|
-
const range =
|
|
2721
|
-
const totals =
|
|
3765
|
+
const range = asRecord5(data["range"]);
|
|
3766
|
+
const totals = asRecord5(data["totals"]);
|
|
2722
3767
|
const timeSeries = asRecords2(data["time_series"]).map((row) => ({
|
|
2723
|
-
bucket:
|
|
2724
|
-
jobs:
|
|
2725
|
-
items:
|
|
2726
|
-
found:
|
|
3768
|
+
bucket: asString12(row["bucket"]),
|
|
3769
|
+
jobs: asNumber7(row["jobs"]),
|
|
3770
|
+
items: asNumber7(row["items"]),
|
|
3771
|
+
found: asNumber7(row["found"])
|
|
2727
3772
|
}));
|
|
2728
3773
|
const bySource = asRecords2(data["by_source"]).map((row) => ({
|
|
2729
|
-
source:
|
|
2730
|
-
jobs:
|
|
2731
|
-
found:
|
|
3774
|
+
source: asString12(row["source"]),
|
|
3775
|
+
jobs: asNumber7(row["jobs"]),
|
|
3776
|
+
found: asNumber7(row["found"])
|
|
2732
3777
|
}));
|
|
2733
3778
|
const byOutcome = asRecords2(data["by_outcome"]).map((row) => ({
|
|
2734
|
-
outcome:
|
|
2735
|
-
count:
|
|
3779
|
+
outcome: asString12(row["outcome"]),
|
|
3780
|
+
count: asNumber7(row["count"])
|
|
2736
3781
|
}));
|
|
2737
3782
|
const confidenceBuckets = asRecords2(data["confidence_buckets"]).map((row) => ({
|
|
2738
|
-
bucket:
|
|
2739
|
-
count:
|
|
3783
|
+
bucket: asString12(row["bucket"]),
|
|
3784
|
+
count: asNumber7(row["count"])
|
|
2740
3785
|
}));
|
|
2741
3786
|
return new _SmartEnrichmentStatistics(
|
|
2742
3787
|
{
|
|
2743
|
-
dateFrom:
|
|
2744
|
-
dateTo:
|
|
2745
|
-
interval:
|
|
3788
|
+
dateFrom: asString12(range["date_from"]),
|
|
3789
|
+
dateTo: asString12(range["date_to"]),
|
|
3790
|
+
interval: asString12(range["interval"])
|
|
2746
3791
|
},
|
|
2747
3792
|
{
|
|
2748
|
-
jobs:
|
|
2749
|
-
items:
|
|
2750
|
-
found:
|
|
2751
|
-
foundRate:
|
|
2752
|
-
avgConfidence:
|
|
3793
|
+
jobs: asNumber7(totals["jobs"]),
|
|
3794
|
+
items: asNumber7(totals["items"]),
|
|
3795
|
+
found: asNumber7(totals["found"]),
|
|
3796
|
+
foundRate: asNumber7(totals["found_rate"]),
|
|
3797
|
+
avgConfidence: asNumber7(totals["avg_confidence"])
|
|
2753
3798
|
},
|
|
2754
3799
|
timeSeries,
|
|
2755
3800
|
bySource,
|
|
@@ -4029,7 +5074,7 @@ var AuthRetryHttpClient = class {
|
|
|
4029
5074
|
};
|
|
4030
5075
|
|
|
4031
5076
|
// src/version.ts
|
|
4032
|
-
var SDK_VERSION = "1.
|
|
5077
|
+
var SDK_VERSION = "1.9.0";
|
|
4033
5078
|
|
|
4034
5079
|
// src/http/SdkVersionHttpClient.ts
|
|
4035
5080
|
var SDK_VERSION_HEADER = "X-Taxora-SDK-Version";
|
|
@@ -4056,6 +5101,7 @@ var TaxoraClient = class {
|
|
|
4056
5101
|
company;
|
|
4057
5102
|
smartEnrichment;
|
|
4058
5103
|
eReporting;
|
|
5104
|
+
billing;
|
|
4059
5105
|
baseUrl;
|
|
4060
5106
|
constructor(options) {
|
|
4061
5107
|
const environment = options.environment ?? Environment.SANDBOX;
|
|
@@ -4076,6 +5122,7 @@ var TaxoraClient = class {
|
|
|
4076
5122
|
retryPolicy
|
|
4077
5123
|
);
|
|
4078
5124
|
this.eReporting = new EReportingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
|
|
5125
|
+
this.billing = new BillingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
|
|
4079
5126
|
}
|
|
4080
5127
|
};
|
|
4081
5128
|
|
|
@@ -4097,6 +5144,68 @@ var Language = {
|
|
|
4097
5144
|
GERMAN: "de"
|
|
4098
5145
|
};
|
|
4099
5146
|
|
|
5147
|
+
// src/support/calculateInvoiceTotals.ts
|
|
5148
|
+
function round2(value) {
|
|
5149
|
+
const magnitude = Math.round(Number((Math.abs(value) * 100).toPrecision(15)));
|
|
5150
|
+
return (value < 0 ? -magnitude : magnitude) / 100;
|
|
5151
|
+
}
|
|
5152
|
+
function calculateInvoiceTotals(lines, options = {}) {
|
|
5153
|
+
let net = 0;
|
|
5154
|
+
let tax = 0;
|
|
5155
|
+
for (const line of lines) {
|
|
5156
|
+
const lineNet = round2(line.quantity * line.price);
|
|
5157
|
+
const percent = line.taxes.reduce((sum, t) => sum + t.percent, 0);
|
|
5158
|
+
net += lineNet;
|
|
5159
|
+
tax += round2(lineNet * percent / 100);
|
|
5160
|
+
}
|
|
5161
|
+
const subtotal = round2(net);
|
|
5162
|
+
const taxAmount = round2(tax);
|
|
5163
|
+
const total = round2(subtotal + taxAmount);
|
|
5164
|
+
let roundingDifference = 0;
|
|
5165
|
+
if (options.expectedTotal !== void 0) {
|
|
5166
|
+
if (!Number.isFinite(options.expectedTotal)) {
|
|
5167
|
+
throw new Error("The expected total must be numeric.");
|
|
5168
|
+
}
|
|
5169
|
+
const tolerance = options.tolerance ?? 0.05;
|
|
5170
|
+
roundingDifference = round2(options.expectedTotal - total);
|
|
5171
|
+
if (Math.abs(roundingDifference) > tolerance) {
|
|
5172
|
+
throw new Error(
|
|
5173
|
+
`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.`
|
|
5174
|
+
);
|
|
5175
|
+
}
|
|
5176
|
+
}
|
|
5177
|
+
return { subtotal, taxAmount, total, roundingDifference };
|
|
5178
|
+
}
|
|
5179
|
+
|
|
5180
|
+
// src/support/complianceTax.ts
|
|
5181
|
+
var ComplianceTax = {
|
|
5182
|
+
/**
|
|
5183
|
+
* A taxed rate above 0 %, category S (Norway: 25 / 15 / 12 % are all S). Where a country files reduced rates
|
|
5184
|
+
* under their own category (France: AA / AAA), pass it as `category`.
|
|
5185
|
+
*/
|
|
5186
|
+
rate(percent, name = "VAT", category = ComplianceTaxCategory.STANDARD) {
|
|
5187
|
+
if (!Number.isFinite(percent) || percent <= 0) {
|
|
5188
|
+
throw new Error("rate() needs a percent above 0; for 0 % use zeroRated(), exempt() or reverseCharge().");
|
|
5189
|
+
}
|
|
5190
|
+
return { name, percent, category };
|
|
5191
|
+
},
|
|
5192
|
+
/** 0 % but taxable (category Z). */
|
|
5193
|
+
zeroRated(name = "VAT") {
|
|
5194
|
+
return { name, percent: 0, category: ComplianceTaxCategory.ZERO };
|
|
5195
|
+
},
|
|
5196
|
+
/** Exempt from VAT (category E); the VATEX exemption code is mandatory. */
|
|
5197
|
+
exempt(exemptionCode, name = "VAT") {
|
|
5198
|
+
if (exemptionCode.trim() === "") {
|
|
5199
|
+
throw new Error("An exempt (category E) tax needs the exemption code (VATEX).");
|
|
5200
|
+
}
|
|
5201
|
+
return { name, percent: 0, category: ComplianceTaxCategory.EXEMPT, comment: exemptionCode };
|
|
5202
|
+
},
|
|
5203
|
+
/** Reverse charge (category AE); the mandatory legal text is added by Taxora. */
|
|
5204
|
+
reverseCharge(name = "VAT") {
|
|
5205
|
+
return { name, percent: 0, category: ComplianceTaxCategory.REVERSE_CHARGE };
|
|
5206
|
+
}
|
|
5207
|
+
};
|
|
5208
|
+
|
|
4100
5209
|
// src/webhooks/WebhookSignature.ts
|
|
4101
5210
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
4102
5211
|
var WEBHOOK_SIGNATURE_HEADER = "Taxora-Signature";
|
|
@@ -4215,18 +5324,29 @@ function toBytes(rawBody) {
|
|
|
4215
5324
|
"invalid_body"
|
|
4216
5325
|
);
|
|
4217
5326
|
}
|
|
5327
|
+
|
|
5328
|
+
// src/webhooks/WebhookEvent.ts
|
|
5329
|
+
function isTransactionBuyerRejected(object) {
|
|
5330
|
+
return object.buyer_rejected === true;
|
|
5331
|
+
}
|
|
4218
5332
|
export {
|
|
4219
5333
|
ApiVersion,
|
|
4220
5334
|
AuthEndpoint,
|
|
4221
5335
|
AuthRetryHttpClient,
|
|
4222
5336
|
AuthenticationException,
|
|
5337
|
+
BillingEndpoint,
|
|
4223
5338
|
CompanyAddress,
|
|
4224
5339
|
CompanyEndpoint,
|
|
5340
|
+
ComplianceDeliveryAction,
|
|
5341
|
+
ComplianceDeliveryDecision,
|
|
4225
5342
|
ComplianceDocumentType,
|
|
4226
5343
|
ComplianceEnrollment,
|
|
4227
5344
|
ComplianceEnrollmentPage,
|
|
4228
5345
|
ComplianceEnrollmentStatus,
|
|
5346
|
+
ComplianceOfferings,
|
|
4229
5347
|
ComplianceService,
|
|
5348
|
+
ComplianceTax,
|
|
5349
|
+
ComplianceTaxCategory,
|
|
4230
5350
|
ComplianceTaxReport,
|
|
4231
5351
|
ComplianceTaxReportPage,
|
|
4232
5352
|
ComplianceTaxReportState,
|
|
@@ -4235,6 +5355,8 @@ export {
|
|
|
4235
5355
|
ComplianceTransactionState,
|
|
4236
5356
|
ComplianceTransactionType,
|
|
4237
5357
|
DEFAULT_WEBHOOK_TOLERANCE_SECONDS,
|
|
5358
|
+
EInvoicingQuota,
|
|
5359
|
+
EInvoicingQuotaPeriod,
|
|
4238
5360
|
EReportingEndpoint,
|
|
4239
5361
|
Environment,
|
|
4240
5362
|
FetchHttpClient,
|
|
@@ -4269,6 +5391,8 @@ export {
|
|
|
4269
5391
|
TaxoraClientFactory,
|
|
4270
5392
|
TaxoraException,
|
|
4271
5393
|
Token,
|
|
5394
|
+
UsageSummary,
|
|
5395
|
+
UsageSummaryPage,
|
|
4272
5396
|
ValidationException,
|
|
4273
5397
|
VatCertificateExport,
|
|
4274
5398
|
VatCollection,
|
|
@@ -4280,6 +5404,7 @@ export {
|
|
|
4280
5404
|
WEBHOOK_ID_HEADER,
|
|
4281
5405
|
WEBHOOK_SIGNATURE_HEADER,
|
|
4282
5406
|
WebhookSignatureVerificationError,
|
|
5407
|
+
calculateInvoiceTotals,
|
|
4283
5408
|
computeWebhookSignature,
|
|
4284
5409
|
constructWebhookEvent,
|
|
4285
5410
|
describeApiError,
|
|
@@ -4297,6 +5422,8 @@ export {
|
|
|
4297
5422
|
generateWebhookSignatureHeader,
|
|
4298
5423
|
getBaseUrl,
|
|
4299
5424
|
getFailedVatStates,
|
|
5425
|
+
isTransactionBuyerRejected,
|
|
5426
|
+
parseComplianceTaxCategory,
|
|
4300
5427
|
parseLocalTaxNumbersCsv,
|
|
4301
5428
|
parseWebhookSignatureHeader,
|
|
4302
5429
|
toComplianceDocumentType,
|