@formfeed/engine 0.3.11 → 0.3.12
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/index.cjs +2414 -241
- package/index.js +2414 -241
- package/package.json +3 -2
- package/types/frame/text-edit.d.cts +8 -1
- package/types/frame/text-edit.d.ts +8 -1
- package/types/index.d.cts +3 -1
- package/types/index.d.ts +3 -1
- package/types/lib/assemble.d.cts +8 -0
- package/types/lib/assemble.d.ts +8 -0
- package/types/lib/einvoice/check.d.cts +181 -0
- package/types/lib/einvoice/check.d.ts +181 -0
- package/types/lib/einvoice/cii.d.cts +18 -0
- package/types/lib/einvoice/cii.d.ts +18 -0
- package/types/lib/einvoice/codes.generated.d.cts +14 -0
- package/types/lib/einvoice/codes.generated.d.ts +14 -0
- package/types/lib/einvoice/decimal.d.cts +29 -0
- package/types/lib/einvoice/decimal.d.ts +29 -0
- package/types/lib/einvoice/display.d.cts +43 -0
- package/types/lib/einvoice/display.d.ts +43 -0
- package/types/lib/einvoice/html-text.d.cts +17 -0
- package/types/lib/einvoice/html-text.d.ts +17 -0
- package/types/lib/einvoice/index.d.cts +45 -0
- package/types/lib/einvoice/index.d.ts +45 -0
- package/types/lib/einvoice/locate.d.cts +21 -0
- package/types/lib/einvoice/locate.d.ts +21 -0
- package/types/lib/einvoice/report.d.cts +59 -0
- package/types/lib/einvoice/report.d.ts +59 -0
- package/types/lib/einvoice/schema.d.cts +293 -0
- package/types/lib/einvoice/schema.d.ts +293 -0
- package/types/lib/einvoice/skeleton.d.cts +10 -0
- package/types/lib/einvoice/skeleton.d.ts +10 -0
- package/types/lib/generated/frames.d.cts +2 -2
- package/types/lib/generated/frames.d.ts +2 -2
- package/types/lib/preview.d.cts +6 -0
- package/types/lib/preview.d.ts +6 -0
- package/types/lib/source-edit.d.cts +5 -0
- package/types/lib/source-edit.d.ts +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formfeed/engine",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
4
4
|
"description": "The Formfeed template engine (Jinja2, Liquid, Handlebars) for the browser and Node.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
"marked": "^18.0.11",
|
|
39
39
|
"n2words": "^6.1.2",
|
|
40
40
|
"nunjucks": "^3.2.4",
|
|
41
|
-
"qrcode": "^1.5.4"
|
|
41
|
+
"qrcode": "^1.5.4",
|
|
42
|
+
"zod": "^4.5.4"
|
|
42
43
|
},
|
|
43
44
|
"keywords": [
|
|
44
45
|
"formfeed",
|
|
@@ -9,7 +9,14 @@
|
|
|
9
9
|
* in place, chips are atomic, and Enter, Tab or leaving the element commit
|
|
10
10
|
* (`formfeed:edit-commit`), Escape cancels. The frame never writes the template: it only sends what
|
|
11
11
|
* the user typed. Ctrl+B, Ctrl+I and Ctrl+U format nothing here; on the design stage they are passed
|
|
12
|
-
* on (`formfeed:edit-style`), where they switch the style of the whole layer.
|
|
12
|
+
* on (`formfeed:edit-style`), where they switch the style of the whole layer. A session the editor
|
|
13
|
+
* asks for is not opened once the user has left this window for the app (`formfeed:edit-cancel`):
|
|
14
|
+
* opening it takes the focus, and that belongs to where the user went.
|
|
15
|
+
*
|
|
16
|
+
* A preview document says which one it is (`doc`, from its `formfeed:document` meta) and when its
|
|
17
|
+
* elements are in place (`formfeed:edit-ready`). The editor renders a moment before the frame takes
|
|
18
|
+
* the new document, and a click in that moment is about a document on its way out: the editor waits
|
|
19
|
+
* for the next one and has the frame ask for the same text there (`formfeed:edit-open`).
|
|
13
20
|
*/
|
|
14
21
|
/** An item as the frame shows it: chips carry the text to display. */
|
|
15
22
|
export type FrameItem = {
|
|
@@ -9,7 +9,14 @@
|
|
|
9
9
|
* in place, chips are atomic, and Enter, Tab or leaving the element commit
|
|
10
10
|
* (`formfeed:edit-commit`), Escape cancels. The frame never writes the template: it only sends what
|
|
11
11
|
* the user typed. Ctrl+B, Ctrl+I and Ctrl+U format nothing here; on the design stage they are passed
|
|
12
|
-
* on (`formfeed:edit-style`), where they switch the style of the whole layer.
|
|
12
|
+
* on (`formfeed:edit-style`), where they switch the style of the whole layer. A session the editor
|
|
13
|
+
* asks for is not opened once the user has left this window for the app (`formfeed:edit-cancel`):
|
|
14
|
+
* opening it takes the focus, and that belongs to where the user went.
|
|
15
|
+
*
|
|
16
|
+
* A preview document says which one it is (`doc`, from its `formfeed:document` meta) and when its
|
|
17
|
+
* elements are in place (`formfeed:edit-ready`). The editor renders a moment before the frame takes
|
|
18
|
+
* the new document, and a click in that moment is about a document on its way out: the editor waits
|
|
19
|
+
* for the next one and has the frame ask for the same text there (`formfeed:edit-open`).
|
|
13
20
|
*/
|
|
14
21
|
/** An item as the frame shows it: chips carry the text to display. */
|
|
15
22
|
export type FrameItem = {
|
package/types/index.d.cts
CHANGED
|
@@ -25,11 +25,13 @@ export { canvasDocument, flowDocument, pagedDocument, pageNumberSpans, paperOf,
|
|
|
25
25
|
export type { PagedOptions, PreviewOptions, PreviewOverflow, RenderedDraft, } from './lib/preview.cjs';
|
|
26
26
|
export { annotateSourcePositions, editAttribute, parseSourceAttribute, sourceAttribute, } from './lib/source-positions.cjs';
|
|
27
27
|
export type { AnnotateOptions, SourceFile, SourceLocation, } from './lib/source-positions.cjs';
|
|
28
|
-
export { applyRegionEdit, chipValues, editableRegion, editableRegionAt, findRegionAgain, regionChips, regionItems, translationItems, translationText, } from './lib/source-edit.cjs';
|
|
28
|
+
export { applyRegionEdit, chipValues, editableRegion, editableRegionAt, findRegionAgain, regionChips, regionItems, regionPosition, translationItems, translationText, } from './lib/source-edit.cjs';
|
|
29
29
|
export type { ApplyResult, ChipKind, EditableRegion, EditItem, RegionChip, RegionLookup, RegionPart, RegionRefusal, } from './lib/source-edit.cjs';
|
|
30
30
|
export { resolveTranslation, translationTarget } from './lib/translation.cjs';
|
|
31
31
|
export { DESIGN_LIMITS, DESIGN_VERSION, dataPathPattern, defaultLayer, designBytes, designOutputHash, designStarters, emitDesign, emptyDesign, genericFamily, googleFont, googleFonts, googleFontsUrl, imageIsEmpty, layerIdPattern, nearestWeight, nextLayerId, parseDesign, } from './lib/design/index.cjs';
|
|
32
32
|
export type { BarcodeLayer, BarcodeSymbology, Binding, CanvasSize, DesignDocument, DesignLayer, DesignStarter, EmittedDesign, FontChoice, GoogleFont, ImageLayer, ImageSource, LayerBase, LayerType, Paint, ParsedDesign, QrLayer, Shadow, ShapeLayer, Stroke, TextFormat, TextItem, TextLayer, TextStyle, } from './lib/design/index.cjs';
|
|
33
|
+
export { CII_GUIDELINES, CODE_LIST_RELEASE, DISPLAY_TERMS, EINVOICE_FLAVOURS, EINVOICE_FLAVOUR_NAMES, EINVOICE_MAX_MESSAGES, EINVOICE_PROFILES, EINVOICE_PROFILE_NAMES, EINVOICE_SPEC_VERSIONS, EINVOICE_XML_NAME, INVOICE_DATA_KEY, VAT_CATEGORIES, capMessages, checkInvoice, defaultEinvoiceOptions, displayCheck, displayMissingText, einvoiceOptionsSchema, einvoiceResult, einvoiceXml, htmlText, invoiceAmountText, invoiceLineOf, invoicePathSegments, invoiceSchema, invoiceSkeleton, invoiceToCii, locateJsonPath, resolveEinvoiceOptions, shownAmounts, } from './lib/einvoice/index.cjs';
|
|
34
|
+
export type { CheckOptions, DisplayCheck, DisplayValue, EinvoiceDisplay, EinvoiceFlavour, EinvoiceMessage, EinvoiceOptions, EinvoiceOptionsInput, EinvoiceProfile, EinvoiceResult, EinvoiceValidation, EinvoiceXml, Identifier, Invoice, InvoiceAddress, InvoiceAllowance, InvoiceBreakdown, InvoiceCheck, InvoiceInput, InvoiceLine, InvoiceParty, InvoiceProblem, InvoiceProblemCode, InvoiceTax, JsonLocation, ResolvedEinvoice, VatCategory, } from './lib/einvoice/index.cjs';
|
|
33
35
|
export { encodeTemplateText } from './lib/source-edit.cjs';
|
|
34
36
|
export type { Dictionaries } from './lib/translation.cjs';
|
|
35
37
|
export type { AssembleExtras, AssembleInput, AssembleVendor, FontFace, HeaderFooterSettings, RenderedDocument, TemplateKind, TemplateSettings, VersionSource, } from './lib/assemble.cjs';
|
package/types/index.d.ts
CHANGED
|
@@ -25,11 +25,13 @@ export { canvasDocument, flowDocument, pagedDocument, pageNumberSpans, paperOf,
|
|
|
25
25
|
export type { PagedOptions, PreviewOptions, PreviewOverflow, RenderedDraft, } from './lib/preview.js';
|
|
26
26
|
export { annotateSourcePositions, editAttribute, parseSourceAttribute, sourceAttribute, } from './lib/source-positions.js';
|
|
27
27
|
export type { AnnotateOptions, SourceFile, SourceLocation, } from './lib/source-positions.js';
|
|
28
|
-
export { applyRegionEdit, chipValues, editableRegion, editableRegionAt, findRegionAgain, regionChips, regionItems, translationItems, translationText, } from './lib/source-edit.js';
|
|
28
|
+
export { applyRegionEdit, chipValues, editableRegion, editableRegionAt, findRegionAgain, regionChips, regionItems, regionPosition, translationItems, translationText, } from './lib/source-edit.js';
|
|
29
29
|
export type { ApplyResult, ChipKind, EditableRegion, EditItem, RegionChip, RegionLookup, RegionPart, RegionRefusal, } from './lib/source-edit.js';
|
|
30
30
|
export { resolveTranslation, translationTarget } from './lib/translation.js';
|
|
31
31
|
export { DESIGN_LIMITS, DESIGN_VERSION, dataPathPattern, defaultLayer, designBytes, designOutputHash, designStarters, emitDesign, emptyDesign, genericFamily, googleFont, googleFonts, googleFontsUrl, imageIsEmpty, layerIdPattern, nearestWeight, nextLayerId, parseDesign, } from './lib/design/index.js';
|
|
32
32
|
export type { BarcodeLayer, BarcodeSymbology, Binding, CanvasSize, DesignDocument, DesignLayer, DesignStarter, EmittedDesign, FontChoice, GoogleFont, ImageLayer, ImageSource, LayerBase, LayerType, Paint, ParsedDesign, QrLayer, Shadow, ShapeLayer, Stroke, TextFormat, TextItem, TextLayer, TextStyle, } from './lib/design/index.js';
|
|
33
|
+
export { CII_GUIDELINES, CODE_LIST_RELEASE, DISPLAY_TERMS, EINVOICE_FLAVOURS, EINVOICE_FLAVOUR_NAMES, EINVOICE_MAX_MESSAGES, EINVOICE_PROFILES, EINVOICE_PROFILE_NAMES, EINVOICE_SPEC_VERSIONS, EINVOICE_XML_NAME, INVOICE_DATA_KEY, VAT_CATEGORIES, capMessages, checkInvoice, defaultEinvoiceOptions, displayCheck, displayMissingText, einvoiceOptionsSchema, einvoiceResult, einvoiceXml, htmlText, invoiceAmountText, invoiceLineOf, invoicePathSegments, invoiceSchema, invoiceSkeleton, invoiceToCii, locateJsonPath, resolveEinvoiceOptions, shownAmounts, } from './lib/einvoice/index.js';
|
|
34
|
+
export type { CheckOptions, DisplayCheck, DisplayValue, EinvoiceDisplay, EinvoiceFlavour, EinvoiceMessage, EinvoiceOptions, EinvoiceOptionsInput, EinvoiceProfile, EinvoiceResult, EinvoiceValidation, EinvoiceXml, Identifier, Invoice, InvoiceAddress, InvoiceAllowance, InvoiceBreakdown, InvoiceCheck, InvoiceInput, InvoiceLine, InvoiceParty, InvoiceProblem, InvoiceProblemCode, InvoiceTax, JsonLocation, ResolvedEinvoice, VatCategory, } from './lib/einvoice/index.js';
|
|
33
35
|
export { encodeTemplateText } from './lib/source-edit.js';
|
|
34
36
|
export type { Dictionaries } from './lib/translation.js';
|
|
35
37
|
export type { AssembleExtras, AssembleInput, AssembleVendor, FontFace, HeaderFooterSettings, RenderedDocument, TemplateKind, TemplateSettings, VersionSource, } from './lib/assemble.js';
|
package/types/lib/assemble.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BrandContext } from './brand.cjs';
|
|
2
|
+
import type { EinvoiceOptionsInput } from './einvoice/schema.cjs';
|
|
2
3
|
import type { EngineId, RenderContext } from './types.cjs';
|
|
3
4
|
/** Template settings (spec 01 §3); every field optional, `defaultSettings()` fills the rest. */
|
|
4
5
|
export interface HeaderFooterSettings {
|
|
@@ -70,6 +71,13 @@ export interface TemplateSettings {
|
|
|
70
71
|
text?: string;
|
|
71
72
|
opacity?: number;
|
|
72
73
|
} | null;
|
|
74
|
+
/**
|
|
75
|
+
* The template is an invoice template (spec 17 §4.1): every PDF rendered from it carries the
|
|
76
|
+
* e-invoice built from the `_invoice` block of the data. A request's `settings.einvoice` and
|
|
77
|
+
* `post.einvoice` merge over this field by field (`resolveEinvoiceOptions`); `false` there
|
|
78
|
+
* switches it off for one request.
|
|
79
|
+
*/
|
|
80
|
+
einvoice?: EinvoiceOptionsInput | false | null;
|
|
73
81
|
/**
|
|
74
82
|
* The visual editor's document (plan 16 §4.1, `DesignDocument` once `parseDesign` accepted it). It
|
|
75
83
|
* is authoring data: nothing renders it, the version's HTML and CSS are what it emitted.
|
package/types/lib/assemble.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BrandContext } from './brand.js';
|
|
2
|
+
import type { EinvoiceOptionsInput } from './einvoice/schema.js';
|
|
2
3
|
import type { EngineId, RenderContext } from './types.js';
|
|
3
4
|
/** Template settings (spec 01 §3); every field optional, `defaultSettings()` fills the rest. */
|
|
4
5
|
export interface HeaderFooterSettings {
|
|
@@ -70,6 +71,13 @@ export interface TemplateSettings {
|
|
|
70
71
|
text?: string;
|
|
71
72
|
opacity?: number;
|
|
72
73
|
} | null;
|
|
74
|
+
/**
|
|
75
|
+
* The template is an invoice template (spec 17 §4.1): every PDF rendered from it carries the
|
|
76
|
+
* e-invoice built from the `_invoice` block of the data. A request's `settings.einvoice` and
|
|
77
|
+
* `post.einvoice` merge over this field by field (`resolveEinvoiceOptions`); `false` there
|
|
78
|
+
* switches it off for one request.
|
|
79
|
+
*/
|
|
80
|
+
einvoice?: EinvoiceOptionsInput | false | null;
|
|
73
81
|
/**
|
|
74
82
|
* The visual editor's document (plan 16 §4.1, `DesignDocument` once `parseDesign` accepted it). It
|
|
75
83
|
* is authoring data: nothing renders it, the version's HTML and CSS are what it emitted.
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { type EinvoiceProfile, type VatCategory } from './schema.cjs';
|
|
2
|
+
/**
|
|
3
|
+
* Reads an `_invoice` block against EN 16931 (spec 17 §4.3): the shape first, then the rules a
|
|
4
|
+
* request can be held to before anything is rendered. What comes out is either the invoice with
|
|
5
|
+
* every number as exact decimal text, ready for the XML, or the list of what is wrong, each finding
|
|
6
|
+
* with the field and the rule it breaks.
|
|
7
|
+
*
|
|
8
|
+
* Nothing is computed for the caller and nothing is rounded into place. A total that is a cent off
|
|
9
|
+
* is a finding, because an e-invoice whose XML disagrees with the seller's books is a tax problem,
|
|
10
|
+
* and correcting it here would hide exactly that.
|
|
11
|
+
*
|
|
12
|
+
* The full rule set (the official schematron, some 430 assertions) runs in the sidecar on every
|
|
13
|
+
* document. This is the part worth knowing early: it needs no JVM, so the gateway answers a bad
|
|
14
|
+
* request at once, and the editor and the CLI say the same thing offline.
|
|
15
|
+
*/
|
|
16
|
+
export interface InvoiceProblem {
|
|
17
|
+
/** What kind of finding, for a caller that words it in its own language. */
|
|
18
|
+
code: InvoiceProblemCode;
|
|
19
|
+
/** The field inside `_invoice`, dotted, list items as `lines[1]` (zero-based). */
|
|
20
|
+
path: string;
|
|
21
|
+
/** The rule of EN 16931 (`BR-CO-15`) or of the French specifications (`BR-FR-10`), where one applies. */
|
|
22
|
+
rule?: string;
|
|
23
|
+
/** The same in English, naming the field and the rule. */
|
|
24
|
+
message: string;
|
|
25
|
+
/** The values `message` was built from. */
|
|
26
|
+
args: Record<string, string | number>;
|
|
27
|
+
}
|
|
28
|
+
export type InvoiceProblemCode = 'missing' | 'invalid' | 'not_a_number' | 'decimals' | 'negative' | 'code_list' | 'date' | 'date_order' | 'scheme_required' | 'vat_id_prefix' | 'iban' | 'sum' | 'breakdown_missing' | 'breakdown_duplicate' | 'rate_required' | 'rate_positive' | 'rate_zero' | 'rate_forbidden' | 'amount_zero' | 'exemption_reason_required' | 'exemption_reason_forbidden' | 'seller_tax_id_required' | 'buyer_tax_id_required' | 'vat_id_forbidden' | 'only_category' | 'delivery_required' | 'account_required' | 'means_code_required' | 'due_or_terms_required' | 'seller_identifier_required' | 'siren_mismatch' | 'fr_seller_siren' | 'fr_buyer_siren' | 'fr_operation' | 'fr_vat_on_debits' | 'profile';
|
|
29
|
+
export interface Identifier {
|
|
30
|
+
value: string;
|
|
31
|
+
scheme?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface InvoiceAddress {
|
|
34
|
+
street?: string;
|
|
35
|
+
street2?: string;
|
|
36
|
+
street3?: string;
|
|
37
|
+
postcode?: string;
|
|
38
|
+
city?: string;
|
|
39
|
+
country: string;
|
|
40
|
+
subdivision?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface InvoiceParty {
|
|
43
|
+
name: string;
|
|
44
|
+
trading_name?: string;
|
|
45
|
+
id?: Identifier;
|
|
46
|
+
legal_registration?: Identifier;
|
|
47
|
+
vat_id?: string;
|
|
48
|
+
address: InvoiceAddress;
|
|
49
|
+
contact?: {
|
|
50
|
+
name?: string;
|
|
51
|
+
phone?: string;
|
|
52
|
+
email?: string;
|
|
53
|
+
};
|
|
54
|
+
electronic_address?: Required<Identifier>;
|
|
55
|
+
}
|
|
56
|
+
export interface InvoiceAllowance {
|
|
57
|
+
amount: string;
|
|
58
|
+
reason?: string;
|
|
59
|
+
reason_code?: string;
|
|
60
|
+
base_amount?: string;
|
|
61
|
+
percent?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface InvoiceTax {
|
|
64
|
+
category: VatCategory;
|
|
65
|
+
/** Percent as decimal text; absent for category O only. */
|
|
66
|
+
rate?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface InvoiceLine {
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
description?: string;
|
|
72
|
+
quantity: string;
|
|
73
|
+
unit_code: string;
|
|
74
|
+
net_price: string;
|
|
75
|
+
price_base_quantity?: string;
|
|
76
|
+
net_amount: string;
|
|
77
|
+
tax: InvoiceTax;
|
|
78
|
+
period?: {
|
|
79
|
+
start?: string;
|
|
80
|
+
end?: string;
|
|
81
|
+
};
|
|
82
|
+
allowances: InvoiceAllowance[];
|
|
83
|
+
charges: InvoiceAllowance[];
|
|
84
|
+
buyer_accounting_reference?: string;
|
|
85
|
+
note?: string;
|
|
86
|
+
seller_item_id?: string;
|
|
87
|
+
buyer_item_id?: string;
|
|
88
|
+
standard_item_id?: Identifier;
|
|
89
|
+
order_line_reference?: string;
|
|
90
|
+
}
|
|
91
|
+
export interface InvoiceBreakdown extends InvoiceTax {
|
|
92
|
+
basis: string;
|
|
93
|
+
amount: string;
|
|
94
|
+
exemption_reason?: string;
|
|
95
|
+
exemption_reason_code?: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* An invoice that passed: every amount is decimal text with at most two places, every code is in
|
|
99
|
+
* its list, and the totals add up. The XML is written from this and from nothing else.
|
|
100
|
+
*/
|
|
101
|
+
export interface Invoice {
|
|
102
|
+
number: string;
|
|
103
|
+
issue_date: string;
|
|
104
|
+
type_code: string;
|
|
105
|
+
currency: string;
|
|
106
|
+
accounting_currency?: string;
|
|
107
|
+
buyer_reference?: string;
|
|
108
|
+
order_reference?: string;
|
|
109
|
+
contract_reference?: string;
|
|
110
|
+
notes: Array<{
|
|
111
|
+
text: string;
|
|
112
|
+
subject_code?: string;
|
|
113
|
+
}>;
|
|
114
|
+
period?: {
|
|
115
|
+
start?: string;
|
|
116
|
+
end?: string;
|
|
117
|
+
};
|
|
118
|
+
preceding_invoices: Array<{
|
|
119
|
+
number: string;
|
|
120
|
+
issue_date?: string;
|
|
121
|
+
}>;
|
|
122
|
+
/** The business process (BT-23); French invoices carry their billing framework here. */
|
|
123
|
+
business_process?: string;
|
|
124
|
+
seller: InvoiceParty & {
|
|
125
|
+
tax_number?: string;
|
|
126
|
+
legal_info?: string;
|
|
127
|
+
vat_on_debits?: boolean;
|
|
128
|
+
};
|
|
129
|
+
buyer: InvoiceParty;
|
|
130
|
+
delivery?: {
|
|
131
|
+
name?: string;
|
|
132
|
+
date?: string;
|
|
133
|
+
address?: InvoiceAddress;
|
|
134
|
+
};
|
|
135
|
+
lines: InvoiceLine[];
|
|
136
|
+
allowances: Array<InvoiceAllowance & {
|
|
137
|
+
tax: InvoiceTax;
|
|
138
|
+
}>;
|
|
139
|
+
charges: Array<InvoiceAllowance & {
|
|
140
|
+
tax: InvoiceTax;
|
|
141
|
+
}>;
|
|
142
|
+
breakdown: InvoiceBreakdown[];
|
|
143
|
+
totals: {
|
|
144
|
+
line_net: string;
|
|
145
|
+
allowances?: string;
|
|
146
|
+
charges?: string;
|
|
147
|
+
tax_basis: string;
|
|
148
|
+
tax_total: string;
|
|
149
|
+
tax_total_accounting?: string;
|
|
150
|
+
grand: string;
|
|
151
|
+
prepaid?: string;
|
|
152
|
+
rounding?: string;
|
|
153
|
+
due: string;
|
|
154
|
+
};
|
|
155
|
+
payment?: {
|
|
156
|
+
means_code?: string;
|
|
157
|
+
iban?: string;
|
|
158
|
+
bic?: string;
|
|
159
|
+
account_name?: string;
|
|
160
|
+
reference?: string;
|
|
161
|
+
terms?: string;
|
|
162
|
+
due_date?: string;
|
|
163
|
+
mandate_id?: string;
|
|
164
|
+
creditor_id?: string;
|
|
165
|
+
debited_iban?: string;
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export type InvoiceCheck = {
|
|
169
|
+
ok: true;
|
|
170
|
+
invoice: Invoice;
|
|
171
|
+
problems: [];
|
|
172
|
+
} | {
|
|
173
|
+
ok: false;
|
|
174
|
+
invoice: null;
|
|
175
|
+
problems: InvoiceProblem[];
|
|
176
|
+
};
|
|
177
|
+
export interface CheckOptions {
|
|
178
|
+
/** The profile the XML will be written in; `basic` cannot carry everything `en16931` can. */
|
|
179
|
+
profile?: EinvoiceProfile;
|
|
180
|
+
}
|
|
181
|
+
export declare function checkInvoice(data: unknown, options?: CheckOptions): InvoiceCheck;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { type EinvoiceProfile, type VatCategory } from './schema.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reads an `_invoice` block against EN 16931 (spec 17 §4.3): the shape first, then the rules a
|
|
4
|
+
* request can be held to before anything is rendered. What comes out is either the invoice with
|
|
5
|
+
* every number as exact decimal text, ready for the XML, or the list of what is wrong, each finding
|
|
6
|
+
* with the field and the rule it breaks.
|
|
7
|
+
*
|
|
8
|
+
* Nothing is computed for the caller and nothing is rounded into place. A total that is a cent off
|
|
9
|
+
* is a finding, because an e-invoice whose XML disagrees with the seller's books is a tax problem,
|
|
10
|
+
* and correcting it here would hide exactly that.
|
|
11
|
+
*
|
|
12
|
+
* The full rule set (the official schematron, some 430 assertions) runs in the sidecar on every
|
|
13
|
+
* document. This is the part worth knowing early: it needs no JVM, so the gateway answers a bad
|
|
14
|
+
* request at once, and the editor and the CLI say the same thing offline.
|
|
15
|
+
*/
|
|
16
|
+
export interface InvoiceProblem {
|
|
17
|
+
/** What kind of finding, for a caller that words it in its own language. */
|
|
18
|
+
code: InvoiceProblemCode;
|
|
19
|
+
/** The field inside `_invoice`, dotted, list items as `lines[1]` (zero-based). */
|
|
20
|
+
path: string;
|
|
21
|
+
/** The rule of EN 16931 (`BR-CO-15`) or of the French specifications (`BR-FR-10`), where one applies. */
|
|
22
|
+
rule?: string;
|
|
23
|
+
/** The same in English, naming the field and the rule. */
|
|
24
|
+
message: string;
|
|
25
|
+
/** The values `message` was built from. */
|
|
26
|
+
args: Record<string, string | number>;
|
|
27
|
+
}
|
|
28
|
+
export type InvoiceProblemCode = 'missing' | 'invalid' | 'not_a_number' | 'decimals' | 'negative' | 'code_list' | 'date' | 'date_order' | 'scheme_required' | 'vat_id_prefix' | 'iban' | 'sum' | 'breakdown_missing' | 'breakdown_duplicate' | 'rate_required' | 'rate_positive' | 'rate_zero' | 'rate_forbidden' | 'amount_zero' | 'exemption_reason_required' | 'exemption_reason_forbidden' | 'seller_tax_id_required' | 'buyer_tax_id_required' | 'vat_id_forbidden' | 'only_category' | 'delivery_required' | 'account_required' | 'means_code_required' | 'due_or_terms_required' | 'seller_identifier_required' | 'siren_mismatch' | 'fr_seller_siren' | 'fr_buyer_siren' | 'fr_operation' | 'fr_vat_on_debits' | 'profile';
|
|
29
|
+
export interface Identifier {
|
|
30
|
+
value: string;
|
|
31
|
+
scheme?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface InvoiceAddress {
|
|
34
|
+
street?: string;
|
|
35
|
+
street2?: string;
|
|
36
|
+
street3?: string;
|
|
37
|
+
postcode?: string;
|
|
38
|
+
city?: string;
|
|
39
|
+
country: string;
|
|
40
|
+
subdivision?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface InvoiceParty {
|
|
43
|
+
name: string;
|
|
44
|
+
trading_name?: string;
|
|
45
|
+
id?: Identifier;
|
|
46
|
+
legal_registration?: Identifier;
|
|
47
|
+
vat_id?: string;
|
|
48
|
+
address: InvoiceAddress;
|
|
49
|
+
contact?: {
|
|
50
|
+
name?: string;
|
|
51
|
+
phone?: string;
|
|
52
|
+
email?: string;
|
|
53
|
+
};
|
|
54
|
+
electronic_address?: Required<Identifier>;
|
|
55
|
+
}
|
|
56
|
+
export interface InvoiceAllowance {
|
|
57
|
+
amount: string;
|
|
58
|
+
reason?: string;
|
|
59
|
+
reason_code?: string;
|
|
60
|
+
base_amount?: string;
|
|
61
|
+
percent?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface InvoiceTax {
|
|
64
|
+
category: VatCategory;
|
|
65
|
+
/** Percent as decimal text; absent for category O only. */
|
|
66
|
+
rate?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface InvoiceLine {
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
description?: string;
|
|
72
|
+
quantity: string;
|
|
73
|
+
unit_code: string;
|
|
74
|
+
net_price: string;
|
|
75
|
+
price_base_quantity?: string;
|
|
76
|
+
net_amount: string;
|
|
77
|
+
tax: InvoiceTax;
|
|
78
|
+
period?: {
|
|
79
|
+
start?: string;
|
|
80
|
+
end?: string;
|
|
81
|
+
};
|
|
82
|
+
allowances: InvoiceAllowance[];
|
|
83
|
+
charges: InvoiceAllowance[];
|
|
84
|
+
buyer_accounting_reference?: string;
|
|
85
|
+
note?: string;
|
|
86
|
+
seller_item_id?: string;
|
|
87
|
+
buyer_item_id?: string;
|
|
88
|
+
standard_item_id?: Identifier;
|
|
89
|
+
order_line_reference?: string;
|
|
90
|
+
}
|
|
91
|
+
export interface InvoiceBreakdown extends InvoiceTax {
|
|
92
|
+
basis: string;
|
|
93
|
+
amount: string;
|
|
94
|
+
exemption_reason?: string;
|
|
95
|
+
exemption_reason_code?: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* An invoice that passed: every amount is decimal text with at most two places, every code is in
|
|
99
|
+
* its list, and the totals add up. The XML is written from this and from nothing else.
|
|
100
|
+
*/
|
|
101
|
+
export interface Invoice {
|
|
102
|
+
number: string;
|
|
103
|
+
issue_date: string;
|
|
104
|
+
type_code: string;
|
|
105
|
+
currency: string;
|
|
106
|
+
accounting_currency?: string;
|
|
107
|
+
buyer_reference?: string;
|
|
108
|
+
order_reference?: string;
|
|
109
|
+
contract_reference?: string;
|
|
110
|
+
notes: Array<{
|
|
111
|
+
text: string;
|
|
112
|
+
subject_code?: string;
|
|
113
|
+
}>;
|
|
114
|
+
period?: {
|
|
115
|
+
start?: string;
|
|
116
|
+
end?: string;
|
|
117
|
+
};
|
|
118
|
+
preceding_invoices: Array<{
|
|
119
|
+
number: string;
|
|
120
|
+
issue_date?: string;
|
|
121
|
+
}>;
|
|
122
|
+
/** The business process (BT-23); French invoices carry their billing framework here. */
|
|
123
|
+
business_process?: string;
|
|
124
|
+
seller: InvoiceParty & {
|
|
125
|
+
tax_number?: string;
|
|
126
|
+
legal_info?: string;
|
|
127
|
+
vat_on_debits?: boolean;
|
|
128
|
+
};
|
|
129
|
+
buyer: InvoiceParty;
|
|
130
|
+
delivery?: {
|
|
131
|
+
name?: string;
|
|
132
|
+
date?: string;
|
|
133
|
+
address?: InvoiceAddress;
|
|
134
|
+
};
|
|
135
|
+
lines: InvoiceLine[];
|
|
136
|
+
allowances: Array<InvoiceAllowance & {
|
|
137
|
+
tax: InvoiceTax;
|
|
138
|
+
}>;
|
|
139
|
+
charges: Array<InvoiceAllowance & {
|
|
140
|
+
tax: InvoiceTax;
|
|
141
|
+
}>;
|
|
142
|
+
breakdown: InvoiceBreakdown[];
|
|
143
|
+
totals: {
|
|
144
|
+
line_net: string;
|
|
145
|
+
allowances?: string;
|
|
146
|
+
charges?: string;
|
|
147
|
+
tax_basis: string;
|
|
148
|
+
tax_total: string;
|
|
149
|
+
tax_total_accounting?: string;
|
|
150
|
+
grand: string;
|
|
151
|
+
prepaid?: string;
|
|
152
|
+
rounding?: string;
|
|
153
|
+
due: string;
|
|
154
|
+
};
|
|
155
|
+
payment?: {
|
|
156
|
+
means_code?: string;
|
|
157
|
+
iban?: string;
|
|
158
|
+
bic?: string;
|
|
159
|
+
account_name?: string;
|
|
160
|
+
reference?: string;
|
|
161
|
+
terms?: string;
|
|
162
|
+
due_date?: string;
|
|
163
|
+
mandate_id?: string;
|
|
164
|
+
creditor_id?: string;
|
|
165
|
+
debited_iban?: string;
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export type InvoiceCheck = {
|
|
169
|
+
ok: true;
|
|
170
|
+
invoice: Invoice;
|
|
171
|
+
problems: [];
|
|
172
|
+
} | {
|
|
173
|
+
ok: false;
|
|
174
|
+
invoice: null;
|
|
175
|
+
problems: InvoiceProblem[];
|
|
176
|
+
};
|
|
177
|
+
export interface CheckOptions {
|
|
178
|
+
/** The profile the XML will be written in; `basic` cannot carry everything `en16931` can. */
|
|
179
|
+
profile?: EinvoiceProfile;
|
|
180
|
+
}
|
|
181
|
+
export declare function checkInvoice(data: unknown, options?: CheckOptions): InvoiceCheck;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Invoice } from './check.cjs';
|
|
2
|
+
import type { EinvoiceProfile } from './schema.cjs';
|
|
3
|
+
/**
|
|
4
|
+
* A checked invoice as Cross Industry Invoice XML, the syntax ZUGFeRD and Factur-X embed (spec 17
|
|
5
|
+
* §3, §5). The element order is the schema's: the XSD of a profile is a sequence, and an element in
|
|
6
|
+
* the wrong place fails before any business rule is looked at.
|
|
7
|
+
*
|
|
8
|
+
* The output is a function of the invoice and the profile alone. No date of writing, no generator
|
|
9
|
+
* comment, no locale: two renders of the same data embed the same bytes, and the golden files in
|
|
10
|
+
* `fixtures/` pin them.
|
|
11
|
+
*/
|
|
12
|
+
/** The identifier a document names its profile by (BT-24). */
|
|
13
|
+
export declare const CII_GUIDELINES: Record<EinvoiceProfile, string>;
|
|
14
|
+
/** The name the XML has inside the PDF, under either of the specification's two names. */
|
|
15
|
+
export declare const EINVOICE_XML_NAME = "factur-x.xml";
|
|
16
|
+
export declare function invoiceToCii(invoice: Invoice, options: {
|
|
17
|
+
profile: EinvoiceProfile;
|
|
18
|
+
}): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Invoice } from './check.js';
|
|
2
|
+
import type { EinvoiceProfile } from './schema.js';
|
|
3
|
+
/**
|
|
4
|
+
* A checked invoice as Cross Industry Invoice XML, the syntax ZUGFeRD and Factur-X embed (spec 17
|
|
5
|
+
* §3, §5). The element order is the schema's: the XSD of a profile is a sequence, and an element in
|
|
6
|
+
* the wrong place fails before any business rule is looked at.
|
|
7
|
+
*
|
|
8
|
+
* The output is a function of the invoice and the profile alone. No date of writing, no generator
|
|
9
|
+
* comment, no locale: two renders of the same data embed the same bytes, and the golden files in
|
|
10
|
+
* `fixtures/` pin them.
|
|
11
|
+
*/
|
|
12
|
+
/** The identifier a document names its profile by (BT-24). */
|
|
13
|
+
export declare const CII_GUIDELINES: Record<EinvoiceProfile, string>;
|
|
14
|
+
/** The name the XML has inside the PDF, under either of the specification's two names. */
|
|
15
|
+
export declare const EINVOICE_XML_NAME = "factur-x.xml";
|
|
16
|
+
export declare function invoiceToCii(invoice: Invoice, options: {
|
|
17
|
+
profile: EinvoiceProfile;
|
|
18
|
+
}): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** The Factur-X release these lists were taken from. */
|
|
2
|
+
export declare const CODE_LIST_RELEASE = "1.09.2";
|
|
3
|
+
/** Invoice type code (BT-3), UNTDID 1001. 62 codes. */
|
|
4
|
+
export declare const documentTypeCodes: ReadonlySet<string>;
|
|
5
|
+
/** Country codes (BT-40, BT-55, BT-80), ISO 3166-1 alpha-2. 253 codes. */
|
|
6
|
+
export declare const countryCodes: ReadonlySet<string>;
|
|
7
|
+
/** Units of measure (BT-130, BT-150), UN/ECE Recommendation 20 with 21. 2162 codes. */
|
|
8
|
+
export declare const unitCodes: ReadonlySet<string>;
|
|
9
|
+
/** Electronic address schemes (BT-34, BT-49), the EAS list. 102 codes. */
|
|
10
|
+
export declare const electronicAddressSchemes: ReadonlySet<string>;
|
|
11
|
+
/** Currencies (BT-5, BT-6), ISO 4217. 178 codes. */
|
|
12
|
+
export declare const currencyCodes: ReadonlySet<string>;
|
|
13
|
+
/** Payment means (BT-81), UNTDID 4461. 84 codes. */
|
|
14
|
+
export declare const paymentMeansCodes: ReadonlySet<string>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** The Factur-X release these lists were taken from. */
|
|
2
|
+
export declare const CODE_LIST_RELEASE = "1.09.2";
|
|
3
|
+
/** Invoice type code (BT-3), UNTDID 1001. 62 codes. */
|
|
4
|
+
export declare const documentTypeCodes: ReadonlySet<string>;
|
|
5
|
+
/** Country codes (BT-40, BT-55, BT-80), ISO 3166-1 alpha-2. 253 codes. */
|
|
6
|
+
export declare const countryCodes: ReadonlySet<string>;
|
|
7
|
+
/** Units of measure (BT-130, BT-150), UN/ECE Recommendation 20 with 21. 2162 codes. */
|
|
8
|
+
export declare const unitCodes: ReadonlySet<string>;
|
|
9
|
+
/** Electronic address schemes (BT-34, BT-49), the EAS list. 102 codes. */
|
|
10
|
+
export declare const electronicAddressSchemes: ReadonlySet<string>;
|
|
11
|
+
/** Currencies (BT-5, BT-6), ISO 4217. 178 codes. */
|
|
12
|
+
export declare const currencyCodes: ReadonlySet<string>;
|
|
13
|
+
/** Payment means (BT-81), UNTDID 4461. 84 codes. */
|
|
14
|
+
export declare const paymentMeansCodes: ReadonlySet<string>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact decimals for an invoice. Totals are compared to the cent and written into the XML as text,
|
|
3
|
+
* so nothing here goes through floating-point arithmetic: a value is read once into its decimal
|
|
4
|
+
* text and from there into a `bigint` of the smallest unit the comparison needs.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The decimal text of a JSON number or a numeric string, without an exponent and without trailing
|
|
8
|
+
* zeros; `null` when it is not a plain decimal.
|
|
9
|
+
*
|
|
10
|
+
* A number is taken at fifteen significant digits, which is every digit a double holds exactly: a
|
|
11
|
+
* request built with ordinary arithmetic sends `0.30000000000000004` for thirty cents, and refusing
|
|
12
|
+
* that as "more than two decimals" would punish the language, not the invoice. A string is taken
|
|
13
|
+
* as written, since whoever sends `"0.300000000000000004"` typed those digits.
|
|
14
|
+
*/
|
|
15
|
+
export declare function decimalText(value: unknown): string | null;
|
|
16
|
+
/** How many decimal places a decimal text carries. */
|
|
17
|
+
export declare function decimalPlaces(text: string): number;
|
|
18
|
+
/**
|
|
19
|
+
* A decimal text as an integer of `10^-scale` units. The text must not carry more places than
|
|
20
|
+
* `scale`; callers check that first and name the field, because that is a finding about the data.
|
|
21
|
+
*/
|
|
22
|
+
export declare function toUnits(text: string, scale: number): bigint;
|
|
23
|
+
/** Integer units back to decimal text with exactly `places` decimals (`376040n, 2` → `3760.40`). */
|
|
24
|
+
export declare function fromUnits(units: bigint, scale: number, places?: number): string;
|
|
25
|
+
/** A decimal text padded to at least `min` decimals, as the XML writes amounts (`3760.4` → `3760.40`). */
|
|
26
|
+
export declare function withPlaces(text: string, min: number): string;
|
|
27
|
+
/** `value / divisor`, rounded half away from zero, as the standard rounds a VAT amount. */
|
|
28
|
+
export declare function divideRounded(value: bigint, divisor: bigint): bigint;
|
|
29
|
+
export declare const abs: (value: bigint) => bigint;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact decimals for an invoice. Totals are compared to the cent and written into the XML as text,
|
|
3
|
+
* so nothing here goes through floating-point arithmetic: a value is read once into its decimal
|
|
4
|
+
* text and from there into a `bigint` of the smallest unit the comparison needs.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The decimal text of a JSON number or a numeric string, without an exponent and without trailing
|
|
8
|
+
* zeros; `null` when it is not a plain decimal.
|
|
9
|
+
*
|
|
10
|
+
* A number is taken at fifteen significant digits, which is every digit a double holds exactly: a
|
|
11
|
+
* request built with ordinary arithmetic sends `0.30000000000000004` for thirty cents, and refusing
|
|
12
|
+
* that as "more than two decimals" would punish the language, not the invoice. A string is taken
|
|
13
|
+
* as written, since whoever sends `"0.300000000000000004"` typed those digits.
|
|
14
|
+
*/
|
|
15
|
+
export declare function decimalText(value: unknown): string | null;
|
|
16
|
+
/** How many decimal places a decimal text carries. */
|
|
17
|
+
export declare function decimalPlaces(text: string): number;
|
|
18
|
+
/**
|
|
19
|
+
* A decimal text as an integer of `10^-scale` units. The text must not carry more places than
|
|
20
|
+
* `scale`; callers check that first and name the field, because that is a finding about the data.
|
|
21
|
+
*/
|
|
22
|
+
export declare function toUnits(text: string, scale: number): bigint;
|
|
23
|
+
/** Integer units back to decimal text with exactly `places` decimals (`376040n, 2` → `3760.40`). */
|
|
24
|
+
export declare function fromUnits(units: bigint, scale: number, places?: number): string;
|
|
25
|
+
/** A decimal text padded to at least `min` decimals, as the XML writes amounts (`3760.4` → `3760.40`). */
|
|
26
|
+
export declare function withPlaces(text: string, min: number): string;
|
|
27
|
+
/** `value / divisor`, rounded half away from zero, as the standard rounds a VAT amount. */
|
|
28
|
+
export declare function divideRounded(value: bigint, divisor: bigint): bigint;
|
|
29
|
+
export declare const abs: (value: bigint) => bigint;
|