@things-factory/quotation 10.1.124 → 10.1.130
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/bootstrap.js +1 -7
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/index.d.ts +2 -1
- package/dist-client/index.js +2 -1
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/quotation-editor.d.ts +44 -0
- package/dist-client/pages/quotation-editor.js +315 -0
- package/dist-client/pages/quotation-editor.js.map +1 -0
- package/dist-client/pages/quotation-list.d.ts +36 -0
- package/dist-client/pages/quotation-list.js +113 -0
- package/dist-client/pages/quotation-list.js.map +1 -0
- package/dist-client/quotation-attach.d.ts +16 -0
- package/dist-client/quotation-attach.js +8 -0
- package/dist-client/quotation-attach.js.map +1 -0
- package/dist-client/quotation-words.d.ts +1 -0
- package/dist-client/quotation-words.js +13 -0
- package/dist-client/quotation-words.js.map +1 -0
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +4 -4
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/index.d.ts +1 -0
- package/dist-server/index.js +1 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/service/index.d.ts +8 -3
- package/dist-server/service/index.js +9 -17
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/quotation/index.d.ts +8 -5
- package/dist-server/service/quotation/index.js +8 -6
- package/dist-server/service/quotation/index.js.map +1 -1
- package/dist-server/service/quotation/quotation-event-query.d.ts +9 -0
- package/dist-server/service/quotation/quotation-event-query.js +55 -0
- package/dist-server/service/quotation/quotation-event-query.js.map +1 -0
- package/dist-server/service/quotation/quotation-event.d.ts +22 -0
- package/dist-server/service/quotation/quotation-event.js +98 -0
- package/dist-server/service/quotation/quotation-event.js.map +1 -0
- package/dist-server/service/quotation/quotation-item-query.d.ts +8 -0
- package/dist-server/service/quotation/quotation-item-query.js +45 -0
- package/dist-server/service/quotation/quotation-item-query.js.map +1 -0
- package/dist-server/service/quotation/quotation-item.d.ts +18 -0
- package/dist-server/service/quotation/quotation-item.js +75 -0
- package/dist-server/service/quotation/quotation-item.js.map +1 -0
- package/dist-server/service/quotation/quotation-mutation.d.ts +7 -6
- package/dist-server/service/quotation/quotation-mutation.js +66 -140
- package/dist-server/service/quotation/quotation-mutation.js.map +1 -1
- package/dist-server/service/quotation/quotation-query.d.ts +10 -5
- package/dist-server/service/quotation/quotation-query.js +69 -41
- package/dist-server/service/quotation/quotation-query.js.map +1 -1
- package/dist-server/service/quotation/quotation-rules.d.ts +6 -0
- package/dist-server/service/quotation/quotation-rules.js +22 -0
- package/dist-server/service/quotation/quotation-rules.js.map +1 -0
- package/dist-server/service/quotation/quotation-service.d.ts +23 -0
- package/dist-server/service/quotation/quotation-service.js +114 -0
- package/dist-server/service/quotation/quotation-service.js.map +1 -0
- package/dist-server/service/quotation/quotation-type.d.ts +4 -18
- package/dist-server/service/quotation/quotation-type.js +9 -63
- package/dist-server/service/quotation/quotation-type.js.map +1 -1
- package/dist-server/service/quotation/quotation.d.ts +19 -11
- package/dist-server/service/quotation/quotation.js +41 -44
- package/dist-server/service/quotation/quotation.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -6
- package/things-factory.config.js +2 -2
- package/translations/en.json +36 -1
- package/translations/ko.json +36 -1
- package/dist-client/actions/main.d.ts +0 -1
- package/dist-client/actions/main.js +0 -2
- package/dist-client/actions/main.js.map +0 -1
- package/dist-client/activities/activity-quotation-edit.d.ts +0 -14
- package/dist-client/activities/activity-quotation-edit.js +0 -87
- package/dist-client/activities/activity-quotation-edit.js.map +0 -1
- package/dist-client/activities/activity-quotation-view.d.ts +0 -14
- package/dist-client/activities/activity-quotation-view.js +0 -87
- package/dist-client/activities/activity-quotation-view.js.map +0 -1
- package/dist-client/pages/main.d.ts +0 -1
- package/dist-client/pages/main.js +0 -36
- package/dist-client/pages/main.js.map +0 -1
- package/dist-client/pages/quotation/quotation-importer.d.ts +0 -23
- package/dist-client/pages/quotation/quotation-importer.js +0 -93
- package/dist-client/pages/quotation/quotation-importer.js.map +0 -1
- package/dist-client/pages/quotation/quotation-list-page.d.ts +0 -55
- package/dist-client/pages/quotation/quotation-list-page.js +0 -262
- package/dist-client/pages/quotation/quotation-list-page.js.map +0 -1
- package/dist-client/reducers/main.d.ts +0 -6
- package/dist-client/reducers/main.js +0 -14
- package/dist-client/reducers/main.js.map +0 -1
- package/dist-server/activities/activity-quotation.d.ts +0 -42
- package/dist-server/activities/activity-quotation.js +0 -114
- package/dist-server/activities/activity-quotation.js.map +0 -1
- package/dist-server/activities/index.d.ts +0 -1
- package/dist-server/activities/index.js +0 -17
- package/dist-server/activities/index.js.map +0 -1
- package/dist-server/service/quotation/event-subscriber.d.ts +0 -7
- package/dist-server/service/quotation/event-subscriber.js +0 -21
- package/dist-server/service/quotation/event-subscriber.js.map +0 -1
- package/dist-server/service/quotation/quotation-history.d.ts +0 -25
- package/dist-server/service/quotation/quotation-history.js +0 -132
- package/dist-server/service/quotation/quotation-history.js.map +0 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.quotationInDomain = quotationInDomain;
|
|
4
|
+
exports.saveQuotation = saveQuotation;
|
|
5
|
+
exports.transitionQuotation = transitionQuotation;
|
|
6
|
+
exports.reviseQuotation = reviseQuotation;
|
|
7
|
+
exports.snapshotOf = snapshotOf;
|
|
8
|
+
const invoice_1 = require("@things-factory/invoice");
|
|
9
|
+
const shell_1 = require("@things-factory/shell");
|
|
10
|
+
const quotation_1 = require("./quotation");
|
|
11
|
+
const quotation_event_1 = require("./quotation-event");
|
|
12
|
+
const quotation_item_1 = require("./quotation-item");
|
|
13
|
+
const quotation_rules_1 = require("./quotation-rules");
|
|
14
|
+
async function quotationInDomain(tx, domain, id) {
|
|
15
|
+
const quotation = await tx.getRepository(quotation_1.Quotation).findOne({ where: { id, domain: { id: domain.id } }, relations: ['items', 'creator'] });
|
|
16
|
+
if (!quotation)
|
|
17
|
+
throw new shell_1.Refusal('quotation-not-found', {}, 'Quotation not found');
|
|
18
|
+
quotation.items.sort((a, b) => a.position - b.position);
|
|
19
|
+
return quotation;
|
|
20
|
+
}
|
|
21
|
+
async function saveQuotation(tx, domain, user, input, id) {
|
|
22
|
+
await (0, invoice_1.lockDocumentDomain)(tx, domain);
|
|
23
|
+
const old = id ? await quotationInDomain(tx, domain, id) : undefined;
|
|
24
|
+
if (old && old.status !== quotation_1.QuotationStatus.DRAFT)
|
|
25
|
+
throw new shell_1.Refusal('quotation-draft-required', {}, 'Revise a sent quotation to change it');
|
|
26
|
+
const header = (0, invoice_1.normalizedHeader)(input);
|
|
27
|
+
if (!(0, invoice_1.validDay)(input.validUntil))
|
|
28
|
+
throw new shell_1.Refusal('quotation-valid-until', {}, 'A valid last day is required');
|
|
29
|
+
/* A revision answers the quotation it revises: it keeps the number. */
|
|
30
|
+
if (old && old.version > 1 && header.number !== old.number)
|
|
31
|
+
throw new shell_1.Refusal('quotation-revision-number', {}, 'A revision keeps its quotation number');
|
|
32
|
+
const repository = tx.getRepository(quotation_1.Quotation);
|
|
33
|
+
const version = old?.version ?? 1;
|
|
34
|
+
const duplicate = await repository.findOne({ where: { domain: { id: domain.id }, number: header.number, version } });
|
|
35
|
+
if (duplicate && duplicate.id !== id)
|
|
36
|
+
throw new shell_1.Refusal('quotation-number-duplicate', {}, 'Quotation number already exists');
|
|
37
|
+
const { items, supplyAmount, taxAmount, totalAmount } = (0, invoice_1.calculateDocument)(input.items, header.currency);
|
|
38
|
+
const quotation = await repository.save(repository.create({ ...old, ...header, version, validUntil: input.validUntil, supplyAmount, taxAmount, totalAmount, domain, creator: old?.creator ?? user, updater: user, items: undefined }));
|
|
39
|
+
const lines = tx.getRepository(quotation_item_1.QuotationItem);
|
|
40
|
+
if (old)
|
|
41
|
+
await lines.softDelete({ quotation: { id: quotation.id } });
|
|
42
|
+
await lines.save(items.map(({ receivedTaxAmount, ...item }) => lines.create({ ...item, quotation, domain, creator: user, updater: user })));
|
|
43
|
+
const saved = await quotationInDomain(tx, domain, quotation.id);
|
|
44
|
+
await recordQuotationEvent(tx, domain, user, saved, old ? 'UPDATED' : 'CREATED');
|
|
45
|
+
return saved;
|
|
46
|
+
}
|
|
47
|
+
async function transitionQuotation(tx, domain, user, id, action, reason) {
|
|
48
|
+
await (0, invoice_1.lockDocumentDomain)(tx, domain);
|
|
49
|
+
const quotation = await quotationInDomain(tx, domain, id);
|
|
50
|
+
const next = (0, quotation_rules_1.quotationTransition)(quotation.status, action, quotation.validUntil, (0, invoice_1.calendarDay)(domain.timezone));
|
|
51
|
+
if (!next)
|
|
52
|
+
throw new shell_1.Refusal('quotation-transition-invalid', {}, 'The quotation status or validity does not allow this');
|
|
53
|
+
if (next === quotation_1.QuotationStatus.REJECTED && !reason?.trim())
|
|
54
|
+
throw new shell_1.Refusal('quotation-reason-required', {}, 'Say why the buyer declined');
|
|
55
|
+
if (next === quotation_1.QuotationStatus.SENT) {
|
|
56
|
+
if (!quotation.issueDate)
|
|
57
|
+
throw new shell_1.Refusal('quotation-issue-date-required', {}, 'A quotation is sent with its issue date');
|
|
58
|
+
/* What the amounts were reckoned with stays on the quotation; a later rule never recomputes them. */
|
|
59
|
+
quotation.taxRounding = invoice_1.TAX_ROUNDING;
|
|
60
|
+
quotation.currencyDigits = (0, invoice_1.currencyDigits)(quotation.currency);
|
|
61
|
+
}
|
|
62
|
+
quotation.status = next;
|
|
63
|
+
if (next === quotation_1.QuotationStatus.REJECTED)
|
|
64
|
+
quotation.outcomeReason = reason.trim();
|
|
65
|
+
quotation.updater = user;
|
|
66
|
+
const { items, ...head } = quotation;
|
|
67
|
+
await tx.getRepository(quotation_1.Quotation).save(head);
|
|
68
|
+
await recordQuotationEvent(tx, domain, user, quotation, action, reason);
|
|
69
|
+
return quotationInDomain(tx, domain, id);
|
|
70
|
+
}
|
|
71
|
+
/** A new draft version of a sent quotation, with the same number; the version it replaces is superseded. */
|
|
72
|
+
async function reviseQuotation(tx, domain, user, id) {
|
|
73
|
+
await (0, invoice_1.lockDocumentDomain)(tx, domain);
|
|
74
|
+
const old = await quotationInDomain(tx, domain, id);
|
|
75
|
+
if (!(0, quotation_rules_1.revisable)(old.status))
|
|
76
|
+
throw new shell_1.Refusal('quotation-revision-invalid', {}, 'Only a sent quotation nobody accepted can be revised');
|
|
77
|
+
const versions = await tx.getRepository(quotation_1.Quotation).find({ where: { domain: { id: domain.id }, number: old.number } });
|
|
78
|
+
if (old.version !== Math.max(...versions.map(one => one.version)))
|
|
79
|
+
throw new shell_1.Refusal('quotation-latest-required', {}, 'Revise the latest version');
|
|
80
|
+
const repository = tx.getRepository(quotation_1.Quotation);
|
|
81
|
+
const { id: _id, items, createdAt, updatedAt, creator, updater, status, taxRounding, currencyDigits: digits, outcomeReason, ...document } = old;
|
|
82
|
+
const revision = await repository.save(repository.create({ ...document, version: old.version + 1, status: quotation_1.QuotationStatus.DRAFT, domain, creator: user, updater: user }));
|
|
83
|
+
const lines = tx.getRepository(quotation_item_1.QuotationItem);
|
|
84
|
+
await lines.save(items.map(({ id: _line, quotation: _parent, quotationId, createdAt, updatedAt, deletedAt, creator, updater, ...line }) => lines.create({ ...line, quotation: revision, domain, creator: user, updater: user })));
|
|
85
|
+
old.status = quotation_1.QuotationStatus.SUPERSEDED;
|
|
86
|
+
old.updater = user;
|
|
87
|
+
const { items: _items, ...head } = old;
|
|
88
|
+
await repository.save(head);
|
|
89
|
+
await recordQuotationEvent(tx, domain, user, old, 'SUPERSEDED');
|
|
90
|
+
const saved = await quotationInDomain(tx, domain, revision.id);
|
|
91
|
+
await recordQuotationEvent(tx, domain, user, saved, 'REVISED');
|
|
92
|
+
return saved;
|
|
93
|
+
}
|
|
94
|
+
function recordQuotationEvent(tx, domain, user, quotation, action, reason) {
|
|
95
|
+
return tx.getRepository(quotation_event_1.QuotationEvent).save({ domain, quotation, actor: user, action, reason, creator: user, updater: user, snapshot: snapshotOf(quotation) });
|
|
96
|
+
}
|
|
97
|
+
/** The quotation as printed. */
|
|
98
|
+
function snapshotOf(quotation) {
|
|
99
|
+
const keys = ['id', ...invoice_1.HEADER_KEYS, 'version', 'status', 'validUntil', 'supplyAmount', 'taxAmount', 'totalAmount', 'taxRounding', 'currencyDigits', 'outcomeReason'];
|
|
100
|
+
return {
|
|
101
|
+
...Object.fromEntries(keys.map(key => [key, quotation[key] ?? null])),
|
|
102
|
+
items: quotation.items.map(({ position, description, quantity, unitPrice, discount, taxKind, taxRate, supplyAmount }) => ({
|
|
103
|
+
position,
|
|
104
|
+
description,
|
|
105
|
+
quantity,
|
|
106
|
+
unitPrice,
|
|
107
|
+
discount,
|
|
108
|
+
taxKind,
|
|
109
|
+
taxRate,
|
|
110
|
+
supplyAmount
|
|
111
|
+
}))
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=quotation-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quotation-service.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { calculateDocument, calendarDay, currencyDigits, HEADER_KEYS, lockDocumentDomain, normalizedHeader, TAX_ROUNDING, validDay } from '@things-factory/invoice'\nimport { Domain, Refusal } from '@things-factory/shell'\nimport { EntityManager } from 'typeorm'\n\nimport { Quotation, QuotationStatus } from './quotation'\nimport { QuotationEvent } from './quotation-event'\nimport { QuotationItem } from './quotation-item'\nimport { QuotationAction, quotationTransition, revisable } from './quotation-rules'\nimport { NewQuotation } from './quotation-type'\n\nexport async function quotationInDomain(tx: EntityManager, domain: Domain, id: string) {\n const quotation = await tx.getRepository(Quotation).findOne({ where: { id, domain: { id: domain.id } }, relations: ['items', 'creator'] })\n if (!quotation) throw new Refusal('quotation-not-found', {}, 'Quotation not found')\n quotation.items.sort((a, b) => a.position - b.position)\n return quotation\n}\n\nexport async function saveQuotation(tx: EntityManager, domain: Domain, user: any, input: NewQuotation, id?: string) {\n await lockDocumentDomain(tx, domain)\n const old = id ? await quotationInDomain(tx, domain, id) : undefined\n if (old && old.status !== QuotationStatus.DRAFT) throw new Refusal('quotation-draft-required', {}, 'Revise a sent quotation to change it')\n\n const header = normalizedHeader(input)\n if (!validDay(input.validUntil)) throw new Refusal('quotation-valid-until', {}, 'A valid last day is required')\n /* A revision answers the quotation it revises: it keeps the number. */\n if (old && old.version > 1 && header.number !== old.number) throw new Refusal('quotation-revision-number', {}, 'A revision keeps its quotation number')\n\n const repository = tx.getRepository(Quotation)\n const version = old?.version ?? 1\n const duplicate = await repository.findOne({ where: { domain: { id: domain.id }, number: header.number, version } })\n if (duplicate && duplicate.id !== id) throw new Refusal('quotation-number-duplicate', {}, 'Quotation number already exists')\n\n const { items, supplyAmount, taxAmount, totalAmount } = calculateDocument(input.items, header.currency)\n const quotation = await repository.save(\n repository.create({ ...old, ...header, version, validUntil: input.validUntil, supplyAmount, taxAmount, totalAmount, domain, creator: old?.creator ?? user, updater: user, items: undefined })\n )\n const lines = tx.getRepository(QuotationItem)\n if (old) await lines.softDelete({ quotation: { id: quotation.id } })\n await lines.save(items.map(({ receivedTaxAmount, ...item }) => lines.create({ ...item, quotation, domain, creator: user, updater: user })))\n\n const saved = await quotationInDomain(tx, domain, quotation.id)\n await recordQuotationEvent(tx, domain, user, saved, old ? 'UPDATED' : 'CREATED')\n return saved\n}\n\nexport async function transitionQuotation(tx: EntityManager, domain: Domain, user: any, id: string, action: QuotationAction, reason?: string) {\n await lockDocumentDomain(tx, domain)\n const quotation = await quotationInDomain(tx, domain, id)\n const next = quotationTransition(quotation.status, action, quotation.validUntil, calendarDay(domain.timezone))\n if (!next) throw new Refusal('quotation-transition-invalid', {}, 'The quotation status or validity does not allow this')\n if (next === QuotationStatus.REJECTED && !reason?.trim()) throw new Refusal('quotation-reason-required', {}, 'Say why the buyer declined')\n if (next === QuotationStatus.SENT) {\n if (!quotation.issueDate) throw new Refusal('quotation-issue-date-required', {}, 'A quotation is sent with its issue date')\n /* What the amounts were reckoned with stays on the quotation; a later rule never recomputes them. */\n quotation.taxRounding = TAX_ROUNDING\n quotation.currencyDigits = currencyDigits(quotation.currency)\n }\n quotation.status = next\n if (next === QuotationStatus.REJECTED) quotation.outcomeReason = reason!.trim()\n quotation.updater = user\n const { items, ...head } = quotation\n await tx.getRepository(Quotation).save(head)\n await recordQuotationEvent(tx, domain, user, quotation, action, reason)\n return quotationInDomain(tx, domain, id)\n}\n\n/** A new draft version of a sent quotation, with the same number; the version it replaces is superseded. */\nexport async function reviseQuotation(tx: EntityManager, domain: Domain, user: any, id: string) {\n await lockDocumentDomain(tx, domain)\n const old = await quotationInDomain(tx, domain, id)\n if (!revisable(old.status)) throw new Refusal('quotation-revision-invalid', {}, 'Only a sent quotation nobody accepted can be revised')\n const versions = await tx.getRepository(Quotation).find({ where: { domain: { id: domain.id }, number: old.number } })\n if (old.version !== Math.max(...versions.map(one => one.version))) throw new Refusal('quotation-latest-required', {}, 'Revise the latest version')\n\n const repository = tx.getRepository(Quotation)\n const { id: _id, items, createdAt, updatedAt, creator, updater, status, taxRounding, currencyDigits: digits, outcomeReason, ...document } = old\n const revision = await repository.save(repository.create({ ...document, version: old.version + 1, status: QuotationStatus.DRAFT, domain, creator: user, updater: user }))\n const lines = tx.getRepository(QuotationItem)\n await lines.save(\n items.map(({ id: _line, quotation: _parent, quotationId, createdAt, updatedAt, deletedAt, creator, updater, ...line }) =>\n lines.create({ ...line, quotation: revision, domain, creator: user, updater: user })\n )\n )\n\n old.status = QuotationStatus.SUPERSEDED\n old.updater = user\n const { items: _items, ...head } = old\n await repository.save(head)\n await recordQuotationEvent(tx, domain, user, old, 'SUPERSEDED')\n const saved = await quotationInDomain(tx, domain, revision.id)\n await recordQuotationEvent(tx, domain, user, saved, 'REVISED')\n return saved\n}\n\nfunction recordQuotationEvent(tx: EntityManager, domain: Domain, user: any, quotation: Quotation, action: string, reason?: string) {\n return tx.getRepository(QuotationEvent).save({ domain, quotation, actor: user, action, reason, creator: user, updater: user, snapshot: snapshotOf(quotation) })\n}\n\n/** The quotation as printed. */\nexport function snapshotOf(quotation: Quotation) {\n const keys = ['id', ...HEADER_KEYS, 'version', 'status', 'validUntil', 'supplyAmount', 'taxAmount', 'totalAmount', 'taxRounding', 'currencyDigits', 'outcomeReason']\n return {\n ...Object.fromEntries(keys.map(key => [key, quotation[key] ?? null])),\n items: quotation.items.map(({ position, description, quantity, unitPrice, discount, taxKind, taxRate, supplyAmount }) => ({\n position,\n description,\n quantity,\n unitPrice,\n discount,\n taxKind,\n taxRate,\n supplyAmount\n }))\n }\n}\n"]}
|
|
@@ -1,21 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Quotation
|
|
3
|
-
export declare class NewQuotation {
|
|
4
|
-
|
|
5
|
-
description?: string;
|
|
6
|
-
state?: QuotationStatus;
|
|
7
|
-
active?: boolean;
|
|
8
|
-
params?: string;
|
|
9
|
-
thumbnail?: FileUpload;
|
|
10
|
-
}
|
|
11
|
-
export declare class QuotationPatch {
|
|
12
|
-
id?: string;
|
|
13
|
-
name?: string;
|
|
14
|
-
description?: string;
|
|
15
|
-
state?: QuotationStatus;
|
|
16
|
-
active?: boolean;
|
|
17
|
-
thumbnail?: FileUpload;
|
|
18
|
-
cuFlag?: string;
|
|
1
|
+
import { DocumentInput } from '@things-factory/invoice';
|
|
2
|
+
import { Quotation } from './quotation';
|
|
3
|
+
export declare class NewQuotation extends DocumentInput {
|
|
4
|
+
validUntil: string;
|
|
19
5
|
}
|
|
20
6
|
export declare class QuotationList {
|
|
21
7
|
items: Quotation[];
|
|
@@ -1,86 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.QuotationList = exports.
|
|
3
|
+
exports.QuotationList = exports.NewQuotation = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const
|
|
5
|
+
const invoice_1 = require("@things-factory/invoice");
|
|
6
6
|
const type_graphql_1 = require("type-graphql");
|
|
7
7
|
const quotation_1 = require("./quotation");
|
|
8
|
-
let NewQuotation = class NewQuotation {
|
|
8
|
+
let NewQuotation = class NewQuotation extends invoice_1.DocumentInput {
|
|
9
9
|
};
|
|
10
10
|
exports.NewQuotation = NewQuotation;
|
|
11
11
|
tslib_1.__decorate([
|
|
12
|
-
(0, type_graphql_1.Field)(),
|
|
12
|
+
(0, type_graphql_1.Field)({ description: 'Last day the offer holds, YYYY-MM-DD' }),
|
|
13
13
|
tslib_1.__metadata("design:type", String)
|
|
14
|
-
], NewQuotation.prototype, "
|
|
15
|
-
tslib_1.__decorate([
|
|
16
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
17
|
-
tslib_1.__metadata("design:type", String)
|
|
18
|
-
], NewQuotation.prototype, "description", void 0);
|
|
19
|
-
tslib_1.__decorate([
|
|
20
|
-
(0, type_graphql_1.Field)(type => quotation_1.QuotationStatus, { nullable: true }),
|
|
21
|
-
tslib_1.__metadata("design:type", String)
|
|
22
|
-
], NewQuotation.prototype, "state", void 0);
|
|
23
|
-
tslib_1.__decorate([
|
|
24
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
25
|
-
tslib_1.__metadata("design:type", Boolean)
|
|
26
|
-
], NewQuotation.prototype, "active", void 0);
|
|
27
|
-
tslib_1.__decorate([
|
|
28
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
29
|
-
tslib_1.__metadata("design:type", String)
|
|
30
|
-
], NewQuotation.prototype, "params", void 0);
|
|
31
|
-
tslib_1.__decorate([
|
|
32
|
-
(0, type_graphql_1.Field)(type => GraphQLUpload_js_1.default, { nullable: true }),
|
|
33
|
-
tslib_1.__metadata("design:type", Object)
|
|
34
|
-
], NewQuotation.prototype, "thumbnail", void 0);
|
|
14
|
+
], NewQuotation.prototype, "validUntil", void 0);
|
|
35
15
|
exports.NewQuotation = NewQuotation = tslib_1.__decorate([
|
|
36
|
-
(0, type_graphql_1.InputType)()
|
|
16
|
+
(0, type_graphql_1.InputType)({ description: 'A quotation draft to create or replace' })
|
|
37
17
|
], NewQuotation);
|
|
38
|
-
let QuotationPatch = class QuotationPatch {
|
|
39
|
-
};
|
|
40
|
-
exports.QuotationPatch = QuotationPatch;
|
|
41
|
-
tslib_1.__decorate([
|
|
42
|
-
(0, type_graphql_1.Field)(type => type_graphql_1.ID, { nullable: true }),
|
|
43
|
-
tslib_1.__metadata("design:type", String)
|
|
44
|
-
], QuotationPatch.prototype, "id", void 0);
|
|
45
|
-
tslib_1.__decorate([
|
|
46
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
47
|
-
tslib_1.__metadata("design:type", String)
|
|
48
|
-
], QuotationPatch.prototype, "name", void 0);
|
|
49
|
-
tslib_1.__decorate([
|
|
50
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
51
|
-
tslib_1.__metadata("design:type", String)
|
|
52
|
-
], QuotationPatch.prototype, "description", void 0);
|
|
53
|
-
tslib_1.__decorate([
|
|
54
|
-
(0, type_graphql_1.Field)(type => quotation_1.QuotationStatus, { nullable: true }),
|
|
55
|
-
tslib_1.__metadata("design:type", String)
|
|
56
|
-
], QuotationPatch.prototype, "state", void 0);
|
|
57
|
-
tslib_1.__decorate([
|
|
58
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
59
|
-
tslib_1.__metadata("design:type", Boolean)
|
|
60
|
-
], QuotationPatch.prototype, "active", void 0);
|
|
61
|
-
tslib_1.__decorate([
|
|
62
|
-
(0, type_graphql_1.Field)(type => GraphQLUpload_js_1.default, { nullable: true }),
|
|
63
|
-
tslib_1.__metadata("design:type", Object)
|
|
64
|
-
], QuotationPatch.prototype, "thumbnail", void 0);
|
|
65
|
-
tslib_1.__decorate([
|
|
66
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
67
|
-
tslib_1.__metadata("design:type", String)
|
|
68
|
-
], QuotationPatch.prototype, "cuFlag", void 0);
|
|
69
|
-
exports.QuotationPatch = QuotationPatch = tslib_1.__decorate([
|
|
70
|
-
(0, type_graphql_1.InputType)()
|
|
71
|
-
], QuotationPatch);
|
|
72
18
|
let QuotationList = class QuotationList {
|
|
73
19
|
};
|
|
74
20
|
exports.QuotationList = QuotationList;
|
|
75
21
|
tslib_1.__decorate([
|
|
76
|
-
(0, type_graphql_1.Field)(type => [quotation_1.Quotation]),
|
|
22
|
+
(0, type_graphql_1.Field)(type => [quotation_1.Quotation], { description: 'Quotations on the page' }),
|
|
77
23
|
tslib_1.__metadata("design:type", Array)
|
|
78
24
|
], QuotationList.prototype, "items", void 0);
|
|
79
25
|
tslib_1.__decorate([
|
|
80
|
-
(0, type_graphql_1.Field)(type => type_graphql_1.Int),
|
|
26
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'Number of quotations matching the list parameters' }),
|
|
81
27
|
tslib_1.__metadata("design:type", Number)
|
|
82
28
|
], QuotationList.prototype, "total", void 0);
|
|
83
29
|
exports.QuotationList = QuotationList = tslib_1.__decorate([
|
|
84
|
-
(0, type_graphql_1.ObjectType)()
|
|
30
|
+
(0, type_graphql_1.ObjectType)({ description: 'A page of quotations' })
|
|
85
31
|
], QuotationList);
|
|
86
32
|
//# sourceMappingURL=quotation-type.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotation-type.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation-type.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"quotation-type.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation-type.ts"],"names":[],"mappings":";;;;AAAA,qDAAuD;AACvD,+CAAgE;AAEhE,2CAAuC;AAGhC,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,uBAAa;CAE9C,CAAA;AAFY,oCAAY;AACyC;IAA/D,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;;gDAAmB;uBADvE,YAAY;IADxB,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,wCAAwC,EAAE,CAAC;GACxD,YAAY,CAExB;AAGM,IAAM,aAAa,GAAnB,MAAM,aAAa;CAGzB,CAAA;AAHY,sCAAa;AAC+C;IAAtE,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,qBAAS,CAAC,EAAE,EAAE,WAAW,EAAE,wBAAwB,EAAE,CAAC;;4CAAmB;AACC;IAAzF,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,WAAW,EAAE,mDAAmD,EAAE,CAAC;;4CAAc;wBAF5F,aAAa;IADzB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,sBAAsB,EAAE,CAAC;GACvC,aAAa,CAGzB","sourcesContent":["import { DocumentInput } from '@things-factory/invoice'\nimport { Field, InputType, Int, ObjectType } from 'type-graphql'\n\nimport { Quotation } from './quotation'\n\n@InputType({ description: 'A quotation draft to create or replace' })\nexport class NewQuotation extends DocumentInput {\n @Field({ description: 'Last day the offer holds, YYYY-MM-DD' }) validUntil: string\n}\n\n@ObjectType({ description: 'A page of quotations' })\nexport class QuotationList {\n @Field(type => [Quotation], { description: 'Quotations on the page' }) items: Quotation[]\n @Field(type => Int, { description: 'Number of quotations matching the list parameters' }) total: number\n}\n"]}
|
|
@@ -1,19 +1,28 @@
|
|
|
1
|
-
import { Domain } from '@things-factory/shell';
|
|
2
1
|
import { User } from '@things-factory/auth-base';
|
|
2
|
+
import { CommercialDocument } from '@things-factory/invoice';
|
|
3
|
+
import { Domain } from '@things-factory/shell';
|
|
4
|
+
import { QuotationItem } from './quotation-item';
|
|
3
5
|
export declare enum QuotationStatus {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
DRAFT = "DRAFT",
|
|
7
|
+
SENT = "SENT",
|
|
8
|
+
ACCEPTED = "ACCEPTED",
|
|
9
|
+
REJECTED = "REJECTED",
|
|
10
|
+
EXPIRED = "EXPIRED",
|
|
11
|
+
SUPERSEDED = "SUPERSEDED"
|
|
6
12
|
}
|
|
7
|
-
|
|
13
|
+
/**
|
|
14
|
+
* A quotation — the same printed head and lines as an invoice, valid until a day. A sent quotation is changed by
|
|
15
|
+
* revising it: the revision keeps the number and takes the next version, and the one before is superseded.
|
|
16
|
+
*/
|
|
17
|
+
export declare class Quotation extends CommercialDocument {
|
|
8
18
|
readonly id: string;
|
|
9
|
-
version?: number;
|
|
10
19
|
domain?: Domain;
|
|
11
20
|
domainId?: string;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
version: number;
|
|
22
|
+
status: QuotationStatus;
|
|
23
|
+
validUntil: string;
|
|
24
|
+
outcomeReason?: string;
|
|
25
|
+
items: QuotationItem[];
|
|
17
26
|
createdAt?: Date;
|
|
18
27
|
updatedAt?: Date;
|
|
19
28
|
deletedAt?: Date;
|
|
@@ -21,5 +30,4 @@ export declare class Quotation {
|
|
|
21
30
|
creatorId?: string;
|
|
22
31
|
updater?: User;
|
|
23
32
|
updaterId?: string;
|
|
24
|
-
thumbnail?: string;
|
|
25
33
|
}
|
|
@@ -2,23 +2,30 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Quotation = exports.QuotationStatus = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const typeorm_1 = require("typeorm");
|
|
6
|
-
const type_graphql_1 = require("type-graphql");
|
|
7
|
-
const shell_1 = require("@things-factory/shell");
|
|
8
5
|
const auth_base_1 = require("@things-factory/auth-base");
|
|
6
|
+
const invoice_1 = require("@things-factory/invoice");
|
|
7
|
+
const shell_1 = require("@things-factory/shell");
|
|
8
|
+
const type_graphql_1 = require("type-graphql");
|
|
9
|
+
const typeorm_1 = require("typeorm");
|
|
10
|
+
const quotation_item_1 = require("./quotation-item");
|
|
9
11
|
var QuotationStatus;
|
|
10
12
|
(function (QuotationStatus) {
|
|
11
|
-
QuotationStatus["
|
|
12
|
-
QuotationStatus["
|
|
13
|
+
QuotationStatus["DRAFT"] = "DRAFT";
|
|
14
|
+
QuotationStatus["SENT"] = "SENT";
|
|
15
|
+
QuotationStatus["ACCEPTED"] = "ACCEPTED";
|
|
16
|
+
QuotationStatus["REJECTED"] = "REJECTED";
|
|
17
|
+
QuotationStatus["EXPIRED"] = "EXPIRED";
|
|
18
|
+
QuotationStatus["SUPERSEDED"] = "SUPERSEDED";
|
|
13
19
|
})(QuotationStatus || (exports.QuotationStatus = QuotationStatus = {}));
|
|
14
20
|
(0, type_graphql_1.registerEnumType)(QuotationStatus, {
|
|
15
21
|
name: 'QuotationStatus',
|
|
16
|
-
description: 'state
|
|
22
|
+
description: 'state of a quotation — draft, sent, accepted, rejected, expired, or replaced by a revision'
|
|
17
23
|
});
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
/**
|
|
25
|
+
* A quotation — the same printed head and lines as an invoice, valid until a day. A sent quotation is changed by
|
|
26
|
+
* revising it: the revision keeps the number and takes the next version, and the one before is superseded.
|
|
27
|
+
*/
|
|
28
|
+
let Quotation = class Quotation extends invoice_1.CommercialDocument {
|
|
22
29
|
};
|
|
23
30
|
exports.Quotation = Quotation;
|
|
24
31
|
tslib_1.__decorate([
|
|
@@ -26,14 +33,9 @@ tslib_1.__decorate([
|
|
|
26
33
|
(0, type_graphql_1.Field)(type => type_graphql_1.ID),
|
|
27
34
|
tslib_1.__metadata("design:type", String)
|
|
28
35
|
], Quotation.prototype, "id", void 0);
|
|
29
|
-
tslib_1.__decorate([
|
|
30
|
-
(0, typeorm_1.VersionColumn)(),
|
|
31
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
32
|
-
tslib_1.__metadata("design:type", Number)
|
|
33
|
-
], Quotation.prototype, "version", void 0);
|
|
34
36
|
tslib_1.__decorate([
|
|
35
37
|
(0, typeorm_1.ManyToOne)(type => shell_1.Domain),
|
|
36
|
-
(0, type_graphql_1.Field)(type => shell_1.Domain),
|
|
38
|
+
(0, type_graphql_1.Field)(type => shell_1.Domain, { nullable: true }),
|
|
37
39
|
tslib_1.__metadata("design:type", shell_1.Domain)
|
|
38
40
|
], Quotation.prototype, "domain", void 0);
|
|
39
41
|
tslib_1.__decorate([
|
|
@@ -41,43 +43,43 @@ tslib_1.__decorate([
|
|
|
41
43
|
tslib_1.__metadata("design:type", String)
|
|
42
44
|
], Quotation.prototype, "domainId", void 0);
|
|
43
45
|
tslib_1.__decorate([
|
|
44
|
-
(0, typeorm_1.Column)(),
|
|
45
|
-
(0, type_graphql_1.Field)({
|
|
46
|
-
tslib_1.__metadata("design:type",
|
|
47
|
-
], Quotation.prototype, "
|
|
46
|
+
(0, typeorm_1.Column)({ type: 'int', default: 1 }),
|
|
47
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'Revision of the quotation number, from 1' }),
|
|
48
|
+
tslib_1.__metadata("design:type", Number)
|
|
49
|
+
], Quotation.prototype, "version", void 0);
|
|
48
50
|
tslib_1.__decorate([
|
|
49
|
-
(0, typeorm_1.Column)({
|
|
50
|
-
(0, type_graphql_1.Field)({
|
|
51
|
+
(0, typeorm_1.Column)({ default: QuotationStatus.DRAFT }),
|
|
52
|
+
(0, type_graphql_1.Field)(type => QuotationStatus, { description: 'Draft, sent, accepted, rejected, expired or superseded' }),
|
|
51
53
|
tslib_1.__metadata("design:type", String)
|
|
52
|
-
], Quotation.prototype, "
|
|
53
|
-
tslib_1.__decorate([
|
|
54
|
-
(0, typeorm_1.Column)({ nullable: true }),
|
|
55
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
56
|
-
tslib_1.__metadata("design:type", Boolean)
|
|
57
|
-
], Quotation.prototype, "active", void 0);
|
|
54
|
+
], Quotation.prototype, "status", void 0);
|
|
58
55
|
tslib_1.__decorate([
|
|
59
|
-
(0, typeorm_1.Column)(
|
|
60
|
-
(0, type_graphql_1.Field)({
|
|
56
|
+
(0, typeorm_1.Column)(),
|
|
57
|
+
(0, type_graphql_1.Field)({ description: 'Last day the offer holds, YYYY-MM-DD' }),
|
|
61
58
|
tslib_1.__metadata("design:type", String)
|
|
62
|
-
], Quotation.prototype, "
|
|
59
|
+
], Quotation.prototype, "validUntil", void 0);
|
|
63
60
|
tslib_1.__decorate([
|
|
64
61
|
(0, typeorm_1.Column)({ nullable: true }),
|
|
65
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
62
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Why the buyer declined, when rejected' }),
|
|
66
63
|
tslib_1.__metadata("design:type", String)
|
|
67
|
-
], Quotation.prototype, "
|
|
64
|
+
], Quotation.prototype, "outcomeReason", void 0);
|
|
65
|
+
tslib_1.__decorate([
|
|
66
|
+
(0, typeorm_1.OneToMany)(type => quotation_item_1.QuotationItem, item => item.quotation),
|
|
67
|
+
(0, type_graphql_1.Field)(type => [quotation_item_1.QuotationItem], { description: 'Lines in printed order' }),
|
|
68
|
+
tslib_1.__metadata("design:type", Array)
|
|
69
|
+
], Quotation.prototype, "items", void 0);
|
|
68
70
|
tslib_1.__decorate([
|
|
69
71
|
(0, typeorm_1.CreateDateColumn)(),
|
|
70
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
72
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'When the quotation was created' }),
|
|
71
73
|
tslib_1.__metadata("design:type", Date)
|
|
72
74
|
], Quotation.prototype, "createdAt", void 0);
|
|
73
75
|
tslib_1.__decorate([
|
|
74
76
|
(0, typeorm_1.UpdateDateColumn)(),
|
|
75
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
77
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'When the quotation last changed' }),
|
|
76
78
|
tslib_1.__metadata("design:type", Date)
|
|
77
79
|
], Quotation.prototype, "updatedAt", void 0);
|
|
78
80
|
tslib_1.__decorate([
|
|
79
81
|
(0, typeorm_1.DeleteDateColumn)(),
|
|
80
|
-
(0, type_graphql_1.Field)({ nullable: true }),
|
|
82
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'When the quotation was deleted' }),
|
|
81
83
|
tslib_1.__metadata("design:type", Date)
|
|
82
84
|
], Quotation.prototype, "deletedAt", void 0);
|
|
83
85
|
tslib_1.__decorate([
|
|
@@ -98,15 +100,10 @@ tslib_1.__decorate([
|
|
|
98
100
|
(0, typeorm_1.RelationId)((quotation) => quotation.updater),
|
|
99
101
|
tslib_1.__metadata("design:type", String)
|
|
100
102
|
], Quotation.prototype, "updaterId", void 0);
|
|
101
|
-
tslib_1.__decorate([
|
|
102
|
-
(0, type_graphql_1.Field)(type => String, { nullable: true }),
|
|
103
|
-
tslib_1.__metadata("design:type", String)
|
|
104
|
-
], Quotation.prototype, "thumbnail", void 0);
|
|
105
103
|
exports.Quotation = Quotation = tslib_1.__decorate([
|
|
106
104
|
(0, typeorm_1.Entity)(),
|
|
107
|
-
(0, typeorm_1.Index)('ix_quotation_0', (quotation) => [quotation.domain, quotation.
|
|
108
|
-
|
|
109
|
-
})
|
|
110
|
-
(0, type_graphql_1.ObjectType)({ description: 'Entity for Quotation' })
|
|
105
|
+
(0, typeorm_1.Index)('ix_quotation_0', (quotation) => [quotation.domain, quotation.number, quotation.version], { unique: true, where: '"deleted_at" IS NULL' }),
|
|
106
|
+
(0, typeorm_1.Index)('ix_quotation_1', (quotation) => [quotation.domain, quotation.status, quotation.validUntil]),
|
|
107
|
+
(0, type_graphql_1.ObjectType)({ description: 'Entity for Quotation — a priced offer, kept with every revision' })
|
|
111
108
|
], Quotation);
|
|
112
109
|
//# sourceMappingURL=quotation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotation.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation.ts"],"names":[],"mappings":";;;;AAAA,
|
|
1
|
+
{"version":3,"file":"quotation.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '@things-factory/auth-base'\nimport { CommercialDocument } from '@things-factory/invoice'\nimport { Domain } from '@things-factory/shell'\nimport { Field, ID, Int, ObjectType, registerEnumType } from 'type-graphql'\nimport { Column, CreateDateColumn, DeleteDateColumn, Entity, Index, ManyToOne, OneToMany, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\n\nimport { QuotationItem } from './quotation-item'\n\nexport enum QuotationStatus {\n DRAFT = 'DRAFT',\n SENT = 'SENT',\n ACCEPTED = 'ACCEPTED',\n REJECTED = 'REJECTED',\n EXPIRED = 'EXPIRED',\n SUPERSEDED = 'SUPERSEDED'\n}\nregisterEnumType(QuotationStatus, {\n name: 'QuotationStatus',\n description: 'state of a quotation — draft, sent, accepted, rejected, expired, or replaced by a revision'\n})\n\n/**\n * A quotation — the same printed head and lines as an invoice, valid until a day. A sent quotation is changed by\n * revising it: the revision keeps the number and takes the next version, and the one before is superseded.\n */\n@Entity()\n@Index('ix_quotation_0', (quotation: Quotation) => [quotation.domain, quotation.number, quotation.version], { unique: true, where: '\"deleted_at\" IS NULL' })\n@Index('ix_quotation_1', (quotation: Quotation) => [quotation.domain, quotation.status, quotation.validUntil])\n@ObjectType({ description: 'Entity for Quotation — a priced offer, kept with every revision' })\nexport class Quotation extends CommercialDocument {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true })\n domain?: Domain\n\n @RelationId((quotation: Quotation) => quotation.domain)\n domainId?: string\n\n @Column({ type: 'int', default: 1 })\n @Field(type => Int, { description: 'Revision of the quotation number, from 1' })\n version: number\n\n @Column({ default: QuotationStatus.DRAFT })\n @Field(type => QuotationStatus, { description: 'Draft, sent, accepted, rejected, expired or superseded' })\n status: QuotationStatus\n\n @Column()\n @Field({ description: 'Last day the offer holds, YYYY-MM-DD' })\n validUntil: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Why the buyer declined, when rejected' })\n outcomeReason?: string\n\n @OneToMany(type => QuotationItem, item => item.quotation)\n @Field(type => [QuotationItem], { description: 'Lines in printed order' })\n items: QuotationItem[]\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'When the quotation was created' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When the quotation last changed' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'When the quotation was deleted' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((quotation: Quotation) => quotation.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((quotation: Quotation) => quotation.updater)\n updaterId?: string\n}\n"]}
|