@getpeppr/cli 0.5.1 → 0.5.2
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/dist/index.js +92 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -631,6 +631,47 @@ function buildCreditNoteXml(input) {
|
|
|
631
631
|
</CreditNote>`;
|
|
632
632
|
}
|
|
633
633
|
|
|
634
|
+
// ../sdk/dist/core/checksums/luhn.js
|
|
635
|
+
function isValidLuhn(input) {
|
|
636
|
+
if (typeof input !== "string")
|
|
637
|
+
return false;
|
|
638
|
+
if (!/^\d+$/.test(input))
|
|
639
|
+
return false;
|
|
640
|
+
let sum = 0;
|
|
641
|
+
let alt = false;
|
|
642
|
+
for (let i = input.length - 1; i >= 0; i--) {
|
|
643
|
+
let n = input.charCodeAt(i) - 48;
|
|
644
|
+
if (alt) {
|
|
645
|
+
n *= 2;
|
|
646
|
+
if (n > 9)
|
|
647
|
+
n -= 9;
|
|
648
|
+
}
|
|
649
|
+
sum += n;
|
|
650
|
+
alt = !alt;
|
|
651
|
+
}
|
|
652
|
+
return sum % 10 === 0;
|
|
653
|
+
}
|
|
654
|
+
var LA_POSTE_SIREN = "356000000";
|
|
655
|
+
function isValidLuhnSiret(input) {
|
|
656
|
+
if (typeof input !== "string")
|
|
657
|
+
return false;
|
|
658
|
+
if (!/^\d{14}$/.test(input))
|
|
659
|
+
return false;
|
|
660
|
+
const siren = input.slice(0, 9);
|
|
661
|
+
if (!isValidLuhn(siren))
|
|
662
|
+
return false;
|
|
663
|
+
if (isValidLuhn(input))
|
|
664
|
+
return true;
|
|
665
|
+
if (siren === LA_POSTE_SIREN) {
|
|
666
|
+
let sum = 0;
|
|
667
|
+
for (let i = 0; i < input.length; i++) {
|
|
668
|
+
sum += input.charCodeAt(i) - 48;
|
|
669
|
+
}
|
|
670
|
+
return sum % 5 === 0;
|
|
671
|
+
}
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
|
|
634
675
|
// ../sdk/dist/core/country-rules.js
|
|
635
676
|
function warn(field, message, ruleId) {
|
|
636
677
|
return { field, message, ruleId };
|
|
@@ -665,15 +706,47 @@ function validateBelgiumSeller(input, _errors, warnings) {
|
|
|
665
706
|
warnings.push(warn("paymentReference", "Belgian sellers typically include a structured communication reference (+++NNN/NNNN/NNNNN+++ format).", "BE-02"));
|
|
666
707
|
}
|
|
667
708
|
}
|
|
709
|
+
var FR_SIREN_RE = /^\d{9}$/;
|
|
668
710
|
var FR_SIRET_RE = /^\d{14}$/;
|
|
669
|
-
var FR_VAT_RE = /^FR[
|
|
711
|
+
var FR_VAT_RE = /^FR[0-9A-HJ-NP-Z]{2}\d{9}$/;
|
|
712
|
+
var FR_VAT_NUMERIC_KEY_RE = /^FR(\d{2})(\d{9})$/;
|
|
713
|
+
var FR_SIREN_BASED_SCHEMES = ["0002", "0009", "0225"];
|
|
714
|
+
function frVatKey(siren) {
|
|
715
|
+
return (12 + 3 * (Number(siren) % 97)) % 97;
|
|
716
|
+
}
|
|
717
|
+
function isValidSirenOrSiret(id) {
|
|
718
|
+
if (FR_SIREN_RE.test(id))
|
|
719
|
+
return isValidLuhn(id);
|
|
720
|
+
if (FR_SIRET_RE.test(id))
|
|
721
|
+
return isValidLuhnSiret(id);
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
670
724
|
function validateFrance(input, _errors, warnings) {
|
|
671
|
-
const { companyId, vatNumber } = input.to ?? {};
|
|
672
|
-
|
|
673
|
-
|
|
725
|
+
const { companyId, companyIdScheme, vatNumber } = input.to ?? {};
|
|
726
|
+
const companyIdIsSiren = !companyIdScheme || FR_SIREN_BASED_SCHEMES.includes(companyIdScheme);
|
|
727
|
+
if (companyId != null && companyId !== "" && companyIdIsSiren) {
|
|
728
|
+
if (typeof companyId !== "string") {
|
|
729
|
+
warnings.push(warn("to.companyId", "French company ID should be a string of 9 (SIREN) or 14 (SIRET) digits.", "FR-01"));
|
|
730
|
+
} else if (!FR_SIREN_RE.test(companyId) && !FR_SIRET_RE.test(companyId)) {
|
|
731
|
+
warnings.push(warn("to.companyId", `French company ID should be a 9-digit SIREN or 14-digit SIRET, got "${companyId}".`, "FR-01"));
|
|
732
|
+
} else if (!isValidSirenOrSiret(companyId)) {
|
|
733
|
+
warnings.push(warn("to.companyId", `French company ID "${companyId}" has an invalid checksum. Verify the SIREN/SIRET.`, "FR-01"));
|
|
734
|
+
}
|
|
674
735
|
}
|
|
675
|
-
if (vatNumber &&
|
|
676
|
-
|
|
736
|
+
if (vatNumber != null && vatNumber !== "") {
|
|
737
|
+
if (typeof vatNumber !== "string") {
|
|
738
|
+
warnings.push(warn("to.vatNumber", "French VAT number should be a string matching FR + 2 characters + 9 digits (SIREN).", "FR-02"));
|
|
739
|
+
} else if (!FR_VAT_RE.test(vatNumber)) {
|
|
740
|
+
warnings.push(warn("to.vatNumber", `French VAT number should match format FR + 2 characters + 9 digits (SIREN), got "${vatNumber}".`, "FR-02"));
|
|
741
|
+
} else {
|
|
742
|
+
const numericKey = FR_VAT_NUMERIC_KEY_RE.exec(vatNumber);
|
|
743
|
+
if (numericKey) {
|
|
744
|
+
const [, key, siren] = numericKey;
|
|
745
|
+
if (Number(key) !== frVatKey(siren)) {
|
|
746
|
+
warnings.push(warn("to.vatNumber", `French VAT number "${vatNumber}" has an invalid verification key \u2014 expected FR${String(frVatKey(siren)).padStart(2, "0")}${siren}. Likely a typo.`, "FR-03"));
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
677
750
|
}
|
|
678
751
|
}
|
|
679
752
|
function validateItaly(input, _errors, warnings) {
|
|
@@ -1088,7 +1161,7 @@ function statusFamily(status) {
|
|
|
1088
1161
|
var TERMINAL_FAILURE_STATUSES = STATUS_PRECEDENCE.filter((e) => e.family === "terminal-failure").map((e) => e.status);
|
|
1089
1162
|
|
|
1090
1163
|
// ../sdk/dist/version.js
|
|
1091
|
-
var SDK_VERSION = "2.
|
|
1164
|
+
var SDK_VERSION = "2.4.0";
|
|
1092
1165
|
|
|
1093
1166
|
// ../sdk/dist/core/client.js
|
|
1094
1167
|
function findHeaderCaseInsensitive(headers, name) {
|
|
@@ -2184,15 +2257,21 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
|
|
|
2184
2257
|
* Transition an invoice to a new state.
|
|
2185
2258
|
* The gateway validates the state machine — invalid transitions return an error.
|
|
2186
2259
|
*
|
|
2260
|
+
* `"paid"` on a French CTC invoice reports the payment collection
|
|
2261
|
+
* (« signalement d'encaissement ») to the tax authority via the gateway —
|
|
2262
|
+
* a legal obligation of the French mandate for service invoices. The full
|
|
2263
|
+
* amount is reported from the invoice's stored tax breakdown (no amount to
|
|
2264
|
+
* pass), at most once per invoice: replays return the same report (200),
|
|
2265
|
+
* a concurrent report returns 409, a non-French invoice returns 422.
|
|
2266
|
+
* The invoice's own status becomes `paid` later, when the network confirms
|
|
2267
|
+
* (webhook / polling), not synchronously with this call.
|
|
2268
|
+
*
|
|
2187
2269
|
* @example
|
|
2188
2270
|
* ```ts
|
|
2189
|
-
*
|
|
2190
|
-
* await peppol.invoices.markAs("inv-123", "paid"
|
|
2191
|
-
* commit: "with_mail",
|
|
2192
|
-
* reason: "Payment received",
|
|
2193
|
-
* });
|
|
2271
|
+
* // France: report that the customer paid this invoice
|
|
2272
|
+
* await peppol.invoices.markAs("inv-123", "paid");
|
|
2194
2273
|
* ```
|
|
2195
|
-
* @throws {PeppolApiError} 501
|
|
2274
|
+
* @throws {PeppolApiError} 422 for "paid" on a non-French-CTC invoice; 501 for states the provider does not support
|
|
2196
2275
|
*/
|
|
2197
2276
|
async markAs(id, state, options) {
|
|
2198
2277
|
return this.adapter.markInvoiceAs(id, state, options);
|