@ekwo-ai/peppol-ubl 0.4.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 +35 -0
- package/LICENSE +21 -0
- package/README.md +247 -0
- package/dist/codelists.d.ts +26 -0
- package/dist/codelists.d.ts.map +1 -0
- package/dist/codelists.js +169 -0
- package/dist/codelists.js.map +1 -0
- package/dist/decimal.d.ts +49 -0
- package/dist/decimal.d.ts.map +1 -0
- package/dist/decimal.js +118 -0
- package/dist/decimal.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/model.d.ts +115 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +271 -0
- package/dist/model.js.map +1 -0
- package/dist/rules.d.ts +30 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +465 -0
- package/dist/rules.js.map +1 -0
- package/dist/types.d.ts +232 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/dist/ubl.d.ts +20 -0
- package/dist/ubl.d.ts.map +1 -0
- package/dist/ubl.js +233 -0
- package/dist/ubl.js.map +1 -0
- package/package.json +61 -0
package/dist/decimal.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact decimals, on the text they arrive as.
|
|
3
|
+
*
|
|
4
|
+
* Every figure of an invoice reaches this package as Postgres wrote it — a
|
|
5
|
+
* `numeric` read as text — and leaves it the same way. The rules of EN 16931
|
|
6
|
+
* compare those figures with `=`, not with a tolerance: a sum that a binary
|
|
7
|
+
* float gets wrong by one part in 2^52 is a rule reported broken on an invoice
|
|
8
|
+
* that is right. So nothing here is a `number`. A decimal is an integer and a
|
|
9
|
+
* scale, the integer is a `bigint`, and the four operations the rules need are
|
|
10
|
+
* exact.
|
|
11
|
+
*/
|
|
12
|
+
const SYNTAX = /^[+-]?(\d+)(\.\d+)?$|^[+-]?(\.\d+)$/;
|
|
13
|
+
/**
|
|
14
|
+
* Reads a decimal, or returns null for anything that is not one. An exponent,
|
|
15
|
+
* a thousands separator and a comma are not decimals: `xs:decimal` refuses the
|
|
16
|
+
* three, and what is refused there is refused here.
|
|
17
|
+
*/
|
|
18
|
+
export function parseDecimal(value) {
|
|
19
|
+
if (value === null || value === undefined)
|
|
20
|
+
return null;
|
|
21
|
+
let text;
|
|
22
|
+
if (typeof value === 'number') {
|
|
23
|
+
if (!Number.isFinite(value))
|
|
24
|
+
return null;
|
|
25
|
+
// A number is accepted for convenience and printed the way JavaScript
|
|
26
|
+
// prints it, which is the shortest text that reads back as the same
|
|
27
|
+
// double. Past 1e21 that text has an exponent and is refused below.
|
|
28
|
+
text = String(value);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
text = value.trim();
|
|
32
|
+
}
|
|
33
|
+
if (!SYNTAX.test(text))
|
|
34
|
+
return null;
|
|
35
|
+
const negative = text.startsWith('-');
|
|
36
|
+
const unsigned = text.replace(/^[+-]/, '');
|
|
37
|
+
const [whole = '', fraction = ''] = unsigned.split('.');
|
|
38
|
+
const units = BigInt((whole || '0') + fraction);
|
|
39
|
+
return { units: negative ? -units : units, scale: fraction.length };
|
|
40
|
+
}
|
|
41
|
+
function align(a, b) {
|
|
42
|
+
const scale = Math.max(a.scale, b.scale);
|
|
43
|
+
return [a.units * 10n ** BigInt(scale - a.scale), b.units * 10n ** BigInt(scale - b.scale), scale];
|
|
44
|
+
}
|
|
45
|
+
export function add(a, b) {
|
|
46
|
+
const [x, y, scale] = align(a, b);
|
|
47
|
+
return { units: x + y, scale };
|
|
48
|
+
}
|
|
49
|
+
export function subtract(a, b) {
|
|
50
|
+
const [x, y, scale] = align(a, b);
|
|
51
|
+
return { units: x - y, scale };
|
|
52
|
+
}
|
|
53
|
+
export function multiply(a, b) {
|
|
54
|
+
return { units: a.units * b.units, scale: a.scale + b.scale };
|
|
55
|
+
}
|
|
56
|
+
export function compare(a, b) {
|
|
57
|
+
const [x, y] = align(a, b);
|
|
58
|
+
return x < y ? -1 : x > y ? 1 : 0;
|
|
59
|
+
}
|
|
60
|
+
export const equal = (a, b) => compare(a, b) === 0;
|
|
61
|
+
export const ZERO = { units: 0n, scale: 0 };
|
|
62
|
+
export const isZero = (a) => a.units === 0n;
|
|
63
|
+
export function abs(a) {
|
|
64
|
+
return a.units < 0n ? { units: -a.units, scale: a.scale } : a;
|
|
65
|
+
}
|
|
66
|
+
export function sum(values) {
|
|
67
|
+
return values.reduce(add, ZERO);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Rounds to `places` decimals the way XPath's `round()` does: half towards
|
|
71
|
+
* positive infinity, so 2.5 becomes 3 and -2.5 becomes -2. It is the rounding
|
|
72
|
+
* the published rules are written with, and it is used here for nothing but
|
|
73
|
+
* re-reading them — this package rounds no figure it writes.
|
|
74
|
+
*/
|
|
75
|
+
export function roundXPath(a, places) {
|
|
76
|
+
if (a.scale <= places)
|
|
77
|
+
return a;
|
|
78
|
+
const divisor = 10n ** BigInt(a.scale - places);
|
|
79
|
+
// floor(x + 1/2), on integers: floor((2·units + divisor) / (2·divisor)).
|
|
80
|
+
const numerator = 2n * a.units + divisor;
|
|
81
|
+
const denominator = 2n * divisor;
|
|
82
|
+
let quotient = numerator / denominator;
|
|
83
|
+
if (numerator % denominator !== 0n && numerator < 0n)
|
|
84
|
+
quotient -= 1n;
|
|
85
|
+
return { units: quotient, scale: places };
|
|
86
|
+
}
|
|
87
|
+
/** How many digits follow the point once the zeros that say nothing are gone. */
|
|
88
|
+
export function significantScale(a) {
|
|
89
|
+
let { units, scale } = a;
|
|
90
|
+
while (scale > 0 && units % 10n === 0n) {
|
|
91
|
+
units /= 10n;
|
|
92
|
+
scale -= 1;
|
|
93
|
+
}
|
|
94
|
+
return scale;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The text of a decimal: a point, no exponent, no sign on zero, and at least
|
|
98
|
+
* `minScale` digits after the point. Trailing zeros beyond that are dropped,
|
|
99
|
+
* which changes no value and keeps `21.0000` — a rate as `numeric(7,4)` stores
|
|
100
|
+
* it — from travelling as four digits of nothing.
|
|
101
|
+
*/
|
|
102
|
+
export function formatDecimal(a, minScale = 0) {
|
|
103
|
+
let { units, scale } = a;
|
|
104
|
+
while (scale > minScale && units % 10n === 0n) {
|
|
105
|
+
units /= 10n;
|
|
106
|
+
scale -= 1;
|
|
107
|
+
}
|
|
108
|
+
if (scale < minScale) {
|
|
109
|
+
units *= 10n ** BigInt(minScale - scale);
|
|
110
|
+
scale = minScale;
|
|
111
|
+
}
|
|
112
|
+
const negative = units < 0n;
|
|
113
|
+
const digits = (negative ? -units : units).toString().padStart(scale + 1, '0');
|
|
114
|
+
const whole = digits.slice(0, digits.length - scale);
|
|
115
|
+
const fraction = scale > 0 ? `.${digits.slice(digits.length - scale)}` : '';
|
|
116
|
+
return `${negative ? '-' : ''}${whole}${fraction}`;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=decimal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decimal.js","sourceRoot":"","sources":["../src/decimal.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A sales invoice or credit note as Peppol carries it: UBL 2.1, in the profile
|
|
3
|
+
* Peppol BIS Billing 3.0 of the European standard EN 16931.
|
|
4
|
+
*
|
|
5
|
+
* Give it a posted document as the books hold it — the header, the lines and
|
|
6
|
+
* the VAT breakdown — and it writes the file, a name for it, and every
|
|
7
|
+
* published rule the file breaks.
|
|
8
|
+
*
|
|
9
|
+
* The figures are the books' figures. Nothing is recomputed: a total, a base
|
|
10
|
+
* and a tax are written as they were posted, and where they do not add up the
|
|
11
|
+
* rule they break is named rather than the figure corrected. An invoice is an
|
|
12
|
+
* accounting document before it is an XML one, and a file that disagrees with
|
|
13
|
+
* the ledger it came from by a cent is worse than a file that is refused.
|
|
14
|
+
*
|
|
15
|
+
* Sending the file is another matter: that takes a certified access point, and
|
|
16
|
+
* this package has none. Sources are in the README. It depends on nothing,
|
|
17
|
+
* reads no database and knows no accounting.
|
|
18
|
+
*/
|
|
19
|
+
import type { PeppolUbl, PeppolUblInput, PeppolUblOptions } from './types.js';
|
|
20
|
+
export { PeppolUblError } from './model.js';
|
|
21
|
+
export { CUSTOMIZATION_ID, PROFILE_ID } from './ubl.js';
|
|
22
|
+
export { SCHEMES_NOT_ON_PEPPOL } from './rules.js';
|
|
23
|
+
export { COUNTRY_CODES, CURRENCY_CODES, ELECTRONIC_ADDRESS_SCHEMES, EXEMPTION_REASON_CODES, IDENTIFIER_SCHEMES, PAYMENT_MEANS_CODES, UNIT_CODES, VAT_CATEGORY_CODES, VAT_IDENTIFIER_PREFIXES, } from './codelists.js';
|
|
24
|
+
export type { DocumentHeaderRow, DocumentLineRow, DocumentTaxRow, ElectronicAddress, IsoDate, Numeric, PeppolUbl, PeppolUblInput, PeppolUblOptions, Violation, } from './types.js';
|
|
25
|
+
/**
|
|
26
|
+
* Writes the document.
|
|
27
|
+
*
|
|
28
|
+
* Throws a {@link PeppolUblError} for what would make the file absurd: a
|
|
29
|
+
* document that is not a sale, that was never numbered, that has no currency
|
|
30
|
+
* or no line, an amount or a date that is not one. Everything else comes back
|
|
31
|
+
* as a violation, beside a file that says what it was given.
|
|
32
|
+
*/
|
|
33
|
+
export declare function generatePeppolUbl(input: PeppolUblInput, options?: PeppolUblOptions): PeppolUbl;
|
|
34
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG9E,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EACL,aAAa,EACb,cAAc,EACd,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,OAAO,EACP,OAAO,EACP,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,SAAS,GACV,MAAM,YAAY,CAAC;AAYpB;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS,CAQlG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A sales invoice or credit note as Peppol carries it: UBL 2.1, in the profile
|
|
3
|
+
* Peppol BIS Billing 3.0 of the European standard EN 16931.
|
|
4
|
+
*
|
|
5
|
+
* Give it a posted document as the books hold it — the header, the lines and
|
|
6
|
+
* the VAT breakdown — and it writes the file, a name for it, and every
|
|
7
|
+
* published rule the file breaks.
|
|
8
|
+
*
|
|
9
|
+
* The figures are the books' figures. Nothing is recomputed: a total, a base
|
|
10
|
+
* and a tax are written as they were posted, and where they do not add up the
|
|
11
|
+
* rule they break is named rather than the figure corrected. An invoice is an
|
|
12
|
+
* accounting document before it is an XML one, and a file that disagrees with
|
|
13
|
+
* the ledger it came from by a cent is worse than a file that is refused.
|
|
14
|
+
*
|
|
15
|
+
* Sending the file is another matter: that takes a certified access point, and
|
|
16
|
+
* this package has none. Sources are in the README. It depends on nothing,
|
|
17
|
+
* reads no database and knows no accounting.
|
|
18
|
+
*/
|
|
19
|
+
import { PeppolUblError, buildModel } from './model.js';
|
|
20
|
+
import { check } from './rules.js';
|
|
21
|
+
import { writeUbl } from './ubl.js';
|
|
22
|
+
export { PeppolUblError } from './model.js';
|
|
23
|
+
export { CUSTOMIZATION_ID, PROFILE_ID } from './ubl.js';
|
|
24
|
+
export { SCHEMES_NOT_ON_PEPPOL } from './rules.js';
|
|
25
|
+
export { COUNTRY_CODES, CURRENCY_CODES, ELECTRONIC_ADDRESS_SCHEMES, EXEMPTION_REASON_CODES, IDENTIFIER_SCHEMES, PAYMENT_MEANS_CODES, UNIT_CODES, VAT_CATEGORY_CODES, VAT_IDENTIFIER_PREFIXES, } from './codelists.js';
|
|
26
|
+
/**
|
|
27
|
+
* A name for the file. The network imposes none — a document travels inside an
|
|
28
|
+
* envelope that names it — so this is the document's own number, with whatever
|
|
29
|
+
* a file system would object to replaced.
|
|
30
|
+
*/
|
|
31
|
+
function filenameOf(number, kind) {
|
|
32
|
+
const safe = number.replace(/[^A-Za-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
33
|
+
return `${kind === 'Invoice' ? 'invoice' : 'credit-note'}-${safe === '' ? 'document' : safe}.xml`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Writes the document.
|
|
37
|
+
*
|
|
38
|
+
* Throws a {@link PeppolUblError} for what would make the file absurd: a
|
|
39
|
+
* document that is not a sale, that was never numbered, that has no currency
|
|
40
|
+
* or no line, an amount or a date that is not one. Everything else comes back
|
|
41
|
+
* as a violation, beside a file that says what it was given.
|
|
42
|
+
*/
|
|
43
|
+
export function generatePeppolUbl(input, options = {}) {
|
|
44
|
+
if (!input || !input.header)
|
|
45
|
+
throw new PeppolUblError('no document was given');
|
|
46
|
+
const model = buildModel(input, options);
|
|
47
|
+
return {
|
|
48
|
+
file: writeUbl(model),
|
|
49
|
+
filename: filenameOf(model.number, model.kind),
|
|
50
|
+
violations: check(model),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EACL,aAAa,EACb,cAAc,EACd,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAcxB;;;;GAIG;AACH,SAAS,UAAU,CAAC,MAAc,EAAE,IAA8B;IAChE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC9E,OAAO,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;AACpG,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAqB,EAAE,UAA4B,EAAE;IACrF,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,MAAM,IAAI,cAAc,CAAC,uBAAuB,CAAC,CAAC;IAC/E,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACzC,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC;QACrB,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC;QAC9C,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC;KACzB,CAAC;AACJ,CAAC"}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* From three reads of the books to what the file will say.
|
|
3
|
+
*
|
|
4
|
+
* Everything that is decided is decided here, once: which number goes in which
|
|
5
|
+
* business term, which lines are lines, how the taxes of the books become the
|
|
6
|
+
* VAT breakdown of the standard. The writer prints this model and the rules
|
|
7
|
+
* re-read it, so the file and what is said of the file cannot disagree.
|
|
8
|
+
*
|
|
9
|
+
* What is refused here is what would make the file absurd rather than wrong:
|
|
10
|
+
* a purchase, a draft without a number, an amount that is not a number. Those
|
|
11
|
+
* throw. Everything else is kept as it was given and left to the rules.
|
|
12
|
+
*/
|
|
13
|
+
import { type Decimal } from './decimal.js';
|
|
14
|
+
import type { ElectronicAddress, PeppolUblInput, PeppolUblOptions } from './types.js';
|
|
15
|
+
export declare class PeppolUblError extends Error {
|
|
16
|
+
name: string;
|
|
17
|
+
}
|
|
18
|
+
export interface PartyModel {
|
|
19
|
+
endpoint: ElectronicAddress | null;
|
|
20
|
+
/** BT-28, only where a legal name stands beside it and differs. */
|
|
21
|
+
tradeName: string | null;
|
|
22
|
+
/** BT-27 / BT-44. */
|
|
23
|
+
registrationName: string | null;
|
|
24
|
+
legalForm: string | null;
|
|
25
|
+
vatId: string | null;
|
|
26
|
+
legalId: string | null;
|
|
27
|
+
legalIdScheme: string | null;
|
|
28
|
+
address: {
|
|
29
|
+
line1: string | null;
|
|
30
|
+
line2: string | null;
|
|
31
|
+
city: string | null;
|
|
32
|
+
postalCode: string | null;
|
|
33
|
+
region: string | null;
|
|
34
|
+
country: string | null;
|
|
35
|
+
} | null;
|
|
36
|
+
contact: {
|
|
37
|
+
phone: string | null;
|
|
38
|
+
email: string | null;
|
|
39
|
+
} | null;
|
|
40
|
+
}
|
|
41
|
+
export interface LineModel {
|
|
42
|
+
id: string;
|
|
43
|
+
quantity: Decimal | null;
|
|
44
|
+
unitCode: string | null;
|
|
45
|
+
netAmount: Decimal;
|
|
46
|
+
name: string | null;
|
|
47
|
+
description: string | null;
|
|
48
|
+
sellerItemId: string | null;
|
|
49
|
+
category: string | null;
|
|
50
|
+
rate: Decimal | null;
|
|
51
|
+
/** BT-146. Null where the books cannot say it. */
|
|
52
|
+
netPrice: Decimal | null;
|
|
53
|
+
/** BT-148 and BT-147, written only where a discount stands between the two prices. */
|
|
54
|
+
grossPrice: Decimal | null;
|
|
55
|
+
priceDiscount: Decimal | null;
|
|
56
|
+
}
|
|
57
|
+
export interface SubtotalModel {
|
|
58
|
+
category: string | null;
|
|
59
|
+
rate: Decimal | null;
|
|
60
|
+
base: Decimal;
|
|
61
|
+
tax: Decimal;
|
|
62
|
+
reasonCode: string | null;
|
|
63
|
+
reason: string | null;
|
|
64
|
+
/** More than one exemption code was found for this group, so none was written. */
|
|
65
|
+
ambiguousReasonCodes: string[];
|
|
66
|
+
}
|
|
67
|
+
export interface DocumentModel {
|
|
68
|
+
kind: 'Invoice' | 'CreditNote';
|
|
69
|
+
/** BT-3, UNTDID 1001: 380 for an invoice, 381 for a credit note. */
|
|
70
|
+
typeCode: '380' | '381';
|
|
71
|
+
number: string;
|
|
72
|
+
issueDate: string;
|
|
73
|
+
dueDate: string | null;
|
|
74
|
+
taxPointDate: string | null;
|
|
75
|
+
note: string | null;
|
|
76
|
+
currency: string;
|
|
77
|
+
buyerReference: string | null;
|
|
78
|
+
orderReference: string | null;
|
|
79
|
+
contractReference: string | null;
|
|
80
|
+
projectReference: string | null;
|
|
81
|
+
preceding: {
|
|
82
|
+
number: string;
|
|
83
|
+
issueDate: string | null;
|
|
84
|
+
} | null;
|
|
85
|
+
seller: PartyModel;
|
|
86
|
+
buyer: PartyModel;
|
|
87
|
+
delivery: {
|
|
88
|
+
date: string | null;
|
|
89
|
+
address: {
|
|
90
|
+
line1: string | null;
|
|
91
|
+
city: string | null;
|
|
92
|
+
postalCode: string | null;
|
|
93
|
+
country: string | null;
|
|
94
|
+
} | null;
|
|
95
|
+
} | null;
|
|
96
|
+
payment: {
|
|
97
|
+
meansCode: string;
|
|
98
|
+
reference: string | null;
|
|
99
|
+
iban: string | null;
|
|
100
|
+
bic: string | null;
|
|
101
|
+
} | null;
|
|
102
|
+
/** An account or a reference was given with no code to file it under, so nothing was written. */
|
|
103
|
+
paymentWithoutMeans: boolean;
|
|
104
|
+
paymentTerms: string | null;
|
|
105
|
+
taxTotal: Decimal;
|
|
106
|
+
subtotals: SubtotalModel[];
|
|
107
|
+
lineTotal: Decimal;
|
|
108
|
+
taxExclusive: Decimal;
|
|
109
|
+
taxInclusive: Decimal;
|
|
110
|
+
prepaid: Decimal | null;
|
|
111
|
+
payable: Decimal;
|
|
112
|
+
lines: LineModel[];
|
|
113
|
+
}
|
|
114
|
+
export declare function buildModel(given: PeppolUblInput, options: PeppolUblOptions): DocumentModel;
|
|
115
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* From three reads of the books to what the file will say.
|
|
3
|
+
*
|
|
4
|
+
* Everything that is decided is decided here, once: which number goes in which
|
|
5
|
+
* business term, which lines are lines, how the taxes of the books become the
|
|
6
|
+
* VAT breakdown of the standard. The writer prints this model and the rules
|
|
7
|
+
* re-read it, so the file and what is said of the file cannot disagree.
|
|
8
|
+
*
|
|
9
|
+
* What is refused here is what would make the file absurd rather than wrong:
|
|
10
|
+
* a purchase, a draft without a number, an amount that is not a number. Those
|
|
11
|
+
* throw. Everything else is kept as it was given and left to the rules.
|
|
12
|
+
*/
|
|
13
|
+
import { ZERO, add, formatDecimal, isZero, multiply, parseDecimal, subtract, } from './decimal.js';
|
|
14
|
+
export class PeppolUblError extends Error {
|
|
15
|
+
name = 'PeppolUblError';
|
|
16
|
+
}
|
|
17
|
+
const DATE = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
|
|
18
|
+
/** A value with something in it, or null. Blank text is the absence of text. */
|
|
19
|
+
function text(value) {
|
|
20
|
+
if (value === null || value === undefined)
|
|
21
|
+
return null;
|
|
22
|
+
const trimmed = String(value).trim();
|
|
23
|
+
return trimmed === '' ? null : trimmed;
|
|
24
|
+
}
|
|
25
|
+
function date(value, what) {
|
|
26
|
+
// A database driver hands a `date` column back as a `Date`: an instant, at a
|
|
27
|
+
// midnight that is UTC for one driver and local for the next. Which day that
|
|
28
|
+
// is depends on where the process runs, so it is refused rather than guessed
|
|
29
|
+
// — `select document_date::text` is the day the books mean.
|
|
30
|
+
if (value instanceof Date) {
|
|
31
|
+
throw new PeppolUblError(`${what} was given as a Date object, which is an instant and not a day: pass it as YYYY-MM-DD text`);
|
|
32
|
+
}
|
|
33
|
+
const raw = text(value);
|
|
34
|
+
if (raw === null)
|
|
35
|
+
return null;
|
|
36
|
+
// A timestamp is accepted for the day it names; anything else is not a date.
|
|
37
|
+
const day = raw.slice(0, 10);
|
|
38
|
+
// 30 February has the shape of a date and is read by JavaScript as 2 March.
|
|
39
|
+
const parsed = DATE.test(day) ? new Date(`${day}T00:00:00Z`) : null;
|
|
40
|
+
if (parsed === null || Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== day) {
|
|
41
|
+
throw new PeppolUblError(`${what} is not a date: ${raw}`);
|
|
42
|
+
}
|
|
43
|
+
return day;
|
|
44
|
+
}
|
|
45
|
+
function amount(value, what) {
|
|
46
|
+
const parsed = parseDecimal(value);
|
|
47
|
+
if (parsed === null)
|
|
48
|
+
throw new PeppolUblError(`${what} is not an amount: ${String(value)}`);
|
|
49
|
+
return parsed;
|
|
50
|
+
}
|
|
51
|
+
function optionalAmount(value, what) {
|
|
52
|
+
if (value === null || value === undefined || value === '')
|
|
53
|
+
return null;
|
|
54
|
+
return amount(value, what);
|
|
55
|
+
}
|
|
56
|
+
/** An identifier as a register compares it: no spaces, no dots. */
|
|
57
|
+
function identifier(value) {
|
|
58
|
+
const raw = text(value);
|
|
59
|
+
return raw === null ? null : raw.replace(/[\s.]/g, '');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* An electronic address: the one the caller gave, else the one the header
|
|
63
|
+
* carries. Never one worked out from another identifier.
|
|
64
|
+
*/
|
|
65
|
+
function endpoint(given, scheme, identifier) {
|
|
66
|
+
const id = text(given ? given.id : identifier);
|
|
67
|
+
if (id === null)
|
|
68
|
+
return null;
|
|
69
|
+
return { scheme: text(given ? given.scheme : scheme) ?? '', id };
|
|
70
|
+
}
|
|
71
|
+
function party(header, who, address, registrationScheme) {
|
|
72
|
+
const seller = who === 'seller';
|
|
73
|
+
const name = text(seller ? header.seller_name : header.buyer_name);
|
|
74
|
+
const legalName = seller ? text(header.seller_legal_name) : null;
|
|
75
|
+
const registrationName = legalName ?? name;
|
|
76
|
+
const fields = {
|
|
77
|
+
line1: text(seller ? header.seller_address_line1 : header.buyer_address_line1),
|
|
78
|
+
line2: text(seller ? header.seller_address_line2 : header.buyer_address_line2),
|
|
79
|
+
city: text(seller ? header.seller_city : header.buyer_city),
|
|
80
|
+
postalCode: text(seller ? header.seller_postal_code : header.buyer_postal_code),
|
|
81
|
+
region: text(seller ? header.seller_region : header.buyer_region),
|
|
82
|
+
country: text(seller ? header.seller_country : header.buyer_country)?.toUpperCase() ?? null,
|
|
83
|
+
};
|
|
84
|
+
const phone = seller ? text(header.seller_phone) : null;
|
|
85
|
+
const email = text(seller ? header.seller_email : header.buyer_email);
|
|
86
|
+
const legalId = identifier(seller ? header.seller_registration_number : header.buyer_registration_number);
|
|
87
|
+
return {
|
|
88
|
+
endpoint: endpoint(address, seller ? header.seller_peppol_scheme : header.buyer_peppol_scheme, seller ? header.seller_peppol_identifier : header.buyer_peppol_identifier),
|
|
89
|
+
tradeName: legalName !== null && name !== null && name !== legalName ? name : null,
|
|
90
|
+
registrationName,
|
|
91
|
+
legalForm: seller ? text(header.seller_legal_form) : null,
|
|
92
|
+
vatId: identifier(seller ? header.seller_vat_number : header.buyer_vat_number)?.toUpperCase() ?? null,
|
|
93
|
+
legalId,
|
|
94
|
+
legalIdScheme: legalId !== null ? text(registrationScheme) : null,
|
|
95
|
+
address: Object.values(fields).some((value) => value !== null) ? fields : null,
|
|
96
|
+
contact: phone !== null || email !== null ? { phone, email } : null,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function line(row, position) {
|
|
100
|
+
const id = text(row.sequence === null || row.sequence === undefined ? null : String(row.sequence)) ?? String(position);
|
|
101
|
+
const what = `line ${id}`;
|
|
102
|
+
const keyed = optionalAmount(row.unit_price, `the unit price of ${what}`);
|
|
103
|
+
const discount = optionalAmount(row.discount_percent, `the discount of ${what}`);
|
|
104
|
+
let netPrice;
|
|
105
|
+
let grossPrice = null;
|
|
106
|
+
let priceDiscount = null;
|
|
107
|
+
if (row.unit_price_includes_tax) {
|
|
108
|
+
// The keyed price holds the tax. The net one is the books' to give.
|
|
109
|
+
netPrice = optionalAmount(row.net_unit_price, `the net unit price of ${what}`);
|
|
110
|
+
}
|
|
111
|
+
else if (keyed !== null && discount !== null && !isZero(discount)) {
|
|
112
|
+
// BT-147 = BT-148 × the discount, BT-146 = BT-148 − BT-147. Exact: a price
|
|
113
|
+
// has as many decimals as it needs, and nothing is rounded to get there.
|
|
114
|
+
grossPrice = keyed;
|
|
115
|
+
priceDiscount = { units: multiply(keyed, discount).units, scale: keyed.scale + discount.scale + 2 };
|
|
116
|
+
netPrice = subtract(keyed, priceDiscount);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
netPrice = optionalAmount(row.net_unit_price, `the net unit price of ${what}`) ?? keyed;
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
id,
|
|
123
|
+
quantity: optionalAmount(row.quantity, `the quantity of ${what}`),
|
|
124
|
+
unitCode: text(row.unit_code),
|
|
125
|
+
netAmount: amount(row.amount_untaxed, `the net amount of ${what}`),
|
|
126
|
+
name: text(row.item_name),
|
|
127
|
+
description: text(row.item_description),
|
|
128
|
+
sellerItemId: text(row.seller_item_identifier),
|
|
129
|
+
category: text(row.vat_category)?.toUpperCase() ?? null,
|
|
130
|
+
rate: optionalAmount(row.vat_rate, `the VAT rate of ${what}`),
|
|
131
|
+
netPrice,
|
|
132
|
+
grossPrice,
|
|
133
|
+
priceDiscount,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The VAT breakdown of the standard from the taxes of the books.
|
|
138
|
+
*
|
|
139
|
+
* The books keep one row per tax; EN 16931 wants one group per category and
|
|
140
|
+
* rate (BG-23). Two taxes at the same rate — goods and services at the standard
|
|
141
|
+
* rate is the ordinary case — are therefore one group here, their bases added
|
|
142
|
+
* and their taxes added. Adding is all that is done: each tax was rounded once
|
|
143
|
+
* by the books and is not rounded again.
|
|
144
|
+
*/
|
|
145
|
+
function subtotals(input) {
|
|
146
|
+
const groups = new Map();
|
|
147
|
+
const keyOf = (category, rate) => `${category ?? ''}|${rate === null ? '' : formatDecimal(rate)}`;
|
|
148
|
+
for (const row of input.taxes) {
|
|
149
|
+
const category = text(row.vat_category)?.toUpperCase() ?? null;
|
|
150
|
+
const rate = optionalAmount(row.tax_rate, 'a VAT rate of the breakdown');
|
|
151
|
+
const key = keyOf(category, rate);
|
|
152
|
+
const group = groups.get(key) ?? {
|
|
153
|
+
category,
|
|
154
|
+
rate,
|
|
155
|
+
base: ZERO,
|
|
156
|
+
tax: ZERO,
|
|
157
|
+
reasonCode: null,
|
|
158
|
+
reason: null,
|
|
159
|
+
ambiguousReasonCodes: [],
|
|
160
|
+
codes: new Set(),
|
|
161
|
+
reasons: new Set(),
|
|
162
|
+
codedByBreakdown: false,
|
|
163
|
+
};
|
|
164
|
+
group.base = add(group.base, amount(row.base_amount, 'a base of the VAT breakdown'));
|
|
165
|
+
group.tax = add(group.tax, amount(row.tax_charged, 'a tax of the VAT breakdown'));
|
|
166
|
+
const code = text(row.exemption_code);
|
|
167
|
+
if (code !== null) {
|
|
168
|
+
group.codes.add(code);
|
|
169
|
+
group.codedByBreakdown = true;
|
|
170
|
+
}
|
|
171
|
+
const reason = text(row.exemption_reason);
|
|
172
|
+
if (reason !== null)
|
|
173
|
+
group.reasons.add(reason);
|
|
174
|
+
groups.set(key, group);
|
|
175
|
+
}
|
|
176
|
+
// A group the breakdown gave no code to takes the one its lines carry.
|
|
177
|
+
for (const row of input.lines) {
|
|
178
|
+
if ((row.line_type ?? 'product') !== 'product')
|
|
179
|
+
continue;
|
|
180
|
+
const code = text(row.tax_exemption_code);
|
|
181
|
+
if (code === null)
|
|
182
|
+
continue;
|
|
183
|
+
const category = text(row.vat_category)?.toUpperCase() ?? null;
|
|
184
|
+
const group = groups.get(keyOf(category, optionalAmount(row.vat_rate, 'a VAT rate')));
|
|
185
|
+
if (group && !group.codedByBreakdown)
|
|
186
|
+
group.codes.add(code);
|
|
187
|
+
}
|
|
188
|
+
return [...groups.values()].map(({ codes, reasons, codedByBreakdown: _, ...group }) => ({
|
|
189
|
+
...group,
|
|
190
|
+
reasonCode: codes.size === 1 ? [...codes][0] : null,
|
|
191
|
+
// Several reasons for one group are several sentences, and a sentence can
|
|
192
|
+
// be joined to another where a code cannot.
|
|
193
|
+
reason: reasons.size > 0 ? [...reasons].join(' ') : null,
|
|
194
|
+
ambiguousReasonCodes: codes.size > 1 ? [...codes].sort() : [],
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
export function buildModel(given, options) {
|
|
198
|
+
const input = given;
|
|
199
|
+
const { header } = input;
|
|
200
|
+
const kind = header.doc_type === 'sale_invoice' ? 'Invoice' : header.doc_type === 'sale_credit_note' ? 'CreditNote' : null;
|
|
201
|
+
if (kind === null) {
|
|
202
|
+
throw new PeppolUblError(`a ${String(header.doc_type)} is not a document this format carries: only a sales invoice and a sales credit note are`);
|
|
203
|
+
}
|
|
204
|
+
const number = text(header.number);
|
|
205
|
+
if (number === null)
|
|
206
|
+
throw new PeppolUblError('the document has no number: it was never issued');
|
|
207
|
+
const issueDate = date(header.document_date, 'the document date');
|
|
208
|
+
if (issueDate === null)
|
|
209
|
+
throw new PeppolUblError('the document has no date');
|
|
210
|
+
const currency = text(header.currency_code)?.toUpperCase() ?? null;
|
|
211
|
+
if (currency === null)
|
|
212
|
+
throw new PeppolUblError('the document has no currency, and there is no default');
|
|
213
|
+
const lines = input.lines
|
|
214
|
+
.filter((row) => (row.line_type ?? 'product') === 'product')
|
|
215
|
+
.map((row, index) => line(row, index + 1));
|
|
216
|
+
if (lines.length === 0)
|
|
217
|
+
throw new PeppolUblError('the document has no line');
|
|
218
|
+
const taxExclusive = amount(header.amount_untaxed, 'the total without VAT');
|
|
219
|
+
const taxInclusive = amount(header.amount_total, 'the total with VAT');
|
|
220
|
+
const paid = optionalAmount(header.amount_paid, 'the amount paid');
|
|
221
|
+
const prepaid = paid !== null && !isZero(paid) ? paid : null;
|
|
222
|
+
const payable = optionalAmount(header.amount_residual, 'the amount due') ?? subtract(taxInclusive, prepaid ?? ZERO);
|
|
223
|
+
const meansCode = text(header.payment_means_code);
|
|
224
|
+
const iban = identifier(header.payee_iban)?.toUpperCase() ?? null;
|
|
225
|
+
const bic = identifier(header.payee_bic)?.toUpperCase() ?? null;
|
|
226
|
+
const reference = text(header.payment_reference);
|
|
227
|
+
const deliveryDate = date(header.delivery_date, 'the delivery date');
|
|
228
|
+
const deliveryFields = {
|
|
229
|
+
line1: text(header.delivery_address_line1),
|
|
230
|
+
city: text(header.delivery_city),
|
|
231
|
+
postalCode: text(header.delivery_postal_code),
|
|
232
|
+
country: text(header.delivery_country)?.toUpperCase() ?? null,
|
|
233
|
+
};
|
|
234
|
+
const deliveryAddress = Object.values(deliveryFields).some((value) => value !== null) ? deliveryFields : null;
|
|
235
|
+
const precedingNumber = text(options.precedingInvoice?.number);
|
|
236
|
+
return {
|
|
237
|
+
kind,
|
|
238
|
+
typeCode: kind === 'Invoice' ? '380' : '381',
|
|
239
|
+
number,
|
|
240
|
+
issueDate,
|
|
241
|
+
dueDate: date(header.due_date, 'the due date'),
|
|
242
|
+
taxPointDate: date(header.tax_point_date, 'the tax point date'),
|
|
243
|
+
note: text(header.note),
|
|
244
|
+
currency,
|
|
245
|
+
buyerReference: text(header.buyer_reference),
|
|
246
|
+
orderReference: text(header.order_reference),
|
|
247
|
+
contractReference: text(header.contract_reference),
|
|
248
|
+
projectReference: text(header.project_reference),
|
|
249
|
+
preceding: precedingNumber === null
|
|
250
|
+
? null
|
|
251
|
+
: { number: precedingNumber, issueDate: date(options.precedingInvoice?.issueDate, 'the date of the preceding invoice') },
|
|
252
|
+
seller: party(header, 'seller', options.sellerEndpoint, options.sellerRegistrationScheme),
|
|
253
|
+
buyer: party(header, 'buyer', options.buyerEndpoint, options.buyerRegistrationScheme),
|
|
254
|
+
delivery: deliveryDate !== null || deliveryAddress !== null ? { date: deliveryDate, address: deliveryAddress } : null,
|
|
255
|
+
payment: meansCode === null ? null : { meansCode, reference, iban, bic },
|
|
256
|
+
paymentWithoutMeans: meansCode === null && (iban !== null || bic !== null || reference !== null),
|
|
257
|
+
paymentTerms: text(header.payment_terms),
|
|
258
|
+
taxTotal: amount(header.amount_tax, 'the total VAT'),
|
|
259
|
+
subtotals: subtotals(input),
|
|
260
|
+
// There is no document-level allowance or charge in this model, so the sum
|
|
261
|
+
// of the lines and the total without VAT are one figure (BR-CO-13). It is
|
|
262
|
+
// the books' figure that is written in both places, not a sum made here.
|
|
263
|
+
lineTotal: taxExclusive,
|
|
264
|
+
taxExclusive,
|
|
265
|
+
taxInclusive,
|
|
266
|
+
prepaid,
|
|
267
|
+
payable,
|
|
268
|
+
lines,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|