@things-factory/invoice 10.1.223 → 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
|
@@ -50,13 +50,14 @@ async function saveInvoice(tx, domain, user, input, id) {
|
|
|
50
50
|
const counterparty = input.counterpartyId ? await tx.getRepository(biz_base_1.Counterparty).findOne({ where: { id: input.counterpartyId, domain: { id: domain.id } } }) : null;
|
|
51
51
|
if (input.counterpartyId && !counterparty)
|
|
52
52
|
throw new shell_1.Refusal('invoice-counterparty-not-found', {}, 'The counterparty was not found - reload and try again');
|
|
53
|
-
const { items, supplyAmount, taxAmount, totalAmount } = (0, invoice_rules_1.calculateDocument)(input.items, header.currency, received);
|
|
53
|
+
const { items, discountAmount, supplyAmount, taxAmount, totalAmount } = (0, invoice_rules_1.calculateDocument)(input.items, header.currency, received, input.discountAmount);
|
|
54
54
|
const invoice = await repository.save(repository.create({
|
|
55
55
|
...old,
|
|
56
56
|
...header,
|
|
57
57
|
counterparty,
|
|
58
58
|
dueDate: input.dueDate ?? null,
|
|
59
59
|
taxBasis: received ? invoice_1.InvoiceTaxBasis.RECEIVED : invoice_1.InvoiceTaxBasis.CALCULATED,
|
|
60
|
+
discountAmount,
|
|
60
61
|
supplyAmount,
|
|
61
62
|
taxAmount,
|
|
62
63
|
totalAmount,
|
|
@@ -111,6 +112,7 @@ function snapshotOf(invoice) {
|
|
|
111
112
|
'status',
|
|
112
113
|
'dueDate',
|
|
113
114
|
'taxBasis',
|
|
115
|
+
'discountAmount',
|
|
114
116
|
'supplyAmount',
|
|
115
117
|
'taxAmount',
|
|
116
118
|
'totalAmount',
|
|
@@ -120,10 +122,11 @@ function snapshotOf(invoice) {
|
|
|
120
122
|
];
|
|
121
123
|
return {
|
|
122
124
|
...Object.fromEntries(keys.map(key => [key, invoice[key] ?? null])),
|
|
123
|
-
items: invoice.items.map(({ position, description, quantity, unitPrice, discount, taxKind, taxRate, supplyAmount, receivedTaxAmount }) => ({
|
|
125
|
+
items: invoice.items.map(({ position, description, quantity, unit, unitPrice, discount, taxKind, taxRate, supplyAmount, receivedTaxAmount }) => ({
|
|
124
126
|
position,
|
|
125
127
|
description,
|
|
126
128
|
quantity,
|
|
129
|
+
unit: unit ?? null,
|
|
127
130
|
unitPrice,
|
|
128
131
|
discount,
|
|
129
132
|
taxKind,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoice-service.js","sourceRoot":"","sources":["../../../server/service/invoice/invoice-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Counterparty } from '@things-factory/biz-base'\nimport { Domain, Refusal, writeLockSupported } from '@things-factory/shell'\nimport { EntityManager } from 'typeorm'\n\nimport { InvoiceEvent } from '../invoice-event/invoice-event'\nimport { InvoiceItem } from '../invoice-item/invoice-item'\nimport { Invoice, InvoiceStatus, InvoiceTaxBasis } from './invoice'\nimport { calculateDocument, currencyDigits, HEADER_KEYS, normalizedHeader, requireDraft, TAX_ROUNDING, validDay } from './invoice-rules'\nimport { NewInvoice } from './invoice-type'\n\nexport type InvoiceTransitionGuard = (tx: EntityManager, domain: Domain, invoice: Invoice, action: 'ISSUED' | 'VOID') => Promise<void>\nconst transitionGuards = new Set<InvoiceTransitionGuard>()\n\n/** An application refuses a transition its own links forbid — Office keeps a linked invoice from being voided. */\nexport function registerInvoiceTransitionGuard(guard: InvoiceTransitionGuard) {\n transitionGuards.add(guard)\n}\n\n/** Serialize document writes on the domain row, where the database can hold it (shell `writeLockSupported`). */\nexport async function lockDocumentDomain(tx: EntityManager, domain: Domain) {\n const found = await tx.getRepository(Domain).findOne({ where: { id: domain.id }, ...(writeLockSupported(tx) ? { lock: { mode: 'pessimistic_write' } } : {}) })\n if (!found) throw new Refusal('invoice-domain-missing', {}, 'Domain not found')\n}\n\nexport async function invoiceInDomain(tx: EntityManager, domain: Domain, id: string) {\n const invoice = await tx.getRepository(Invoice).findOne({ where: { id, domain: { id: domain.id } }, relations: ['items', 'creator'] })\n if (!invoice) throw new Refusal('invoice-not-found', {}, 'Invoice not found')\n invoice.items.sort((a, b) => a.position - b.position)\n return invoice\n}\n\nexport async function saveInvoice(tx: EntityManager, domain: Domain, user: any, input: NewInvoice, id?: string) {\n await lockDocumentDomain(tx, domain)\n const old = id ? await invoiceInDomain(tx, domain, id) : undefined\n if (old) requireDraft(old.status)\n\n const header = normalizedHeader(input)\n if (input.dueDate && !validDay(input.dueDate)) throw new Refusal('invoice-date-invalid', {}, 'Use a valid YYYY-MM-DD date')\n if (input.issueDate && input.dueDate && input.dueDate < input.issueDate) throw new Refusal('invoice-due-before-issue', {}, 'Due date precedes issue date')\n const received = input.taxBasis === InvoiceTaxBasis.RECEIVED\n if (received && header.direction !== 'PURCHASE') throw new Refusal('invoice-received-purchase-only', {}, 'Only a received purchase invoice keeps printed tax')\n\n const repository = tx.getRepository(Invoice)\n const duplicate = await repository.findOne({ where: { domain: { id: domain.id }, number: header.number } })\n if (duplicate && duplicate.id !== id) throw new Refusal('invoice-number-duplicate', {}, 'Invoice number already exists')\n\n const counterparty = input.counterpartyId ? await tx.getRepository(Counterparty).findOne({ where: { id: input.counterpartyId, domain: { id: domain.id } } }) : null\n if (input.counterpartyId && !counterparty) throw new Refusal('invoice-counterparty-not-found', {}, 'The counterparty was not found - reload and try again')\n\n const { items, supplyAmount, taxAmount, totalAmount } = calculateDocument(input.items, header.currency, received)\n const invoice = await repository.save(\n repository.create({\n ...old,\n ...header,\n counterparty,\n dueDate: input.dueDate ?? null,\n taxBasis: received ? InvoiceTaxBasis.RECEIVED : InvoiceTaxBasis.CALCULATED,\n supplyAmount,\n taxAmount,\n totalAmount,\n domain,\n creator: old?.creator ?? user,\n updater: user,\n items: undefined\n })\n )\n\n const lines = tx.getRepository(InvoiceItem)\n if (old) await lines.softDelete({ invoice: { id: invoice.id } })\n await lines.save(items.map(item => lines.create({ ...item, invoice, domain, creator: user, updater: user })))\n\n const saved = await invoiceInDomain(tx, domain, invoice.id)\n await recordInvoiceEvent(tx, domain, user, saved, old ? 'UPDATED' : 'CREATED')\n return saved\n}\n\nexport async function transitionInvoice(tx: EntityManager, domain: Domain, user: any, id: string, action: 'ISSUED' | 'VOID', reason?: string) {\n await lockDocumentDomain(tx, domain)\n const invoice = await invoiceInDomain(tx, domain, id)\n for (const guard of transitionGuards) await guard(tx, domain, invoice, action)\n\n if (action === 'ISSUED') {\n requireDraft(invoice.status)\n if (!invoice.issueDate || !invoice.dueDate || !invoice.items.length) {\n throw new Refusal('invoice-issue-required', {}, 'Issue date, due date and items are required')\n }\n /* What the amounts were reckoned with stays on the document; a later rule never recomputes them. */\n invoice.taxRounding = TAX_ROUNDING\n invoice.currencyDigits = currencyDigits(invoice.currency)\n invoice.status = InvoiceStatus.ISSUED\n } else {\n if (invoice.status === InvoiceStatus.VOID || !reason?.trim()) throw new Refusal('invoice-void-invalid', {}, 'A cancellation needs a reason and a non-void invoice')\n invoice.voidReason = reason.trim()\n invoice.status = InvoiceStatus.VOID\n }\n invoice.updater = user\n const { items, ...head } = invoice\n await tx.getRepository(Invoice).save(head)\n await recordInvoiceEvent(tx, domain, user, invoice, action, reason)\n return invoiceInDomain(tx, domain, id)\n}\n\nfunction recordInvoiceEvent(tx: EntityManager, domain: Domain, user: any, invoice: Invoice, action: string, reason?: string) {\n return tx.getRepository(InvoiceEvent).save({ domain, invoice, actor: user, action, reason, creator: user, updater: user, snapshot: snapshotOf(invoice) })\n}\n\n/** The document as printed — what an issued invoice said stays readable whatever changes later. */\nexport function snapshotOf(invoice: Invoice) {\n const keys = [\n 'id',\n ...HEADER_KEYS,\n 'status',\n 'dueDate',\n 'taxBasis',\n 'supplyAmount',\n 'taxAmount',\n 'totalAmount',\n 'taxRounding',\n 'currencyDigits',\n 'voidReason'\n ]\n return {\n ...Object.fromEntries(keys.map(key => [key, invoice[key] ?? null])),\n items: invoice.items.map(({ position, description, quantity, unitPrice, discount, taxKind, taxRate, supplyAmount, receivedTaxAmount }) => ({\n position,\n description,\n quantity,\n unitPrice,\n discount,\n taxKind,\n taxRate,\n supplyAmount,\n receivedTaxAmount: receivedTaxAmount ?? null\n }))\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"invoice-service.js","sourceRoot":"","sources":["../../../server/service/invoice/invoice-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Counterparty } from '@things-factory/biz-base'\nimport { Domain, Refusal, writeLockSupported } from '@things-factory/shell'\nimport { EntityManager } from 'typeorm'\n\nimport { InvoiceEvent } from '../invoice-event/invoice-event'\nimport { InvoiceItem } from '../invoice-item/invoice-item'\nimport { Invoice, InvoiceStatus, InvoiceTaxBasis } from './invoice'\nimport { calculateDocument, currencyDigits, HEADER_KEYS, normalizedHeader, requireDraft, TAX_ROUNDING, validDay } from './invoice-rules'\nimport { NewInvoice } from './invoice-type'\n\nexport type InvoiceTransitionGuard = (tx: EntityManager, domain: Domain, invoice: Invoice, action: 'ISSUED' | 'VOID') => Promise<void>\nconst transitionGuards = new Set<InvoiceTransitionGuard>()\n\n/** An application refuses a transition its own links forbid — Office keeps a linked invoice from being voided. */\nexport function registerInvoiceTransitionGuard(guard: InvoiceTransitionGuard) {\n transitionGuards.add(guard)\n}\n\n/** Serialize document writes on the domain row, where the database can hold it (shell `writeLockSupported`). */\nexport async function lockDocumentDomain(tx: EntityManager, domain: Domain) {\n const found = await tx.getRepository(Domain).findOne({ where: { id: domain.id }, ...(writeLockSupported(tx) ? { lock: { mode: 'pessimistic_write' } } : {}) })\n if (!found) throw new Refusal('invoice-domain-missing', {}, 'Domain not found')\n}\n\nexport async function invoiceInDomain(tx: EntityManager, domain: Domain, id: string) {\n const invoice = await tx.getRepository(Invoice).findOne({ where: { id, domain: { id: domain.id } }, relations: ['items', 'creator'] })\n if (!invoice) throw new Refusal('invoice-not-found', {}, 'Invoice not found')\n invoice.items.sort((a, b) => a.position - b.position)\n return invoice\n}\n\nexport async function saveInvoice(tx: EntityManager, domain: Domain, user: any, input: NewInvoice, id?: string) {\n await lockDocumentDomain(tx, domain)\n const old = id ? await invoiceInDomain(tx, domain, id) : undefined\n if (old) requireDraft(old.status)\n\n const header = normalizedHeader(input)\n if (input.dueDate && !validDay(input.dueDate)) throw new Refusal('invoice-date-invalid', {}, 'Use a valid YYYY-MM-DD date')\n if (input.issueDate && input.dueDate && input.dueDate < input.issueDate) throw new Refusal('invoice-due-before-issue', {}, 'Due date precedes issue date')\n const received = input.taxBasis === InvoiceTaxBasis.RECEIVED\n if (received && header.direction !== 'PURCHASE') throw new Refusal('invoice-received-purchase-only', {}, 'Only a received purchase invoice keeps printed tax')\n\n const repository = tx.getRepository(Invoice)\n const duplicate = await repository.findOne({ where: { domain: { id: domain.id }, number: header.number } })\n if (duplicate && duplicate.id !== id) throw new Refusal('invoice-number-duplicate', {}, 'Invoice number already exists')\n\n const counterparty = input.counterpartyId ? await tx.getRepository(Counterparty).findOne({ where: { id: input.counterpartyId, domain: { id: domain.id } } }) : null\n if (input.counterpartyId && !counterparty) throw new Refusal('invoice-counterparty-not-found', {}, 'The counterparty was not found - reload and try again')\n\n const { items, discountAmount, supplyAmount, taxAmount, totalAmount } = calculateDocument(input.items, header.currency, received, input.discountAmount)\n const invoice = await repository.save(\n repository.create({\n ...old,\n ...header,\n counterparty,\n dueDate: input.dueDate ?? null,\n taxBasis: received ? InvoiceTaxBasis.RECEIVED : InvoiceTaxBasis.CALCULATED,\n discountAmount,\n supplyAmount,\n taxAmount,\n totalAmount,\n domain,\n creator: old?.creator ?? user,\n updater: user,\n items: undefined\n })\n )\n\n const lines = tx.getRepository(InvoiceItem)\n if (old) await lines.softDelete({ invoice: { id: invoice.id } })\n await lines.save(items.map(item => lines.create({ ...item, invoice, domain, creator: user, updater: user })))\n\n const saved = await invoiceInDomain(tx, domain, invoice.id)\n await recordInvoiceEvent(tx, domain, user, saved, old ? 'UPDATED' : 'CREATED')\n return saved\n}\n\nexport async function transitionInvoice(tx: EntityManager, domain: Domain, user: any, id: string, action: 'ISSUED' | 'VOID', reason?: string) {\n await lockDocumentDomain(tx, domain)\n const invoice = await invoiceInDomain(tx, domain, id)\n for (const guard of transitionGuards) await guard(tx, domain, invoice, action)\n\n if (action === 'ISSUED') {\n requireDraft(invoice.status)\n if (!invoice.issueDate || !invoice.dueDate || !invoice.items.length) {\n throw new Refusal('invoice-issue-required', {}, 'Issue date, due date and items are required')\n }\n /* What the amounts were reckoned with stays on the document; a later rule never recomputes them. */\n invoice.taxRounding = TAX_ROUNDING\n invoice.currencyDigits = currencyDigits(invoice.currency)\n invoice.status = InvoiceStatus.ISSUED\n } else {\n if (invoice.status === InvoiceStatus.VOID || !reason?.trim()) throw new Refusal('invoice-void-invalid', {}, 'A cancellation needs a reason and a non-void invoice')\n invoice.voidReason = reason.trim()\n invoice.status = InvoiceStatus.VOID\n }\n invoice.updater = user\n const { items, ...head } = invoice\n await tx.getRepository(Invoice).save(head)\n await recordInvoiceEvent(tx, domain, user, invoice, action, reason)\n return invoiceInDomain(tx, domain, id)\n}\n\nfunction recordInvoiceEvent(tx: EntityManager, domain: Domain, user: any, invoice: Invoice, action: string, reason?: string) {\n return tx.getRepository(InvoiceEvent).save({ domain, invoice, actor: user, action, reason, creator: user, updater: user, snapshot: snapshotOf(invoice) })\n}\n\n/** The document as printed — what an issued invoice said stays readable whatever changes later. */\nexport function snapshotOf(invoice: Invoice) {\n const keys = [\n 'id',\n ...HEADER_KEYS,\n 'status',\n 'dueDate',\n 'taxBasis',\n 'discountAmount',\n 'supplyAmount',\n 'taxAmount',\n 'totalAmount',\n 'taxRounding',\n 'currencyDigits',\n 'voidReason'\n ]\n return {\n ...Object.fromEntries(keys.map(key => [key, invoice[key] ?? null])),\n items: invoice.items.map(({ position, description, quantity, unit, unitPrice, discount, taxKind, taxRate, supplyAmount, receivedTaxAmount }) => ({\n position,\n description,\n quantity,\n unit: unit ?? null,\n unitPrice,\n discount,\n taxKind,\n taxRate,\n supplyAmount,\n receivedTaxAmount: receivedTaxAmount ?? null\n }))\n }\n}\n"]}
|
|
@@ -4,6 +4,7 @@ import { TaxKind } from './invoice-rules';
|
|
|
4
4
|
export declare class DocumentLineInput {
|
|
5
5
|
description: string;
|
|
6
6
|
quantity: number;
|
|
7
|
+
unit?: string;
|
|
7
8
|
unitPrice: number;
|
|
8
9
|
discount: number;
|
|
9
10
|
taxKind: TaxKind;
|
|
@@ -29,6 +30,7 @@ export declare abstract class DocumentInput {
|
|
|
29
30
|
notes?: string;
|
|
30
31
|
sourceReference?: string;
|
|
31
32
|
items: DocumentLineInput[];
|
|
33
|
+
discountAmount?: number;
|
|
32
34
|
}
|
|
33
35
|
export declare class NewInvoice extends DocumentInput {
|
|
34
36
|
counterpartyId?: string;
|
|
@@ -39,3 +41,13 @@ export declare class InvoiceList {
|
|
|
39
41
|
items: Invoice[];
|
|
40
42
|
total: number;
|
|
41
43
|
}
|
|
44
|
+
/** The forms a commercial document is made in for whoever reads it (ADR-0170 decision 10). */
|
|
45
|
+
export declare enum DocumentFileFormat {
|
|
46
|
+
HTML = "HTML",
|
|
47
|
+
PDF = "PDF"
|
|
48
|
+
}
|
|
49
|
+
export declare class DocumentFile {
|
|
50
|
+
fileName: string;
|
|
51
|
+
mimeType: string;
|
|
52
|
+
content: string;
|
|
53
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InvoiceList = exports.NewInvoice = exports.DocumentInput = exports.DocumentLineInput = void 0;
|
|
3
|
+
exports.DocumentFile = exports.DocumentFileFormat = exports.InvoiceList = exports.NewInvoice = exports.DocumentInput = exports.DocumentLineInput = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const type_graphql_1 = require("type-graphql");
|
|
6
6
|
const commercial_document_1 = require("./commercial-document");
|
|
@@ -17,6 +17,10 @@ tslib_1.__decorate([
|
|
|
17
17
|
(0, type_graphql_1.Field)(type => type_graphql_1.Float, { description: 'Quantity' }),
|
|
18
18
|
tslib_1.__metadata("design:type", Number)
|
|
19
19
|
], DocumentLineInput.prototype, "quantity", void 0);
|
|
20
|
+
tslib_1.__decorate([
|
|
21
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Unit the quantity counts in, as printed' }),
|
|
22
|
+
tslib_1.__metadata("design:type", String)
|
|
23
|
+
], DocumentLineInput.prototype, "unit", void 0);
|
|
20
24
|
tslib_1.__decorate([
|
|
21
25
|
(0, type_graphql_1.Field)(type => type_graphql_1.Float, { description: 'Price of one unit' }),
|
|
22
26
|
tslib_1.__metadata("design:type", Number)
|
|
@@ -112,6 +116,10 @@ tslib_1.__decorate([
|
|
|
112
116
|
(0, type_graphql_1.Field)(type => [DocumentLineInput], { description: 'Lines in printed order' }),
|
|
113
117
|
tslib_1.__metadata("design:type", Array)
|
|
114
118
|
], DocumentInput.prototype, "items", void 0);
|
|
119
|
+
tslib_1.__decorate([
|
|
120
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Float, { nullable: true, defaultValue: 0, description: 'Discount of the whole document, taken off the lines before tax; none when empty' }),
|
|
121
|
+
tslib_1.__metadata("design:type", Number)
|
|
122
|
+
], DocumentInput.prototype, "discountAmount", void 0);
|
|
115
123
|
exports.DocumentInput = DocumentInput = tslib_1.__decorate([
|
|
116
124
|
(0, type_graphql_1.InputType)({ description: 'The printed head and lines of a commercial document' })
|
|
117
125
|
], DocumentInput);
|
|
@@ -147,4 +155,32 @@ tslib_1.__decorate([
|
|
|
147
155
|
exports.InvoiceList = InvoiceList = tslib_1.__decorate([
|
|
148
156
|
(0, type_graphql_1.ObjectType)({ description: 'A page of invoices' })
|
|
149
157
|
], InvoiceList);
|
|
158
|
+
/** The forms a commercial document is made in for whoever reads it (ADR-0170 decision 10). */
|
|
159
|
+
var DocumentFileFormat;
|
|
160
|
+
(function (DocumentFileFormat) {
|
|
161
|
+
DocumentFileFormat["HTML"] = "HTML";
|
|
162
|
+
DocumentFileFormat["PDF"] = "PDF";
|
|
163
|
+
})(DocumentFileFormat || (exports.DocumentFileFormat = DocumentFileFormat = {}));
|
|
164
|
+
(0, type_graphql_1.registerEnumType)(DocumentFileFormat, {
|
|
165
|
+
name: 'DocumentFileFormat',
|
|
166
|
+
description: 'The form of a printed commercial document: an HTML page to preview and print, or a PDF to download.'
|
|
167
|
+
});
|
|
168
|
+
let DocumentFile = class DocumentFile {
|
|
169
|
+
};
|
|
170
|
+
exports.DocumentFile = DocumentFile;
|
|
171
|
+
tslib_1.__decorate([
|
|
172
|
+
(0, type_graphql_1.Field)({ description: 'The file name, with its extension - the kind and the number.' }),
|
|
173
|
+
tslib_1.__metadata("design:type", String)
|
|
174
|
+
], DocumentFile.prototype, "fileName", void 0);
|
|
175
|
+
tslib_1.__decorate([
|
|
176
|
+
(0, type_graphql_1.Field)({ description: 'The media type.' }),
|
|
177
|
+
tslib_1.__metadata("design:type", String)
|
|
178
|
+
], DocumentFile.prototype, "mimeType", void 0);
|
|
179
|
+
tslib_1.__decorate([
|
|
180
|
+
(0, type_graphql_1.Field)({ description: 'The bytes of the file, base64.' }),
|
|
181
|
+
tslib_1.__metadata("design:type", String)
|
|
182
|
+
], DocumentFile.prototype, "content", void 0);
|
|
183
|
+
exports.DocumentFile = DocumentFile = tslib_1.__decorate([
|
|
184
|
+
(0, type_graphql_1.ObjectType)({ description: 'A printed commercial document - its file name, its media type and its bytes in base64.' })
|
|
185
|
+
], DocumentFile);
|
|
150
186
|
//# sourceMappingURL=invoice-type.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoice-type.js","sourceRoot":"","sources":["../../../server/service/invoice/invoice-type.ts"],"names":[],"mappings":";;;;AAAA,+
|
|
1
|
+
{"version":3,"file":"invoice-type.js","sourceRoot":"","sources":["../../../server/service/invoice/invoice-type.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, Float, InputType, Int, ObjectType, registerEnumType } from 'type-graphql'\n\nimport { DocumentDirection } from './commercial-document'\nimport { Invoice, InvoiceTaxBasis } from './invoice'\nimport { TaxKind } from './invoice-rules'\n\n@InputType({ description: 'One line of a commercial document to save' })\nexport class DocumentLineInput {\n @Field({ description: 'What the line charges for' }) description: string\n @Field(type => Float, { description: 'Quantity' }) quantity: number\n @Field({ nullable: true, description: 'Unit the quantity counts in, as printed' }) unit?: string\n @Field(type => Float, { description: 'Price of one unit' }) unitPrice: number\n @Field(type => Float, { defaultValue: 0, description: 'Discount taken off the line, in the document currency' }) discount: number\n @Field(type => TaxKind, { defaultValue: TaxKind.TAXABLE, description: 'Taxable, zero-rated or exempt' }) taxKind: TaxKind\n @Field(type => Float, { defaultValue: 0, description: 'Tax rate in percent; zero unless taxable' }) taxRate: number\n @Field(type => Float, { nullable: true, description: 'Tax the sender printed on the line — only on a received invoice' }) receivedTaxAmount?: number\n}\n\n/** The head and lines every commercial document writes; invoice and quotation inputs add their own dates. */\n@InputType({ description: 'The printed head and lines of a commercial document' })\nexport abstract class DocumentInput {\n @Field({ description: 'Document number, unique in the domain' }) number: string\n @Field(type => DocumentDirection, { description: 'SALES for a document the domain sends, PURCHASE for one it receives' }) direction: DocumentDirection\n @Field({ description: 'ISO 4217 currency code' }) currency: string\n @Field({ description: 'Language the document is printed in: ko or en' }) language: string\n @Field({ description: 'Supplier name as printed' }) supplierName: string\n @Field({ nullable: true, description: 'Supplier address as printed' }) supplierAddress?: string\n @Field({ nullable: true, description: 'Supplier business or tax registration number' }) supplierRegistrationNumber?: string\n @Field({ description: 'Buyer name as printed' }) buyerName: string\n @Field({ nullable: true, description: 'Buyer address as printed' }) buyerAddress?: string\n @Field({ nullable: true, description: 'Buyer business or tax registration number' }) buyerRegistrationNumber?: string\n @Field({ nullable: true, description: 'Issue date, YYYY-MM-DD' }) issueDate?: string\n @Field({ nullable: true, description: 'Buyer purchase order reference' }) purchaseOrder?: string\n @Field({ nullable: true, description: 'Payment terms as printed' }) paymentTerms?: string\n @Field({ nullable: true, description: 'Bank account to pay into, as printed' }) bankDetails?: string\n @Field({ nullable: true, description: 'Notes printed on the document' }) notes?: string\n @Field({ nullable: true, description: 'Reference to the record the document was made from' }) sourceReference?: string\n @Field(type => [DocumentLineInput], { description: 'Lines in printed order' }) items: DocumentLineInput[]\n @Field(type => Float, { nullable: true, defaultValue: 0, description: 'Discount of the whole document, taken off the lines before tax; none when empty' })\n discountAmount?: number\n}\n\n@InputType({ description: 'An invoice draft to create or replace' })\nexport class NewInvoice extends DocumentInput {\n @Field({ nullable: true, description: 'Id of the other party among the domain counterparties; empty leaves it unknown' }) counterpartyId?: string\n @Field({ nullable: true, description: 'Payment due date, YYYY-MM-DD' }) dueDate?: string\n @Field(type => InvoiceTaxBasis, { defaultValue: InvoiceTaxBasis.CALCULATED, description: 'Reckoned tax, or the tax a received invoice printed' })\n taxBasis: InvoiceTaxBasis\n}\n\n@ObjectType({ description: 'A page of invoices' })\nexport class InvoiceList {\n @Field(type => [Invoice], { description: 'Invoices on the page' }) items: Invoice[]\n @Field(type => Int, { description: 'Number of invoices matching the list parameters' }) total: number\n}\n\n/** The forms a commercial document is made in for whoever reads it (ADR-0170 decision 10). */\nexport enum DocumentFileFormat {\n HTML = 'HTML',\n PDF = 'PDF'\n}\n\nregisterEnumType(DocumentFileFormat, {\n name: 'DocumentFileFormat',\n description: 'The form of a printed commercial document: an HTML page to preview and print, or a PDF to download.'\n})\n\n@ObjectType({ description: 'A printed commercial document - its file name, its media type and its bytes in base64.' })\nexport class DocumentFile {\n @Field({ description: 'The file name, with its extension - the kind and the number.' }) fileName: string\n @Field({ description: 'The media type.' }) mimeType: string\n @Field({ description: 'The bytes of the file, base64.' }) content: string\n}\n"]}
|