@things-factory/invoice 10.1.226 → 10.1.227
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/document/document-amount.d.ts +7 -2
- package/dist-client/document/document-amount.js +11 -6
- package/dist-client/document/document-amount.js.map +1 -1
- package/dist-client/document/document-defaults.d.ts +24 -0
- package/dist-client/document/document-defaults.js +43 -0
- package/dist-client/document/document-defaults.js.map +1 -0
- package/dist-client/document/document-form.d.ts +24 -6
- package/dist-client/document/document-form.js +126 -15
- package/dist-client/document/document-form.js.map +1 -1
- package/dist-client/document/document-lines.d.ts +35 -1
- package/dist-client/document/document-lines.js +58 -9
- package/dist-client/document/document-lines.js.map +1 -1
- package/dist-client/document/document-party.d.ts +37 -0
- package/dist-client/document/document-party.js +217 -0
- package/dist-client/document/document-party.js.map +1 -0
- package/dist-client/document/document-preview.d.ts +37 -0
- package/dist-client/document/document-preview.js +181 -0
- package/dist-client/document/document-preview.js.map +1 -0
- package/dist-client/document/document-print.d.ts +3 -13
- package/dist-client/document/document-print.js +3 -45
- package/dist-client/document/document-print.js.map +1 -1
- package/dist-client/index.d.ts +9 -3
- package/dist-client/index.js +9 -3
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/invoice-editor.d.ts +37 -12
- package/dist-client/pages/invoice-editor.js +171 -79
- package/dist-client/pages/invoice-editor.js.map +1 -1
- package/dist-client/pages/invoice-list.d.ts +21 -1
- package/dist-client/pages/invoice-list.js +145 -14
- package/dist-client/pages/invoice-list.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/viewparts/document-defaults-setting-let.d.ts +19 -0
- package/dist-client/viewparts/document-defaults-setting-let.js +139 -0
- package/dist-client/viewparts/document-defaults-setting-let.js.map +1 -0
- package/dist-server/role-tasks.js +5 -2
- package/dist-server/role-tasks.js.map +1 -1
- package/dist-server/service/document-defaults/document-defaults-resolver.d.ts +28 -0
- package/dist-server/service/document-defaults/document-defaults-resolver.js +136 -0
- package/dist-server/service/document-defaults/document-defaults-resolver.js.map +1 -0
- package/dist-server/service/document-defaults/document-defaults-rules.d.ts +25 -0
- package/dist-server/service/document-defaults/document-defaults-rules.js +50 -0
- package/dist-server/service/document-defaults/document-defaults-rules.js.map +1 -0
- package/dist-server/service/document-defaults/index.d.ts +6 -0
- package/dist-server/service/document-defaults/index.js +11 -0
- package/dist-server/service/document-defaults/index.js.map +1 -0
- package/dist-server/service/index.d.ts +2 -0
- package/dist-server/service/index.js +4 -1
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/invoice/commercial-document.d.ts +2 -0
- package/dist-server/service/invoice/commercial-document.js +11 -1
- package/dist-server/service/invoice/commercial-document.js.map +1 -1
- package/dist-server/service/invoice/invoice-document-file.d.ts +20 -0
- package/dist-server/service/invoice/invoice-document-file.js +38 -0
- package/dist-server/service/invoice/invoice-document-file.js.map +1 -0
- package/dist-server/service/invoice/invoice-query.d.ts +3 -1
- package/dist-server/service/invoice/invoice-query.js +33 -0
- package/dist-server/service/invoice/invoice-query.js.map +1 -1
- package/dist-server/service/invoice/invoice-rules.d.ts +18 -11
- package/dist-server/service/invoice/invoice-rules.js +49 -96
- package/dist-server/service/invoice/invoice-rules.js.map +1 -1
- package/dist-server/service/invoice/invoice-service.d.ts +1 -0
- package/dist-server/service/invoice/invoice-service.js +5 -2
- package/dist-server/service/invoice/invoice-service.js.map +1 -1
- package/dist-server/service/invoice/invoice-type.d.ts +12 -0
- package/dist-server/service/invoice/invoice-type.js +37 -1
- package/dist-server/service/invoice/invoice-type.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-shared/document-amounts.d.ts +56 -0
- package/dist-shared/document-amounts.js +134 -0
- package/dist-shared/document-amounts.js.map +1 -0
- package/dist-shared/document-money.d.ts +12 -0
- package/dist-shared/document-money.js +38 -0
- package/dist-shared/document-money.js.map +1 -0
- package/dist-shared/document-print.d.ts +37 -0
- package/dist-shared/document-print.js +87 -0
- package/dist-shared/document-print.js.map +1 -0
- package/dist-shared/document-start.d.ts +15 -0
- package/dist-shared/document-start.js +51 -0
- package/dist-shared/document-start.js.map +1 -0
- package/dist-shared/invoice-standing.d.ts +25 -0
- package/dist-shared/invoice-standing.js +51 -0
- package/dist-shared/invoice-standing.js.map +1 -0
- package/package.json +9 -7
- package/shared/document-amounts.ts +209 -0
- package/shared/document-money.ts +33 -0
- package/shared/document-print.ts +131 -0
- package/shared/document-start.ts +46 -0
- package/shared/invoice-standing.ts +49 -0
- package/translations/en.json +32 -6
- package/translations/es.json +22 -1
- package/translations/ja.json +22 -1
- package/translations/ko.json +32 -6
- package/translations/ms.json +22 -1
- package/translations/zh.json +22 -1
- package/tsconfig.shared.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commercial-document.js","sourceRoot":"","sources":["../../../server/service/invoice/commercial-document.ts"],"names":[],"mappings":"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","sourcesContent":["import { moneyColumn } from '@things-factory/shell'\nimport { Field, Float, Int, ObjectType, registerEnumType } from 'type-graphql'\nimport { Column } from 'typeorm'\n\nimport { TaxKind } from './invoice-rules'\n\n/*\n * The head, the amounts and the line of a commercial document. Invoice and quotation each keep their own table and\n * extend these, so the printed fields and their rules are written once (ADR-0120 decision 1).\n */\n\nregisterEnumType(TaxKind, { name: 'TaxKind', description: 'taxation of a line — taxable, zero-rated or exempt' })\n\nexport enum DocumentDirection {\n SALES = 'SALES',\n PURCHASE = 'PURCHASE'\n}\nregisterEnumType(DocumentDirection, { name: 'DocumentDirection', description: 'whether the domain sends the document or receives it' })\n\nconst amount = moneyColumn({ scale: 4 })\n\n@ObjectType({ description: 'The printed head and totals of a commercial document' })\nexport abstract class CommercialDocument {\n @Column()\n @Field({ description: 'Document number, unique in the domain' })\n number: string\n\n @Column({ default: DocumentDirection.SALES })\n @Field(type => DocumentDirection, { description: 'SALES for a document the domain sends, PURCHASE for one it receives' })\n direction: DocumentDirection\n\n @Column({ default: 'KRW' })\n @Field({ description: 'ISO 4217 currency code' })\n currency: string\n\n @Column({ default: 'ko' })\n @Field({ description: 'Language the document is printed in: ko or en' })\n language: string\n\n @Column()\n @Field({ description: 'Supplier name as printed' })\n supplierName: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Supplier address as printed' })\n supplierAddress?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Supplier business or tax registration number' })\n supplierRegistrationNumber?: string\n\n @Column()\n @Field({ description: 'Buyer name as printed' })\n buyerName: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Buyer address as printed' })\n buyerAddress?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Buyer business or tax registration number' })\n buyerRegistrationNumber?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Issue date, YYYY-MM-DD' })\n issueDate?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Buyer purchase order reference' })\n purchaseOrder?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Payment terms as printed' })\n paymentTerms?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Bank account to pay into, as printed' })\n bankDetails?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Notes printed on the document' })\n notes?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Reference to the record the document was made from' })\n sourceReference?: string\n\n @Column({ ...amount, default: 0 })\n @Field(type => Float, { description: 'Sum of the line supply amounts' })\n supplyAmount: number\n\n @Column({ ...amount, default: 0 })\n @Field(type => Float, { description: 'Sum of the tax of each tax kind and rate group' })\n taxAmount: number\n\n @Column({ ...amount, default: 0 })\n @Field(type => Float, { description: 'Supply plus tax' })\n totalAmount: number\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'How the tax was rounded, written when the document went out' })\n taxRounding?: string\n\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'Decimal places of the currency, written when the document went out' })\n currencyDigits?: number\n}\n\n@ObjectType({ description: 'One line of a commercial document' })\nexport abstract class CommercialLine {\n @Column({ type: 'int' })\n @Field(type => Int, { description: 'Order of the line on the document' })\n position: number\n\n @Column()\n @Field({ description: 'What the line charges for' })\n description: string\n\n @Column(amount)\n @Field(type => Float, { description: 'Quantity' })\n quantity: number\n\n @Column(amount)\n @Field(type => Float, { description: 'Price of one unit' })\n unitPrice: number\n\n @Column(amount)\n @Field(type => Float, { description: 'Discount taken off the line, in the document currency' })\n discount: number\n\n @Column({ default: TaxKind.TAXABLE })\n @Field(type => TaxKind, { description: 'Taxable, zero-rated or exempt' })\n taxKind: TaxKind\n\n @Column(amount)\n @Field(type => Float, { description: 'Tax rate in percent; zero unless taxable' })\n taxRate: number\n\n @Column(amount)\n @Field(type => Float, { description: 'Quantity times price, less the discount' })\n supplyAmount: number\n}\n\n@ObjectType({ description: 'The supply and tax of one tax kind and rate on a document' })\nexport class DocumentTaxGroup {\n @Field(type => TaxKind, { description: 'Taxable, zero-rated or exempt' })\n taxKind: string\n\n @Field(type => Float, { description: 'Tax rate in percent' })\n taxRate: number\n\n @Field(type => Float, { description: 'Supply of the lines in the group' })\n supplyAmount: number\n\n @Field(type => Float, { description: 'Tax of the group' })\n taxAmount: number\n}\n"]}
|
|
1
|
+
{"version":3,"file":"commercial-document.js","sourceRoot":"","sources":["../../../server/service/invoice/commercial-document.ts"],"names":[],"mappings":"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","sourcesContent":["import { moneyColumn } from '@things-factory/shell'\nimport { Field, Float, Int, ObjectType, registerEnumType } from 'type-graphql'\nimport { Column } from 'typeorm'\n\nimport { TaxKind } from './invoice-rules'\n\n/*\n * The head, the amounts and the line of a commercial document. Invoice and quotation each keep their own table and\n * extend these, so the printed fields and their rules are written once (ADR-0120 decision 1).\n */\n\nregisterEnumType(TaxKind, { name: 'TaxKind', description: 'taxation of a line — taxable, zero-rated or exempt' })\n\nexport enum DocumentDirection {\n SALES = 'SALES',\n PURCHASE = 'PURCHASE'\n}\nregisterEnumType(DocumentDirection, { name: 'DocumentDirection', description: 'whether the domain sends the document or receives it' })\n\nconst amount = moneyColumn({ scale: 4 })\n\n@ObjectType({ description: 'The printed head and totals of a commercial document' })\nexport abstract class CommercialDocument {\n @Column()\n @Field({ description: 'Document number, unique in the domain' })\n number: string\n\n @Column({ default: DocumentDirection.SALES })\n @Field(type => DocumentDirection, { description: 'SALES for a document the domain sends, PURCHASE for one it receives' })\n direction: DocumentDirection\n\n @Column({ default: 'KRW' })\n @Field({ description: 'ISO 4217 currency code' })\n currency: string\n\n @Column({ default: 'ko' })\n @Field({ description: 'Language the document is printed in: ko or en' })\n language: string\n\n @Column()\n @Field({ description: 'Supplier name as printed' })\n supplierName: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Supplier address as printed' })\n supplierAddress?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Supplier business or tax registration number' })\n supplierRegistrationNumber?: string\n\n @Column()\n @Field({ description: 'Buyer name as printed' })\n buyerName: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Buyer address as printed' })\n buyerAddress?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Buyer business or tax registration number' })\n buyerRegistrationNumber?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Issue date, YYYY-MM-DD' })\n issueDate?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Buyer purchase order reference' })\n purchaseOrder?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Payment terms as printed' })\n paymentTerms?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Bank account to pay into, as printed' })\n bankDetails?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Notes printed on the document' })\n notes?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Reference to the record the document was made from' })\n sourceReference?: string\n\n @Column({ ...amount, default: 0 })\n @Field(type => Float, { description: 'Discount of the whole document, taken off the lines before tax and spread over its tax groups' })\n discountAmount: number\n\n @Column({ ...amount, default: 0 })\n @Field(type => Float, { description: 'Sum of the line supply amounts, less the discount of the whole document' })\n supplyAmount: number\n\n @Column({ ...amount, default: 0 })\n @Field(type => Float, { description: 'Sum of the tax of each tax kind and rate group' })\n taxAmount: number\n\n @Column({ ...amount, default: 0 })\n @Field(type => Float, { description: 'Supply plus tax' })\n totalAmount: number\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'How the tax was rounded, written when the document went out' })\n taxRounding?: string\n\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'Decimal places of the currency, written when the document went out' })\n currencyDigits?: number\n}\n\n@ObjectType({ description: 'One line of a commercial document' })\nexport abstract class CommercialLine {\n @Column({ type: 'int' })\n @Field(type => Int, { description: 'Order of the line on the document' })\n position: number\n\n @Column()\n @Field({ description: 'What the line charges for' })\n description: string\n\n @Column(amount)\n @Field(type => Float, { description: 'Quantity' })\n quantity: number\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Unit the quantity counts in, as printed - pieces, a lot, man-months' })\n unit?: string\n\n @Column(amount)\n @Field(type => Float, { description: 'Price of one unit' })\n unitPrice: number\n\n @Column(amount)\n @Field(type => Float, { description: 'Discount taken off the line, in the document currency' })\n discount: number\n\n @Column({ default: TaxKind.TAXABLE })\n @Field(type => TaxKind, { description: 'Taxable, zero-rated or exempt' })\n taxKind: TaxKind\n\n @Column(amount)\n @Field(type => Float, { description: 'Tax rate in percent; zero unless taxable' })\n taxRate: number\n\n @Column(amount)\n @Field(type => Float, { description: 'Quantity times price, less the discount' })\n supplyAmount: number\n}\n\n@ObjectType({ description: 'The supply and tax of one tax kind and rate on a document' })\nexport class DocumentTaxGroup {\n @Field(type => TaxKind, { description: 'Taxable, zero-rated or exempt' })\n taxKind: string\n\n @Field(type => Float, { description: 'Tax rate in percent' })\n taxRate: number\n\n @Field(type => Float, { description: 'Supply of the lines in the group' })\n supplyAmount: number\n\n @Field(type => Float, { description: 'Tax of the group' })\n taxAmount: number\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type PrintedDocument, type PrintLanguage } from '@things-factory/invoice/dist-shared/document-print.js';
|
|
2
|
+
import { DocumentFile, DocumentFileFormat } from './invoice-type';
|
|
3
|
+
/** The words of a print in `language` - one the print has words for; any other is refused, not printed half in another. */
|
|
4
|
+
export declare function printTranslator(context: any, language: unknown): {
|
|
5
|
+
language: PrintLanguage;
|
|
6
|
+
t: (key: string) => string;
|
|
7
|
+
};
|
|
8
|
+
/** A saved document with what its print shows beside its lines - the groups and the lines' supply before the discount. */
|
|
9
|
+
export declare function printedDocumentOf(document: PrintedDocument): PrintedDocument;
|
|
10
|
+
/** The document in `format`: the page as it is, or printed to PDF on A4. Named 「청구서-INV-2026-001.pdf」 in the print's words. */
|
|
11
|
+
export declare function documentFileOf(document: PrintedDocument, format: DocumentFileFormat, print: {
|
|
12
|
+
language: PrintLanguage;
|
|
13
|
+
t: (key: string) => string;
|
|
14
|
+
title: string;
|
|
15
|
+
terms: {
|
|
16
|
+
label: string;
|
|
17
|
+
value?: string | null;
|
|
18
|
+
}[];
|
|
19
|
+
numberLabel?: string;
|
|
20
|
+
}): Promise<DocumentFile>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printTranslator = printTranslator;
|
|
4
|
+
exports.printedDocumentOf = printedDocumentOf;
|
|
5
|
+
exports.documentFileOf = documentFileOf;
|
|
6
|
+
const pdf_1 = require("@things-factory/pdf");
|
|
7
|
+
const shell_1 = require("@things-factory/shell");
|
|
8
|
+
const document_print_js_1 = require("@things-factory/invoice/dist-shared/document-print.js");
|
|
9
|
+
const invoice_rules_1 = require("./invoice-rules");
|
|
10
|
+
const invoice_type_1 = require("./invoice-type");
|
|
11
|
+
/*
|
|
12
|
+
* A stored commercial document as the file its reader gets (ADR-0170 decision 10) - the page the preview shows and
|
|
13
|
+
* the browser prints, or the PDF to download - from the one layout `shared/document-print.ts`. The invoice and the
|
|
14
|
+
* quotation doors read their document and hand it here with their kind's words.
|
|
15
|
+
*/
|
|
16
|
+
/** The words of a print in `language` - one the print has words for; any other is refused, not printed half in another. */
|
|
17
|
+
function printTranslator(context, language) {
|
|
18
|
+
const lng = (0, document_print_js_1.printLanguageOf)(language);
|
|
19
|
+
if (!lng)
|
|
20
|
+
throw new shell_1.Refusal('invoice-print-language-invalid', {}, 'Print in Korean or English');
|
|
21
|
+
const fixed = context.i18next.getFixedT(lng);
|
|
22
|
+
return { language: lng, t: (key) => String(fixed(key)) };
|
|
23
|
+
}
|
|
24
|
+
/** A saved document with what its print shows beside its lines - the groups and the lines' supply before the discount. */
|
|
25
|
+
function printedDocumentOf(document) {
|
|
26
|
+
return { ...document, taxGroups: (0, invoice_rules_1.taxGroupsOf)(document), lineSupplyAmount: (0, invoice_rules_1.lineSupplyOf)(document) };
|
|
27
|
+
}
|
|
28
|
+
/** The document in `format`: the page as it is, or printed to PDF on A4. Named 「청구서-INV-2026-001.pdf」 in the print's words. */
|
|
29
|
+
async function documentFileOf(document, format, print) {
|
|
30
|
+
const printed = printedDocumentOf(document);
|
|
31
|
+
if (format === invoice_type_1.DocumentFileFormat.PDF) {
|
|
32
|
+
const body = `<style>${document_print_js_1.DOCUMENT_CSS}</style>${(0, document_print_js_1.documentBody)(printed, print)}`;
|
|
33
|
+
const pdf = await new pdf_1.PdfService().generatePdf({ content_template: '{{{body}}}', page_size: 'A4' }, { body });
|
|
34
|
+
return { fileName: (0, document_print_js_1.documentFileName)(print.title, document.number, 'pdf'), mimeType: 'application/pdf', content: pdf.toString('base64') };
|
|
35
|
+
}
|
|
36
|
+
return { fileName: (0, document_print_js_1.documentFileName)(print.title, document.number, 'html'), mimeType: 'text/html', content: Buffer.from((0, document_print_js_1.documentHtml)(printed, print), 'utf8').toString('base64') };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=invoice-document-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invoice-document-file.js","sourceRoot":"","sources":["../../../server/service/invoice/invoice-document-file.ts"],"names":[],"mappings":";;AAsBA,0CAKC;AAGD,8CAEC;AAGD,wCAYC;AA/CD,6CAAgD;AAChD,iDAA+C;AAC/C,6FAQ8D;AAE9D,mDAA2D;AAC3D,iDAAiE;AAEjE;;;;GAIG;AAEH,2HAA2H;AAC3H,SAAgB,eAAe,CAAC,OAAY,EAAE,QAAiB;IAC7D,MAAM,GAAG,GAAG,IAAA,mCAAe,EAAC,QAAQ,CAAC,CAAA;IACrC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,eAAO,CAAC,gCAAgC,EAAE,EAAE,EAAE,4BAA4B,CAAC,CAAA;IAC/F,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;IAC5C,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAA;AAClE,CAAC;AAED,0HAA0H;AAC1H,SAAgB,iBAAiB,CAAC,QAAyB;IACzD,OAAO,EAAE,GAAG,QAAQ,EAAE,SAAS,EAAE,IAAA,2BAAW,EAAC,QAAe,CAAC,EAAE,gBAAgB,EAAE,IAAA,4BAAY,EAAC,QAAe,CAAC,EAAE,CAAA;AAClH,CAAC;AAED,+HAA+H;AACxH,KAAK,UAAU,cAAc,CAClC,QAAyB,EACzB,MAA0B,EAC1B,KAAsJ;IAEtJ,MAAM,OAAO,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAA;IAC3C,IAAI,MAAM,KAAK,iCAAkB,CAAC,GAAG,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,UAAU,gCAAY,WAAW,IAAA,gCAAY,EAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAA;QAC5E,MAAM,GAAG,GAAG,MAAM,IAAI,gBAAU,EAAE,CAAC,WAAW,CAAC,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;QAC7G,OAAO,EAAE,QAAQ,EAAE,IAAA,oCAAgB,EAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAA;IAC1I,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,IAAA,oCAAgB,EAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,IAAA,gCAAY,EAAC,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAA;AACnL,CAAC","sourcesContent":["import { PdfService } from '@things-factory/pdf'\nimport { Refusal } from '@things-factory/shell'\nimport {\n documentBody,\n documentFileName,\n documentHtml,\n DOCUMENT_CSS,\n printLanguageOf,\n type PrintedDocument,\n type PrintLanguage\n} from '@things-factory/invoice/dist-shared/document-print.js'\n\nimport { lineSupplyOf, taxGroupsOf } from './invoice-rules'\nimport { DocumentFile, DocumentFileFormat } from './invoice-type'\n\n/*\n * A stored commercial document as the file its reader gets (ADR-0170 decision 10) - the page the preview shows and\n * the browser prints, or the PDF to download - from the one layout `shared/document-print.ts`. The invoice and the\n * quotation doors read their document and hand it here with their kind's words.\n */\n\n/** The words of a print in `language` - one the print has words for; any other is refused, not printed half in another. */\nexport function printTranslator(context: any, language: unknown): { language: PrintLanguage; t: (key: string) => string } {\n const lng = printLanguageOf(language)\n if (!lng) throw new Refusal('invoice-print-language-invalid', {}, 'Print in Korean or English')\n const fixed = context.i18next.getFixedT(lng)\n return { language: lng, t: (key: string) => String(fixed(key)) }\n}\n\n/** A saved document with what its print shows beside its lines - the groups and the lines' supply before the discount. */\nexport function printedDocumentOf(document: PrintedDocument): PrintedDocument {\n return { ...document, taxGroups: taxGroupsOf(document as any), lineSupplyAmount: lineSupplyOf(document as any) }\n}\n\n/** The document in `format`: the page as it is, or printed to PDF on A4. Named 「청구서-INV-2026-001.pdf」 in the print's words. */\nexport async function documentFileOf(\n document: PrintedDocument,\n format: DocumentFileFormat,\n print: { language: PrintLanguage; t: (key: string) => string; title: string; terms: { label: string; value?: string | null }[]; numberLabel?: string }\n): Promise<DocumentFile> {\n const printed = printedDocumentOf(document)\n if (format === DocumentFileFormat.PDF) {\n const body = `<style>${DOCUMENT_CSS}</style>${documentBody(printed, print)}`\n const pdf = await new PdfService().generatePdf({ content_template: '{{{body}}}', page_size: 'A4' }, { body })\n return { fileName: documentFileName(print.title, document.number, 'pdf'), mimeType: 'application/pdf', content: pdf.toString('base64') }\n }\n return { fileName: documentFileName(print.title, document.number, 'html'), mimeType: 'text/html', content: Buffer.from(documentHtml(printed, print), 'utf8').toString('base64') }\n}\n"]}
|
|
@@ -5,13 +5,15 @@ import { Domain, ListParam } from '@things-factory/shell';
|
|
|
5
5
|
import { InvoiceEvent } from '../invoice-event/invoice-event';
|
|
6
6
|
import { InvoiceItem } from '../invoice-item/invoice-item';
|
|
7
7
|
import { Invoice } from './invoice';
|
|
8
|
-
import { InvoiceList } from './invoice-type';
|
|
8
|
+
import { DocumentFile, DocumentFileFormat, InvoiceList } from './invoice-type';
|
|
9
9
|
export declare class InvoiceQuery {
|
|
10
10
|
invoice(id: string, context: ResolverContext): Promise<Invoice>;
|
|
11
11
|
invoices(params: ListParam, context: ResolverContext): Promise<InvoiceList>;
|
|
12
12
|
invoiceEvents(invoiceId: string, context: ResolverContext): Promise<InvoiceEvent[]>;
|
|
13
|
+
invoiceDocumentFile(id: string, format: DocumentFileFormat, language: string, context: ResolverContext): Promise<DocumentFile>;
|
|
13
14
|
items(invoice: Invoice): Promise<InvoiceItem[]>;
|
|
14
15
|
taxGroups(invoice: Invoice): Promise<import("./invoice-rules").TaxGroup[]>;
|
|
16
|
+
lineSupplyAmount(invoice: Invoice): Promise<number>;
|
|
15
17
|
files(row: Invoice): Promise<Attachment[]>;
|
|
16
18
|
counterparty(row: Invoice): Counterparty | Promise<Counterparty>;
|
|
17
19
|
domain(row: Invoice): Domain | Promise<Domain>;
|
|
@@ -11,8 +11,10 @@ const invoice_event_1 = require("../invoice-event/invoice-event");
|
|
|
11
11
|
const invoice_item_1 = require("../invoice-item/invoice-item");
|
|
12
12
|
const commercial_document_1 = require("./commercial-document");
|
|
13
13
|
const invoice_1 = require("./invoice");
|
|
14
|
+
const invoice_document_file_1 = require("./invoice-document-file");
|
|
14
15
|
const invoice_files_1 = require("./invoice-files");
|
|
15
16
|
const invoice_rules_1 = require("./invoice-rules");
|
|
17
|
+
const invoice_service_1 = require("./invoice-service");
|
|
16
18
|
const invoice_type_1 = require("./invoice-type");
|
|
17
19
|
let InvoiceQuery = class InvoiceQuery {
|
|
18
20
|
invoice(id, context) {
|
|
@@ -34,12 +36,25 @@ let InvoiceQuery = class InvoiceQuery {
|
|
|
34
36
|
order: { createdAt: 'ASC' }
|
|
35
37
|
});
|
|
36
38
|
}
|
|
39
|
+
/* The invoice as its buyer reads it (ADR-0170 decision 10) - the page to preview and print, or the PDF to download. */
|
|
40
|
+
async invoiceDocumentFile(id, format, language, context) {
|
|
41
|
+
const invoice = await (0, invoice_service_1.invoiceInDomain)((0, shell_1.getRepository)(invoice_1.Invoice).manager, context.state.domain, id);
|
|
42
|
+
const print = (0, invoice_document_file_1.printTranslator)(context, language || invoice.language);
|
|
43
|
+
return (0, invoice_document_file_1.documentFileOf)(invoice, format, {
|
|
44
|
+
...print,
|
|
45
|
+
title: print.t('title.invoice-print'),
|
|
46
|
+
terms: [{ label: print.t('field.invoice-due-date'), value: invoice.dueDate }]
|
|
47
|
+
});
|
|
48
|
+
}
|
|
37
49
|
async items(invoice) {
|
|
38
50
|
return invoice.items ?? (0, shell_1.getRepository)(invoice_item_1.InvoiceItem).find({ where: { invoice: { id: invoice.id } }, order: { position: 'ASC' } });
|
|
39
51
|
}
|
|
40
52
|
async taxGroups(invoice) {
|
|
41
53
|
return (0, invoice_rules_1.taxGroupsOf)({ ...invoice, items: await this.items(invoice) });
|
|
42
54
|
}
|
|
55
|
+
async lineSupplyAmount(invoice) {
|
|
56
|
+
return (0, invoice_rules_1.lineSupplyOf)({ ...invoice, items: await this.items(invoice) });
|
|
57
|
+
}
|
|
43
58
|
files(row) {
|
|
44
59
|
return (0, invoice_files_1.filesOf)((0, shell_1.getRepository)(invoice_1.Invoice).manager, { id: row.domainId }, row.id);
|
|
45
60
|
}
|
|
@@ -84,6 +99,17 @@ tslib_1.__decorate([
|
|
|
84
99
|
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
85
100
|
tslib_1.__metadata("design:returntype", void 0)
|
|
86
101
|
], InvoiceQuery.prototype, "invoiceEvents", null);
|
|
102
|
+
tslib_1.__decorate([
|
|
103
|
+
(0, type_graphql_1.Directive)('@privilege(category: "invoice", privilege: "query", domainOwnerGranted: true)'),
|
|
104
|
+
(0, type_graphql_1.Query)(type => invoice_type_1.DocumentFile, { description: 'Prints an invoice as an HTML page or a PDF, in Korean or English - its own language when none is named' }),
|
|
105
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('id')),
|
|
106
|
+
tslib_1.__param(1, (0, type_graphql_1.Arg)('format', type => invoice_type_1.DocumentFileFormat, { description: 'An HTML page or a PDF' })),
|
|
107
|
+
tslib_1.__param(2, (0, type_graphql_1.Arg)('language', { nullable: true, description: 'The language of the print, ko or en - the invoice\'s own when empty' })),
|
|
108
|
+
tslib_1.__param(3, (0, type_graphql_1.Ctx)()),
|
|
109
|
+
tslib_1.__metadata("design:type", Function),
|
|
110
|
+
tslib_1.__metadata("design:paramtypes", [String, String, String, Object]),
|
|
111
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
112
|
+
], InvoiceQuery.prototype, "invoiceDocumentFile", null);
|
|
87
113
|
tslib_1.__decorate([
|
|
88
114
|
(0, type_graphql_1.FieldResolver)(type => [invoice_item_1.InvoiceItem]),
|
|
89
115
|
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
@@ -98,6 +124,13 @@ tslib_1.__decorate([
|
|
|
98
124
|
tslib_1.__metadata("design:paramtypes", [invoice_1.Invoice]),
|
|
99
125
|
tslib_1.__metadata("design:returntype", Promise)
|
|
100
126
|
], InvoiceQuery.prototype, "taxGroups", null);
|
|
127
|
+
tslib_1.__decorate([
|
|
128
|
+
(0, type_graphql_1.FieldResolver)(type => type_graphql_1.Float, { description: "The lines' supply before the discount of the whole document" }),
|
|
129
|
+
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
130
|
+
tslib_1.__metadata("design:type", Function),
|
|
131
|
+
tslib_1.__metadata("design:paramtypes", [invoice_1.Invoice]),
|
|
132
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
133
|
+
], InvoiceQuery.prototype, "lineSupplyAmount", null);
|
|
101
134
|
tslib_1.__decorate([
|
|
102
135
|
(0, type_graphql_1.FieldResolver)(type => [attachment_base_1.Attachment], { description: 'The files the invoice keeps, the first attached first' }),
|
|
103
136
|
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoice-query.js","sourceRoot":"","sources":["../../../server/service/invoice/invoice-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Attachment } from '@things-factory/attachment-base'\nimport { User } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam, SpeakRefusals } from '@things-factory/shell'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\n\nimport { InvoiceEvent } from '../invoice-event/invoice-event'\nimport { InvoiceItem } from '../invoice-item/invoice-item'\nimport { DocumentTaxGroup } from './commercial-document'\nimport { Invoice } from './invoice'\nimport { filesOf } from './invoice-files'\nimport { taxGroupsOf } from './invoice-rules'\nimport { InvoiceList } from './invoice-type'\n\n@UseMiddleware(SpeakRefusals)\n@Resolver(Invoice)\nexport class InvoiceQuery {\n @Directive('@privilege(category: \"invoice\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => Invoice, { nullable: true, description: 'To fetch an invoice' })\n invoice(@Arg('id') id: string, @Ctx() context: ResolverContext) {\n return getRepository(Invoice).findOne({ where: { id, domain: { id: context.state.domain.id } } })\n }\n\n @Directive('@privilege(category: \"invoice\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => InvoiceList, { description: 'To fetch a page of invoices' })\n async invoices(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<InvoiceList> {\n const query = getQueryBuilderFromListParams({\n domain: context.state.domain,\n params,\n repository: getRepository(Invoice),\n searchables: ['number', 'supplierName', 'buyerName', 'purchaseOrder', 'notes']\n })\n const [items, total] = await query.getManyAndCount()\n return { items, total }\n }\n\n @Directive('@privilege(category: \"invoice\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [InvoiceEvent], { description: 'To fetch what happened to an invoice, oldest first' })\n invoiceEvents(@Arg('invoiceId') invoiceId: string, @Ctx() context: ResolverContext) {\n return getRepository(InvoiceEvent).find({\n where: { domain: { id: context.state.domain.id }, invoice: { id: invoiceId } },\n order: { createdAt: 'ASC' }\n })\n }\n\n @FieldResolver(type => [InvoiceItem])\n async items(@Root() invoice: Invoice) {\n return invoice.items ?? getRepository(InvoiceItem).find({ where: { invoice: { id: invoice.id } }, order: { position: 'ASC' } })\n }\n\n @FieldResolver(type => [DocumentTaxGroup])\n async taxGroups(@Root() invoice: Invoice) {\n return taxGroupsOf({ ...invoice, items: await this.items(invoice) })\n }\n\n @FieldResolver(type => [Attachment], { description: 'The files the invoice keeps, the first attached first' })\n files(@Root() row: Invoice) {\n return filesOf(getRepository(Invoice).manager, { id: row.domainId! }, row.id)\n }\n\n @FieldResolver(type => Counterparty, { nullable: true })\n counterparty(@Root() row: Invoice) {\n return row.counterparty ?? (row.counterpartyId ? getRepository(Counterparty).findOneBy({ id: row.counterpartyId }) : undefined)\n }\n\n @FieldResolver(type => Domain, { nullable: true })\n domain(@Root() row: Invoice) {\n return row.domain ?? (row.domainId ? getRepository(Domain).findOneBy({ id: row.domainId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n creator(@Root() row: Invoice) {\n return row.creator ?? (row.creatorId ? getRepository(User).findOneBy({ id: row.creatorId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n updater(@Root() row: Invoice) {\n return row.updater ?? (row.updaterId ? getRepository(User).findOneBy({ id: row.updaterId }) : undefined)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"invoice-query.js","sourceRoot":"","sources":["../../../server/service/invoice/invoice-query.ts"],"names":[],"mappings":";;;;AAAA,qEAA4D;AAC5D,yDAAgD;AAChD,uDAAuD;AACvD,iDAAsH;AACtH,+CAAoH;AAEpH,kEAA6D;AAC7D,+DAA0D;AAC1D,+DAAwD;AACxD,uCAAmC;AACnC,mEAAyE;AACzE,mDAAyC;AACzC,mDAA2D;AAC3D,uDAAmD;AACnD,iDAA8E;AAIvE,IAAM,YAAY,GAAlB,MAAM,YAAY;IAGvB,OAAO,CAAY,EAAU,EAAS,OAAwB;QAC5D,OAAO,IAAA,qBAAa,EAAC,iBAAO,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;IACnG,CAAC;IAIK,AAAN,KAAK,CAAC,QAAQ,CAA0B,MAAiB,EAAS,OAAwB;QACxF,MAAM,KAAK,GAAG,IAAA,qCAA6B,EAAC;YAC1C,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM;YAC5B,MAAM;YACN,UAAU,EAAE,IAAA,qBAAa,EAAC,iBAAO,CAAC;YAClC,WAAW,EAAE,CAAC,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,CAAC;SAC/E,CAAC,CAAA;QACF,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE,CAAA;QACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IACzB,CAAC;IAID,aAAa,CAAmB,SAAiB,EAAS,OAAwB;QAChF,OAAO,IAAA,qBAAa,EAAC,4BAAY,CAAC,CAAC,IAAI,CAAC;YACtC,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;YAC9E,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;SAC5B,CAAC,CAAA;IACJ,CAAC;IAED,uHAAuH;IAGjH,AAAN,KAAK,CAAC,mBAAmB,CACZ,EAAU,EACgE,MAA0B,EACU,QAAgB,EAClI,OAAwB;QAE/B,MAAM,OAAO,GAAG,MAAM,IAAA,iCAAe,EAAC,IAAA,qBAAa,EAAC,iBAAO,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;QAC/F,MAAM,KAAK,GAAG,IAAA,uCAAe,EAAC,OAAO,EAAE,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAA;QACpE,OAAO,IAAA,sCAAc,EAAC,OAAO,EAAE,MAAM,EAAE;YACrC,GAAG,KAAK;YACR,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,qBAAqB,CAAC;YACrC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,wBAAwB,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;SAC9E,CAAC,CAAA;IACJ,CAAC;IAGK,AAAN,KAAK,CAAC,KAAK,CAAS,OAAgB;QAClC,OAAO,OAAO,CAAC,KAAK,IAAI,IAAA,qBAAa,EAAC,0BAAW,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;IACjI,CAAC;IAGK,AAAN,KAAK,CAAC,SAAS,CAAS,OAAgB;QACtC,OAAO,IAAA,2BAAW,EAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACtE,CAAC;IAGK,AAAN,KAAK,CAAC,gBAAgB,CAAS,OAAgB;QAC7C,OAAO,IAAA,4BAAY,EAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACvE,CAAC;IAGD,KAAK,CAAS,GAAY;QACxB,OAAO,IAAA,uBAAO,EAAC,IAAA,qBAAa,EAAC,iBAAO,CAAC,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,QAAS,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAA;IAC/E,CAAC;IAGD,YAAY,CAAS,GAAY;QAC/B,OAAO,GAAG,CAAC,YAAY,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,IAAA,qBAAa,EAAC,uBAAY,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IACjI,CAAC;IAGD,MAAM,CAAS,GAAY;QACzB,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAA,qBAAa,EAAC,cAAM,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IACzG,CAAC;IAGD,OAAO,CAAS,GAAY;QAC1B,OAAO,GAAG,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAA,qBAAa,EAAC,gBAAI,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAC1G,CAAC;IAGD,OAAO,CAAS,GAAY;QAC1B,OAAO,GAAG,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAA,qBAAa,EAAC,gBAAI,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAC1G,CAAC;CACF,CAAA;AAtFY,oCAAY;AAGvB;IAFC,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC;IACtE,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;2CAEpC;AAIK;IAFL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,0BAAW,EAAE,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;IAC3D,mBAAA,IAAA,mBAAI,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAjB,iBAAS;;4CASxD;AAID;IAFC,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,4BAAY,CAAC,EAAE,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC;IACtF,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;iDAKxD;AAKK;IAFL,IAAA,wBAAS,EAAC,+EAA+E,CAAC;IAC1F,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,2BAAY,EAAE,EAAE,WAAW,EAAE,wGAAwG,EAAE,CAAC;IAEpJ,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IACT,mBAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,iCAAkB,EAAE,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC,CAAA;IACnF,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qEAAqE,EAAE,CAAC,CAAA;IACvH,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;uDASP;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,0BAAW,CAAC,CAAC;IACxB,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAU,iBAAO;;yCAEnC;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,sCAAgB,CAAC,CAAC;IACzB,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAU,iBAAO;;6CAEvC;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAK,EAAE,EAAE,WAAW,EAAE,6DAA6D,EAAE,CAAC;IACrF,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAU,iBAAO;;oDAE9C;AAGD;IADC,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,4BAAU,CAAC,EAAE,EAAE,WAAW,EAAE,uDAAuD,EAAE,CAAC;IACvG,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAM,iBAAO;;yCAEzB;AAGD;IADC,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,uBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAM,iBAAO;;gDAEhC;AAGD;IADC,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAM,iBAAO;;0CAE1B;AAGD;IADC,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvC,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAM,iBAAO;;2CAE3B;AAGD;IADC,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvC,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAM,iBAAO;;2CAE3B;uBArFU,YAAY;IAFxB,IAAA,4BAAa,EAAC,qBAAa,CAAC;IAC5B,IAAA,uBAAQ,EAAC,iBAAO,CAAC;GACL,YAAY,CAsFxB","sourcesContent":["import { Attachment } from '@things-factory/attachment-base'\nimport { User } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam, SpeakRefusals } from '@things-factory/shell'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Float, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\n\nimport { InvoiceEvent } from '../invoice-event/invoice-event'\nimport { InvoiceItem } from '../invoice-item/invoice-item'\nimport { DocumentTaxGroup } from './commercial-document'\nimport { Invoice } from './invoice'\nimport { documentFileOf, printTranslator } from './invoice-document-file'\nimport { filesOf } from './invoice-files'\nimport { lineSupplyOf, taxGroupsOf } from './invoice-rules'\nimport { invoiceInDomain } from './invoice-service'\nimport { DocumentFile, DocumentFileFormat, InvoiceList } from './invoice-type'\n\n@UseMiddleware(SpeakRefusals)\n@Resolver(Invoice)\nexport class InvoiceQuery {\n @Directive('@privilege(category: \"invoice\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => Invoice, { nullable: true, description: 'To fetch an invoice' })\n invoice(@Arg('id') id: string, @Ctx() context: ResolverContext) {\n return getRepository(Invoice).findOne({ where: { id, domain: { id: context.state.domain.id } } })\n }\n\n @Directive('@privilege(category: \"invoice\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => InvoiceList, { description: 'To fetch a page of invoices' })\n async invoices(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<InvoiceList> {\n const query = getQueryBuilderFromListParams({\n domain: context.state.domain,\n params,\n repository: getRepository(Invoice),\n searchables: ['number', 'supplierName', 'buyerName', 'purchaseOrder', 'notes']\n })\n const [items, total] = await query.getManyAndCount()\n return { items, total }\n }\n\n @Directive('@privilege(category: \"invoice\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [InvoiceEvent], { description: 'To fetch what happened to an invoice, oldest first' })\n invoiceEvents(@Arg('invoiceId') invoiceId: string, @Ctx() context: ResolverContext) {\n return getRepository(InvoiceEvent).find({\n where: { domain: { id: context.state.domain.id }, invoice: { id: invoiceId } },\n order: { createdAt: 'ASC' }\n })\n }\n\n /* The invoice as its buyer reads it (ADR-0170 decision 10) - the page to preview and print, or the PDF to download. */\n @Directive('@privilege(category: \"invoice\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => DocumentFile, { description: 'Prints an invoice as an HTML page or a PDF, in Korean or English - its own language when none is named' })\n async invoiceDocumentFile(\n @Arg('id') id: string,\n @Arg('format', type => DocumentFileFormat, { description: 'An HTML page or a PDF' }) format: DocumentFileFormat,\n @Arg('language', { nullable: true, description: 'The language of the print, ko or en - the invoice\\'s own when empty' }) language: string,\n @Ctx() context: ResolverContext\n ): Promise<DocumentFile> {\n const invoice = await invoiceInDomain(getRepository(Invoice).manager, context.state.domain, id)\n const print = printTranslator(context, language || invoice.language)\n return documentFileOf(invoice, format, {\n ...print,\n title: print.t('title.invoice-print'),\n terms: [{ label: print.t('field.invoice-due-date'), value: invoice.dueDate }]\n })\n }\n\n @FieldResolver(type => [InvoiceItem])\n async items(@Root() invoice: Invoice) {\n return invoice.items ?? getRepository(InvoiceItem).find({ where: { invoice: { id: invoice.id } }, order: { position: 'ASC' } })\n }\n\n @FieldResolver(type => [DocumentTaxGroup])\n async taxGroups(@Root() invoice: Invoice) {\n return taxGroupsOf({ ...invoice, items: await this.items(invoice) })\n }\n\n @FieldResolver(type => Float, { description: \"The lines' supply before the discount of the whole document\" })\n async lineSupplyAmount(@Root() invoice: Invoice) {\n return lineSupplyOf({ ...invoice, items: await this.items(invoice) })\n }\n\n @FieldResolver(type => [Attachment], { description: 'The files the invoice keeps, the first attached first' })\n files(@Root() row: Invoice) {\n return filesOf(getRepository(Invoice).manager, { id: row.domainId! }, row.id)\n }\n\n @FieldResolver(type => Counterparty, { nullable: true })\n counterparty(@Root() row: Invoice) {\n return row.counterparty ?? (row.counterpartyId ? getRepository(Counterparty).findOneBy({ id: row.counterpartyId }) : undefined)\n }\n\n @FieldResolver(type => Domain, { nullable: true })\n domain(@Root() row: Invoice) {\n return row.domain ?? (row.domainId ? getRepository(Domain).findOneBy({ id: row.domainId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n creator(@Root() row: Invoice) {\n return row.creator ?? (row.creatorId ? getRepository(User).findOneBy({ id: row.creatorId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n updater(@Root() row: Invoice) {\n return row.updater ?? (row.updaterId ? getRepository(User).findOneBy({ id: row.updaterId }) : undefined)\n }\n}\n"]}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type TaxGroup } from '@things-factory/invoice/dist-shared/document-amounts.js';
|
|
2
|
+
export type { TaxGroup };
|
|
1
3
|
export declare enum TaxKind {
|
|
2
4
|
TAXABLE = "TAXABLE",
|
|
3
5
|
ZERO_RATED = "ZERO_RATED",
|
|
@@ -9,6 +11,8 @@ export declare const MAX_LINES = 500;
|
|
|
9
11
|
export interface LineValues {
|
|
10
12
|
description: string;
|
|
11
13
|
quantity: number;
|
|
14
|
+
/** The unit the quantity counts in, as printed - no arithmetic reads it. */
|
|
15
|
+
unit?: string | null;
|
|
12
16
|
unitPrice: number;
|
|
13
17
|
discount: number;
|
|
14
18
|
taxKind: TaxKind | string;
|
|
@@ -16,23 +20,20 @@ export interface LineValues {
|
|
|
16
20
|
/** The tax the sender printed on the line — only on a received document. */
|
|
17
21
|
receivedTaxAmount?: number | null;
|
|
18
22
|
}
|
|
19
|
-
export interface TaxGroup {
|
|
20
|
-
taxKind: string;
|
|
21
|
-
taxRate: number;
|
|
22
|
-
supplyAmount: number;
|
|
23
|
-
taxAmount: number;
|
|
24
|
-
}
|
|
25
23
|
export declare function validDay(day: string): boolean;
|
|
26
24
|
/** Decimal places of a document's currency (`shared/currency-digits.ts`); a code the platform does not know is refused. */
|
|
27
25
|
export declare function currencyDigits(currency: string): number;
|
|
28
26
|
/**
|
|
29
|
-
* The supply of each line, the tax of each (kind, rate) group and the document totals
|
|
27
|
+
* The supply of each line, the tax of each (kind, rate) group and the document totals - reckoned by
|
|
28
|
+
* `shared/document-amounts.ts`, the screens' own arithmetic; here a line that cannot be reckoned refuses the document,
|
|
29
|
+
* and so does a discount of the whole document below zero or past the lines' supply. `received` keeps the line tax
|
|
30
30
|
* the sender printed; otherwise lines carry no tax and the groups reckon it.
|
|
31
31
|
*/
|
|
32
|
-
export declare function calculateDocument(lines: readonly LineValues[], currency: string, received?: boolean): {
|
|
32
|
+
export declare function calculateDocument(lines: readonly LineValues[], currency: string, received?: boolean, discountAmount?: number | null): {
|
|
33
33
|
items: {
|
|
34
34
|
description: any;
|
|
35
35
|
quantity: any;
|
|
36
|
+
unit: any;
|
|
36
37
|
unitPrice: any;
|
|
37
38
|
discount: any;
|
|
38
39
|
taxKind: TaxKind;
|
|
@@ -42,16 +43,22 @@ export declare function calculateDocument(lines: readonly LineValues[], currency
|
|
|
42
43
|
position: number;
|
|
43
44
|
}[];
|
|
44
45
|
taxGroups: TaxGroup[];
|
|
46
|
+
lineSupplyAmount: number;
|
|
47
|
+
discountAmount: number;
|
|
45
48
|
supplyAmount: number;
|
|
46
49
|
taxAmount: number;
|
|
47
50
|
totalAmount: number;
|
|
48
51
|
};
|
|
49
|
-
|
|
50
|
-
export declare function taxGroupsOf(document: {
|
|
52
|
+
type SavedDocument = {
|
|
51
53
|
currency: string;
|
|
52
54
|
taxBasis?: string;
|
|
55
|
+
discountAmount?: number | null;
|
|
53
56
|
items?: readonly LineValues[];
|
|
54
|
-
}
|
|
57
|
+
};
|
|
58
|
+
/** The groups of a saved document, read back from its lines and its discount. */
|
|
59
|
+
export declare function taxGroupsOf(document: SavedDocument): TaxGroup[];
|
|
60
|
+
/** The lines' supply of a saved document before its discount, read back from its lines. */
|
|
61
|
+
export declare function lineSupplyOf(document: SavedDocument): number;
|
|
55
62
|
/** The fields every commercial document prints in its head, in the order the snapshot keeps them. */
|
|
56
63
|
export declare const HEADER_KEYS: readonly ["number", "direction", "currency", "language", "supplierName", "buyerName", "issueDate", "supplierAddress", "supplierRegistrationNumber", "buyerAddress", "buyerRegistrationNumber", "purchaseOrder", "paymentTerms", "bankDetails", "notes", "sourceReference"];
|
|
57
64
|
/** The head of a document, trimmed and checked. */
|
|
@@ -5,20 +5,13 @@ exports.validDay = validDay;
|
|
|
5
5
|
exports.currencyDigits = currencyDigits;
|
|
6
6
|
exports.calculateDocument = calculateDocument;
|
|
7
7
|
exports.taxGroupsOf = taxGroupsOf;
|
|
8
|
+
exports.lineSupplyOf = lineSupplyOf;
|
|
8
9
|
exports.normalizedHeader = normalizedHeader;
|
|
9
10
|
exports.requireDraft = requireDraft;
|
|
10
11
|
exports.settlement = settlement;
|
|
11
12
|
const currency_digits_js_1 = require("@things-factory/invoice/dist-shared/currency-digits.js");
|
|
13
|
+
const document_amounts_js_1 = require("@things-factory/invoice/dist-shared/document-amounts.js");
|
|
12
14
|
const shell_1 = require("@things-factory/shell");
|
|
13
|
-
/*
|
|
14
|
-
* Invoice arithmetic and lifecycle rules, shared by the invoice and quotation services and their tests (ADR-0120
|
|
15
|
-
* decision 4).
|
|
16
|
-
*
|
|
17
|
-
* - Lines carry supply and a tax kind and rate; the tax is reckoned once per (kind, rate) group: the group's supply
|
|
18
|
-
* total × rate ÷ 100, rounded half-up to the currency's minor unit. The document tax is the sum of the groups.
|
|
19
|
-
* - Everything is counted in integers: quantities, prices and rates to four decimals, money in minor units.
|
|
20
|
-
* - A received document keeps the tax its sender printed on each line; nothing recomputes it.
|
|
21
|
-
*/
|
|
22
15
|
var TaxKind;
|
|
23
16
|
(function (TaxKind) {
|
|
24
17
|
TaxKind["TAXABLE"] = "TAXABLE";
|
|
@@ -28,8 +21,6 @@ var TaxKind;
|
|
|
28
21
|
/** How a document's tax was rounded — written onto the document when it is issued or sent. */
|
|
29
22
|
exports.TAX_ROUNDING = 'HALF_UP';
|
|
30
23
|
exports.MAX_LINES = 500;
|
|
31
|
-
const MAX_MINOR = 10n ** 14n;
|
|
32
|
-
const SCALE = 10000n;
|
|
33
24
|
function validDay(day) {
|
|
34
25
|
return /^\d{4}-\d{2}-\d{2}$/.test(day) && !Number.isNaN(Date.parse(day)) && new Date(day).toISOString().slice(0, 10) === day;
|
|
35
26
|
}
|
|
@@ -40,108 +31,70 @@ function currencyDigits(currency) {
|
|
|
40
31
|
throw new shell_1.Refusal('invoice-currency-invalid', {}, 'Use a three-letter currency code');
|
|
41
32
|
return digits;
|
|
42
33
|
}
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
function minorOf(value, digits) {
|
|
55
|
-
const factor = 10 ** digits;
|
|
56
|
-
const whole = Math.round(value * factor);
|
|
57
|
-
return Math.abs(value * factor - whole) > 1e-6 ? undefined : BigInt(whole);
|
|
58
|
-
}
|
|
59
|
-
const amountOf = (minor, digits) => Number(minor) / 10 ** digits;
|
|
34
|
+
/** The refusal each problem of a line is - written out whole, so every code this package speaks is read where it is. */
|
|
35
|
+
const LINE_REFUSALS = {
|
|
36
|
+
'item-invalid': () => new shell_1.Refusal('invoice-item-invalid', {}, 'Check description, quantity, price, discount, tax kind and rate'),
|
|
37
|
+
'discount-exceeds': () => new shell_1.Refusal('invoice-discount-exceeds', {}, 'Discount exceeds the line amount'),
|
|
38
|
+
'amount-too-large': () => new shell_1.Refusal('invoice-amount-too-large', {}, 'Amount is too large'),
|
|
39
|
+
'received-tax-required': () => new shell_1.Refusal('invoice-received-tax-required', {}, 'Enter the tax printed on each received line')
|
|
40
|
+
};
|
|
41
|
+
/** The refusal a discount of the whole document that cannot be taken is - written out whole, as the lines' are. */
|
|
42
|
+
const DISCOUNT_REFUSALS = {
|
|
43
|
+
'document-discount-invalid': () => new shell_1.Refusal('invoice-document-discount-invalid', {}, "The discount is zero or more and at most the lines' supply")
|
|
44
|
+
};
|
|
60
45
|
/**
|
|
61
|
-
* The supply of each line, the tax of each (kind, rate) group and the document totals
|
|
46
|
+
* The supply of each line, the tax of each (kind, rate) group and the document totals - reckoned by
|
|
47
|
+
* `shared/document-amounts.ts`, the screens' own arithmetic; here a line that cannot be reckoned refuses the document,
|
|
48
|
+
* and so does a discount of the whole document below zero or past the lines' supply. `received` keeps the line tax
|
|
62
49
|
* the sender printed; otherwise lines carry no tax and the groups reckon it.
|
|
63
50
|
*/
|
|
64
|
-
function calculateDocument(lines, currency, received = false) {
|
|
65
|
-
|
|
66
|
-
const factor = 10n ** BigInt(digits);
|
|
51
|
+
function calculateDocument(lines, currency, received = false, discountAmount) {
|
|
52
|
+
currencyDigits(currency);
|
|
67
53
|
if (!Array.isArray(lines) || !lines.length || lines.length > exports.MAX_LINES) {
|
|
68
54
|
throw new shell_1.Refusal('invoice-items-required', {}, 'An invoice needs 1 to 500 items');
|
|
69
55
|
}
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
quantity <= 0n ||
|
|
83
|
-
unitPrice < 0n ||
|
|
84
|
-
discount < 0n ||
|
|
85
|
-
rate < 0n ||
|
|
86
|
-
rate > 100n * SCALE ||
|
|
87
|
-
!Object.values(TaxKind).includes(kind) ||
|
|
88
|
-
(kind !== TaxKind.TAXABLE && rate !== 0n)) {
|
|
89
|
-
throw new shell_1.Refusal('invoice-item-invalid', {}, 'Check description, quantity, price, discount, tax kind and rate');
|
|
90
|
-
}
|
|
91
|
-
const gross = divideHalfUp(quantity * unitPrice * factor, SCALE * SCALE);
|
|
92
|
-
if (discount > gross)
|
|
93
|
-
throw new shell_1.Refusal('invoice-discount-exceeds', {}, 'Discount exceeds the line amount');
|
|
94
|
-
const supply = gross - discount;
|
|
95
|
-
if (supply > MAX_MINOR)
|
|
96
|
-
throw new shell_1.Refusal('invoice-amount-too-large', {}, 'Amount is too large');
|
|
97
|
-
let printed;
|
|
98
|
-
if (received) {
|
|
99
|
-
printed = line.receivedTaxAmount === null || line.receivedTaxAmount === undefined ? undefined : minorOf(line.receivedTaxAmount, digits);
|
|
100
|
-
if (printed === undefined || printed < 0n)
|
|
101
|
-
throw new shell_1.Refusal('invoice-received-tax-required', {}, 'Enter the tax printed on each received line');
|
|
102
|
-
}
|
|
103
|
-
const key = `${kind}:${rate}`;
|
|
104
|
-
const group = groups.get(key) ?? { taxKind: kind, rate, supply: 0n, tax: 0n };
|
|
105
|
-
group.supply += supply;
|
|
106
|
-
if (printed !== undefined)
|
|
107
|
-
group.tax += printed;
|
|
108
|
-
groups.set(key, group);
|
|
109
|
-
return {
|
|
56
|
+
/* Saved lines name every value: a blank discount is not taken as none here, as a screen takes it. */
|
|
57
|
+
const named = lines.map(line => ({ ...line, discount: line.discount ?? NaN }));
|
|
58
|
+
const amounts = (0, document_amounts_js_1.documentAmounts)(named, currency, received, discountAmount ?? 0);
|
|
59
|
+
const refused = amounts.lines.find(line => line.problem);
|
|
60
|
+
if (refused)
|
|
61
|
+
throw LINE_REFUSALS[refused.problem]();
|
|
62
|
+
if (amounts.discountProblem)
|
|
63
|
+
throw DISCOUNT_REFUSALS[amounts.discountProblem]();
|
|
64
|
+
if (amounts.tooLarge)
|
|
65
|
+
throw new shell_1.Refusal('invoice-amount-too-large', {}, 'Amount is too large');
|
|
66
|
+
return {
|
|
67
|
+
items: lines.map((line, position) => ({
|
|
110
68
|
description: line.description.trim(),
|
|
111
69
|
quantity: line.quantity,
|
|
70
|
+
unit: line.unit?.trim() || null,
|
|
112
71
|
unitPrice: line.unitPrice,
|
|
113
72
|
discount: line.discount,
|
|
114
|
-
taxKind:
|
|
73
|
+
taxKind: line.taxKind,
|
|
115
74
|
taxRate: line.taxRate,
|
|
116
|
-
supplyAmount:
|
|
117
|
-
receivedTaxAmount:
|
|
75
|
+
supplyAmount: amounts.lines[position].supplyAmount,
|
|
76
|
+
receivedTaxAmount: amounts.lines[position].receivedTaxAmount ?? null,
|
|
118
77
|
position
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
supply += group.supply;
|
|
127
|
-
tax += group.tax;
|
|
128
|
-
return { taxKind: group.taxKind, taxRate: Number(group.rate) / 10_000, supplyAmount: amountOf(group.supply, digits), taxAmount: amountOf(group.tax, digits) };
|
|
129
|
-
});
|
|
130
|
-
if (supply + tax > MAX_MINOR)
|
|
131
|
-
throw new shell_1.Refusal('invoice-amount-too-large', {}, 'Amount is too large');
|
|
132
|
-
return {
|
|
133
|
-
items: calculated,
|
|
134
|
-
taxGroups,
|
|
135
|
-
supplyAmount: amountOf(supply, digits),
|
|
136
|
-
taxAmount: amountOf(tax, digits),
|
|
137
|
-
totalAmount: amountOf(supply + tax, digits)
|
|
78
|
+
})),
|
|
79
|
+
taxGroups: amounts.taxGroups,
|
|
80
|
+
lineSupplyAmount: amounts.lineSupplyAmount,
|
|
81
|
+
discountAmount: amounts.discountAmount,
|
|
82
|
+
supplyAmount: amounts.supplyAmount,
|
|
83
|
+
taxAmount: amounts.taxAmount,
|
|
84
|
+
totalAmount: amounts.totalAmount
|
|
138
85
|
};
|
|
139
86
|
}
|
|
140
|
-
/** The groups of a saved document, read back from its lines. */
|
|
87
|
+
/** The groups of a saved document, read back from its lines and its discount. */
|
|
141
88
|
function taxGroupsOf(document) {
|
|
142
89
|
if (!document.items?.length)
|
|
143
90
|
return [];
|
|
144
|
-
return calculateDocument(document.items, document.currency, document.taxBasis === 'RECEIVED').taxGroups;
|
|
91
|
+
return calculateDocument(document.items, document.currency, document.taxBasis === 'RECEIVED', document.discountAmount).taxGroups;
|
|
92
|
+
}
|
|
93
|
+
/** The lines' supply of a saved document before its discount, read back from its lines. */
|
|
94
|
+
function lineSupplyOf(document) {
|
|
95
|
+
if (!document.items?.length)
|
|
96
|
+
return 0;
|
|
97
|
+
return calculateDocument(document.items, document.currency, document.taxBasis === 'RECEIVED', document.discountAmount).lineSupplyAmount;
|
|
145
98
|
}
|
|
146
99
|
/** The fields every commercial document prints in its head, in the order the snapshot keeps them. */
|
|
147
100
|
exports.HEADER_KEYS = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoice-rules.js","sourceRoot":"","sources":["../../../server/service/invoice/invoice-rules.ts"],"names":[],"mappings":";;;AA4CA,4BAEC;AAGD,wCAIC;AA0BD,8CA8EC;AAGD,kCAGC;AAuBD,4CAgBC;AAED,oCAEC;AAGD,gCASC;AA1ND,+FAAmG;AACnG,iDAA+C;AAE/C;;;;;;;;GAQG;AAEH,IAAY,OAIX;AAJD,WAAY,OAAO;IACjB,8BAAmB,CAAA;IACnB,oCAAyB,CAAA;IACzB,4BAAiB,CAAA;AACnB,CAAC,EAJW,OAAO,uBAAP,OAAO,QAIlB;AAED,8FAA8F;AACjF,QAAA,YAAY,GAAG,SAAS,CAAA;AAExB,QAAA,SAAS,GAAG,GAAG,CAAA;AAC5B,MAAM,SAAS,GAAG,GAAG,IAAI,GAAG,CAAA;AAC5B,MAAM,KAAK,GAAG,MAAO,CAAA;AAoBrB,SAAgB,QAAQ,CAAC,GAAW;IAClC,OAAO,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,CAAA;AAC9H,CAAC;AAED,2HAA2H;AAC3H,SAAgB,cAAc,CAAC,QAAgB;IAC7C,MAAM,MAAM,GAAG,IAAA,mCAAQ,EAAC,QAAQ,CAAC,CAAA;IACjC,IAAI,MAAM,KAAK,SAAS;QAAE,MAAM,IAAI,eAAO,CAAC,0BAA0B,EAAE,EAAE,EAAE,kCAAkC,CAAC,CAAA;IAC/G,OAAO,MAAM,CAAA;AACf,CAAC;AAED,yGAAyG;AACzG,SAAS,MAAM,CAAC,KAAa;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,CAAA;IACxC,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC5E,CAAC;AAED,mEAAmE;AACnE,SAAS,YAAY,CAAC,SAAiB,EAAE,WAAmB;IAC1D,OAAO,CAAC,SAAS,GAAG,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,WAAW,GAAG,EAAE,CAAC,CAAA;AAC5D,CAAC;AAED,SAAS,OAAO,CAAC,KAAa,EAAE,MAAc;IAC5C,MAAM,MAAM,GAAG,EAAE,IAAI,MAAM,CAAA;IAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,CAAA;IACxC,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC5E,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAE,MAAc,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,MAAM,CAAA;AAEhF;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,KAA4B,EAAE,QAAgB,EAAE,QAAQ,GAAG,KAAK;IAChG,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAA;IACvC,MAAM,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,CAAA;IACpC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,iBAAS,EAAE,CAAC;QACvE,MAAM,IAAI,eAAO,CAAC,wBAAwB,EAAE,EAAE,EAAE,iCAAiC,CAAC,CAAA;IACpF,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAA0E,CAAA;IAChG,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE;QAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACtC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACxC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAkB,CAAA;QACpC,IACE,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE;YACzB,QAAQ,KAAK,SAAS;YACtB,SAAS,KAAK,SAAS;YACvB,IAAI,KAAK,SAAS;YAClB,QAAQ,KAAK,SAAS;YACtB,QAAQ,IAAI,EAAE;YACd,SAAS,GAAG,EAAE;YACd,QAAQ,GAAG,EAAE;YACb,IAAI,GAAG,EAAE;YACT,IAAI,GAAG,IAAI,GAAG,KAAK;YACnB,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;YACtC,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,IAAI,IAAI,KAAK,EAAE,CAAC,EACzC,CAAC;YACD,MAAM,IAAI,eAAO,CAAC,sBAAsB,EAAE,EAAE,EAAE,iEAAiE,CAAC,CAAA;QAClH,CAAC;QAED,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,GAAG,SAAS,GAAG,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC,CAAA;QACxE,IAAI,QAAQ,GAAG,KAAK;YAAE,MAAM,IAAI,eAAO,CAAC,0BAA0B,EAAE,EAAE,EAAE,kCAAkC,CAAC,CAAA;QAC3G,MAAM,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAA;QAC/B,IAAI,MAAM,GAAG,SAAS;YAAE,MAAM,IAAI,eAAO,CAAC,0BAA0B,EAAE,EAAE,EAAE,qBAAqB,CAAC,CAAA;QAEhG,IAAI,OAA2B,CAAA;QAC/B,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,GAAG,IAAI,CAAC,iBAAiB,KAAK,IAAI,IAAI,IAAI,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAA;YACvI,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,GAAG,EAAE;gBAAE,MAAM,IAAI,eAAO,CAAC,+BAA+B,EAAE,EAAE,EAAE,6CAA6C,CAAC,CAAA;QAClJ,CAAC;QAED,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,EAAE,CAAA;QAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,CAAA;QAC7E,KAAK,CAAC,MAAM,IAAI,MAAM,CAAA;QACtB,IAAI,OAAO,KAAK,SAAS;YAAE,KAAK,CAAC,GAAG,IAAI,OAAO,CAAA;QAC/C,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAEtB,OAAO;YACL,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;YACpC,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,YAAY,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;YACtC,iBAAiB,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;YAC3E,QAAQ;SACT,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,MAAM,SAAS,GAAe,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QAC7D,IAAI,CAAC,QAAQ;YAAE,KAAK,CAAC,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,KAAK,CAAC,CAAA;QAChF,MAAM,IAAI,KAAK,CAAC,MAAM,CAAA;QACtB,GAAG,IAAI,KAAK,CAAC,GAAG,CAAA;QAChB,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,CAAA;IAC/J,CAAC,CAAC,CAAA;IACF,IAAI,MAAM,GAAG,GAAG,GAAG,SAAS;QAAE,MAAM,IAAI,eAAO,CAAC,0BAA0B,EAAE,EAAE,EAAE,qBAAqB,CAAC,CAAA;IAEtG,OAAO;QACL,KAAK,EAAE,UAAU;QACjB,SAAS;QACT,YAAY,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACtC,SAAS,EAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAChC,WAAW,EAAE,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE,MAAM,CAAC;KAC5C,CAAA;AACH,CAAC;AAED,gEAAgE;AAChE,SAAgB,WAAW,CAAC,QAAgF;IAC1G,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM;QAAE,OAAO,EAAE,CAAA;IACtC,OAAO,iBAAiB,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,SAAS,CAAA;AACzG,CAAC;AAED,qGAAqG;AACxF,QAAA,WAAW,GAAG;IACzB,QAAQ;IACR,WAAW;IACX,UAAU;IACV,UAAU;IACV,cAAc;IACd,WAAW;IACX,WAAW;IACX,iBAAiB;IACjB,4BAA4B;IAC5B,cAAc;IACd,yBAAyB;IACzB,eAAe;IACf,cAAc;IACd,aAAa;IACb,OAAO;IACP,iBAAiB;CACT,CAAA;AAEV,mDAAmD;AACnD,SAAgB,gBAAgB,CAAC,KAA0B;IACzD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAChD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAClE,IACE,CAAC,MAAM;QACP,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QACxC,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QACrC,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;QAChD,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EACtC,CAAC;QACD,MAAM,IAAI,eAAO,CAAC,wBAAwB,EAAE,EAAE,EAAE,+CAA+C,CAAC,CAAA;IAClG,CAAC;IACD,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;QAAE,MAAM,IAAI,eAAO,CAAC,sBAAsB,EAAE,EAAE,EAAE,6BAA6B,CAAC,CAAA;IAC/H,cAAc,CAAC,QAAQ,CAAC,CAAA;IACxB,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,mBAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAA8C,CAAA;IACjI,OAAO,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAA;AACxC,CAAC;AAED,SAAgB,YAAY,CAAC,MAAc;IACzC,IAAI,MAAM,KAAK,OAAO;QAAE,MAAM,IAAI,eAAO,CAAC,wBAAwB,EAAE,EAAE,EAAE,2CAA2C,CAAC,CAAA;AACtH,CAAC;AAED,iEAAiE;AACjE,SAAgB,UAAU,CAAC,KAAa,EAAE,IAAY,EAAE,QAAQ,GAAG,KAAK;IACtE,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAA;IACvC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,IAAI,MAAM,CAAC,CAAA;IACnD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,IAAI,MAAM,CAAC,CAAA;IACjD,OAAO;QACL,UAAU,EAAE,SAAS,GAAG,EAAE,IAAI,MAAM;QACpC,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,GAAG,EAAE,IAAI,MAAM;QACnE,aAAa,EAAE,SAAS,IAAI,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;KACxF,CAAA;AACH,CAAC","sourcesContent":["import { currencyDigits as digitsOf } from '@things-factory/invoice/dist-shared/currency-digits.js'\nimport { Refusal } from '@things-factory/shell'\n\n/*\n * Invoice arithmetic and lifecycle rules, shared by the invoice and quotation services and their tests (ADR-0120\n * decision 4).\n *\n * - Lines carry supply and a tax kind and rate; the tax is reckoned once per (kind, rate) group: the group's supply\n * total × rate ÷ 100, rounded half-up to the currency's minor unit. The document tax is the sum of the groups.\n * - Everything is counted in integers: quantities, prices and rates to four decimals, money in minor units.\n * - A received document keeps the tax its sender printed on each line; nothing recomputes it.\n */\n\nexport enum TaxKind {\n TAXABLE = 'TAXABLE',\n ZERO_RATED = 'ZERO_RATED',\n EXEMPT = 'EXEMPT'\n}\n\n/** How a document's tax was rounded — written onto the document when it is issued or sent. */\nexport const TAX_ROUNDING = 'HALF_UP'\n\nexport const MAX_LINES = 500\nconst MAX_MINOR = 10n ** 14n\nconst SCALE = 10_000n\n\nexport interface LineValues {\n description: string\n quantity: number\n unitPrice: number\n discount: number\n taxKind: TaxKind | string\n taxRate: number\n /** The tax the sender printed on the line — only on a received document. */\n receivedTaxAmount?: number | null\n}\n\nexport interface TaxGroup {\n taxKind: string\n taxRate: number\n supplyAmount: number\n taxAmount: number\n}\n\nexport function validDay(day: string): boolean {\n return /^\\d{4}-\\d{2}-\\d{2}$/.test(day) && !Number.isNaN(Date.parse(day)) && new Date(day).toISOString().slice(0, 10) === day\n}\n\n/** Decimal places of a document's currency (`shared/currency-digits.ts`); a code the platform does not know is refused. */\nexport function currencyDigits(currency: string): number {\n const digits = digitsOf(currency)\n if (digits === undefined) throw new Refusal('invoice-currency-invalid', {}, 'Use a three-letter currency code')\n return digits\n}\n\n/** A number with at most four decimals, as an integer of ten-thousandths; undefined when it has more. */\nfunction scaled(value: number): bigint | undefined {\n if (!Number.isFinite(value)) return undefined\n const whole = Math.round(value * 10_000)\n return Math.abs(value * 10_000 - whole) > 1e-6 ? undefined : BigInt(whole)\n}\n\n/** numerator ÷ denominator, rounded half-up; both non-negative. */\nfunction divideHalfUp(numerator: bigint, denominator: bigint): bigint {\n return (numerator * 2n + denominator) / (denominator * 2n)\n}\n\nfunction minorOf(value: number, digits: number): bigint | undefined {\n const factor = 10 ** digits\n const whole = Math.round(value * factor)\n return Math.abs(value * factor - whole) > 1e-6 ? undefined : BigInt(whole)\n}\n\nconst amountOf = (minor: bigint, digits: number) => Number(minor) / 10 ** digits\n\n/**\n * The supply of each line, the tax of each (kind, rate) group and the document totals. `received` keeps the line tax\n * the sender printed; otherwise lines carry no tax and the groups reckon it.\n */\nexport function calculateDocument(lines: readonly LineValues[], currency: string, received = false) {\n const digits = currencyDigits(currency)\n const factor = 10n ** BigInt(digits)\n if (!Array.isArray(lines) || !lines.length || lines.length > MAX_LINES) {\n throw new Refusal('invoice-items-required', {}, 'An invoice needs 1 to 500 items')\n }\n\n const groups = new Map<string, { taxKind: string; rate: bigint; supply: bigint; tax: bigint }>()\n const calculated = lines.map((line, position) => {\n const quantity = scaled(line.quantity)\n const unitPrice = scaled(line.unitPrice)\n const rate = scaled(line.taxRate)\n const discount = minorOf(line.discount, digits)\n const kind = line.taxKind as TaxKind\n if (\n !line.description?.trim() ||\n quantity === undefined ||\n unitPrice === undefined ||\n rate === undefined ||\n discount === undefined ||\n quantity <= 0n ||\n unitPrice < 0n ||\n discount < 0n ||\n rate < 0n ||\n rate > 100n * SCALE ||\n !Object.values(TaxKind).includes(kind) ||\n (kind !== TaxKind.TAXABLE && rate !== 0n)\n ) {\n throw new Refusal('invoice-item-invalid', {}, 'Check description, quantity, price, discount, tax kind and rate')\n }\n\n const gross = divideHalfUp(quantity * unitPrice * factor, SCALE * SCALE)\n if (discount > gross) throw new Refusal('invoice-discount-exceeds', {}, 'Discount exceeds the line amount')\n const supply = gross - discount\n if (supply > MAX_MINOR) throw new Refusal('invoice-amount-too-large', {}, 'Amount is too large')\n\n let printed: bigint | undefined\n if (received) {\n printed = line.receivedTaxAmount === null || line.receivedTaxAmount === undefined ? undefined : minorOf(line.receivedTaxAmount, digits)\n if (printed === undefined || printed < 0n) throw new Refusal('invoice-received-tax-required', {}, 'Enter the tax printed on each received line')\n }\n\n const key = `${kind}:${rate}`\n const group = groups.get(key) ?? { taxKind: kind, rate, supply: 0n, tax: 0n }\n group.supply += supply\n if (printed !== undefined) group.tax += printed\n groups.set(key, group)\n\n return {\n description: line.description.trim(),\n quantity: line.quantity,\n unitPrice: line.unitPrice,\n discount: line.discount,\n taxKind: kind,\n taxRate: line.taxRate,\n supplyAmount: amountOf(supply, digits),\n receivedTaxAmount: printed === undefined ? null : amountOf(printed, digits),\n position\n }\n })\n\n let supply = 0n\n let tax = 0n\n const taxGroups: TaxGroup[] = [...groups.values()].map(group => {\n if (!received) group.tax = divideHalfUp(group.supply * group.rate, 100n * SCALE)\n supply += group.supply\n tax += group.tax\n return { taxKind: group.taxKind, taxRate: Number(group.rate) / 10_000, supplyAmount: amountOf(group.supply, digits), taxAmount: amountOf(group.tax, digits) }\n })\n if (supply + tax > MAX_MINOR) throw new Refusal('invoice-amount-too-large', {}, 'Amount is too large')\n\n return {\n items: calculated,\n taxGroups,\n supplyAmount: amountOf(supply, digits),\n taxAmount: amountOf(tax, digits),\n totalAmount: amountOf(supply + tax, digits)\n }\n}\n\n/** The groups of a saved document, read back from its lines. */\nexport function taxGroupsOf(document: { currency: string; taxBasis?: string; items?: readonly LineValues[] }): TaxGroup[] {\n if (!document.items?.length) return []\n return calculateDocument(document.items, document.currency, document.taxBasis === 'RECEIVED').taxGroups\n}\n\n/** The fields every commercial document prints in its head, in the order the snapshot keeps them. */\nexport const HEADER_KEYS = [\n 'number',\n 'direction',\n 'currency',\n 'language',\n 'supplierName',\n 'buyerName',\n 'issueDate',\n 'supplierAddress',\n 'supplierRegistrationNumber',\n 'buyerAddress',\n 'buyerRegistrationNumber',\n 'purchaseOrder',\n 'paymentTerms',\n 'bankDetails',\n 'notes',\n 'sourceReference'\n] as const\n\n/** The head of a document, trimmed and checked. */\nexport function normalizedHeader(input: Record<string, any>) {\n const number = String(input.number ?? '').trim()\n const currency = String(input.currency ?? '').trim().toUpperCase()\n if (\n !number ||\n !String(input.supplierName ?? '').trim() ||\n !String(input.buyerName ?? '').trim() ||\n !['SALES', 'PURCHASE'].includes(input.direction) ||\n !['ko', 'en'].includes(input.language)\n ) {\n throw new Refusal('invoice-header-invalid', {}, 'Check number, parties, direction and language')\n }\n if (input.issueDate && !validDay(input.issueDate)) throw new Refusal('invoice-date-invalid', {}, 'Use a valid YYYY-MM-DD date')\n currencyDigits(currency)\n const header = Object.fromEntries(HEADER_KEYS.map(key => [key, input[key] ?? null])) as Record<(typeof HEADER_KEYS)[number], any>\n return { ...header, number, currency }\n}\n\nexport function requireDraft(status: string) {\n if (status !== 'DRAFT') throw new Refusal('invoice-draft-required', {}, 'Issued and void invoices cannot be edited')\n}\n\n/** How much of a total is paid, in the currency's minor unit. */\nexport function settlement(total: number, paid: number, currency = 'KRW') {\n const digits = currencyDigits(currency)\n const totalMinor = Math.round(total * 10 ** digits)\n const paidMinor = Math.round(paid * 10 ** digits)\n return {\n paidAmount: paidMinor / 10 ** digits,\n remainingAmount: Math.max(0, totalMinor - paidMinor) / 10 ** digits,\n paymentStatus: paidMinor >= totalMinor ? 'PAID' : paidMinor <= 0 ? 'UNPAID' : 'PARTIAL'\n }\n}\n\n"]}
|
|
1
|
+
{"version":3,"file":"invoice-rules.js","sourceRoot":"","sources":["../../../server/service/invoice/invoice-rules.ts"],"names":[],"mappings":"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","sourcesContent":["import { currencyDigits as digitsOf } from '@things-factory/invoice/dist-shared/currency-digits.js'\nimport { documentAmounts, type DiscountProblem, type LineProblem, type TaxGroup } from '@things-factory/invoice/dist-shared/document-amounts.js'\nimport { Refusal } from '@things-factory/shell'\n\n/*\n * Invoice lifecycle rules and the refusals of its arithmetic, shared by the invoice and quotation services and their\n * tests (ADR-0120 decision 4). The arithmetic itself is `shared/document-amounts.ts`, which the screens read too.\n */\nexport type { TaxGroup }\n\nexport enum TaxKind {\n TAXABLE = 'TAXABLE',\n ZERO_RATED = 'ZERO_RATED',\n EXEMPT = 'EXEMPT'\n}\n\n/** How a document's tax was rounded — written onto the document when it is issued or sent. */\nexport const TAX_ROUNDING = 'HALF_UP'\n\nexport const MAX_LINES = 500\n\nexport interface LineValues {\n description: string\n quantity: number\n /** The unit the quantity counts in, as printed - no arithmetic reads it. */\n unit?: string | null\n unitPrice: number\n discount: number\n taxKind: TaxKind | string\n taxRate: number\n /** The tax the sender printed on the line — only on a received document. */\n receivedTaxAmount?: number | null\n}\n\nexport function validDay(day: string): boolean {\n return /^\\d{4}-\\d{2}-\\d{2}$/.test(day) && !Number.isNaN(Date.parse(day)) && new Date(day).toISOString().slice(0, 10) === day\n}\n\n/** Decimal places of a document's currency (`shared/currency-digits.ts`); a code the platform does not know is refused. */\nexport function currencyDigits(currency: string): number {\n const digits = digitsOf(currency)\n if (digits === undefined) throw new Refusal('invoice-currency-invalid', {}, 'Use a three-letter currency code')\n return digits\n}\n\n/** The refusal each problem of a line is - written out whole, so every code this package speaks is read where it is. */\nconst LINE_REFUSALS: Record<LineProblem, () => Refusal> = {\n 'item-invalid': () => new Refusal('invoice-item-invalid', {}, 'Check description, quantity, price, discount, tax kind and rate'),\n 'discount-exceeds': () => new Refusal('invoice-discount-exceeds', {}, 'Discount exceeds the line amount'),\n 'amount-too-large': () => new Refusal('invoice-amount-too-large', {}, 'Amount is too large'),\n 'received-tax-required': () => new Refusal('invoice-received-tax-required', {}, 'Enter the tax printed on each received line')\n}\n\n/** The refusal a discount of the whole document that cannot be taken is - written out whole, as the lines' are. */\nconst DISCOUNT_REFUSALS: Record<DiscountProblem, () => Refusal> = {\n 'document-discount-invalid': () => new Refusal('invoice-document-discount-invalid', {}, \"The discount is zero or more and at most the lines' supply\")\n}\n\n/**\n * The supply of each line, the tax of each (kind, rate) group and the document totals - reckoned by\n * `shared/document-amounts.ts`, the screens' own arithmetic; here a line that cannot be reckoned refuses the document,\n * and so does a discount of the whole document below zero or past the lines' supply. `received` keeps the line tax\n * the sender printed; otherwise lines carry no tax and the groups reckon it.\n */\nexport function calculateDocument(lines: readonly LineValues[], currency: string, received = false, discountAmount?: number | null) {\n currencyDigits(currency)\n if (!Array.isArray(lines) || !lines.length || lines.length > MAX_LINES) {\n throw new Refusal('invoice-items-required', {}, 'An invoice needs 1 to 500 items')\n }\n /* Saved lines name every value: a blank discount is not taken as none here, as a screen takes it. */\n const named = lines.map(line => ({ ...line, discount: line.discount ?? NaN }))\n const amounts = documentAmounts(named, currency, received, discountAmount ?? 0)\n const refused = amounts.lines.find(line => line.problem)\n if (refused) throw LINE_REFUSALS[refused.problem!]()\n if (amounts.discountProblem) throw DISCOUNT_REFUSALS[amounts.discountProblem]()\n if (amounts.tooLarge) throw new Refusal('invoice-amount-too-large', {}, 'Amount is too large')\n\n return {\n items: lines.map((line, position) => ({\n description: line.description.trim(),\n quantity: line.quantity,\n unit: line.unit?.trim() || null,\n unitPrice: line.unitPrice,\n discount: line.discount,\n taxKind: line.taxKind as TaxKind,\n taxRate: line.taxRate,\n supplyAmount: amounts.lines[position].supplyAmount!,\n receivedTaxAmount: amounts.lines[position].receivedTaxAmount ?? null,\n position\n })),\n taxGroups: amounts.taxGroups,\n lineSupplyAmount: amounts.lineSupplyAmount,\n discountAmount: amounts.discountAmount,\n supplyAmount: amounts.supplyAmount,\n taxAmount: amounts.taxAmount,\n totalAmount: amounts.totalAmount\n }\n}\n\ntype SavedDocument = { currency: string; taxBasis?: string; discountAmount?: number | null; items?: readonly LineValues[] }\n\n/** The groups of a saved document, read back from its lines and its discount. */\nexport function taxGroupsOf(document: SavedDocument): TaxGroup[] {\n if (!document.items?.length) return []\n return calculateDocument(document.items, document.currency, document.taxBasis === 'RECEIVED', document.discountAmount).taxGroups\n}\n\n/** The lines' supply of a saved document before its discount, read back from its lines. */\nexport function lineSupplyOf(document: SavedDocument): number {\n if (!document.items?.length) return 0\n return calculateDocument(document.items, document.currency, document.taxBasis === 'RECEIVED', document.discountAmount).lineSupplyAmount\n}\n\n/** The fields every commercial document prints in its head, in the order the snapshot keeps them. */\nexport const HEADER_KEYS = [\n 'number',\n 'direction',\n 'currency',\n 'language',\n 'supplierName',\n 'buyerName',\n 'issueDate',\n 'supplierAddress',\n 'supplierRegistrationNumber',\n 'buyerAddress',\n 'buyerRegistrationNumber',\n 'purchaseOrder',\n 'paymentTerms',\n 'bankDetails',\n 'notes',\n 'sourceReference'\n] as const\n\n/** The head of a document, trimmed and checked. */\nexport function normalizedHeader(input: Record<string, any>) {\n const number = String(input.number ?? '').trim()\n const currency = String(input.currency ?? '').trim().toUpperCase()\n if (\n !number ||\n !String(input.supplierName ?? '').trim() ||\n !String(input.buyerName ?? '').trim() ||\n !['SALES', 'PURCHASE'].includes(input.direction) ||\n !['ko', 'en'].includes(input.language)\n ) {\n throw new Refusal('invoice-header-invalid', {}, 'Check number, parties, direction and language')\n }\n if (input.issueDate && !validDay(input.issueDate)) throw new Refusal('invoice-date-invalid', {}, 'Use a valid YYYY-MM-DD date')\n currencyDigits(currency)\n const header = Object.fromEntries(HEADER_KEYS.map(key => [key, input[key] ?? null])) as Record<(typeof HEADER_KEYS)[number], any>\n return { ...header, number, currency }\n}\n\nexport function requireDraft(status: string) {\n if (status !== 'DRAFT') throw new Refusal('invoice-draft-required', {}, 'Issued and void invoices cannot be edited')\n}\n\n/** How much of a total is paid, in the currency's minor unit. */\nexport function settlement(total: number, paid: number, currency = 'KRW') {\n const digits = currencyDigits(currency)\n const totalMinor = Math.round(total * 10 ** digits)\n const paidMinor = Math.round(paid * 10 ** digits)\n return {\n paidAmount: paidMinor / 10 ** digits,\n remainingAmount: Math.max(0, totalMinor - paidMinor) / 10 ** digits,\n paymentStatus: paidMinor >= totalMinor ? 'PAID' : paidMinor <= 0 ? 'UNPAID' : 'PARTIAL'\n }\n}\n\n"]}
|