@things-factory/sales 0.0.0-stage → 10.1.176
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/assets/images/hatiolab-logo.png +0 -0
- package/dist-client/bootstrap.d.ts +1 -0
- package/dist-client/bootstrap.js +5 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/entity360/opportunity-360.d.ts +12 -0
- package/dist-client/entity360/opportunity-360.js +114 -0
- package/dist-client/entity360/opportunity-360.js.map +1 -0
- package/dist-client/index.d.ts +4 -0
- package/dist-client/index.js +5 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/opportunity-attach.d.ts +17 -0
- package/dist-client/opportunity-attach.js +8 -0
- package/dist-client/opportunity-attach.js.map +1 -0
- package/dist-client/opportunity-words.d.ts +1 -0
- package/dist-client/opportunity-words.js +13 -0
- package/dist-client/opportunity-words.js.map +1 -0
- package/dist-client/pages/opportunity-editor.d.ts +81 -0
- package/dist-client/pages/opportunity-editor.js +482 -0
- package/dist-client/pages/opportunity-editor.js.map +1 -0
- package/dist-client/pages/opportunity-list.d.ts +40 -0
- package/dist-client/pages/opportunity-list.js +134 -0
- package/dist-client/pages/opportunity-list.js.map +1 -0
- 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 -0
- package/dist-client/route.js +17 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-server/controllers/index.d.ts +0 -0
- package/dist-server/controllers/index.js +1 -0
- package/dist-server/controllers/index.js.map +1 -0
- package/dist-server/index.d.ts +4 -0
- package/dist-server/index.js +8 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/middlewares/index.d.ts +2 -0
- package/dist-server/middlewares/index.js +7 -0
- package/dist-server/middlewares/index.js.map +1 -0
- package/dist-server/migrations/index.d.ts +1 -0
- package/dist-server/migrations/index.js +12 -0
- package/dist-server/migrations/index.js.map +1 -0
- package/dist-server/routes.d.ts +0 -0
- package/dist-server/routes.js +24 -0
- package/dist-server/routes.js.map +1 -0
- package/dist-server/service/index.d.ts +16 -0
- package/dist-server/service/index.js +21 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/opportunity/index.d.ts +8 -0
- package/dist-server/service/opportunity/index.js +12 -0
- package/dist-server/service/opportunity/index.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-event-query.d.ts +9 -0
- package/dist-server/service/opportunity/opportunity-event-query.js +55 -0
- package/dist-server/service/opportunity/opportunity-event-query.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-event.d.ts +27 -0
- package/dist-server/service/opportunity/opportunity-event.js +115 -0
- package/dist-server/service/opportunity/opportunity-event.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-mutation.d.ts +7 -0
- package/dist-server/service/opportunity/opportunity-mutation.js +64 -0
- package/dist-server/service/opportunity/opportunity-mutation.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-query.d.ts +19 -0
- package/dist-server/service/opportunity/opportunity-query.js +144 -0
- package/dist-server/service/opportunity/opportunity-query.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-rules.d.ts +22 -0
- package/dist-server/service/opportunity/opportunity-rules.js +59 -0
- package/dist-server/service/opportunity/opportunity-rules.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-service.d.ts +19 -0
- package/dist-server/service/opportunity/opportunity-service.js +107 -0
- package/dist-server/service/opportunity/opportunity-service.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-type.d.ts +23 -0
- package/dist-server/service/opportunity/opportunity-type.js +86 -0
- package/dist-server/service/opportunity/opportunity-type.js.map +1 -0
- package/dist-server/service/opportunity/opportunity.d.ts +35 -0
- package/dist-server/service/opportunity/opportunity.js +147 -0
- package/dist-server/service/opportunity/opportunity.js.map +1 -0
- package/dist-server/service/quotation/index.d.ts +10 -0
- package/dist-server/service/quotation/index.js +14 -0
- package/dist-server/service/quotation/index.js.map +1 -0
- 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 +11 -0
- package/dist-server/service/quotation/quotation-mutation.js +117 -0
- package/dist-server/service/quotation/quotation-mutation.js.map +1 -0
- package/dist-server/service/quotation/quotation-query.d.ts +17 -0
- package/dist-server/service/quotation/quotation-query.js +125 -0
- package/dist-server/service/quotation/quotation-query.js.map +1 -0
- 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 +115 -0
- package/dist-server/service/quotation/quotation-service.js.map +1 -0
- package/dist-server/service/quotation/quotation-type.d.ts +9 -0
- package/dist-server/service/quotation/quotation-type.js +32 -0
- package/dist-server/service/quotation/quotation-type.js.map +1 -0
- package/dist-server/service/quotation/quotation.d.ts +33 -0
- package/dist-server/service/quotation/quotation.js +109 -0
- package/dist-server/service/quotation/quotation.js.map +1 -0
- package/dist-server/service/sales-event.d.ts +15 -0
- package/dist-server/service/sales-event.js +12 -0
- package/dist-server/service/sales-event.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/helps/quotation/quotation.md +160 -0
- package/package.json +46 -4
- package/things-factory.config.js +13 -0
- package/translations/en.json +98 -0
- package/translations/es.json +98 -0
- package/translations/ja.json +98 -0
- package/translations/ko.json +98 -0
- package/translations/ms.json +98 -0
- package/translations/zh.json +98 -0
- package/README.md +0 -3
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.quotationTransition = quotationTransition;
|
|
4
|
+
exports.revisable = revisable;
|
|
5
|
+
const quotation_1 = require("./quotation");
|
|
6
|
+
/** The status an action leads to from where the quotation stands on `today`, or undefined when it may not. */
|
|
7
|
+
function quotationTransition(status, action, validUntil, today) {
|
|
8
|
+
if (action === 'SENT' && status === quotation_1.QuotationStatus.DRAFT && validUntil >= today)
|
|
9
|
+
return quotation_1.QuotationStatus.SENT;
|
|
10
|
+
if (action === 'ACCEPTED' && status === quotation_1.QuotationStatus.SENT && validUntil >= today)
|
|
11
|
+
return quotation_1.QuotationStatus.ACCEPTED;
|
|
12
|
+
if (action === 'REJECTED' && status === quotation_1.QuotationStatus.SENT)
|
|
13
|
+
return quotation_1.QuotationStatus.REJECTED;
|
|
14
|
+
if (action === 'EXPIRED' && status === quotation_1.QuotationStatus.SENT && validUntil < today)
|
|
15
|
+
return quotation_1.QuotationStatus.EXPIRED;
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
/** Only the latest sent version that nobody accepted may be revised. */
|
|
19
|
+
function revisable(status) {
|
|
20
|
+
return [quotation_1.QuotationStatus.SENT, quotation_1.QuotationStatus.REJECTED, quotation_1.QuotationStatus.EXPIRED].includes(status);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=quotation-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quotation-rules.js","sourceRoot":"","sources":["../../../server/service/quotation/quotation-rules.ts"],"names":[],"mappings":";;AAKA,kDAMC;AAGD,8BAEC;AAhBD,2CAA6C;AAI7C,8GAA8G;AAC9G,SAAgB,mBAAmB,CAAC,MAAc,EAAE,MAAuB,EAAE,UAAkB,EAAE,KAAa;IAC5G,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,2BAAe,CAAC,KAAK,IAAI,UAAU,IAAI,KAAK;QAAE,OAAO,2BAAe,CAAC,IAAI,CAAA;IAC7G,IAAI,MAAM,KAAK,UAAU,IAAI,MAAM,KAAK,2BAAe,CAAC,IAAI,IAAI,UAAU,IAAI,KAAK;QAAE,OAAO,2BAAe,CAAC,QAAQ,CAAA;IACpH,IAAI,MAAM,KAAK,UAAU,IAAI,MAAM,KAAK,2BAAe,CAAC,IAAI;QAAE,OAAO,2BAAe,CAAC,QAAQ,CAAA;IAC7F,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,2BAAe,CAAC,IAAI,IAAI,UAAU,GAAG,KAAK;QAAE,OAAO,2BAAe,CAAC,OAAO,CAAA;IACjH,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,wEAAwE;AACxE,SAAgB,SAAS,CAAC,MAAc;IACtC,OAAO,CAAC,2BAAe,CAAC,IAAI,EAAE,2BAAe,CAAC,QAAQ,EAAE,2BAAe,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAyB,CAAC,CAAA;AACtH,CAAC","sourcesContent":["import { QuotationStatus } from './quotation'\n\nexport type QuotationAction = 'SENT' | 'ACCEPTED' | 'REJECTED' | 'EXPIRED'\n\n/** The status an action leads to from where the quotation stands on `today`, or undefined when it may not. */\nexport function quotationTransition(status: string, action: QuotationAction, validUntil: string, today: string): QuotationStatus | undefined {\n if (action === 'SENT' && status === QuotationStatus.DRAFT && validUntil >= today) return QuotationStatus.SENT\n if (action === 'ACCEPTED' && status === QuotationStatus.SENT && validUntil >= today) return QuotationStatus.ACCEPTED\n if (action === 'REJECTED' && status === QuotationStatus.SENT) return QuotationStatus.REJECTED\n if (action === 'EXPIRED' && status === QuotationStatus.SENT && validUntil < today) return QuotationStatus.EXPIRED\n return undefined\n}\n\n/** Only the latest sent version that nobody accepted may be revised. */\nexport function revisable(status: string): boolean {\n return [QuotationStatus.SENT, QuotationStatus.REJECTED, QuotationStatus.EXPIRED].includes(status as QuotationStatus)\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Domain } from '@things-factory/shell';
|
|
2
|
+
import { EntityManager } from 'typeorm';
|
|
3
|
+
import { Quotation } from './quotation';
|
|
4
|
+
import { QuotationAction } from './quotation-rules';
|
|
5
|
+
import { NewQuotation } from './quotation-type';
|
|
6
|
+
export declare function quotationInDomain(tx: EntityManager, domain: Domain, id: string): Promise<Quotation>;
|
|
7
|
+
export declare function saveQuotation(tx: EntityManager, domain: Domain, user: any, input: NewQuotation, id?: string): Promise<Quotation>;
|
|
8
|
+
export declare function transitionQuotation(tx: EntityManager, domain: Domain, user: any, id: string, action: QuotationAction, reason?: string): Promise<Quotation>;
|
|
9
|
+
/** A new draft version of a sent quotation, with the same number; the version it replaces is superseded. */
|
|
10
|
+
export declare function reviseQuotation(tx: EntityManager, domain: Domain, user: any, id: string): Promise<Quotation>;
|
|
11
|
+
/** The quotation as printed. */
|
|
12
|
+
export declare function snapshotOf(quotation: Quotation): {
|
|
13
|
+
items: {
|
|
14
|
+
position: number;
|
|
15
|
+
description: string;
|
|
16
|
+
quantity: number;
|
|
17
|
+
unitPrice: number;
|
|
18
|
+
discount: number;
|
|
19
|
+
taxKind: import("@things-factory/invoice").TaxKind;
|
|
20
|
+
taxRate: number;
|
|
21
|
+
supplyAmount: number;
|
|
22
|
+
}[];
|
|
23
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
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 sales_event_1 = require("../sales-event");
|
|
11
|
+
const quotation_1 = require("./quotation");
|
|
12
|
+
const quotation_event_1 = require("./quotation-event");
|
|
13
|
+
const quotation_item_1 = require("./quotation-item");
|
|
14
|
+
const quotation_rules_1 = require("./quotation-rules");
|
|
15
|
+
async function quotationInDomain(tx, domain, id) {
|
|
16
|
+
const quotation = await tx.getRepository(quotation_1.Quotation).findOne({ where: { id, domain: { id: domain.id } }, relations: ['items', 'creator'] });
|
|
17
|
+
if (!quotation)
|
|
18
|
+
throw new shell_1.Refusal('quotation-not-found', {}, 'Quotation not found');
|
|
19
|
+
quotation.items.sort((a, b) => a.position - b.position);
|
|
20
|
+
return quotation;
|
|
21
|
+
}
|
|
22
|
+
async function saveQuotation(tx, domain, user, input, id) {
|
|
23
|
+
await (0, invoice_1.lockDocumentDomain)(tx, domain);
|
|
24
|
+
const old = id ? await quotationInDomain(tx, domain, id) : undefined;
|
|
25
|
+
if (old && old.status !== quotation_1.QuotationStatus.DRAFT)
|
|
26
|
+
throw new shell_1.Refusal('quotation-draft-required', {}, 'Revise a sent quotation to change it');
|
|
27
|
+
const header = (0, invoice_1.normalizedHeader)(input);
|
|
28
|
+
if (!(0, invoice_1.validDay)(input.validUntil))
|
|
29
|
+
throw new shell_1.Refusal('quotation-valid-until', {}, 'A valid last day is required');
|
|
30
|
+
/* A revision answers the quotation it revises: it keeps the number. */
|
|
31
|
+
if (old && old.version > 1 && header.number !== old.number)
|
|
32
|
+
throw new shell_1.Refusal('quotation-revision-number', {}, 'A revision keeps its quotation number');
|
|
33
|
+
const repository = tx.getRepository(quotation_1.Quotation);
|
|
34
|
+
const version = old?.version ?? 1;
|
|
35
|
+
const duplicate = await repository.findOne({ where: { domain: { id: domain.id }, number: header.number, version } });
|
|
36
|
+
if (duplicate && duplicate.id !== id)
|
|
37
|
+
throw new shell_1.Refusal('quotation-number-duplicate', {}, 'Quotation number already exists');
|
|
38
|
+
const { items, supplyAmount, taxAmount, totalAmount } = (0, invoice_1.calculateDocument)(input.items, header.currency);
|
|
39
|
+
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 }));
|
|
40
|
+
const lines = tx.getRepository(quotation_item_1.QuotationItem);
|
|
41
|
+
if (old)
|
|
42
|
+
await lines.softDelete({ quotation: { id: quotation.id } });
|
|
43
|
+
await lines.save(items.map(({ receivedTaxAmount, ...item }) => lines.create({ ...item, quotation, domain, creator: user, updater: user })));
|
|
44
|
+
const saved = await quotationInDomain(tx, domain, quotation.id);
|
|
45
|
+
await recordQuotationEvent(tx, domain, user, saved, old ? 'UPDATED' : 'CREATED');
|
|
46
|
+
return saved;
|
|
47
|
+
}
|
|
48
|
+
async function transitionQuotation(tx, domain, user, id, action, reason) {
|
|
49
|
+
await (0, invoice_1.lockDocumentDomain)(tx, domain);
|
|
50
|
+
const quotation = await quotationInDomain(tx, domain, id);
|
|
51
|
+
const next = (0, quotation_rules_1.quotationTransition)(quotation.status, action, quotation.validUntil, (0, invoice_1.calendarDay)(domain.timezone));
|
|
52
|
+
if (!next)
|
|
53
|
+
throw new shell_1.Refusal('quotation-transition-invalid', {}, 'The quotation status or validity does not allow this');
|
|
54
|
+
if (next === quotation_1.QuotationStatus.REJECTED && !reason?.trim())
|
|
55
|
+
throw new shell_1.Refusal('quotation-reason-required', {}, 'Say why the buyer declined');
|
|
56
|
+
if (next === quotation_1.QuotationStatus.SENT) {
|
|
57
|
+
if (!quotation.issueDate)
|
|
58
|
+
throw new shell_1.Refusal('quotation-issue-date-required', {}, 'A quotation is sent with its issue date');
|
|
59
|
+
/* What the amounts were reckoned with stays on the quotation; a later rule never recomputes them. */
|
|
60
|
+
quotation.taxRounding = invoice_1.TAX_ROUNDING;
|
|
61
|
+
quotation.currencyDigits = (0, invoice_1.currencyDigits)(quotation.currency);
|
|
62
|
+
}
|
|
63
|
+
quotation.status = next;
|
|
64
|
+
if (next === quotation_1.QuotationStatus.REJECTED)
|
|
65
|
+
quotation.outcomeReason = reason.trim();
|
|
66
|
+
quotation.updater = user;
|
|
67
|
+
const { items, ...head } = quotation;
|
|
68
|
+
await tx.getRepository(quotation_1.Quotation).save(head);
|
|
69
|
+
await recordQuotationEvent(tx, domain, user, quotation, action, reason);
|
|
70
|
+
return quotationInDomain(tx, domain, id);
|
|
71
|
+
}
|
|
72
|
+
/** A new draft version of a sent quotation, with the same number; the version it replaces is superseded. */
|
|
73
|
+
async function reviseQuotation(tx, domain, user, id) {
|
|
74
|
+
await (0, invoice_1.lockDocumentDomain)(tx, domain);
|
|
75
|
+
const old = await quotationInDomain(tx, domain, id);
|
|
76
|
+
if (!(0, quotation_rules_1.revisable)(old.status))
|
|
77
|
+
throw new shell_1.Refusal('quotation-revision-invalid', {}, 'Only a sent quotation nobody accepted can be revised');
|
|
78
|
+
const versions = await tx.getRepository(quotation_1.Quotation).find({ where: { domain: { id: domain.id }, number: old.number } });
|
|
79
|
+
if (old.version !== Math.max(...versions.map(one => one.version)))
|
|
80
|
+
throw new shell_1.Refusal('quotation-latest-required', {}, 'Revise the latest version');
|
|
81
|
+
const repository = tx.getRepository(quotation_1.Quotation);
|
|
82
|
+
const { id: _id, items, createdAt, updatedAt, creator, updater, status, taxRounding, currencyDigits: digits, outcomeReason, ...document } = old;
|
|
83
|
+
const revision = await repository.save(repository.create({ ...document, version: old.version + 1, status: quotation_1.QuotationStatus.DRAFT, domain, creator: user, updater: user }));
|
|
84
|
+
const lines = tx.getRepository(quotation_item_1.QuotationItem);
|
|
85
|
+
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 })));
|
|
86
|
+
old.status = quotation_1.QuotationStatus.SUPERSEDED;
|
|
87
|
+
old.updater = user;
|
|
88
|
+
const { items: _items, ...head } = old;
|
|
89
|
+
await repository.save(head);
|
|
90
|
+
await recordQuotationEvent(tx, domain, user, old, 'SUPERSEDED');
|
|
91
|
+
const saved = await quotationInDomain(tx, domain, revision.id);
|
|
92
|
+
await recordQuotationEvent(tx, domain, user, saved, 'REVISED');
|
|
93
|
+
return saved;
|
|
94
|
+
}
|
|
95
|
+
function recordQuotationEvent(tx, domain, user, quotation, action, reason) {
|
|
96
|
+
return (0, sales_event_1.recordSalesEvent)(tx, quotation_event_1.QuotationEvent, { quotation }, { domain, user, action, reason, snapshot: snapshotOf(quotation) });
|
|
97
|
+
}
|
|
98
|
+
/** The quotation as printed. */
|
|
99
|
+
function snapshotOf(quotation) {
|
|
100
|
+
const keys = ['id', ...invoice_1.HEADER_KEYS, 'version', 'status', 'validUntil', 'supplyAmount', 'taxAmount', 'totalAmount', 'taxRounding', 'currencyDigits', 'outcomeReason'];
|
|
101
|
+
return {
|
|
102
|
+
...Object.fromEntries(keys.map(key => [key, quotation[key] ?? null])),
|
|
103
|
+
items: quotation.items.map(({ position, description, quantity, unitPrice, discount, taxKind, taxRate, supplyAmount }) => ({
|
|
104
|
+
position,
|
|
105
|
+
description,
|
|
106
|
+
quantity,
|
|
107
|
+
unitPrice,
|
|
108
|
+
discount,
|
|
109
|
+
taxKind,
|
|
110
|
+
taxRate,
|
|
111
|
+
supplyAmount
|
|
112
|
+
}))
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//# 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 { recordSalesEvent } from '../sales-event'\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 recordSalesEvent(tx, QuotationEvent, { quotation }, { domain, user, action, reason, 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"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DocumentInput } from '@things-factory/invoice';
|
|
2
|
+
import { Quotation } from './quotation';
|
|
3
|
+
export declare class NewQuotation extends DocumentInput {
|
|
4
|
+
validUntil: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class QuotationList {
|
|
7
|
+
items: Quotation[];
|
|
8
|
+
total: number;
|
|
9
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QuotationList = exports.NewQuotation = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const invoice_1 = require("@things-factory/invoice");
|
|
6
|
+
const type_graphql_1 = require("type-graphql");
|
|
7
|
+
const quotation_1 = require("./quotation");
|
|
8
|
+
let NewQuotation = class NewQuotation extends invoice_1.DocumentInput {
|
|
9
|
+
};
|
|
10
|
+
exports.NewQuotation = NewQuotation;
|
|
11
|
+
tslib_1.__decorate([
|
|
12
|
+
(0, type_graphql_1.Field)({ description: 'Last day the offer holds, YYYY-MM-DD' }),
|
|
13
|
+
tslib_1.__metadata("design:type", String)
|
|
14
|
+
], NewQuotation.prototype, "validUntil", void 0);
|
|
15
|
+
exports.NewQuotation = NewQuotation = tslib_1.__decorate([
|
|
16
|
+
(0, type_graphql_1.InputType)({ description: 'A quotation draft to create or replace' })
|
|
17
|
+
], NewQuotation);
|
|
18
|
+
let QuotationList = class QuotationList {
|
|
19
|
+
};
|
|
20
|
+
exports.QuotationList = QuotationList;
|
|
21
|
+
tslib_1.__decorate([
|
|
22
|
+
(0, type_graphql_1.Field)(type => [quotation_1.Quotation], { description: 'Quotations on the page' }),
|
|
23
|
+
tslib_1.__metadata("design:type", Array)
|
|
24
|
+
], QuotationList.prototype, "items", void 0);
|
|
25
|
+
tslib_1.__decorate([
|
|
26
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'Number of quotations matching the list parameters' }),
|
|
27
|
+
tslib_1.__metadata("design:type", Number)
|
|
28
|
+
], QuotationList.prototype, "total", void 0);
|
|
29
|
+
exports.QuotationList = QuotationList = tslib_1.__decorate([
|
|
30
|
+
(0, type_graphql_1.ObjectType)({ description: 'A page of quotations' })
|
|
31
|
+
], QuotationList);
|
|
32
|
+
//# sourceMappingURL=quotation-type.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
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';
|
|
5
|
+
export declare enum QuotationStatus {
|
|
6
|
+
DRAFT = "DRAFT",
|
|
7
|
+
SENT = "SENT",
|
|
8
|
+
ACCEPTED = "ACCEPTED",
|
|
9
|
+
REJECTED = "REJECTED",
|
|
10
|
+
EXPIRED = "EXPIRED",
|
|
11
|
+
SUPERSEDED = "SUPERSEDED"
|
|
12
|
+
}
|
|
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 {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
domain?: Domain;
|
|
20
|
+
domainId?: string;
|
|
21
|
+
version: number;
|
|
22
|
+
status: QuotationStatus;
|
|
23
|
+
validUntil: string;
|
|
24
|
+
outcomeReason?: string;
|
|
25
|
+
items: QuotationItem[];
|
|
26
|
+
createdAt?: Date;
|
|
27
|
+
updatedAt?: Date;
|
|
28
|
+
deletedAt?: Date;
|
|
29
|
+
creator?: User;
|
|
30
|
+
creatorId?: string;
|
|
31
|
+
updater?: User;
|
|
32
|
+
updaterId?: string;
|
|
33
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Quotation = exports.QuotationStatus = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
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");
|
|
11
|
+
var QuotationStatus;
|
|
12
|
+
(function (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";
|
|
19
|
+
})(QuotationStatus || (exports.QuotationStatus = QuotationStatus = {}));
|
|
20
|
+
(0, type_graphql_1.registerEnumType)(QuotationStatus, {
|
|
21
|
+
name: 'QuotationStatus',
|
|
22
|
+
description: 'state of a quotation — draft, sent, accepted, rejected, expired, or replaced by a revision'
|
|
23
|
+
});
|
|
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 {
|
|
29
|
+
};
|
|
30
|
+
exports.Quotation = Quotation;
|
|
31
|
+
tslib_1.__decorate([
|
|
32
|
+
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
|
33
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.ID),
|
|
34
|
+
tslib_1.__metadata("design:type", String)
|
|
35
|
+
], Quotation.prototype, "id", void 0);
|
|
36
|
+
tslib_1.__decorate([
|
|
37
|
+
(0, typeorm_1.ManyToOne)(type => shell_1.Domain),
|
|
38
|
+
(0, type_graphql_1.Field)(type => shell_1.Domain, { nullable: true }),
|
|
39
|
+
tslib_1.__metadata("design:type", shell_1.Domain)
|
|
40
|
+
], Quotation.prototype, "domain", void 0);
|
|
41
|
+
tslib_1.__decorate([
|
|
42
|
+
(0, typeorm_1.RelationId)((quotation) => quotation.domain),
|
|
43
|
+
tslib_1.__metadata("design:type", String)
|
|
44
|
+
], Quotation.prototype, "domainId", void 0);
|
|
45
|
+
tslib_1.__decorate([
|
|
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);
|
|
50
|
+
tslib_1.__decorate([
|
|
51
|
+
(0, typeorm_1.Column)({ default: QuotationStatus.DRAFT }),
|
|
52
|
+
(0, type_graphql_1.Field)(type => QuotationStatus, { description: 'Draft, sent, accepted, rejected, expired or superseded' }),
|
|
53
|
+
tslib_1.__metadata("design:type", String)
|
|
54
|
+
], Quotation.prototype, "status", void 0);
|
|
55
|
+
tslib_1.__decorate([
|
|
56
|
+
(0, typeorm_1.Column)(),
|
|
57
|
+
(0, type_graphql_1.Field)({ description: 'Last day the offer holds, YYYY-MM-DD' }),
|
|
58
|
+
tslib_1.__metadata("design:type", String)
|
|
59
|
+
], Quotation.prototype, "validUntil", void 0);
|
|
60
|
+
tslib_1.__decorate([
|
|
61
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
62
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Why the buyer declined, when rejected' }),
|
|
63
|
+
tslib_1.__metadata("design:type", String)
|
|
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);
|
|
70
|
+
tslib_1.__decorate([
|
|
71
|
+
(0, typeorm_1.CreateDateColumn)(),
|
|
72
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'When the quotation was created' }),
|
|
73
|
+
tslib_1.__metadata("design:type", Date)
|
|
74
|
+
], Quotation.prototype, "createdAt", void 0);
|
|
75
|
+
tslib_1.__decorate([
|
|
76
|
+
(0, typeorm_1.UpdateDateColumn)(),
|
|
77
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'When the quotation last changed' }),
|
|
78
|
+
tslib_1.__metadata("design:type", Date)
|
|
79
|
+
], Quotation.prototype, "updatedAt", void 0);
|
|
80
|
+
tslib_1.__decorate([
|
|
81
|
+
(0, typeorm_1.DeleteDateColumn)(),
|
|
82
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'When the quotation was deleted' }),
|
|
83
|
+
tslib_1.__metadata("design:type", Date)
|
|
84
|
+
], Quotation.prototype, "deletedAt", void 0);
|
|
85
|
+
tslib_1.__decorate([
|
|
86
|
+
(0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
|
|
87
|
+
(0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true }),
|
|
88
|
+
tslib_1.__metadata("design:type", auth_base_1.User)
|
|
89
|
+
], Quotation.prototype, "creator", void 0);
|
|
90
|
+
tslib_1.__decorate([
|
|
91
|
+
(0, typeorm_1.RelationId)((quotation) => quotation.creator),
|
|
92
|
+
tslib_1.__metadata("design:type", String)
|
|
93
|
+
], Quotation.prototype, "creatorId", void 0);
|
|
94
|
+
tslib_1.__decorate([
|
|
95
|
+
(0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
|
|
96
|
+
(0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true }),
|
|
97
|
+
tslib_1.__metadata("design:type", auth_base_1.User)
|
|
98
|
+
], Quotation.prototype, "updater", void 0);
|
|
99
|
+
tslib_1.__decorate([
|
|
100
|
+
(0, typeorm_1.RelationId)((quotation) => quotation.updater),
|
|
101
|
+
tslib_1.__metadata("design:type", String)
|
|
102
|
+
], Quotation.prototype, "updaterId", void 0);
|
|
103
|
+
exports.Quotation = Quotation = tslib_1.__decorate([
|
|
104
|
+
(0, typeorm_1.Entity)(),
|
|
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' })
|
|
108
|
+
], Quotation);
|
|
109
|
+
//# sourceMappingURL=quotation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Domain } from '@things-factory/shell';
|
|
2
|
+
import { DeepPartial, EntityManager, EntityTarget, ObjectLiteral } from 'typeorm';
|
|
3
|
+
/** What every history row of this module carries — who, what happened, why, and the record as it then stood. */
|
|
4
|
+
export type SalesEventFacts = {
|
|
5
|
+
domain: Domain;
|
|
6
|
+
user: any;
|
|
7
|
+
action: string;
|
|
8
|
+
reason?: string;
|
|
9
|
+
snapshot: any;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The one writer of this module's histories (ADR-0138 decision 6). A quotation's and an opportunity's events are
|
|
13
|
+
* written the same way; `subject` names the record the event happened to and any fact only its history keeps.
|
|
14
|
+
*/
|
|
15
|
+
export declare function recordSalesEvent<E extends ObjectLiteral>(tx: EntityManager, target: EntityTarget<E>, subject: DeepPartial<E>, facts: SalesEventFacts): Promise<DeepPartial<E> & E>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.recordSalesEvent = recordSalesEvent;
|
|
4
|
+
/**
|
|
5
|
+
* The one writer of this module's histories (ADR-0138 decision 6). A quotation's and an opportunity's events are
|
|
6
|
+
* written the same way; `subject` names the record the event happened to and any fact only its history keeps.
|
|
7
|
+
*/
|
|
8
|
+
function recordSalesEvent(tx, target, subject, facts) {
|
|
9
|
+
const { domain, user, action, reason, snapshot } = facts;
|
|
10
|
+
return tx.getRepository(target).save({ ...subject, domain, actor: user, action, reason, snapshot, creator: user, updater: user });
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=sales-event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sales-event.js","sourceRoot":"","sources":["../../server/service/sales-event.ts"],"names":[],"mappings":";;AAUA,4CAGC;AAPD;;;GAGG;AACH,SAAgB,gBAAgB,CAA0B,EAAiB,EAAE,MAAuB,EAAE,OAAuB,EAAE,KAAsB;IACnJ,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;IACxD,OAAO,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAoB,CAAC,CAAA;AACrJ,CAAC","sourcesContent":["import { Domain } from '@things-factory/shell'\nimport { DeepPartial, EntityManager, EntityTarget, ObjectLiteral } from 'typeorm'\n\n/** What every history row of this module carries — who, what happened, why, and the record as it then stood. */\nexport type SalesEventFacts = { domain: Domain; user: any; action: string; reason?: string; snapshot: any }\n\n/**\n * The one writer of this module's histories (ADR-0138 decision 6). A quotation's and an opportunity's events are\n * written the same way; `subject` names the record the event happened to and any fact only its history keeps.\n */\nexport function recordSalesEvent<E extends ObjectLiteral>(tx: EntityManager, target: EntityTarget<E>, subject: DeepPartial<E>, facts: SalesEventFacts) {\n const { domain, user, action, reason, snapshot } = facts\n return tx.getRepository(target).save({ ...subject, domain, actor: user, action, reason, snapshot, creator: user, updater: user } as DeepPartial<E>)\n}\n"]}
|