@getpeppr/cli 0.11.0 → 0.12.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 +45 -0
- package/README.md +18 -9
- package/dist/index.js +226 -91
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -65,7 +65,7 @@ function formatWarning(item) {
|
|
|
65
65
|
function formatSection(title, errors, warnings) {
|
|
66
66
|
const lines = [sectionHeader(title)];
|
|
67
67
|
if (errors.length === 0 && warnings.length === 0) {
|
|
68
|
-
lines.push(` ${pc.green("\u2713")}
|
|
68
|
+
lines.push(` ${pc.green("\u2713")} No findings`);
|
|
69
69
|
return lines.join("\n");
|
|
70
70
|
}
|
|
71
71
|
if (errors.length === 0) {
|
|
@@ -94,7 +94,7 @@ Validating: ${pc.bold(filename)}
|
|
|
94
94
|
lines.push("");
|
|
95
95
|
lines.push(
|
|
96
96
|
formatSection(
|
|
97
|
-
"
|
|
97
|
+
"Offline Checks (partial)",
|
|
98
98
|
result.schematron.errors,
|
|
99
99
|
result.schematron.warnings
|
|
100
100
|
)
|
|
@@ -111,10 +111,10 @@ Validating: ${pc.bold(filename)}
|
|
|
111
111
|
lines.push(sectionHeader("Summary"));
|
|
112
112
|
const { totalErrors, totalWarnings, valid } = result;
|
|
113
113
|
if (valid && totalWarnings === 0) {
|
|
114
|
-
lines.push(` ${pc.green(pc.bold("\u2713
|
|
114
|
+
lines.push(` ${pc.green(pc.bold("\u2713 Pre-flight checks passed"))}`);
|
|
115
115
|
} else if (valid) {
|
|
116
116
|
lines.push(
|
|
117
|
-
` ${pc.green(pc.bold("\u2713
|
|
117
|
+
` ${pc.green(pc.bold("\u2713 Pre-flight checks passed"))} ${pc.dim(`(${totalWarnings} warning${totalWarnings === 1 ? "" : "s"})`)}`
|
|
118
118
|
);
|
|
119
119
|
} else {
|
|
120
120
|
const parts = [];
|
|
@@ -123,14 +123,14 @@ Validating: ${pc.bold(filename)}
|
|
|
123
123
|
parts.push(`${totalWarnings} warning${totalWarnings === 1 ? "" : "s"}`);
|
|
124
124
|
}
|
|
125
125
|
lines.push(
|
|
126
|
-
` ${pc.red(pc.bold(
|
|
126
|
+
` ${pc.red(pc.bold("\u2717 Pre-flight checks found errors"))} ${pc.dim(`(${parts.join(", ")})`)}`
|
|
127
127
|
);
|
|
128
128
|
}
|
|
129
129
|
lines.push("");
|
|
130
130
|
return lines.join("\n");
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
//
|
|
133
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/canonical-schemes.js
|
|
134
134
|
var ALIAS_TO_EAS = /* @__PURE__ */ new Map([
|
|
135
135
|
["AD:VAT", "9922"],
|
|
136
136
|
["AE:TIN", "0235"],
|
|
@@ -356,7 +356,7 @@ function countryForScheme(scheme) {
|
|
|
356
356
|
var CANONICAL_SCHEME_COUNT = ALIAS_TO_EAS.size;
|
|
357
357
|
var SCHEME_COUNTRY_COUNT = EAS_TO_COUNTRY.size;
|
|
358
358
|
|
|
359
|
-
//
|
|
359
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/peppol-id.js
|
|
360
360
|
function isWellFormedPeppolId(peppolId) {
|
|
361
361
|
if (!peppolId.includes(":"))
|
|
362
362
|
return false;
|
|
@@ -382,7 +382,7 @@ function parsePeppolId(peppolId) {
|
|
|
382
382
|
};
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
-
//
|
|
385
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/iso6523-icd-codes.js
|
|
386
386
|
var ICD_CODES = /* @__PURE__ */ new Set([
|
|
387
387
|
"0002",
|
|
388
388
|
"0003",
|
|
@@ -645,7 +645,7 @@ function canCarryPartyIdentification(scheme, context) {
|
|
|
645
645
|
return scheme === "SEPA" && context !== "AccountingCustomerParty";
|
|
646
646
|
}
|
|
647
647
|
|
|
648
|
-
//
|
|
648
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/ubl-builder.js
|
|
649
649
|
var UBL_NS = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2";
|
|
650
650
|
var CAC_NS = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2";
|
|
651
651
|
var CBC_NS = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2";
|
|
@@ -1296,7 +1296,7 @@ function buildCreditNoteXml(input) {
|
|
|
1296
1296
|
</CreditNote>`;
|
|
1297
1297
|
}
|
|
1298
1298
|
|
|
1299
|
-
//
|
|
1299
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/checksums/luhn.js
|
|
1300
1300
|
function isValidLuhn(input) {
|
|
1301
1301
|
if (typeof input !== "string")
|
|
1302
1302
|
return false;
|
|
@@ -1337,7 +1337,7 @@ function isValidLuhnSiret(input) {
|
|
|
1337
1337
|
return false;
|
|
1338
1338
|
}
|
|
1339
1339
|
|
|
1340
|
-
//
|
|
1340
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/country-rules.js
|
|
1341
1341
|
function warn(field, message, ruleId) {
|
|
1342
1342
|
return { field, message, ruleId };
|
|
1343
1343
|
}
|
|
@@ -1478,7 +1478,7 @@ function validateCountryRules(input) {
|
|
|
1478
1478
|
return { errors, warnings };
|
|
1479
1479
|
}
|
|
1480
1480
|
|
|
1481
|
-
//
|
|
1481
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/code-lists.js
|
|
1482
1482
|
var CURRENCIES = /* @__PURE__ */ new Map([
|
|
1483
1483
|
["EUR", { code: "EUR", name: "Euro", minorUnits: 2 }],
|
|
1484
1484
|
["USD", { code: "USD", name: "US Dollar", minorUnits: 2 }],
|
|
@@ -1681,7 +1681,7 @@ function getCreditNoteTypeCodes() {
|
|
|
1681
1681
|
return [...CREDIT_NOTE_TYPE_CODES];
|
|
1682
1682
|
}
|
|
1683
1683
|
|
|
1684
|
-
//
|
|
1684
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/validator.js
|
|
1685
1685
|
function error(field, message, ruleId, suggestion) {
|
|
1686
1686
|
return { field, message, ruleId, suggestion };
|
|
1687
1687
|
}
|
|
@@ -1698,16 +1698,19 @@ function assertString(value, fieldPath, errors) {
|
|
|
1698
1698
|
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
1699
1699
|
var ALLOWANCE_CHARGE_CONTRACT_RULE = "GETPEPPR-ALLOWANCE-CHARGE-AMOUNT";
|
|
1700
1700
|
var DERIVED_AMOUNT_CONTRACT_RULE2 = "GETPEPPR-DERIVED-AMOUNT";
|
|
1701
|
+
var BUYER_ADDRESS_DELIVERY_RULE = "GETPEPPR-BUYER-ADDRESS";
|
|
1702
|
+
var LINE_VAT_RATE_RULE = "GETPEPPR-LINE-VAT-RATE";
|
|
1703
|
+
var TAX_CURRENCY_RATE_RULE = "GETPEPPR-TAX-CURRENCY-RATE";
|
|
1701
1704
|
function survivesCents2(value) {
|
|
1702
1705
|
return Number.isFinite(value) && Number.isFinite(value * 100);
|
|
1703
1706
|
}
|
|
1704
1707
|
function validateParty(party, path) {
|
|
1705
1708
|
const errors = [];
|
|
1706
1709
|
if (party.name === void 0 || party.name === null || party.name === "") {
|
|
1707
|
-
errors.push(error(`${path}.name`, "Business name is required", "BR-
|
|
1710
|
+
errors.push(error(`${path}.name`, "Business name is required", "BR-07"));
|
|
1708
1711
|
} else if (!assertString(party.name, `${path}.name`, errors)) {
|
|
1709
1712
|
} else if (!party.name.trim()) {
|
|
1710
|
-
errors.push(error(`${path}.name`, "Business name is required", "BR-
|
|
1713
|
+
errors.push(error(`${path}.name`, "Business name is required", "BR-07"));
|
|
1711
1714
|
}
|
|
1712
1715
|
if (party.peppolId === void 0 || party.peppolId === null || party.peppolId === "") {
|
|
1713
1716
|
errors.push(error(`${path}.peppolId`, "Peppol participant ID is required", void 0, 'Format: "scheme:id", e.g. "0208:0685660237" for Belgian companies'));
|
|
@@ -1726,22 +1729,22 @@ function validateParty(party, path) {
|
|
|
1726
1729
|
function validateBuyerAddress(party, path) {
|
|
1727
1730
|
const errors = [];
|
|
1728
1731
|
if (party.street === void 0 || party.street === null || party.street === "") {
|
|
1729
|
-
errors.push(error(`${path}.street`, "Street address is required for the buyer",
|
|
1732
|
+
errors.push(error(`${path}.street`, "Street address is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "123 Business Street"'));
|
|
1730
1733
|
} else if (!assertString(party.street, `${path}.street`, errors)) {
|
|
1731
1734
|
} else if (!party.street.trim()) {
|
|
1732
|
-
errors.push(error(`${path}.street`, "Street address is required for the buyer",
|
|
1735
|
+
errors.push(error(`${path}.street`, "Street address is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "123 Business Street"'));
|
|
1733
1736
|
}
|
|
1734
1737
|
if (party.city === void 0 || party.city === null || party.city === "") {
|
|
1735
|
-
errors.push(error(`${path}.city`, "City is required for the buyer",
|
|
1738
|
+
errors.push(error(`${path}.city`, "City is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "Brussels"'));
|
|
1736
1739
|
} else if (!assertString(party.city, `${path}.city`, errors)) {
|
|
1737
1740
|
} else if (!party.city.trim()) {
|
|
1738
|
-
errors.push(error(`${path}.city`, "City is required for the buyer",
|
|
1741
|
+
errors.push(error(`${path}.city`, "City is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "Brussels"'));
|
|
1739
1742
|
}
|
|
1740
1743
|
if (party.postalCode === void 0 || party.postalCode === null || party.postalCode === "") {
|
|
1741
|
-
errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer",
|
|
1744
|
+
errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "1000"'));
|
|
1742
1745
|
} else if (!assertString(party.postalCode, `${path}.postalCode`, errors)) {
|
|
1743
1746
|
} else if (!party.postalCode.trim()) {
|
|
1744
|
-
errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer",
|
|
1747
|
+
errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "1000"'));
|
|
1745
1748
|
}
|
|
1746
1749
|
return errors;
|
|
1747
1750
|
}
|
|
@@ -1770,7 +1773,7 @@ function validateLine(line, index, isCreditNote = false) {
|
|
|
1770
1773
|
}
|
|
1771
1774
|
}
|
|
1772
1775
|
if (line.vatRate === void 0 || line.vatRate === null) {
|
|
1773
|
-
errors.push(error(`${path}.vatRate`, "VAT rate is required",
|
|
1776
|
+
errors.push(error(`${path}.vatRate`, "VAT rate is required", LINE_VAT_RATE_RULE));
|
|
1774
1777
|
} else if (line.vatRate < 0 || line.vatRate > 100) {
|
|
1775
1778
|
errors.push(error(`${path}.vatRate`, `VAT rate must be between 0 and 100, got ${line.vatRate}`, void 0, "Use 0 for zero-rated, 21 for standard Belgian VAT, etc."));
|
|
1776
1779
|
}
|
|
@@ -1945,10 +1948,25 @@ function validateInvoice(input) {
|
|
|
1945
1948
|
}
|
|
1946
1949
|
}
|
|
1947
1950
|
if (!input.buyerReference && !input.orderReference) {
|
|
1948
|
-
warnings.push(warning(
|
|
1951
|
+
warnings.push(warning(
|
|
1952
|
+
"buyerReference",
|
|
1953
|
+
"Either buyerReference or orderReference is required by Peppol BIS 3.0 (BT-10).",
|
|
1954
|
+
// GPR-1267 — PEPPOL-EN16931-R003 IS this rule: "A buyer reference or
|
|
1955
|
+
// purchase order reference MUST be provided." BR-10 is the buyer
|
|
1956
|
+
// postal address.
|
|
1957
|
+
"PEPPOL-EN16931-R003"
|
|
1958
|
+
));
|
|
1949
1959
|
}
|
|
1950
1960
|
if (input.taxCurrency && input.taxCurrency !== (input.currency ?? "EUR") && !input.taxCurrencyRate) {
|
|
1951
|
-
errors.push(error(
|
|
1961
|
+
errors.push(error(
|
|
1962
|
+
"taxCurrencyRate",
|
|
1963
|
+
"Tax currency rate is required when taxCurrency differs from document currency",
|
|
1964
|
+
// GPR-1267 — BR-53 mandates the BT-111 total in accounting currency,
|
|
1965
|
+
// which the builder derives itself; the client-supplied rate is an
|
|
1966
|
+
// input-contract requirement, not that rule.
|
|
1967
|
+
TAX_CURRENCY_RATE_RULE,
|
|
1968
|
+
"Set taxCurrencyRate to the exchange rate from document currency to tax currency"
|
|
1969
|
+
));
|
|
1952
1970
|
}
|
|
1953
1971
|
if (input.taxCurrency && input.taxCurrency === (input.currency ?? "EUR")) {
|
|
1954
1972
|
warnings.push(warning("taxCurrency", "Tax currency is the same as document currency \u2014 TaxCurrencyCode will be omitted"));
|
|
@@ -1963,7 +1981,7 @@ function validateInvoice(input) {
|
|
|
1963
1981
|
errors.push(error("taxCurrency", `Invalid tax currency code: "${input.taxCurrency}"`, void 0, 'Use ISO 4217 (e.g., "EUR", "USD")'));
|
|
1964
1982
|
}
|
|
1965
1983
|
if (!input.dueDate) {
|
|
1966
|
-
warnings.push(warning("dueDate", "No due date specified. Recommended for payment terms."
|
|
1984
|
+
warnings.push(warning("dueDate", "No due date specified. Recommended for payment terms."));
|
|
1967
1985
|
}
|
|
1968
1986
|
if (!input.to?.vatNumber) {
|
|
1969
1987
|
warnings.push(warning("to.vatNumber", "Buyer VAT number not provided. May be required for B2B."));
|
|
@@ -1983,16 +2001,16 @@ function validateInvoice(input) {
|
|
|
1983
2001
|
};
|
|
1984
2002
|
}
|
|
1985
2003
|
|
|
1986
|
-
//
|
|
2004
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/schematron.js
|
|
1987
2005
|
function violation(ruleId, severity, message, field) {
|
|
1988
2006
|
return { ruleId, severity, message, field };
|
|
1989
2007
|
}
|
|
1990
|
-
var
|
|
1991
|
-
function
|
|
1992
|
-
if (!
|
|
1993
|
-
|
|
2008
|
+
var _sdkUnitCodes;
|
|
2009
|
+
function getSdkUnitCodes() {
|
|
2010
|
+
if (!_sdkUnitCodes) {
|
|
2011
|
+
_sdkUnitCodes = new Set(getAllUnits().map((u) => u.code));
|
|
1994
2012
|
}
|
|
1995
|
-
return
|
|
2013
|
+
return _sdkUnitCodes;
|
|
1996
2014
|
}
|
|
1997
2015
|
var VALID_VAT_CATEGORIES = /* @__PURE__ */ new Set(["S", "Z", "E", "AE", "K", "G", "O", "L", "M", "B"]);
|
|
1998
2016
|
var SENDABLE_VAT_CATEGORIES = ["S", "Z", "E", "AE", "K", "G", "O"];
|
|
@@ -2012,18 +2030,18 @@ var br02 = (input) => {
|
|
|
2012
2030
|
}
|
|
2013
2031
|
return [];
|
|
2014
2032
|
};
|
|
2015
|
-
var
|
|
2033
|
+
var issueDateDefaulted = (input) => {
|
|
2016
2034
|
if (!input.date) {
|
|
2017
2035
|
return [
|
|
2018
|
-
violation("
|
|
2036
|
+
violation("GETPEPPR-ISSUE-DATE-DEFAULTED", "warning", "Invoice issue date is not set. The SDK will default to today's date.", "date")
|
|
2019
2037
|
];
|
|
2020
2038
|
}
|
|
2021
2039
|
return [];
|
|
2022
2040
|
};
|
|
2023
|
-
var
|
|
2041
|
+
var sellerVatRecommendation = (input) => {
|
|
2024
2042
|
if (input.from && !input.from.vatNumber) {
|
|
2025
2043
|
return [
|
|
2026
|
-
violation("
|
|
2044
|
+
violation("GETPEPPR-FROM-VAT-RECOMMENDED", "warning", "Seller party has no VAT number. The gateway will use the account's VAT registration.", "from.vatNumber")
|
|
2027
2045
|
];
|
|
2028
2046
|
}
|
|
2029
2047
|
return [];
|
|
@@ -2034,29 +2052,29 @@ var br07 = (input) => {
|
|
|
2034
2052
|
}
|
|
2035
2053
|
return [];
|
|
2036
2054
|
};
|
|
2037
|
-
var
|
|
2055
|
+
var br16 = (input) => {
|
|
2038
2056
|
if (!input.lines || input.lines.length === 0) {
|
|
2039
|
-
return [violation("BR-
|
|
2057
|
+
return [violation("BR-16", "error", "Invoice must have at least one line item.", "lines")];
|
|
2040
2058
|
}
|
|
2041
2059
|
return [];
|
|
2042
2060
|
};
|
|
2043
|
-
var
|
|
2061
|
+
var paymentTimingRecommendation = (input) => {
|
|
2044
2062
|
if (!input.dueDate && !input.paymentTerms) {
|
|
2045
2063
|
return [
|
|
2046
|
-
violation("
|
|
2064
|
+
violation("GETPEPPR-PAYMENT-TIMING-RECOMMENDED", "warning", "Neither due date nor payment terms specified. At least one is recommended.", "dueDate")
|
|
2047
2065
|
];
|
|
2048
2066
|
}
|
|
2049
2067
|
return [];
|
|
2050
2068
|
};
|
|
2051
|
-
var
|
|
2069
|
+
var buyerOrOrderReferenceRecommendation = (input) => {
|
|
2052
2070
|
if (!input.buyerReference && !input.orderReference) {
|
|
2053
2071
|
return [
|
|
2054
|
-
violation("
|
|
2072
|
+
violation("GETPEPPR-BUYER-OR-ORDER-REFERENCE-RECOMMENDED", "warning", "Neither buyerReference nor orderReference specified. Peppol BIS 3.0 requires at least one.", "buyerReference")
|
|
2055
2073
|
];
|
|
2056
2074
|
}
|
|
2057
2075
|
return [];
|
|
2058
2076
|
};
|
|
2059
|
-
var
|
|
2077
|
+
var lineNetSanity = (input) => {
|
|
2060
2078
|
const violations = [];
|
|
2061
2079
|
if (!input.lines)
|
|
2062
2080
|
return violations;
|
|
@@ -2066,12 +2084,12 @@ var brCo10 = (input) => {
|
|
|
2066
2084
|
if (!Number.isFinite(net)) {
|
|
2067
2085
|
const baseQty = line.baseQuantity ?? 1;
|
|
2068
2086
|
const detail = baseQty === 0 ? "baseQuantity is 0, causing division by zero." : "Computed line amount is not a finite number.";
|
|
2069
|
-
violations.push(violation("
|
|
2087
|
+
violations.push(violation("GETPEPPR-LINE-NET-SANITY", "error", `Line ${i}: invalid net amount. ${detail}`, `lines[${i}]`));
|
|
2070
2088
|
}
|
|
2071
2089
|
}
|
|
2072
2090
|
return violations;
|
|
2073
2091
|
};
|
|
2074
|
-
var
|
|
2092
|
+
var computedVatSanity = (input) => {
|
|
2075
2093
|
if (!input.lines || input.lines.length === 0)
|
|
2076
2094
|
return [];
|
|
2077
2095
|
let totalVat = 0;
|
|
@@ -2090,17 +2108,17 @@ var brCo13 = (input) => {
|
|
|
2090
2108
|
}
|
|
2091
2109
|
if (!Number.isFinite(totalVat)) {
|
|
2092
2110
|
return [
|
|
2093
|
-
violation("
|
|
2111
|
+
violation("GETPEPPR-COMPUTED-VAT-SANITY", "error", "Computed total VAT amount is not a finite number. Check line amounts and VAT rates.")
|
|
2094
2112
|
];
|
|
2095
2113
|
}
|
|
2096
2114
|
if (totalVat < -0.01) {
|
|
2097
2115
|
return [
|
|
2098
|
-
violation("
|
|
2116
|
+
violation("GETPEPPR-COMPUTED-VAT-SANITY", "warning", `Computed total VAT is negative (${totalVat.toFixed(2)}). This is unusual for an invoice.`)
|
|
2099
2117
|
];
|
|
2100
2118
|
}
|
|
2101
2119
|
return [];
|
|
2102
2120
|
};
|
|
2103
|
-
var
|
|
2121
|
+
var taxInclusiveSanity = (input) => {
|
|
2104
2122
|
if (!input.lines || input.lines.length === 0)
|
|
2105
2123
|
return [];
|
|
2106
2124
|
let lineTotal = 0;
|
|
@@ -2123,17 +2141,17 @@ var brCo15 = (input) => {
|
|
|
2123
2141
|
const taxInclusive = lineTotal + vatTotal;
|
|
2124
2142
|
if (!Number.isFinite(taxInclusive)) {
|
|
2125
2143
|
return [
|
|
2126
|
-
violation("
|
|
2144
|
+
violation("GETPEPPR-TAX-INCLUSIVE-SANITY", "error", "Computed tax-inclusive amount is not a finite number.")
|
|
2127
2145
|
];
|
|
2128
2146
|
}
|
|
2129
2147
|
if (taxInclusive < -0.01) {
|
|
2130
2148
|
return [
|
|
2131
|
-
violation("
|
|
2149
|
+
violation("GETPEPPR-TAX-INCLUSIVE-SANITY", "warning", `Computed tax-inclusive amount is negative (${taxInclusive.toFixed(2)}). Consider using a credit note instead.`)
|
|
2132
2150
|
];
|
|
2133
2151
|
}
|
|
2134
2152
|
return [];
|
|
2135
2153
|
};
|
|
2136
|
-
var
|
|
2154
|
+
var payableSanity = (input) => {
|
|
2137
2155
|
if (!input.lines || input.lines.length === 0)
|
|
2138
2156
|
return [];
|
|
2139
2157
|
let lineTotal = 0;
|
|
@@ -2159,12 +2177,12 @@ var brCo16 = (input) => {
|
|
|
2159
2177
|
const payable = taxInclusive - prepaid + rounding;
|
|
2160
2178
|
if (!Number.isFinite(payable)) {
|
|
2161
2179
|
return [
|
|
2162
|
-
violation("
|
|
2180
|
+
violation("GETPEPPR-PAYABLE-SANITY", "error", "Computed payable amount is not a finite number.")
|
|
2163
2181
|
];
|
|
2164
2182
|
}
|
|
2165
2183
|
if (payable < -0.01) {
|
|
2166
2184
|
return [
|
|
2167
|
-
violation("
|
|
2185
|
+
violation("GETPEPPR-PAYABLE-SANITY", "warning", `Computed payable amount is negative (${payable.toFixed(2)}). Check invoice totals, prepaid amount (${prepaid}), and rounding amount (${rounding}).`)
|
|
2168
2186
|
];
|
|
2169
2187
|
}
|
|
2170
2188
|
return [];
|
|
@@ -2372,14 +2390,98 @@ function validateUblBuilderVat(input) {
|
|
|
2372
2390
|
...UBL_BUILDER_VAT_RULES.flatMap((rule) => rule(input))
|
|
2373
2391
|
];
|
|
2374
2392
|
}
|
|
2375
|
-
var
|
|
2393
|
+
var buyerPeppolIdRequired = (input) => {
|
|
2376
2394
|
if (!input.to?.peppolId) {
|
|
2377
2395
|
return [
|
|
2378
|
-
violation("PEPPOL-
|
|
2396
|
+
violation("GETPEPPR-BUYER-PEPPOL-ID-REQUIRED", "error", "Buyer electronic address (peppolId) is required for Peppol delivery.", "to.peppolId")
|
|
2379
2397
|
];
|
|
2380
2398
|
}
|
|
2381
2399
|
return [];
|
|
2382
2400
|
};
|
|
2401
|
+
var PROFILE_INVOICE_TYPE_CODES = /* @__PURE__ */ new Set([
|
|
2402
|
+
"71",
|
|
2403
|
+
"80",
|
|
2404
|
+
"82",
|
|
2405
|
+
"84",
|
|
2406
|
+
"102",
|
|
2407
|
+
"218",
|
|
2408
|
+
"219",
|
|
2409
|
+
"326",
|
|
2410
|
+
"331",
|
|
2411
|
+
"380",
|
|
2412
|
+
"382",
|
|
2413
|
+
"383",
|
|
2414
|
+
"384",
|
|
2415
|
+
"386",
|
|
2416
|
+
"388",
|
|
2417
|
+
"393",
|
|
2418
|
+
"395",
|
|
2419
|
+
"553",
|
|
2420
|
+
"575",
|
|
2421
|
+
"623",
|
|
2422
|
+
"780",
|
|
2423
|
+
"817",
|
|
2424
|
+
"870",
|
|
2425
|
+
"875",
|
|
2426
|
+
"876",
|
|
2427
|
+
"877"
|
|
2428
|
+
]);
|
|
2429
|
+
var PROFILE_CREDIT_NOTE_TYPE_CODES = /* @__PURE__ */ new Set([
|
|
2430
|
+
"381",
|
|
2431
|
+
"396",
|
|
2432
|
+
"81",
|
|
2433
|
+
"83",
|
|
2434
|
+
"532"
|
|
2435
|
+
]);
|
|
2436
|
+
function normalizeXmlSpace(value) {
|
|
2437
|
+
return value.replace(/[\u0009\u000A\u000D\u0020]+/g, " ").replace(/^ /, "").replace(/ $/, "");
|
|
2438
|
+
}
|
|
2439
|
+
function normalizedTypeCode(value, fallback) {
|
|
2440
|
+
if (value === void 0 || value === null)
|
|
2441
|
+
return String(fallback);
|
|
2442
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
2443
|
+
return String(value);
|
|
2444
|
+
if (typeof value === "string" && value.length <= 16)
|
|
2445
|
+
return normalizeXmlSpace(value);
|
|
2446
|
+
return void 0;
|
|
2447
|
+
}
|
|
2448
|
+
function normalizedCountry(value) {
|
|
2449
|
+
return typeof value === "string" ? normalizeXmlSpace(value).toUpperCase() : void 0;
|
|
2450
|
+
}
|
|
2451
|
+
var invoiceTypeCodeForProfile = (input) => {
|
|
2452
|
+
if (input.isCreditNote)
|
|
2453
|
+
return [];
|
|
2454
|
+
const invoiceTypeCode = normalizedTypeCode(input.invoiceTypeCode, 380);
|
|
2455
|
+
if (invoiceTypeCode !== void 0 && PROFILE_INVOICE_TYPE_CODES.has(invoiceTypeCode))
|
|
2456
|
+
return [];
|
|
2457
|
+
return [
|
|
2458
|
+
violation("PEPPOL-EN16931-P0100", "error", "Invoice type code is not allowed by the Peppol billing profile used by the local UBL builder.", "invoiceTypeCode")
|
|
2459
|
+
];
|
|
2460
|
+
};
|
|
2461
|
+
var creditNoteTypeCodeForProfile = (input) => {
|
|
2462
|
+
if (!input.isCreditNote)
|
|
2463
|
+
return [];
|
|
2464
|
+
const invoiceTypeCode = normalizedTypeCode(input.invoiceTypeCode, 381);
|
|
2465
|
+
if (invoiceTypeCode !== void 0 && PROFILE_CREDIT_NOTE_TYPE_CODES.has(invoiceTypeCode))
|
|
2466
|
+
return [];
|
|
2467
|
+
return [
|
|
2468
|
+
violation("PEPPOL-EN16931-P0101", "error", "Credit note type code is not allowed by the Peppol billing profile used by the local UBL builder.", "invoiceTypeCode")
|
|
2469
|
+
];
|
|
2470
|
+
};
|
|
2471
|
+
var germanInvoiceTypeCode = (input) => {
|
|
2472
|
+
if (input.isCreditNote)
|
|
2473
|
+
return [];
|
|
2474
|
+
const invoiceTypeCode = normalizedTypeCode(input.invoiceTypeCode, 380);
|
|
2475
|
+
if (invoiceTypeCode !== "326" && invoiceTypeCode !== "384")
|
|
2476
|
+
return [];
|
|
2477
|
+
const sellerCountry = normalizedCountry(input.from?.country);
|
|
2478
|
+
const buyerCountry = normalizedCountry(input.to?.country);
|
|
2479
|
+
if (sellerCountry === "DE" && buyerCountry === "DE")
|
|
2480
|
+
return [];
|
|
2481
|
+
return [
|
|
2482
|
+
violation("PEPPOL-EN16931-P0112", "error", "Invoice type code 326 or 384 is only allowed when both buyer and seller are German organizations.", "invoiceTypeCode")
|
|
2483
|
+
];
|
|
2484
|
+
};
|
|
2383
2485
|
var vatCategoryCodes = (input) => {
|
|
2384
2486
|
const violations = [];
|
|
2385
2487
|
const sendable = SENDABLE_VAT_CATEGORIES.join(", ");
|
|
@@ -2400,17 +2502,17 @@ var vatCategoryCodes = (input) => {
|
|
|
2400
2502
|
(input.charges ?? []).forEach((c, i) => check(c.vatCategory, `charges[${i}].vatCategory`, `Charge ${i}`));
|
|
2401
2503
|
return violations;
|
|
2402
2504
|
};
|
|
2403
|
-
var
|
|
2505
|
+
var unitCodeRecognised = (input) => {
|
|
2404
2506
|
const violations = [];
|
|
2405
2507
|
if (!input.lines)
|
|
2406
2508
|
return violations;
|
|
2407
|
-
const knownCodes =
|
|
2509
|
+
const knownCodes = getSdkUnitCodes();
|
|
2408
2510
|
for (let i = 0; i < input.lines.length; i++) {
|
|
2409
2511
|
const line = input.lines[i];
|
|
2410
2512
|
if (line.unit) {
|
|
2411
2513
|
const resolved = resolveUnit(line.unit);
|
|
2412
2514
|
if (!knownCodes.has(resolved)) {
|
|
2413
|
-
violations.push(violation("
|
|
2515
|
+
violations.push(violation("GETPEPPR-UNIT-CODE-RECOGNISED", "warning", `Line ${i}: unit "${line.unit}" (resolved: "${resolved}") is not in the SDK's common unit list. It may still be a valid UN/ECE Rec20/21 code; network validation decides. Common codes: EA, HUR, DAY, KGM.`, `lines[${i}].unit`));
|
|
2414
2516
|
}
|
|
2415
2517
|
}
|
|
2416
2518
|
}
|
|
@@ -2419,17 +2521,17 @@ var peppolR080 = (input) => {
|
|
|
2419
2521
|
var ALL_RULES = [
|
|
2420
2522
|
// Required fields (BR)
|
|
2421
2523
|
br02,
|
|
2422
|
-
|
|
2423
|
-
|
|
2524
|
+
issueDateDefaulted,
|
|
2525
|
+
sellerVatRecommendation,
|
|
2424
2526
|
br07,
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2527
|
+
br16,
|
|
2528
|
+
paymentTimingRecommendation,
|
|
2529
|
+
buyerOrOrderReferenceRecommendation,
|
|
2428
2530
|
// Calculations (BR-CO)
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2531
|
+
lineNetSanity,
|
|
2532
|
+
computedVatSanity,
|
|
2533
|
+
taxInclusiveSanity,
|
|
2534
|
+
payableSanity,
|
|
2433
2535
|
// Tax categories (one family per category)
|
|
2434
2536
|
brS05,
|
|
2435
2537
|
brZ05,
|
|
@@ -2444,9 +2546,12 @@ var ALL_RULES = [
|
|
|
2444
2546
|
brO10,
|
|
2445
2547
|
brIc10,
|
|
2446
2548
|
// Peppol-specific
|
|
2447
|
-
|
|
2549
|
+
buyerPeppolIdRequired,
|
|
2550
|
+
invoiceTypeCodeForProfile,
|
|
2551
|
+
creditNoteTypeCodeForProfile,
|
|
2552
|
+
germanInvoiceTypeCode,
|
|
2448
2553
|
vatCategoryCodes,
|
|
2449
|
-
|
|
2554
|
+
unitCodeRecognised
|
|
2450
2555
|
];
|
|
2451
2556
|
function validateSchematron(input) {
|
|
2452
2557
|
const errors = [];
|
|
@@ -2470,17 +2575,17 @@ function validateSchematron(input) {
|
|
|
2470
2575
|
}
|
|
2471
2576
|
var SDK_SCHEMATRON_RULE_IDS = [
|
|
2472
2577
|
"BR-02",
|
|
2473
|
-
"
|
|
2474
|
-
"
|
|
2578
|
+
"GETPEPPR-ISSUE-DATE-DEFAULTED",
|
|
2579
|
+
"GETPEPPR-FROM-VAT-RECOMMENDED",
|
|
2475
2580
|
"BR-07",
|
|
2476
|
-
"BR-
|
|
2477
|
-
"
|
|
2478
|
-
"
|
|
2581
|
+
"BR-16",
|
|
2582
|
+
"GETPEPPR-PAYMENT-TIMING-RECOMMENDED",
|
|
2583
|
+
"GETPEPPR-BUYER-OR-ORDER-REFERENCE-RECOMMENDED",
|
|
2479
2584
|
"BR-CL-17",
|
|
2480
|
-
"
|
|
2481
|
-
"
|
|
2482
|
-
"
|
|
2483
|
-
"
|
|
2585
|
+
"GETPEPPR-LINE-NET-SANITY",
|
|
2586
|
+
"GETPEPPR-COMPUTED-VAT-SANITY",
|
|
2587
|
+
"GETPEPPR-TAX-INCLUSIVE-SANITY",
|
|
2588
|
+
"GETPEPPR-PAYABLE-SANITY",
|
|
2484
2589
|
"BR-S-05",
|
|
2485
2590
|
"BR-Z-05",
|
|
2486
2591
|
"BR-E-05",
|
|
@@ -2504,11 +2609,14 @@ var SDK_SCHEMATRON_RULE_IDS = [
|
|
|
2504
2609
|
"BR-G-10",
|
|
2505
2610
|
"BR-O-10",
|
|
2506
2611
|
"BR-IC-10",
|
|
2507
|
-
"PEPPOL-
|
|
2508
|
-
"PEPPOL-EN16931-
|
|
2612
|
+
"GETPEPPR-BUYER-PEPPOL-ID-REQUIRED",
|
|
2613
|
+
"PEPPOL-EN16931-P0100",
|
|
2614
|
+
"PEPPOL-EN16931-P0101",
|
|
2615
|
+
"PEPPOL-EN16931-P0112",
|
|
2616
|
+
"GETPEPPR-UNIT-CODE-RECOGNISED"
|
|
2509
2617
|
];
|
|
2510
2618
|
|
|
2511
|
-
//
|
|
2619
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/status-precedence.js
|
|
2512
2620
|
var STATUS_PRECEDENCE = [
|
|
2513
2621
|
{ status: "failed", family: "terminal-failure" },
|
|
2514
2622
|
{ status: "rejected", family: "terminal-failure" },
|
|
@@ -2532,10 +2640,10 @@ function statusFamily(status) {
|
|
|
2532
2640
|
}
|
|
2533
2641
|
var TERMINAL_FAILURE_STATUSES = STATUS_PRECEDENCE.filter((e) => e.family === "terminal-failure").map((e) => e.status);
|
|
2534
2642
|
|
|
2535
|
-
//
|
|
2536
|
-
var SDK_VERSION = "5.
|
|
2643
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/version.js
|
|
2644
|
+
var SDK_VERSION = "5.2.0";
|
|
2537
2645
|
|
|
2538
|
-
//
|
|
2646
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/api-result.js
|
|
2539
2647
|
var API_RESULT_HEADER_NAMES = {
|
|
2540
2648
|
requestId: "Getpeppr-Request-Id",
|
|
2541
2649
|
resultCode: "Getpeppr-Result-Code",
|
|
@@ -2652,7 +2760,7 @@ function parseApiResultHeaders(headers) {
|
|
|
2652
2760
|
return Object.keys(result).length === 0 ? void 0 : result;
|
|
2653
2761
|
}
|
|
2654
2762
|
|
|
2655
|
-
//
|
|
2763
|
+
// ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/client.js
|
|
2656
2764
|
function normalizeHeaderValue(value) {
|
|
2657
2765
|
return value.replace(/^[\t\n\r ]+|[\t\n\r ]+$/g, "");
|
|
2658
2766
|
}
|
|
@@ -4158,7 +4266,8 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
|
|
|
4158
4266
|
/**
|
|
4159
4267
|
* Validate an invoice server-side using the getpeppr gateway's offline SDK-backed checks.
|
|
4160
4268
|
* The gateway runs SDK validation, verifies UBL XML generation, and evaluates offline
|
|
4161
|
-
*
|
|
4269
|
+
* pre-flight checks without sending the invoice to Storecove. This is not a Peppol
|
|
4270
|
+
* conformance verdict.
|
|
4162
4271
|
*
|
|
4163
4272
|
* @example
|
|
4164
4273
|
* ```ts
|
|
@@ -4423,6 +4532,8 @@ var DirectoryOperations = class {
|
|
|
4423
4532
|
}
|
|
4424
4533
|
/**
|
|
4425
4534
|
* Search the Peppol Directory for participants.
|
|
4535
|
+
* Pagination is exact and participant-based. Queries wider than the public
|
|
4536
|
+
* Directory's accessible result window are rejected; add narrower criteria.
|
|
4426
4537
|
*
|
|
4427
4538
|
* @example
|
|
4428
4539
|
* ```ts
|
|
@@ -4888,14 +4999,32 @@ var TransportOperations = class {
|
|
|
4888
4999
|
};
|
|
4889
5000
|
|
|
4890
5001
|
// src/commands/validate.ts
|
|
4891
|
-
|
|
5002
|
+
var SEND_INERT_INVOICE_TYPE_PROFILE_RULES = /* @__PURE__ */ new Set([
|
|
5003
|
+
"PEPPOL-EN16931-P0100",
|
|
5004
|
+
"PEPPOL-EN16931-P0101",
|
|
5005
|
+
"PEPPOL-EN16931-P0112"
|
|
5006
|
+
]);
|
|
5007
|
+
function isSendRelevantSchematronViolation(violation2) {
|
|
5008
|
+
return !(violation2.field === "invoiceTypeCode" && SEND_INERT_INVOICE_TYPE_PROFILE_RULES.has(violation2.ruleId));
|
|
5009
|
+
}
|
|
5010
|
+
function runMergedValidation(input, schematronFilter) {
|
|
4892
5011
|
const structure = validateInvoice(input);
|
|
4893
|
-
const
|
|
5012
|
+
const completeSchematron = validateSchematron(input);
|
|
5013
|
+
const schematron = {
|
|
5014
|
+
errors: completeSchematron.errors.filter(schematronFilter),
|
|
5015
|
+
warnings: completeSchematron.warnings
|
|
5016
|
+
};
|
|
4894
5017
|
const countryRules = validateCountryRules(input);
|
|
5018
|
+
const countryKeys = new Set(
|
|
5019
|
+
countryRules.warnings.map((w) => JSON.stringify([w.field, w.message, w.ruleId]))
|
|
5020
|
+
);
|
|
5021
|
+
const structureWarnings = structure.warnings.filter(
|
|
5022
|
+
(w) => !countryKeys.has(JSON.stringify([w.field, w.message, w.ruleId]))
|
|
5023
|
+
);
|
|
4895
5024
|
const totalErrors = structure.errors.length + schematron.errors.length + countryRules.errors.length;
|
|
4896
|
-
const totalWarnings =
|
|
5025
|
+
const totalWarnings = structureWarnings.length + schematron.warnings.length + countryRules.warnings.length;
|
|
4897
5026
|
return {
|
|
4898
|
-
structure: { errors: structure.errors, warnings:
|
|
5027
|
+
structure: { errors: structure.errors, warnings: structureWarnings },
|
|
4899
5028
|
schematron: { errors: schematron.errors, warnings: schematron.warnings },
|
|
4900
5029
|
countryRules: {
|
|
4901
5030
|
errors: countryRules.errors,
|
|
@@ -4906,6 +5035,12 @@ function runValidation(input) {
|
|
|
4906
5035
|
valid: totalErrors === 0
|
|
4907
5036
|
};
|
|
4908
5037
|
}
|
|
5038
|
+
function runValidation(input) {
|
|
5039
|
+
return runMergedValidation(input, () => true);
|
|
5040
|
+
}
|
|
5041
|
+
function runSendPreflight(input) {
|
|
5042
|
+
return runMergedValidation(input, isSendRelevantSchematronViolation);
|
|
5043
|
+
}
|
|
4909
5044
|
function registerValidateCommand(program2) {
|
|
4910
5045
|
program2.command("validate").description("Validate a Peppol invoice JSON file").argument("<file>", "path to invoice JSON file").option("--json", "output results as JSON").option("--quiet", "exit code only, no output").action(async (file, options) => {
|
|
4911
5046
|
const input = readAndValidateInvoiceJson(file);
|
|
@@ -5753,7 +5888,7 @@ function formatSendResult(result, mode) {
|
|
|
5753
5888
|
var API_BASE = "https://api.getpeppr.dev/v1";
|
|
5754
5889
|
var LOCAL_BASE = "http://localhost:3001/api/v1";
|
|
5755
5890
|
function registerSendCommand(program2) {
|
|
5756
|
-
program2.command("send").description("Send an invoice to the Peppol network via getpeppr API").argument("[file]", "optional path to invoice JSON (mutex with --to/--amount/...)").option("--prod", "target production (live keys + confirmation)").option("--local", "target localhost:3001 dev server").option("--key <key>", "override API key \u2014 for CI/scripted use only; visible in `ps` and shell history. Prefer GETPEPPR_API_KEY env var.").option("--to <peppol-id>", "recipient peppol id (e.g., 9925:BE0314595348)").option("--country <iso>", "recipient ISO 3166-1 alpha-2 country override (e.g., BE)").option("--amount <number>", "line amount in major currency units (decimal allowed)").option("--currency <iso>", "ISO 4217 currency (default EUR)").option("--desc <text>", "line description").option("--attachment", "attach the test PDF").option("--watch", "poll status until a terminal state (60s timeout)").option("-y, --yes", "skip --prod confirmation prompt").option("--no-validate", "skip
|
|
5891
|
+
program2.command("send").description("Send an invoice to the Peppol network via getpeppr API").argument("[file]", "optional path to invoice JSON (mutex with --to/--amount/...)").option("--prod", "target production (live keys + confirmation)").option("--local", "target localhost:3001 dev server").option("--key <key>", "override API key \u2014 for CI/scripted use only; visible in `ps` and shell history. Prefer GETPEPPR_API_KEY env var.").option("--to <peppol-id>", "recipient peppol id (e.g., 9925:BE0314595348)").option("--country <iso>", "recipient ISO 3166-1 alpha-2 country override (e.g., BE)").option("--amount <number>", "line amount in major currency units (decimal allowed)").option("--currency <iso>", "ISO 4217 currency (default EUR)").option("--desc <text>", "line description").option("--attachment", "attach the test PDF").option("--watch", "poll status until a terminal state (60s timeout)").option("-y, --yes", "skip --prod confirmation prompt").option("--no-validate", "skip the JSON send preflight").option("--json", "output JSON").option("--quiet", "exit code only, no output").action(async (file, flags) => {
|
|
5757
5892
|
let auth;
|
|
5758
5893
|
try {
|
|
5759
5894
|
auth = resolveApiKey({
|
|
@@ -5827,7 +5962,7 @@ function registerSendCommand(program2) {
|
|
|
5827
5962
|
}
|
|
5828
5963
|
}
|
|
5829
5964
|
if (flags.validate !== false) {
|
|
5830
|
-
const result2 =
|
|
5965
|
+
const result2 = runSendPreflight(payload);
|
|
5831
5966
|
if (!result2.valid) {
|
|
5832
5967
|
process.stderr.write(
|
|
5833
5968
|
`${pc6.red("\u2717")} Pre-validation failed (${result2.totalErrors} errors). Use --no-validate to skip.
|