@things-factory/asset 0.0.0-stage → 10.1.229
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/components/asset-documents.d.ts +22 -0
- package/dist-client/components/asset-documents.js +190 -0
- package/dist-client/components/asset-documents.js.map +1 -0
- package/dist-client/grid.d.ts +13 -0
- package/dist-client/grid.js +56 -0
- package/dist-client/grid.js.map +1 -0
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +2 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/pages/asset-list.d.ts +53 -0
- package/dist-client/pages/asset-list.js +171 -0
- package/dist-client/pages/asset-list.js.map +1 -0
- package/dist-client/pages/asset-reconciliation.d.ts +25 -0
- package/dist-client/pages/asset-reconciliation.js +165 -0
- package/dist-client/pages/asset-reconciliation.js.map +1 -0
- package/dist-client/pages/investee-page.d.ts +47 -0
- package/dist-client/pages/investee-page.js +322 -0
- package/dist-client/pages/investee-page.js.map +1 -0
- package/dist-client/pages/investment-list.d.ts +53 -0
- package/dist-client/pages/investment-list.js +131 -0
- package/dist-client/pages/investment-list.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +18 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-client/words.d.ts +25 -0
- package/dist-client/words.js +23 -0
- package/dist-client/words.js.map +1 -0
- package/dist-server/attachment-reader.d.ts +3 -0
- package/dist-server/attachment-reader.js +20 -0
- package/dist-server/attachment-reader.js.map +1 -0
- package/dist-server/index.d.ts +1 -0
- package/dist-server/index.js +11 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/role-tasks.d.ts +2 -0
- package/dist-server/role-tasks.js +13 -0
- package/dist-server/role-tasks.js.map +1 -0
- package/dist-server/service/asset/asset-mutation.d.ts +5 -0
- package/dist-server/service/asset/asset-mutation.js +31 -0
- package/dist-server/service/asset/asset-mutation.js.map +1 -0
- package/dist-server/service/asset/asset-query.d.ts +19 -0
- package/dist-server/service/asset/asset-query.js +108 -0
- package/dist-server/service/asset/asset-query.js.map +1 -0
- package/dist-server/service/asset/asset-rules.d.ts +87 -0
- package/dist-server/service/asset/asset-rules.js +148 -0
- package/dist-server/service/asset/asset-rules.js.map +1 -0
- package/dist-server/service/asset/asset-service.d.ts +23 -0
- package/dist-server/service/asset/asset-service.js +84 -0
- package/dist-server/service/asset/asset-service.js.map +1 -0
- package/dist-server/service/asset/asset-type.d.ts +48 -0
- package/dist-server/service/asset/asset-type.js +182 -0
- package/dist-server/service/asset/asset-type.js.map +1 -0
- package/dist-server/service/asset/asset.d.ts +38 -0
- package/dist-server/service/asset/asset.js +159 -0
- package/dist-server/service/asset/asset.js.map +1 -0
- package/dist-server/service/asset/index.d.ts +6 -0
- package/dist-server/service/asset/index.js +10 -0
- package/dist-server/service/asset/index.js.map +1 -0
- package/dist-server/service/asset-document/asset-document-resolver.d.ts +32 -0
- package/dist-server/service/asset-document/asset-document-resolver.js +94 -0
- package/dist-server/service/asset-document/asset-document-resolver.js.map +1 -0
- package/dist-server/service/asset-document/asset-document-service.d.ts +34 -0
- package/dist-server/service/asset-document/asset-document-service.js +79 -0
- package/dist-server/service/asset-document/asset-document-service.js.map +1 -0
- package/dist-server/service/asset-document/index.d.ts +4 -0
- package/dist-server/service/asset-document/index.js +8 -0
- package/dist-server/service/asset-document/index.js.map +1 -0
- package/dist-server/service/checks.d.ts +12 -0
- package/dist-server/service/checks.js +27 -0
- package/dist-server/service/checks.js.map +1 -0
- package/dist-server/service/index.d.ts +18 -0
- package/dist-server/service/index.js +40 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/investment/index.d.ts +11 -0
- package/dist-server/service/investment/index.js +15 -0
- package/dist-server/service/investment/index.js.map +1 -0
- package/dist-server/service/investment/investee.d.ts +33 -0
- package/dist-server/service/investment/investee.js +135 -0
- package/dist-server/service/investment/investee.js.map +1 -0
- package/dist-server/service/investment/investment-board-seat.d.ts +25 -0
- package/dist-server/service/investment/investment-board-seat.js +98 -0
- package/dist-server/service/investment/investment-board-seat.js.map +1 -0
- package/dist-server/service/investment/investment-filing.d.ts +29 -0
- package/dist-server/service/investment/investment-filing.js +113 -0
- package/dist-server/service/investment/investment-filing.js.map +1 -0
- package/dist-server/service/investment/investment-holding.d.ts +31 -0
- package/dist-server/service/investment/investment-holding.js +127 -0
- package/dist-server/service/investment/investment-holding.js.map +1 -0
- package/dist-server/service/investment/investment-mutation.d.ts +15 -0
- package/dist-server/service/investment/investment-mutation.js +112 -0
- package/dist-server/service/investment/investment-mutation.js.map +1 -0
- package/dist-server/service/investment/investment-query.d.ts +33 -0
- package/dist-server/service/investment/investment-query.js +192 -0
- package/dist-server/service/investment/investment-query.js.map +1 -0
- package/dist-server/service/investment/investment-report.d.ts +37 -0
- package/dist-server/service/investment/investment-report.js +152 -0
- package/dist-server/service/investment/investment-report.js.map +1 -0
- package/dist-server/service/investment/investment-request.d.ts +31 -0
- package/dist-server/service/investment/investment-request.js +123 -0
- package/dist-server/service/investment/investment-request.js.map +1 -0
- package/dist-server/service/investment/investment-rules.d.ts +81 -0
- package/dist-server/service/investment/investment-rules.js +121 -0
- package/dist-server/service/investment/investment-rules.js.map +1 -0
- package/dist-server/service/investment/investment-service.d.ts +34 -0
- package/dist-server/service/investment/investment-service.js +280 -0
- package/dist-server/service/investment/investment-service.js.map +1 -0
- package/dist-server/service/investment/investment-type.d.ts +85 -0
- package/dist-server/service/investment/investment-type.js +324 -0
- package/dist-server/service/investment/investment-type.js.map +1 -0
- package/dist-server/service/patches.d.ts +24 -0
- package/dist-server/service/patches.js +63 -0
- package/dist-server/service/patches.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +49 -4
- package/things-factory.config.js +11 -0
- package/translations/en.json +187 -0
- package/translations/es.json +187 -0
- package/translations/ja.json +187 -0
- package/translations/ko.json +187 -0
- package/translations/ms.json +187 -0
- package/translations/zh.json +187 -0
- package/README.md +0 -3
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export declare enum HoldingReason {
|
|
2
|
+
FOUNDING = "FOUNDING",
|
|
3
|
+
SUBSCRIPTION = "SUBSCRIPTION",
|
|
4
|
+
DILUTION = "DILUTION",
|
|
5
|
+
PURCHASE = "PURCHASE",
|
|
6
|
+
SALE = "SALE",
|
|
7
|
+
BONUS_ISSUE = "BONUS_ISSUE",
|
|
8
|
+
OTHER = "OTHER"
|
|
9
|
+
}
|
|
10
|
+
export declare enum Contribution {
|
|
11
|
+
CASH = "CASH",
|
|
12
|
+
IN_KIND = "IN_KIND",
|
|
13
|
+
TECHNOLOGY = "TECHNOLOGY",
|
|
14
|
+
NONE = "NONE"
|
|
15
|
+
}
|
|
16
|
+
export declare enum InvestmentReportKind {
|
|
17
|
+
AUDITED = "AUDITED",
|
|
18
|
+
INTERIM = "INTERIM",
|
|
19
|
+
MANAGEMENT = "MANAGEMENT"
|
|
20
|
+
}
|
|
21
|
+
export declare enum InvestmentRequestKind {
|
|
22
|
+
WRITTEN_RESOLUTION = "WRITTEN_RESOLUTION",
|
|
23
|
+
RECEIPT_ACKNOWLEDGEMENT = "RECEIPT_ACKNOWLEDGEMENT",
|
|
24
|
+
MEETING_NOTICE = "MEETING_NOTICE",
|
|
25
|
+
OTHER = "OTHER"
|
|
26
|
+
}
|
|
27
|
+
/** Days ahead a request's or a filing's due day is shown before it passes (decision 11). */
|
|
28
|
+
export declare const REQUEST_NOTICE_DAYS = 7;
|
|
29
|
+
export declare const FILING_NOTICE_DAYS = 30;
|
|
30
|
+
export type Holding = {
|
|
31
|
+
recordDate: string;
|
|
32
|
+
ourShares: number;
|
|
33
|
+
totalShares: number;
|
|
34
|
+
};
|
|
35
|
+
/** Our part of the company: our shares over all its shares. */
|
|
36
|
+
export declare const ratioOf: (holding: Pick<Holding, "ourShares" | "totalShares">) => number;
|
|
37
|
+
/** The holding as it stood on a day - the last record on or before it. */
|
|
38
|
+
export declare function holdingAt<T extends Holding>(holdings: T[], day: string): T | undefined;
|
|
39
|
+
/** Our part of a report's equity - its equity times our part at its end; null when either is unknown. */
|
|
40
|
+
export declare function ourShareOf(report: {
|
|
41
|
+
periodTo: string;
|
|
42
|
+
totalEquity?: number | null;
|
|
43
|
+
}, holdings: Holding[]): number | null;
|
|
44
|
+
/** Add days to a day. */
|
|
45
|
+
export declare function addDays(day: string, days: number): string;
|
|
46
|
+
/**
|
|
47
|
+
* The last fiscal year whose report is due by now: its end, and the day its report was due - the end of the month the
|
|
48
|
+
* reporting months after it. Null when the investee has no year end or reporting months written, or that year ended
|
|
49
|
+
* before we held its shares (`since`).
|
|
50
|
+
*/
|
|
51
|
+
export declare function lastDueYear(investee: {
|
|
52
|
+
fiscalYearEndMonth?: number | null;
|
|
53
|
+
reportDueMonths?: number | null;
|
|
54
|
+
}, today: string, since?: string | null): {
|
|
55
|
+
periodTo: string;
|
|
56
|
+
dueOn: string;
|
|
57
|
+
} | null;
|
|
58
|
+
/**
|
|
59
|
+
* The report that is late (decision 8): the last fiscal year whose report is due, when no report ending that day has been
|
|
60
|
+
* received. Nothing is made ahead for it - it is computed.
|
|
61
|
+
*/
|
|
62
|
+
export declare function lateReport(investee: {
|
|
63
|
+
fiscalYearEndMonth?: number | null;
|
|
64
|
+
reportDueMonths?: number | null;
|
|
65
|
+
}, reports: {
|
|
66
|
+
periodTo: string;
|
|
67
|
+
receivedOn?: string | null;
|
|
68
|
+
}[], holdings: Holding[], today: string): {
|
|
69
|
+
periodTo: string;
|
|
70
|
+
dueOn: string;
|
|
71
|
+
} | null;
|
|
72
|
+
/** A request not yet answered whose due day has passed or comes within the notice days. */
|
|
73
|
+
export declare const requestDue: (request: {
|
|
74
|
+
dueOn?: string | null;
|
|
75
|
+
sentOn?: string | null;
|
|
76
|
+
}, today: string) => boolean;
|
|
77
|
+
/** A filing not yet made whose due day has passed or comes within the notice days. */
|
|
78
|
+
export declare const filingDue: (filing: {
|
|
79
|
+
dueOn?: string | null;
|
|
80
|
+
filedOn?: string | null;
|
|
81
|
+
}, today: string) => boolean;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.filingDue = exports.requestDue = exports.ratioOf = exports.FILING_NOTICE_DAYS = exports.REQUEST_NOTICE_DAYS = exports.InvestmentRequestKind = exports.InvestmentReportKind = exports.Contribution = exports.HoldingReason = void 0;
|
|
4
|
+
exports.holdingAt = holdingAt;
|
|
5
|
+
exports.ourShareOf = ourShareOf;
|
|
6
|
+
exports.addDays = addDays;
|
|
7
|
+
exports.lastDueYear = lastDueYear;
|
|
8
|
+
exports.lateReport = lateReport;
|
|
9
|
+
const type_graphql_1 = require("type-graphql");
|
|
10
|
+
/*
|
|
11
|
+
* The rules of the shares the domain holds in other companies (ADR-0175 decisions 6-10) - apart from the entities, so
|
|
12
|
+
* the screens' words and the tests read them without a type-graphql class. Pure; days are `YYYY-MM-DD`.
|
|
13
|
+
*/
|
|
14
|
+
var HoldingReason;
|
|
15
|
+
(function (HoldingReason) {
|
|
16
|
+
HoldingReason["FOUNDING"] = "FOUNDING";
|
|
17
|
+
HoldingReason["SUBSCRIPTION"] = "SUBSCRIPTION";
|
|
18
|
+
HoldingReason["DILUTION"] = "DILUTION";
|
|
19
|
+
HoldingReason["PURCHASE"] = "PURCHASE";
|
|
20
|
+
HoldingReason["SALE"] = "SALE";
|
|
21
|
+
HoldingReason["BONUS_ISSUE"] = "BONUS_ISSUE";
|
|
22
|
+
HoldingReason["OTHER"] = "OTHER";
|
|
23
|
+
})(HoldingReason || (exports.HoldingReason = HoldingReason = {}));
|
|
24
|
+
(0, type_graphql_1.registerEnumType)(HoldingReason, {
|
|
25
|
+
name: 'HoldingReason',
|
|
26
|
+
description: 'Why the holding changed - shares taken at founding, a subscription to new shares, dilution by shares issued to others, a purchase, a sale, a bonus issue, or other'
|
|
27
|
+
});
|
|
28
|
+
var Contribution;
|
|
29
|
+
(function (Contribution) {
|
|
30
|
+
Contribution["CASH"] = "CASH";
|
|
31
|
+
Contribution["IN_KIND"] = "IN_KIND";
|
|
32
|
+
Contribution["TECHNOLOGY"] = "TECHNOLOGY";
|
|
33
|
+
Contribution["NONE"] = "NONE";
|
|
34
|
+
})(Contribution || (exports.Contribution = Contribution = {}));
|
|
35
|
+
(0, type_graphql_1.registerEnumType)(Contribution, { name: 'Contribution', description: 'What was given for the shares - cash, property in kind, technology, or nothing' });
|
|
36
|
+
var InvestmentReportKind;
|
|
37
|
+
(function (InvestmentReportKind) {
|
|
38
|
+
InvestmentReportKind["AUDITED"] = "AUDITED";
|
|
39
|
+
InvestmentReportKind["INTERIM"] = "INTERIM";
|
|
40
|
+
InvestmentReportKind["MANAGEMENT"] = "MANAGEMENT";
|
|
41
|
+
})(InvestmentReportKind || (exports.InvestmentReportKind = InvestmentReportKind = {}));
|
|
42
|
+
(0, type_graphql_1.registerEnumType)(InvestmentReportKind, { name: 'InvestmentReportKind', description: 'An audited financial statement, an interim report or a management report' });
|
|
43
|
+
var InvestmentRequestKind;
|
|
44
|
+
(function (InvestmentRequestKind) {
|
|
45
|
+
InvestmentRequestKind["WRITTEN_RESOLUTION"] = "WRITTEN_RESOLUTION";
|
|
46
|
+
InvestmentRequestKind["RECEIPT_ACKNOWLEDGEMENT"] = "RECEIPT_ACKNOWLEDGEMENT";
|
|
47
|
+
InvestmentRequestKind["MEETING_NOTICE"] = "MEETING_NOTICE";
|
|
48
|
+
InvestmentRequestKind["OTHER"] = "OTHER";
|
|
49
|
+
})(InvestmentRequestKind || (exports.InvestmentRequestKind = InvestmentRequestKind = {}));
|
|
50
|
+
(0, type_graphql_1.registerEnumType)(InvestmentRequestKind, {
|
|
51
|
+
name: 'InvestmentRequestKind',
|
|
52
|
+
description: "What the investee asks of its shareholders - a written resolution, an acknowledgement of receipt, a meeting's notice, or other"
|
|
53
|
+
});
|
|
54
|
+
/** Days ahead a request's or a filing's due day is shown before it passes (decision 11). */
|
|
55
|
+
exports.REQUEST_NOTICE_DAYS = 7;
|
|
56
|
+
exports.FILING_NOTICE_DAYS = 30;
|
|
57
|
+
/** Our part of the company: our shares over all its shares. */
|
|
58
|
+
const ratioOf = (holding) => (Number(holding.totalShares) > 0 ? Number(holding.ourShares) / Number(holding.totalShares) : 0);
|
|
59
|
+
exports.ratioOf = ratioOf;
|
|
60
|
+
/** The holding as it stood on a day - the last record on or before it. */
|
|
61
|
+
function holdingAt(holdings, day) {
|
|
62
|
+
return [...holdings].filter(one => one.recordDate <= day).sort((a, b) => a.recordDate.localeCompare(b.recordDate)).pop();
|
|
63
|
+
}
|
|
64
|
+
/** Our part of a report's equity - its equity times our part at its end; null when either is unknown. */
|
|
65
|
+
function ourShareOf(report, holdings) {
|
|
66
|
+
const holding = holdingAt(holdings, report.periodTo);
|
|
67
|
+
if (!holding || report.totalEquity === null || report.totalEquity === undefined)
|
|
68
|
+
return null;
|
|
69
|
+
return Math.round(Number(report.totalEquity) * (0, exports.ratioOf)(holding));
|
|
70
|
+
}
|
|
71
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
72
|
+
/** The last day of a month, month counted from 1 and allowed past 12. */
|
|
73
|
+
function monthEnd(year, month) {
|
|
74
|
+
const date = new Date(Date.UTC(year, month, 0));
|
|
75
|
+
return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`;
|
|
76
|
+
}
|
|
77
|
+
/** Add days to a day. */
|
|
78
|
+
function addDays(day, days) {
|
|
79
|
+
const date = new Date(`${day}T00:00:00Z`);
|
|
80
|
+
date.setUTCDate(date.getUTCDate() + days);
|
|
81
|
+
return date.toISOString().slice(0, 10);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The last fiscal year whose report is due by now: its end, and the day its report was due - the end of the month the
|
|
85
|
+
* reporting months after it. Null when the investee has no year end or reporting months written, or that year ended
|
|
86
|
+
* before we held its shares (`since`).
|
|
87
|
+
*/
|
|
88
|
+
function lastDueYear(investee, today, since) {
|
|
89
|
+
const month = Number(investee.fiscalYearEndMonth);
|
|
90
|
+
const months = Number(investee.reportDueMonths);
|
|
91
|
+
if (!(month >= 1 && month <= 12) || !(months >= 0) || investee.reportDueMonths === null || investee.reportDueMonths === undefined)
|
|
92
|
+
return null;
|
|
93
|
+
const year = Number(today.slice(0, 4));
|
|
94
|
+
for (let y = year; y > year - 50; y--) {
|
|
95
|
+
const periodTo = monthEnd(y, month);
|
|
96
|
+
const dueOn = monthEnd(y, month + months);
|
|
97
|
+
if (dueOn < today)
|
|
98
|
+
return since && periodTo < since ? null : { periodTo, dueOn };
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The report that is late (decision 8): the last fiscal year whose report is due, when no report ending that day has been
|
|
104
|
+
* received. Nothing is made ahead for it - it is computed.
|
|
105
|
+
*/
|
|
106
|
+
function lateReport(investee, reports, holdings, today) {
|
|
107
|
+
const since = [...holdings].map(one => one.recordDate).sort()[0];
|
|
108
|
+
if (!since)
|
|
109
|
+
return null;
|
|
110
|
+
const due = lastDueYear(investee, today, since);
|
|
111
|
+
if (!due)
|
|
112
|
+
return null;
|
|
113
|
+
return reports.some(one => one.periodTo === due.periodTo && !!one.receivedOn) ? null : due;
|
|
114
|
+
}
|
|
115
|
+
/** A request not yet answered whose due day has passed or comes within the notice days. */
|
|
116
|
+
const requestDue = (request, today) => !request.sentOn && !!request.dueOn && request.dueOn <= addDays(today, exports.REQUEST_NOTICE_DAYS);
|
|
117
|
+
exports.requestDue = requestDue;
|
|
118
|
+
/** A filing not yet made whose due day has passed or comes within the notice days. */
|
|
119
|
+
const filingDue = (filing, today) => !filing.filedOn && !!filing.dueOn && filing.dueOn <= addDays(today, exports.FILING_NOTICE_DAYS);
|
|
120
|
+
exports.filingDue = filingDue;
|
|
121
|
+
//# sourceMappingURL=investment-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"investment-rules.js","sourceRoot":"","sources":["../../../server/service/investment/investment-rules.ts"],"names":[],"mappings":"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","sourcesContent":["import { registerEnumType } from 'type-graphql'\n\n/*\n * The rules of the shares the domain holds in other companies (ADR-0175 decisions 6-10) - apart from the entities, so\n * the screens' words and the tests read them without a type-graphql class. Pure; days are `YYYY-MM-DD`.\n */\n\nexport enum HoldingReason {\n FOUNDING = 'FOUNDING',\n SUBSCRIPTION = 'SUBSCRIPTION',\n DILUTION = 'DILUTION',\n PURCHASE = 'PURCHASE',\n SALE = 'SALE',\n BONUS_ISSUE = 'BONUS_ISSUE',\n OTHER = 'OTHER'\n}\nregisterEnumType(HoldingReason, {\n name: 'HoldingReason',\n description: 'Why the holding changed - shares taken at founding, a subscription to new shares, dilution by shares issued to others, a purchase, a sale, a bonus issue, or other'\n})\n\nexport enum Contribution {\n CASH = 'CASH',\n IN_KIND = 'IN_KIND',\n TECHNOLOGY = 'TECHNOLOGY',\n NONE = 'NONE'\n}\nregisterEnumType(Contribution, { name: 'Contribution', description: 'What was given for the shares - cash, property in kind, technology, or nothing' })\n\nexport enum InvestmentReportKind {\n AUDITED = 'AUDITED',\n INTERIM = 'INTERIM',\n MANAGEMENT = 'MANAGEMENT'\n}\nregisterEnumType(InvestmentReportKind, { name: 'InvestmentReportKind', description: 'An audited financial statement, an interim report or a management report' })\n\nexport enum InvestmentRequestKind {\n WRITTEN_RESOLUTION = 'WRITTEN_RESOLUTION',\n RECEIPT_ACKNOWLEDGEMENT = 'RECEIPT_ACKNOWLEDGEMENT',\n MEETING_NOTICE = 'MEETING_NOTICE',\n OTHER = 'OTHER'\n}\nregisterEnumType(InvestmentRequestKind, {\n name: 'InvestmentRequestKind',\n description: \"What the investee asks of its shareholders - a written resolution, an acknowledgement of receipt, a meeting's notice, or other\"\n})\n\n/** Days ahead a request's or a filing's due day is shown before it passes (decision 11). */\nexport const REQUEST_NOTICE_DAYS = 7\nexport const FILING_NOTICE_DAYS = 30\n\nexport type Holding = { recordDate: string; ourShares: number; totalShares: number }\n\n/** Our part of the company: our shares over all its shares. */\nexport const ratioOf = (holding: Pick<Holding, 'ourShares' | 'totalShares'>) => (Number(holding.totalShares) > 0 ? Number(holding.ourShares) / Number(holding.totalShares) : 0)\n\n/** The holding as it stood on a day - the last record on or before it. */\nexport function holdingAt<T extends Holding>(holdings: T[], day: string): T | undefined {\n return [...holdings].filter(one => one.recordDate <= day).sort((a, b) => a.recordDate.localeCompare(b.recordDate)).pop()\n}\n\n/** Our part of a report's equity - its equity times our part at its end; null when either is unknown. */\nexport function ourShareOf(report: { periodTo: string; totalEquity?: number | null }, holdings: Holding[]): number | null {\n const holding = holdingAt(holdings, report.periodTo)\n if (!holding || report.totalEquity === null || report.totalEquity === undefined) return null\n return Math.round(Number(report.totalEquity) * ratioOf(holding))\n}\n\nconst pad = (n: number) => String(n).padStart(2, '0')\n/** The last day of a month, month counted from 1 and allowed past 12. */\nfunction monthEnd(year: number, month: number): string {\n const date = new Date(Date.UTC(year, month, 0))\n return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`\n}\n\n/** Add days to a day. */\nexport function addDays(day: string, days: number): string {\n const date = new Date(`${day}T00:00:00Z`)\n date.setUTCDate(date.getUTCDate() + days)\n return date.toISOString().slice(0, 10)\n}\n\n/**\n * The last fiscal year whose report is due by now: its end, and the day its report was due - the end of the month the\n * reporting months after it. Null when the investee has no year end or reporting months written, or that year ended\n * before we held its shares (`since`).\n */\nexport function lastDueYear(investee: { fiscalYearEndMonth?: number | null; reportDueMonths?: number | null }, today: string, since?: string | null): { periodTo: string; dueOn: string } | null {\n const month = Number(investee.fiscalYearEndMonth)\n const months = Number(investee.reportDueMonths)\n if (!(month >= 1 && month <= 12) || !(months >= 0) || investee.reportDueMonths === null || investee.reportDueMonths === undefined) return null\n const year = Number(today.slice(0, 4))\n for (let y = year; y > year - 50; y--) {\n const periodTo = monthEnd(y, month)\n const dueOn = monthEnd(y, month + months)\n if (dueOn < today) return since && periodTo < since ? null : { periodTo, dueOn }\n }\n return null\n}\n\n/**\n * The report that is late (decision 8): the last fiscal year whose report is due, when no report ending that day has been\n * received. Nothing is made ahead for it - it is computed.\n */\nexport function lateReport(\n investee: { fiscalYearEndMonth?: number | null; reportDueMonths?: number | null },\n reports: { periodTo: string; receivedOn?: string | null }[],\n holdings: Holding[],\n today: string\n): { periodTo: string; dueOn: string } | null {\n const since = [...holdings].map(one => one.recordDate).sort()[0]\n if (!since) return null\n const due = lastDueYear(investee, today, since)\n if (!due) return null\n return reports.some(one => one.periodTo === due.periodTo && !!one.receivedOn) ? null : due\n}\n\n/** A request not yet answered whose due day has passed or comes within the notice days. */\nexport const requestDue = (request: { dueOn?: string | null; sentOn?: string | null }, today: string) =>\n !request.sentOn && !!request.dueOn && request.dueOn <= addDays(today, REQUEST_NOTICE_DAYS)\n\n/** A filing not yet made whose due day has passed or comes within the notice days. */\nexport const filingDue = (filing: { dueOn?: string | null; filedOn?: string | null }, today: string) =>\n !filing.filedOn && !!filing.dueOn && filing.dueOn <= addDays(today, FILING_NOTICE_DAYS)\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Domain } from '@things-factory/shell';
|
|
2
|
+
import { EntityManager } from 'typeorm';
|
|
3
|
+
import { Patch } from '../patches';
|
|
4
|
+
import { Investee } from './investee';
|
|
5
|
+
import { InvestmentBoardSeat } from './investment-board-seat';
|
|
6
|
+
import { InvestmentFiling } from './investment-filing';
|
|
7
|
+
import { InvestmentHolding } from './investment-holding';
|
|
8
|
+
import { InvestmentReport } from './investment-report';
|
|
9
|
+
import { InvestmentRequest } from './investment-request';
|
|
10
|
+
export declare function saveInvestees(tx: EntityManager, domain: Domain, user: any, patches: Patch[]): Promise<Investee[]>;
|
|
11
|
+
/** The investees with where the holding stands now. */
|
|
12
|
+
export declare function withHoldings(tx: EntityManager, domain: Domain, investees: Investee[], today: string): Promise<Investee[]>;
|
|
13
|
+
export declare function saveHoldings(tx: EntityManager, domain: Domain, user: any, investeeId: string, patches: Patch[]): Promise<InvestmentHolding[]>;
|
|
14
|
+
export declare function saveBoardSeats(tx: EntityManager, domain: Domain, user: any, investeeId: string, patches: Patch[]): Promise<InvestmentBoardSeat[]>;
|
|
15
|
+
export declare function saveReports(tx: EntityManager, domain: Domain, user: any, investeeId: string, patches: Patch[]): Promise<InvestmentReport[]>;
|
|
16
|
+
export declare function saveRequests(tx: EntityManager, domain: Domain, user: any, investeeId: string, patches: Patch[]): Promise<InvestmentRequest[]>;
|
|
17
|
+
export declare function saveFilings(tx: EntityManager, domain: Domain, user: any, investeeId: string, patches: Patch[]): Promise<InvestmentFiling[]>;
|
|
18
|
+
export declare const holdingsOf: (tx: EntityManager, domain: Domain, investeeId: string) => Promise<InvestmentHolding[]>;
|
|
19
|
+
export declare const boardSeatsOf: (tx: EntityManager, domain: Domain, investeeId: string) => Promise<InvestmentBoardSeat[]>;
|
|
20
|
+
/** The reports with our part of each one's equity and how many documents each has. */
|
|
21
|
+
export declare function reportsOf(tx: EntityManager, domain: Domain, investeeId: string): Promise<InvestmentReport[]>;
|
|
22
|
+
export declare function requestsOf(tx: EntityManager, domain: Domain, investeeId: string): Promise<InvestmentRequest[]>;
|
|
23
|
+
export declare function filingsOf(tx: EntityManager, domain: Domain, investeeId: string): Promise<InvestmentFiling[]>;
|
|
24
|
+
/** Each investee's report that is late, computed from its year end and reporting months. */
|
|
25
|
+
export declare function lateInvestmentReports(tx: EntityManager, domain: Domain, today: string): Promise<{
|
|
26
|
+
periodTo: string;
|
|
27
|
+
dueOn: string;
|
|
28
|
+
investeeId: string;
|
|
29
|
+
investeeName: string;
|
|
30
|
+
}[]>;
|
|
31
|
+
/** Requests not answered whose due day has passed or is near. */
|
|
32
|
+
export declare function dueInvestmentRequests(tx: EntityManager, domain: Domain, today: string): Promise<InvestmentRequest[]>;
|
|
33
|
+
/** Filings not made whose due day has passed or is near. */
|
|
34
|
+
export declare function dueInvestmentFilings(tx: EntityManager, domain: Domain, today: string): Promise<InvestmentFiling[]>;
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.boardSeatsOf = exports.holdingsOf = void 0;
|
|
4
|
+
exports.saveInvestees = saveInvestees;
|
|
5
|
+
exports.withHoldings = withHoldings;
|
|
6
|
+
exports.saveHoldings = saveHoldings;
|
|
7
|
+
exports.saveBoardSeats = saveBoardSeats;
|
|
8
|
+
exports.saveReports = saveReports;
|
|
9
|
+
exports.saveRequests = saveRequests;
|
|
10
|
+
exports.saveFilings = saveFilings;
|
|
11
|
+
exports.reportsOf = reportsOf;
|
|
12
|
+
exports.requestsOf = requestsOf;
|
|
13
|
+
exports.filingsOf = filingsOf;
|
|
14
|
+
exports.lateInvestmentReports = lateInvestmentReports;
|
|
15
|
+
exports.dueInvestmentRequests = dueInvestmentRequests;
|
|
16
|
+
exports.dueInvestmentFilings = dueInvestmentFilings;
|
|
17
|
+
const biz_base_1 = require("@things-factory/biz-base");
|
|
18
|
+
const shell_1 = require("@things-factory/shell");
|
|
19
|
+
const typeorm_1 = require("typeorm");
|
|
20
|
+
const asset_document_service_1 = require("../asset-document/asset-document-service");
|
|
21
|
+
const patches_1 = require("../patches");
|
|
22
|
+
const investee_1 = require("./investee");
|
|
23
|
+
const investment_board_seat_1 = require("./investment-board-seat");
|
|
24
|
+
const investment_filing_1 = require("./investment-filing");
|
|
25
|
+
const investment_holding_1 = require("./investment-holding");
|
|
26
|
+
const investment_report_1 = require("./investment-report");
|
|
27
|
+
const investment_request_1 = require("./investment-request");
|
|
28
|
+
const investment_rules_1 = require("./investment-rules");
|
|
29
|
+
const currencyOf = (value, fallback) => {
|
|
30
|
+
const text = String(value ?? '').trim().toUpperCase();
|
|
31
|
+
if (!text)
|
|
32
|
+
return fallback;
|
|
33
|
+
if (!/^[A-Z]{3}$/.test(text))
|
|
34
|
+
throw new shell_1.Refusal('investment-currency-invalid', { value: text }, `'${text}' is not a currency code (KRW, USD, MYR)`);
|
|
35
|
+
return text;
|
|
36
|
+
};
|
|
37
|
+
/* ----------------------------------------------------------------------------------------------------------------- */
|
|
38
|
+
/* Investees (decision 6) */
|
|
39
|
+
function saveInvestees(tx, domain, user, patches) {
|
|
40
|
+
return (0, patches_1.applyPatches)(tx, investee_1.Investee, domain, user, patches, async (patch, old) => {
|
|
41
|
+
const row = {};
|
|
42
|
+
const name = (0, patches_1.touched)(patch, 'name', old) ? (0, patches_1.textOf)(patch.name) : old.name;
|
|
43
|
+
if (!name)
|
|
44
|
+
throw new shell_1.Refusal('investee-name-required', {}, 'Name the company');
|
|
45
|
+
row.name = name;
|
|
46
|
+
for (const field of ['registrationNo', 'representative', 'description'])
|
|
47
|
+
if ((0, patches_1.touched)(patch, field, old))
|
|
48
|
+
row[field] = (0, patches_1.textOf)(patch[field]);
|
|
49
|
+
if ((0, patches_1.touched)(patch, 'country', old))
|
|
50
|
+
row.country = (0, patches_1.textOf)(patch.country)?.toUpperCase() ?? null;
|
|
51
|
+
if ((0, patches_1.touched)(patch, 'currency', old))
|
|
52
|
+
row.currency = currencyOf(patch.currency, 'KRW');
|
|
53
|
+
if ((0, patches_1.touched)(patch, 'fiscalYearEndMonth', old)) {
|
|
54
|
+
const month = patch.fiscalYearEndMonth === null || patch.fiscalYearEndMonth === undefined || patch.fiscalYearEndMonth === '' ? null : Number(patch.fiscalYearEndMonth);
|
|
55
|
+
if (month !== null && !(Number.isInteger(month) && month >= 1 && month <= 12))
|
|
56
|
+
throw new shell_1.Refusal('investee-year-end-invalid', {}, 'The year ends in a month, 1 to 12');
|
|
57
|
+
row.fiscalYearEndMonth = month;
|
|
58
|
+
}
|
|
59
|
+
if ((0, patches_1.touched)(patch, 'reportDueMonths', old)) {
|
|
60
|
+
const months = patch.reportDueMonths === null || patch.reportDueMonths === undefined || patch.reportDueMonths === '' ? null : Number(patch.reportDueMonths);
|
|
61
|
+
if (months !== null && !(Number.isInteger(months) && months >= 0 && months <= 24))
|
|
62
|
+
throw new shell_1.Refusal('investee-report-months-invalid', {}, 'The report is due 0 to 24 months after the year ends');
|
|
63
|
+
row.reportDueMonths = months;
|
|
64
|
+
}
|
|
65
|
+
if ((0, patches_1.touched)(patch, 'active', old))
|
|
66
|
+
row.active = patch.active !== false;
|
|
67
|
+
if ((0, patches_1.touched)(patch, 'counterpartyId', old)) {
|
|
68
|
+
if (!patch.counterpartyId)
|
|
69
|
+
row.counterparty = null;
|
|
70
|
+
else {
|
|
71
|
+
const counterparty = await tx.getRepository(biz_base_1.Counterparty).findOne({ where: { domain: { id: domain.id }, id: patch.counterpartyId } });
|
|
72
|
+
if (!counterparty)
|
|
73
|
+
throw new shell_1.Refusal('investee-counterparty-not-found', {}, 'The counterparty was not found');
|
|
74
|
+
row.counterparty = counterparty;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return row;
|
|
78
|
+
}, 'investee-not-found');
|
|
79
|
+
}
|
|
80
|
+
/** The investees with where the holding stands now. */
|
|
81
|
+
async function withHoldings(tx, domain, investees, today) {
|
|
82
|
+
if (!investees.length)
|
|
83
|
+
return investees;
|
|
84
|
+
const holdings = await tx.getRepository(investment_holding_1.InvestmentHolding).find({ where: { domain: { id: domain.id }, investee: { id: (0, typeorm_1.In)(investees.map(one => one.id)) } }, relations: ['investee'] });
|
|
85
|
+
for (const investee of investees) {
|
|
86
|
+
const now = (0, investment_rules_1.holdingAt)(holdings.filter(one => one.investeeId === investee.id), today);
|
|
87
|
+
investee.ourShares = now?.ourShares;
|
|
88
|
+
investee.totalShares = now?.totalShares;
|
|
89
|
+
investee.ratio = now ? (0, investment_rules_1.ratioOf)(now) : undefined;
|
|
90
|
+
}
|
|
91
|
+
return investees;
|
|
92
|
+
}
|
|
93
|
+
async function investeeOf(tx, domain, investeeId) {
|
|
94
|
+
const investee = await tx.getRepository(investee_1.Investee).findOne({ where: { domain: { id: domain.id }, id: investeeId } });
|
|
95
|
+
if (!investee)
|
|
96
|
+
throw new shell_1.Refusal('investee-not-found', {}, 'The company was not found - reload and try again');
|
|
97
|
+
return investee;
|
|
98
|
+
}
|
|
99
|
+
/* ----------------------------------------------------------------------------------------------------------------- */
|
|
100
|
+
/* An investee's rows (decisions 7-10) - each grid saves under its investee */
|
|
101
|
+
async function saveHoldings(tx, domain, user, investeeId, patches) {
|
|
102
|
+
const investee = await investeeOf(tx, domain, investeeId);
|
|
103
|
+
return (0, patches_1.applyPatches)(tx, investment_holding_1.InvestmentHolding, domain, user, patches, (patch, old) => {
|
|
104
|
+
const row = { investee };
|
|
105
|
+
row.recordDate = (0, patches_1.touched)(patch, 'recordDate', old) ? (0, patches_1.dayOf)(patch.recordDate, 'investment-holding-day-invalid', true) : old.recordDate;
|
|
106
|
+
if ((0, patches_1.touched)(patch, 'ourShares', old))
|
|
107
|
+
row.ourShares = (0, patches_1.amountOf)(patch.ourShares, 'investment-shares-invalid') ?? 0;
|
|
108
|
+
if ((0, patches_1.touched)(patch, 'totalShares', old))
|
|
109
|
+
row.totalShares = (0, patches_1.amountOf)(patch.totalShares, 'investment-shares-invalid') ?? 0;
|
|
110
|
+
const ours = Number(row.ourShares ?? old.ourShares);
|
|
111
|
+
const total = Number(row.totalShares ?? old.totalShares);
|
|
112
|
+
if (!(total > 0))
|
|
113
|
+
throw new shell_1.Refusal('investment-total-shares-required', {}, "Give all the company's shares");
|
|
114
|
+
if (ours > total)
|
|
115
|
+
throw new shell_1.Refusal('investment-shares-above-total', {}, "Our shares are more than all the company's shares");
|
|
116
|
+
if ((0, patches_1.touched)(patch, 'reason', old))
|
|
117
|
+
row.reason = patch.reason ?? investment_rules_1.HoldingReason.OTHER;
|
|
118
|
+
if ((0, patches_1.touched)(patch, 'contribution', old))
|
|
119
|
+
row.contribution = patch.contribution ?? investment_rules_1.Contribution.NONE;
|
|
120
|
+
if ((0, patches_1.touched)(patch, 'amount', old))
|
|
121
|
+
row.amount = (0, patches_1.amountOf)(patch.amount, 'investment-amount-invalid');
|
|
122
|
+
if ((0, patches_1.touched)(patch, 'currency', old))
|
|
123
|
+
row.currency = currencyOf(patch.currency, null);
|
|
124
|
+
if ((0, patches_1.touched)(patch, 'description', old))
|
|
125
|
+
row.description = (0, patches_1.textOf)(patch.description);
|
|
126
|
+
return row;
|
|
127
|
+
}, 'investment-row-not-found', { investee: { id: investee.id } });
|
|
128
|
+
}
|
|
129
|
+
async function saveBoardSeats(tx, domain, user, investeeId, patches) {
|
|
130
|
+
const investee = await investeeOf(tx, domain, investeeId);
|
|
131
|
+
return (0, patches_1.applyPatches)(tx, investment_board_seat_1.InvestmentBoardSeat, domain, user, patches, (patch, old) => {
|
|
132
|
+
const row = { investee };
|
|
133
|
+
const director = (0, patches_1.touched)(patch, 'director', old) ? (0, patches_1.textOf)(patch.director) : old.director;
|
|
134
|
+
if (!director)
|
|
135
|
+
throw new shell_1.Refusal('investment-director-required', {}, 'Name who sits on the board');
|
|
136
|
+
row.director = director;
|
|
137
|
+
if ((0, patches_1.touched)(patch, 'appointedOn', old))
|
|
138
|
+
row.appointedOn = (0, patches_1.dayOf)(patch.appointedOn, 'investment-day-invalid');
|
|
139
|
+
if ((0, patches_1.touched)(patch, 'resignedOn', old))
|
|
140
|
+
row.resignedOn = (0, patches_1.dayOf)(patch.resignedOn, 'investment-day-invalid');
|
|
141
|
+
const from = row.appointedOn !== undefined ? row.appointedOn : old?.appointedOn;
|
|
142
|
+
const to = row.resignedOn !== undefined ? row.resignedOn : old?.resignedOn;
|
|
143
|
+
if (from && to && to < from)
|
|
144
|
+
throw new shell_1.Refusal('investment-resigned-before-appointed', {}, 'They left before they were appointed');
|
|
145
|
+
if ((0, patches_1.touched)(patch, 'description', old))
|
|
146
|
+
row.description = (0, patches_1.textOf)(patch.description);
|
|
147
|
+
return row;
|
|
148
|
+
}, 'investment-row-not-found', { investee: { id: investee.id } });
|
|
149
|
+
}
|
|
150
|
+
const REPORT_FIGURES = ['revenue', 'shareCapital', 'totalEquity', 'totalAssets'];
|
|
151
|
+
async function saveReports(tx, domain, user, investeeId, patches) {
|
|
152
|
+
const investee = await investeeOf(tx, domain, investeeId);
|
|
153
|
+
return (0, patches_1.applyPatches)(tx, investment_report_1.InvestmentReport, domain, user, patches, (patch, old) => {
|
|
154
|
+
const row = { investee };
|
|
155
|
+
row.periodTo = (0, patches_1.touched)(patch, 'periodTo', old) ? (0, patches_1.dayOf)(patch.periodTo, 'investment-report-period-invalid', true) : old.periodTo;
|
|
156
|
+
if ((0, patches_1.touched)(patch, 'periodFrom', old))
|
|
157
|
+
row.periodFrom = (0, patches_1.dayOf)(patch.periodFrom, 'investment-report-period-invalid');
|
|
158
|
+
const from = row.periodFrom !== undefined ? row.periodFrom : old?.periodFrom;
|
|
159
|
+
if (from && from > row.periodTo)
|
|
160
|
+
throw new shell_1.Refusal('investment-report-period-reversed', {}, 'The period ends before it starts');
|
|
161
|
+
if ((0, patches_1.touched)(patch, 'kind', old))
|
|
162
|
+
row.kind = patch.kind ?? investment_rules_1.InvestmentReportKind.AUDITED;
|
|
163
|
+
for (const field of ['standard', 'auditor', 'description'])
|
|
164
|
+
if ((0, patches_1.touched)(patch, field, old))
|
|
165
|
+
row[field] = (0, patches_1.textOf)(patch[field]);
|
|
166
|
+
for (const field of ['signedOn', 'receivedOn'])
|
|
167
|
+
if ((0, patches_1.touched)(patch, field, old))
|
|
168
|
+
row[field] = (0, patches_1.dayOf)(patch[field], 'investment-day-invalid');
|
|
169
|
+
/* Equity and net income may be below zero; the rest are amounts. */
|
|
170
|
+
for (const field of REPORT_FIGURES)
|
|
171
|
+
if ((0, patches_1.touched)(patch, field, old))
|
|
172
|
+
row[field] = field === 'totalEquity' ? numberOf(patch[field]) : (0, patches_1.amountOf)(patch[field], 'investment-amount-invalid');
|
|
173
|
+
if ((0, patches_1.touched)(patch, 'netIncome', old))
|
|
174
|
+
row.netIncome = numberOf(patch.netIncome);
|
|
175
|
+
return row;
|
|
176
|
+
}, 'investment-row-not-found', { investee: { id: investee.id } });
|
|
177
|
+
}
|
|
178
|
+
function numberOf(value) {
|
|
179
|
+
if (value === null || value === undefined || value === '')
|
|
180
|
+
return null;
|
|
181
|
+
const number = Number(value);
|
|
182
|
+
if (!Number.isFinite(number))
|
|
183
|
+
throw new shell_1.Refusal('investment-amount-invalid', { value }, 'Write a number');
|
|
184
|
+
return number;
|
|
185
|
+
}
|
|
186
|
+
async function saveRequests(tx, domain, user, investeeId, patches) {
|
|
187
|
+
const investee = await investeeOf(tx, domain, investeeId);
|
|
188
|
+
return (0, patches_1.applyPatches)(tx, investment_request_1.InvestmentRequest, domain, user, patches, (patch, old) => {
|
|
189
|
+
const row = { investee };
|
|
190
|
+
const title = (0, patches_1.touched)(patch, 'title', old) ? (0, patches_1.textOf)(patch.title) : old.title;
|
|
191
|
+
if (!title)
|
|
192
|
+
throw new shell_1.Refusal('investment-request-title-required', {}, 'Say what the request is about');
|
|
193
|
+
row.title = title;
|
|
194
|
+
if ((0, patches_1.touched)(patch, 'kind', old))
|
|
195
|
+
row.kind = patch.kind ?? investment_rules_1.InvestmentRequestKind.WRITTEN_RESOLUTION;
|
|
196
|
+
for (const field of ['receivedOn', 'dueOn', 'sentOn'])
|
|
197
|
+
if ((0, patches_1.touched)(patch, field, old))
|
|
198
|
+
row[field] = (0, patches_1.dayOf)(patch[field], 'investment-day-invalid');
|
|
199
|
+
for (const field of ['signer', 'items', 'description'])
|
|
200
|
+
if ((0, patches_1.touched)(patch, field, old))
|
|
201
|
+
row[field] = (0, patches_1.textOf)(patch[field]);
|
|
202
|
+
return row;
|
|
203
|
+
}, 'investment-row-not-found', { investee: { id: investee.id } });
|
|
204
|
+
}
|
|
205
|
+
async function saveFilings(tx, domain, user, investeeId, patches) {
|
|
206
|
+
const investee = await investeeOf(tx, domain, investeeId);
|
|
207
|
+
return (0, patches_1.applyPatches)(tx, investment_filing_1.InvestmentFiling, domain, user, patches, (patch, old) => {
|
|
208
|
+
const row = { investee };
|
|
209
|
+
const name = (0, patches_1.touched)(patch, 'name', old) ? (0, patches_1.textOf)(patch.name) : old.name;
|
|
210
|
+
if (!name)
|
|
211
|
+
throw new shell_1.Refusal('investment-filing-name-required', {}, 'Say what is to be filed');
|
|
212
|
+
row.name = name;
|
|
213
|
+
for (const field of ['law', 'authority', 'description'])
|
|
214
|
+
if ((0, patches_1.touched)(patch, field, old))
|
|
215
|
+
row[field] = (0, patches_1.textOf)(patch[field]);
|
|
216
|
+
for (const field of ['dueOn', 'filedOn'])
|
|
217
|
+
if ((0, patches_1.touched)(patch, field, old))
|
|
218
|
+
row[field] = (0, patches_1.dayOf)(patch[field], 'investment-day-invalid');
|
|
219
|
+
return row;
|
|
220
|
+
}, 'investment-row-not-found', { investee: { id: investee.id } });
|
|
221
|
+
}
|
|
222
|
+
/* ----------------------------------------------------------------------------------------------------------------- */
|
|
223
|
+
/* Reading an investee's rows */
|
|
224
|
+
const rowsOf = (tx, target, domain, investeeId, order) => tx.getRepository(target).find({ where: { domain: { id: domain.id }, investee: { id: investeeId } }, order: order });
|
|
225
|
+
const holdingsOf = (tx, domain, investeeId) => rowsOf(tx, investment_holding_1.InvestmentHolding, domain, investeeId, { recordDate: 'ASC' });
|
|
226
|
+
exports.holdingsOf = holdingsOf;
|
|
227
|
+
const boardSeatsOf = (tx, domain, investeeId) => rowsOf(tx, investment_board_seat_1.InvestmentBoardSeat, domain, investeeId, { appointedOn: 'ASC' });
|
|
228
|
+
exports.boardSeatsOf = boardSeatsOf;
|
|
229
|
+
/** The reports with our part of each one's equity and how many documents each has. */
|
|
230
|
+
async function reportsOf(tx, domain, investeeId) {
|
|
231
|
+
const [reports, holdings] = await Promise.all([rowsOf(tx, investment_report_1.InvestmentReport, domain, investeeId, { periodTo: 'DESC' }), (0, exports.holdingsOf)(tx, domain, investeeId)]);
|
|
232
|
+
const counts = await (0, asset_document_service_1.documentCounts)(tx, domain, 'InvestmentReport', reports.map(one => one.id));
|
|
233
|
+
for (const one of reports) {
|
|
234
|
+
one.ourShare = (0, investment_rules_1.ourShareOf)(one, holdings) ?? undefined;
|
|
235
|
+
one.documents = counts.get(one.id) ?? 0;
|
|
236
|
+
}
|
|
237
|
+
return reports;
|
|
238
|
+
}
|
|
239
|
+
async function requestsOf(tx, domain, investeeId) {
|
|
240
|
+
const requests = await rowsOf(tx, investment_request_1.InvestmentRequest, domain, investeeId, { receivedOn: 'DESC' });
|
|
241
|
+
const counts = await (0, asset_document_service_1.documentCounts)(tx, domain, 'InvestmentRequest', requests.map(one => one.id));
|
|
242
|
+
for (const one of requests)
|
|
243
|
+
one.documents = counts.get(one.id) ?? 0;
|
|
244
|
+
return requests;
|
|
245
|
+
}
|
|
246
|
+
async function filingsOf(tx, domain, investeeId) {
|
|
247
|
+
const filings = await rowsOf(tx, investment_filing_1.InvestmentFiling, domain, investeeId, { dueOn: 'DESC' });
|
|
248
|
+
const counts = await (0, asset_document_service_1.documentCounts)(tx, domain, 'InvestmentFiling', filings.map(one => one.id));
|
|
249
|
+
for (const one of filings)
|
|
250
|
+
one.documents = counts.get(one.id) ?? 0;
|
|
251
|
+
return filings;
|
|
252
|
+
}
|
|
253
|
+
/* ----------------------------------------------------------------------------------------------------------------- */
|
|
254
|
+
/* What is late or due (decision 11) - an application's management check counts these */
|
|
255
|
+
/** Each investee's report that is late, computed from its year end and reporting months. */
|
|
256
|
+
async function lateInvestmentReports(tx, domain, today) {
|
|
257
|
+
const investees = await tx.getRepository(investee_1.Investee).find({ where: { domain: { id: domain.id }, active: true } });
|
|
258
|
+
if (!investees.length)
|
|
259
|
+
return [];
|
|
260
|
+
const ids = investees.map(one => one.id);
|
|
261
|
+
const [holdings, reports] = await Promise.all([
|
|
262
|
+
tx.getRepository(investment_holding_1.InvestmentHolding).find({ where: { domain: { id: domain.id }, investee: { id: (0, typeorm_1.In)(ids) } }, relations: ['investee'] }),
|
|
263
|
+
tx.getRepository(investment_report_1.InvestmentReport).find({ where: { domain: { id: domain.id }, investee: { id: (0, typeorm_1.In)(ids) } }, relations: ['investee'] })
|
|
264
|
+
]);
|
|
265
|
+
return investees.flatMap(investee => {
|
|
266
|
+
const late = (0, investment_rules_1.lateReport)(investee, reports.filter(one => one.investeeId === investee.id), holdings.filter(one => one.investeeId === investee.id), today);
|
|
267
|
+
return late ? [{ investeeId: investee.id, investeeName: investee.name, ...late }] : [];
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/** Requests not answered whose due day has passed or is near. */
|
|
271
|
+
async function dueInvestmentRequests(tx, domain, today) {
|
|
272
|
+
const open = await tx.getRepository(investment_request_1.InvestmentRequest).find({ where: { domain: { id: domain.id }, sentOn: (0, typeorm_1.IsNull)(), dueOn: (0, typeorm_1.Not)((0, typeorm_1.IsNull)()) }, relations: ['investee'] });
|
|
273
|
+
return open.filter(one => (0, investment_rules_1.requestDue)(one, today)).sort((a, b) => a.dueOn.localeCompare(b.dueOn));
|
|
274
|
+
}
|
|
275
|
+
/** Filings not made whose due day has passed or is near. */
|
|
276
|
+
async function dueInvestmentFilings(tx, domain, today) {
|
|
277
|
+
const open = await tx.getRepository(investment_filing_1.InvestmentFiling).find({ where: { domain: { id: domain.id }, filedOn: (0, typeorm_1.IsNull)(), dueOn: (0, typeorm_1.Not)((0, typeorm_1.IsNull)()) }, relations: ['investee'] });
|
|
278
|
+
return open.filter(one => (0, investment_rules_1.filingDue)(one, today)).sort((a, b) => a.dueOn.localeCompare(b.dueOn));
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=investment-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"investment-service.js","sourceRoot":"","sources":["../../../server/service/investment/investment-service.ts"],"names":[],"mappings":";;;AAwBA,sCAsCC;AAGD,oCAUC;AAWD,oCA2BC;AAED,wCAwBC;AAID,kCAyBC;AASD,oCAqBC;AAED,kCAoBC;AAYD,8BAQC;AAED,gCAKC;AAED,8BAKC;AAMD,sDAiBC;AAGD,sDAGC;AAGD,oDAGC;AAjSD,uDAAuD;AACvD,iDAAuD;AACvD,qCAAwD;AAExD,qFAAyE;AACzE,wCAAkF;AAClF,yCAAqC;AACrC,mEAA6D;AAC7D,2DAAsD;AACtD,6DAAwD;AACxD,2DAAsD;AACtD,6DAAwD;AACxD,yDAAgL;AAEhL,MAAM,UAAU,GAAG,CAAC,KAAU,EAAE,QAAuB,EAAE,EAAE;IACzD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IACrD,IAAI,CAAC,IAAI;QAAE,OAAO,QAAQ,CAAA;IAC1B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,eAAO,CAAC,6BAA6B,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,IAAI,IAAI,0CAA0C,CAAC,CAAA;IACnJ,OAAO,IAAI,CAAA;AACb,CAAC,CAAA;AAED,uHAAuH;AACvH,4BAA4B;AAE5B,SAAgB,aAAa,CAAC,EAAiB,EAAE,MAAc,EAAE,IAAS,EAAE,OAAgB;IAC1F,OAAO,IAAA,sBAAY,EACjB,EAAE,EACF,mBAAQ,EACR,MAAM,EACN,IAAI,EACJ,OAAO,EACP,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,GAAG,GAAwB,EAAE,CAAA;QACnC,MAAM,IAAI,GAAG,IAAA,iBAAO,EAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAA,gBAAM,EAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAI,CAAC,IAAI,CAAA;QACzE,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,eAAO,CAAC,wBAAwB,EAAE,EAAE,EAAE,kBAAkB,CAAC,CAAA;QAC9E,GAAG,CAAC,IAAI,GAAG,IAAI,CAAA;QACf,KAAK,MAAM,KAAK,IAAI,CAAC,gBAAgB,EAAE,gBAAgB,EAAE,aAAa,CAAC;YAAE,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC;gBAAE,GAAG,CAAC,KAAK,CAAC,GAAG,IAAA,gBAAM,EAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;QAC1I,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,OAAO,GAAG,IAAA,gBAAM,EAAC,KAAK,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,IAAI,IAAI,CAAA;QAC9F,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;QACrF,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,CAAC,EAAE,CAAC;YAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,kBAAkB,KAAK,IAAI,IAAI,KAAK,CAAC,kBAAkB,KAAK,SAAS,IAAI,KAAK,CAAC,kBAAkB,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAA;YACtK,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;gBAAE,MAAM,IAAI,eAAO,CAAC,2BAA2B,EAAE,EAAE,EAAE,mCAAmC,CAAC,CAAA;YACtK,GAAG,CAAC,kBAAkB,GAAG,KAAK,CAAA;QAChC,CAAC;QACD,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,CAAC,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,eAAe,KAAK,IAAI,IAAI,KAAK,CAAC,eAAe,KAAK,SAAS,IAAI,KAAK,CAAC,eAAe,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAA;YAC3J,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,IAAI,MAAM,IAAI,EAAE,CAAC;gBAAE,MAAM,IAAI,eAAO,CAAC,gCAAgC,EAAE,EAAE,EAAE,sDAAsD,CAAC,CAAA;YAClM,GAAG,CAAC,eAAe,GAAG,MAAM,CAAA;QAC9B,CAAC;QACD,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,KAAK,CAAA;QACtE,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,CAAC,EAAE,CAAC;YAC1C,IAAI,CAAC,KAAK,CAAC,cAAc;gBAAE,GAAG,CAAC,YAAY,GAAG,IAAI,CAAA;iBAC7C,CAAC;gBACJ,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,uBAAY,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,cAAc,EAAS,EAAE,CAAC,CAAA;gBAC5I,IAAI,CAAC,YAAY;oBAAE,MAAM,IAAI,eAAO,CAAC,iCAAiC,EAAE,EAAE,EAAE,gCAAgC,CAAC,CAAA;gBAC7G,GAAG,CAAC,YAAY,GAAG,YAAY,CAAA;YACjC,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC,EACD,oBAAoB,CACrB,CAAA;AACH,CAAC;AAED,uDAAuD;AAChD,KAAK,UAAU,YAAY,CAAC,EAAiB,EAAE,MAAc,EAAE,SAAqB,EAAE,KAAa;IACxG,IAAI,CAAC,SAAS,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IACvC,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,sCAAiB,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAS,EAAE,SAAS,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;IAC7L,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,IAAA,4BAAS,EAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAA;QACpF,QAAQ,CAAC,SAAS,GAAG,GAAG,EAAE,SAAS,CAAA;QACnC,QAAQ,CAAC,WAAW,GAAG,GAAG,EAAE,WAAW,CAAA;QACvC,QAAQ,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,IAAA,0BAAO,EAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACjD,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,EAAiB,EAAE,MAAc,EAAE,UAAkB;IAC7E,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,mBAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,UAAU,EAAS,EAAE,CAAC,CAAA;IAC1H,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,eAAO,CAAC,oBAAoB,EAAE,EAAE,EAAE,kDAAkD,CAAC,CAAA;IAC9G,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,uHAAuH;AACvH,8EAA8E;AAEvE,KAAK,UAAU,YAAY,CAAC,EAAiB,EAAE,MAAc,EAAE,IAAS,EAAE,UAAkB,EAAE,OAAgB;IACnH,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAA;IACzD,OAAO,IAAA,sBAAY,EACjB,EAAE,EACF,sCAAiB,EACjB,MAAM,EACN,IAAI,EACJ,OAAO,EACP,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACb,MAAM,GAAG,GAAwB,EAAE,QAAQ,EAAE,CAAA;QAC7C,GAAG,CAAC,UAAU,GAAG,IAAA,iBAAO,EAAC,KAAK,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAA,eAAK,EAAC,KAAK,CAAC,UAAU,EAAE,gCAAgC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAI,CAAC,UAAU,CAAA;QACtI,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,WAAW,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,SAAS,GAAG,IAAA,kBAAQ,EAAC,KAAK,CAAC,SAAS,EAAE,2BAA2B,CAAC,IAAI,CAAC,CAAA;QACjH,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,aAAa,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,WAAW,GAAG,IAAA,kBAAQ,EAAC,KAAK,CAAC,WAAW,EAAE,2BAA2B,CAAC,IAAI,CAAC,CAAA;QACvH,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,IAAI,GAAI,CAAC,SAAS,CAAC,CAAA;QACpD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,IAAI,GAAI,CAAC,WAAW,CAAC,CAAA;QACzD,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;YAAE,MAAM,IAAI,eAAO,CAAC,kCAAkC,EAAE,EAAE,EAAE,+BAA+B,CAAC,CAAA;QAC5G,IAAI,IAAI,GAAG,KAAK;YAAE,MAAM,IAAI,eAAO,CAAC,+BAA+B,EAAE,EAAE,EAAE,mDAAmD,CAAC,CAAA;QAC7H,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,gCAAa,CAAC,KAAK,CAAA;QACnF,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,cAAc,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,IAAI,+BAAY,CAAC,IAAI,CAAA;QACnG,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,MAAM,GAAG,IAAA,kBAAQ,EAAC,KAAK,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAA;QACnG,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;QACpF,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,aAAa,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,WAAW,GAAG,IAAA,gBAAM,EAAC,KAAK,CAAC,WAAW,CAAC,CAAA;QACnF,OAAO,GAAG,CAAA;IACZ,CAAC,EACD,0BAA0B,EAC1B,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,CAClC,CAAA;AACH,CAAC;AAEM,KAAK,UAAU,cAAc,CAAC,EAAiB,EAAE,MAAc,EAAE,IAAS,EAAE,UAAkB,EAAE,OAAgB;IACrH,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAA;IACzD,OAAO,IAAA,sBAAY,EACjB,EAAE,EACF,2CAAmB,EACnB,MAAM,EACN,IAAI,EACJ,OAAO,EACP,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACb,MAAM,GAAG,GAAwB,EAAE,QAAQ,EAAE,CAAA;QAC7C,MAAM,QAAQ,GAAG,IAAA,iBAAO,EAAC,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAA,gBAAM,EAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAI,CAAC,QAAQ,CAAA;QACzF,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,eAAO,CAAC,8BAA8B,EAAE,EAAE,EAAE,4BAA4B,CAAC,CAAA;QAClG,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACvB,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,aAAa,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,WAAW,GAAG,IAAA,eAAK,EAAC,KAAK,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAAA;QAC5G,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,YAAY,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,UAAU,GAAG,IAAA,eAAK,EAAC,KAAK,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAA;QACzG,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,EAAE,WAAW,CAAA;QAC/E,MAAM,EAAE,GAAG,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,CAAA;QAC1E,IAAI,IAAI,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI;YAAE,MAAM,IAAI,eAAO,CAAC,sCAAsC,EAAE,EAAE,EAAE,sCAAsC,CAAC,CAAA;QAClI,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,aAAa,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,WAAW,GAAG,IAAA,gBAAM,EAAC,KAAK,CAAC,WAAW,CAAC,CAAA;QACnF,OAAO,GAAG,CAAA;IACZ,CAAC,EACD,0BAA0B,EAC1B,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,CAClC,CAAA;AACH,CAAC;AAED,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,CAAU,CAAA;AAElF,KAAK,UAAU,WAAW,CAAC,EAAiB,EAAE,MAAc,EAAE,IAAS,EAAE,UAAkB,EAAE,OAAgB;IAClH,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAA;IACzD,OAAO,IAAA,sBAAY,EACjB,EAAE,EACF,oCAAgB,EAChB,MAAM,EACN,IAAI,EACJ,OAAO,EACP,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACb,MAAM,GAAG,GAAwB,EAAE,QAAQ,EAAE,CAAA;QAC7C,GAAG,CAAC,QAAQ,GAAG,IAAA,iBAAO,EAAC,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAA,eAAK,EAAC,KAAK,CAAC,QAAQ,EAAE,kCAAkC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAI,CAAC,QAAQ,CAAA;QAChI,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,YAAY,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,UAAU,GAAG,IAAA,eAAK,EAAC,KAAK,CAAC,UAAU,EAAE,kCAAkC,CAAC,CAAA;QACnH,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,CAAA;QAC5E,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,CAAC,QAAQ;YAAE,MAAM,IAAI,eAAO,CAAC,mCAAmC,EAAE,EAAE,EAAE,kCAAkC,CAAC,CAAA;QAC/H,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,uCAAoB,CAAC,OAAO,CAAA;QACtF,KAAK,MAAM,KAAK,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,aAAa,CAAC;YAAE,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC;gBAAE,GAAG,CAAC,KAAK,CAAC,GAAG,IAAA,gBAAM,EAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;QAC7H,KAAK,MAAM,KAAK,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC;YAAE,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC;gBAAE,GAAG,CAAC,KAAK,CAAC,GAAG,IAAA,eAAK,EAAC,KAAK,CAAC,KAAK,CAAC,EAAE,wBAAwB,CAAC,CAAA;QAC1I,oEAAoE;QACpE,KAAK,MAAM,KAAK,IAAI,cAAc;YAAE,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC;gBAAE,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAA,kBAAQ,EAAC,KAAK,CAAC,KAAK,CAAC,EAAE,2BAA2B,CAAC,CAAA;QACvL,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,WAAW,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QAC/E,OAAO,GAAG,CAAA;IACZ,CAAC,EACD,0BAA0B,EAC1B,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,CAClC,CAAA;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAU;IAC1B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IACtE,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IAC5B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,eAAO,CAAC,2BAA2B,EAAE,EAAE,KAAK,EAAE,EAAE,gBAAgB,CAAC,CAAA;IACzG,OAAO,MAAM,CAAA;AACf,CAAC;AAEM,KAAK,UAAU,YAAY,CAAC,EAAiB,EAAE,MAAc,EAAE,IAAS,EAAE,UAAkB,EAAE,OAAgB;IACnH,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAA;IACzD,OAAO,IAAA,sBAAY,EACjB,EAAE,EACF,sCAAiB,EACjB,MAAM,EACN,IAAI,EACJ,OAAO,EACP,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACb,MAAM,GAAG,GAAwB,EAAE,QAAQ,EAAE,CAAA;QAC7C,MAAM,KAAK,GAAG,IAAA,iBAAO,EAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAA,gBAAM,EAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAI,CAAC,KAAK,CAAA;QAC7E,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,eAAO,CAAC,mCAAmC,EAAE,EAAE,EAAE,+BAA+B,CAAC,CAAA;QACvG,GAAG,CAAC,KAAK,GAAG,KAAK,CAAA;QACjB,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC;YAAE,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,wCAAqB,CAAC,kBAAkB,CAAA;QAClG,KAAK,MAAM,KAAK,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,QAAQ,CAAC;YAAE,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC;gBAAE,GAAG,CAAC,KAAK,CAAC,GAAG,IAAA,eAAK,EAAC,KAAK,CAAC,KAAK,CAAC,EAAE,wBAAwB,CAAC,CAAA;QACjJ,KAAK,MAAM,KAAK,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,aAAa,CAAC;YAAE,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC;gBAAE,GAAG,CAAC,KAAK,CAAC,GAAG,IAAA,gBAAM,EAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;QACzH,OAAO,GAAG,CAAA;IACZ,CAAC,EACD,0BAA0B,EAC1B,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,CAClC,CAAA;AACH,CAAC;AAEM,KAAK,UAAU,WAAW,CAAC,EAAiB,EAAE,MAAc,EAAE,IAAS,EAAE,UAAkB,EAAE,OAAgB;IAClH,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAA;IACzD,OAAO,IAAA,sBAAY,EACjB,EAAE,EACF,oCAAgB,EAChB,MAAM,EACN,IAAI,EACJ,OAAO,EACP,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACb,MAAM,GAAG,GAAwB,EAAE,QAAQ,EAAE,CAAA;QAC7C,MAAM,IAAI,GAAG,IAAA,iBAAO,EAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAA,gBAAM,EAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAI,CAAC,IAAI,CAAA;QACzE,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,eAAO,CAAC,iCAAiC,EAAE,EAAE,EAAE,yBAAyB,CAAC,CAAA;QAC9F,GAAG,CAAC,IAAI,GAAG,IAAI,CAAA;QACf,KAAK,MAAM,KAAK,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,aAAa,CAAC;YAAE,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC;gBAAE,GAAG,CAAC,KAAK,CAAC,GAAG,IAAA,gBAAM,EAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;QAC1H,KAAK,MAAM,KAAK,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC;YAAE,IAAI,IAAA,iBAAO,EAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC;gBAAE,GAAG,CAAC,KAAK,CAAC,GAAG,IAAA,eAAK,EAAC,KAAK,CAAC,KAAK,CAAC,EAAE,wBAAwB,CAAC,CAAA;QACpI,OAAO,GAAG,CAAA;IACZ,CAAC,EACD,0BAA0B,EAC1B,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,CAClC,CAAA;AACH,CAAC;AAED,uHAAuH;AACvH,gCAAgC;AAEhC,MAAM,MAAM,GAAG,CAAI,EAAiB,EAAE,MAAW,EAAE,MAAc,EAAE,UAAkB,EAAE,KAAqC,EAAE,EAAE,CAC9H,EAAE,CAAC,aAAa,CAAI,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAS,EAAE,KAAK,EAAE,KAAY,EAAE,CAAC,CAAA;AAE/H,MAAM,UAAU,GAAG,CAAC,EAAiB,EAAE,MAAc,EAAE,UAAkB,EAAE,EAAE,CAAC,MAAM,CAAoB,EAAE,EAAE,sCAAiB,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAA;AAAnK,QAAA,UAAU,cAAyJ;AACzK,MAAM,YAAY,GAAG,CAAC,EAAiB,EAAE,MAAc,EAAE,UAAkB,EAAE,EAAE,CAAC,MAAM,CAAsB,EAAE,EAAE,2CAAmB,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAA;AAA1K,QAAA,YAAY,gBAA8J;AAEvL,sFAAsF;AAC/E,KAAK,UAAU,SAAS,CAAC,EAAiB,EAAE,MAAc,EAAE,UAAkB;IACnF,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAmB,EAAE,EAAE,oCAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,IAAA,kBAAU,EAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;IAC7K,MAAM,MAAM,GAAG,MAAM,IAAA,uCAAc,EAAC,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IAC/F,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,GAAG,CAAC,QAAQ,GAAG,IAAA,6BAAU,EAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,SAAS,CAAA;QACrD,GAAG,CAAC,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;IACzC,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAEM,KAAK,UAAU,UAAU,CAAC,EAAiB,EAAE,MAAc,EAAE,UAAkB;IACpF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAoB,EAAE,EAAE,sCAAiB,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAA;IACnH,MAAM,MAAM,GAAG,MAAM,IAAA,uCAAc,EAAC,EAAE,EAAE,MAAM,EAAE,mBAAmB,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IACjG,KAAK,MAAM,GAAG,IAAI,QAAQ;QAAE,GAAG,CAAC,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;IACnE,OAAO,QAAQ,CAAA;AACjB,CAAC;AAEM,KAAK,UAAU,SAAS,CAAC,EAAiB,EAAE,MAAc,EAAE,UAAkB;IACnF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAmB,EAAE,EAAE,oCAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;IAC3G,MAAM,MAAM,GAAG,MAAM,IAAA,uCAAc,EAAC,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IAC/F,KAAK,MAAM,GAAG,IAAI,OAAO;QAAE,GAAG,CAAC,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;IAClE,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,uHAAuH;AACvH,wFAAwF;AAExF,4FAA4F;AACrF,KAAK,UAAU,qBAAqB,CAAC,EAAiB,EAAE,MAAc,EAAE,KAAa;IAC1F,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,mBAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAS,EAAE,CAAC,CAAA;IACtH,IAAI,CAAC,SAAS,CAAC,MAAM;QAAE,OAAO,EAAE,CAAA;IAChC,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACxC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC5C,EAAE,CAAC,aAAa,CAAC,sCAAiB,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,GAAG,CAAC,EAAE,EAAS,EAAE,SAAS,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7I,EAAE,CAAC,aAAa,CAAC,oCAAgB,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,GAAG,CAAC,EAAE,EAAS,EAAE,SAAS,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;KAC7I,CAAC,CAAA;IACF,OAAO,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;QAClC,MAAM,IAAI,GAAG,IAAA,6BAAU,EACrB,QAAQ,EACR,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,EAAE,CAAC,EACrD,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,EAAE,CAAC,EACtD,KAAK,CACN,CAAA;QACD,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,YAAY,EAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACxF,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,iEAAiE;AAC1D,KAAK,UAAU,qBAAqB,CAAC,EAAiB,EAAE,MAAc,EAAE,KAAa;IAC1F,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,sCAAiB,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAA,gBAAM,GAAE,EAAE,KAAK,EAAE,IAAA,aAAG,EAAC,IAAA,gBAAM,GAAE,CAAC,EAAS,EAAE,SAAS,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;IAC7K,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,IAAA,6BAAU,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAM,CAAC,aAAa,CAAC,CAAC,CAAC,KAAM,CAAC,CAAC,CAAA;AACpG,CAAC;AAED,4DAA4D;AACrD,KAAK,UAAU,oBAAoB,CAAC,EAAiB,EAAE,MAAc,EAAE,KAAa;IACzF,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,oCAAgB,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAA,gBAAM,GAAE,EAAE,KAAK,EAAE,IAAA,aAAG,EAAC,IAAA,gBAAM,GAAE,CAAC,EAAS,EAAE,SAAS,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;IAC7K,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,IAAA,4BAAS,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAM,CAAC,aAAa,CAAC,CAAC,CAAC,KAAM,CAAC,CAAC,CAAA;AACnG,CAAC","sourcesContent":["import { Counterparty } from '@things-factory/biz-base'\nimport { Domain, Refusal } from '@things-factory/shell'\nimport { EntityManager, In, IsNull, Not } from 'typeorm'\n\nimport { documentCounts } from '../asset-document/asset-document-service'\nimport { amountOf, applyPatches, dayOf, Patch, textOf, touched } from '../patches'\nimport { Investee } from './investee'\nimport { InvestmentBoardSeat } from './investment-board-seat'\nimport { InvestmentFiling } from './investment-filing'\nimport { InvestmentHolding } from './investment-holding'\nimport { InvestmentReport } from './investment-report'\nimport { InvestmentRequest } from './investment-request'\nimport { Contribution, filingDue, HoldingReason, holdingAt, InvestmentReportKind, InvestmentRequestKind, lateReport, ourShareOf, ratioOf, requestDue } from './investment-rules'\n\nconst currencyOf = (value: any, fallback: string | null) => {\n const text = String(value ?? '').trim().toUpperCase()\n if (!text) return fallback\n if (!/^[A-Z]{3}$/.test(text)) throw new Refusal('investment-currency-invalid', { value: text }, `'${text}' is not a currency code (KRW, USD, MYR)`)\n return text\n}\n\n/* ----------------------------------------------------------------------------------------------------------------- */\n/* Investees (decision 6) */\n\nexport function saveInvestees(tx: EntityManager, domain: Domain, user: any, patches: Patch[]) {\n return applyPatches(\n tx,\n Investee,\n domain,\n user,\n patches,\n async (patch, old) => {\n const row: Record<string, any> = {}\n const name = touched(patch, 'name', old) ? textOf(patch.name) : old!.name\n if (!name) throw new Refusal('investee-name-required', {}, 'Name the company')\n row.name = name\n for (const field of ['registrationNo', 'representative', 'description']) if (touched(patch, field, old)) row[field] = textOf(patch[field])\n if (touched(patch, 'country', old)) row.country = textOf(patch.country)?.toUpperCase() ?? null\n if (touched(patch, 'currency', old)) row.currency = currencyOf(patch.currency, 'KRW')\n if (touched(patch, 'fiscalYearEndMonth', old)) {\n const month = patch.fiscalYearEndMonth === null || patch.fiscalYearEndMonth === undefined || patch.fiscalYearEndMonth === '' ? null : Number(patch.fiscalYearEndMonth)\n if (month !== null && !(Number.isInteger(month) && month >= 1 && month <= 12)) throw new Refusal('investee-year-end-invalid', {}, 'The year ends in a month, 1 to 12')\n row.fiscalYearEndMonth = month\n }\n if (touched(patch, 'reportDueMonths', old)) {\n const months = patch.reportDueMonths === null || patch.reportDueMonths === undefined || patch.reportDueMonths === '' ? null : Number(patch.reportDueMonths)\n if (months !== null && !(Number.isInteger(months) && months >= 0 && months <= 24)) throw new Refusal('investee-report-months-invalid', {}, 'The report is due 0 to 24 months after the year ends')\n row.reportDueMonths = months\n }\n if (touched(patch, 'active', old)) row.active = patch.active !== false\n if (touched(patch, 'counterpartyId', old)) {\n if (!patch.counterpartyId) row.counterparty = null\n else {\n const counterparty = await tx.getRepository(Counterparty).findOne({ where: { domain: { id: domain.id }, id: patch.counterpartyId } as any })\n if (!counterparty) throw new Refusal('investee-counterparty-not-found', {}, 'The counterparty was not found')\n row.counterparty = counterparty\n }\n }\n return row\n },\n 'investee-not-found'\n )\n}\n\n/** The investees with where the holding stands now. */\nexport async function withHoldings(tx: EntityManager, domain: Domain, investees: Investee[], today: string) {\n if (!investees.length) return investees\n const holdings = await tx.getRepository(InvestmentHolding).find({ where: { domain: { id: domain.id }, investee: { id: In(investees.map(one => one.id)) } } as any, relations: ['investee'] })\n for (const investee of investees) {\n const now = holdingAt(holdings.filter(one => one.investeeId === investee.id), today)\n investee.ourShares = now?.ourShares\n investee.totalShares = now?.totalShares\n investee.ratio = now ? ratioOf(now) : undefined\n }\n return investees\n}\n\nasync function investeeOf(tx: EntityManager, domain: Domain, investeeId: string) {\n const investee = await tx.getRepository(Investee).findOne({ where: { domain: { id: domain.id }, id: investeeId } as any })\n if (!investee) throw new Refusal('investee-not-found', {}, 'The company was not found - reload and try again')\n return investee\n}\n\n/* ----------------------------------------------------------------------------------------------------------------- */\n/* An investee's rows (decisions 7-10) - each grid saves under its investee */\n\nexport async function saveHoldings(tx: EntityManager, domain: Domain, user: any, investeeId: string, patches: Patch[]) {\n const investee = await investeeOf(tx, domain, investeeId)\n return applyPatches(\n tx,\n InvestmentHolding,\n domain,\n user,\n patches,\n (patch, old) => {\n const row: Record<string, any> = { investee }\n row.recordDate = touched(patch, 'recordDate', old) ? dayOf(patch.recordDate, 'investment-holding-day-invalid', true) : old!.recordDate\n if (touched(patch, 'ourShares', old)) row.ourShares = amountOf(patch.ourShares, 'investment-shares-invalid') ?? 0\n if (touched(patch, 'totalShares', old)) row.totalShares = amountOf(patch.totalShares, 'investment-shares-invalid') ?? 0\n const ours = Number(row.ourShares ?? old!.ourShares)\n const total = Number(row.totalShares ?? old!.totalShares)\n if (!(total > 0)) throw new Refusal('investment-total-shares-required', {}, \"Give all the company's shares\")\n if (ours > total) throw new Refusal('investment-shares-above-total', {}, \"Our shares are more than all the company's shares\")\n if (touched(patch, 'reason', old)) row.reason = patch.reason ?? HoldingReason.OTHER\n if (touched(patch, 'contribution', old)) row.contribution = patch.contribution ?? Contribution.NONE\n if (touched(patch, 'amount', old)) row.amount = amountOf(patch.amount, 'investment-amount-invalid')\n if (touched(patch, 'currency', old)) row.currency = currencyOf(patch.currency, null)\n if (touched(patch, 'description', old)) row.description = textOf(patch.description)\n return row\n },\n 'investment-row-not-found',\n { investee: { id: investee.id } }\n )\n}\n\nexport async function saveBoardSeats(tx: EntityManager, domain: Domain, user: any, investeeId: string, patches: Patch[]) {\n const investee = await investeeOf(tx, domain, investeeId)\n return applyPatches(\n tx,\n InvestmentBoardSeat,\n domain,\n user,\n patches,\n (patch, old) => {\n const row: Record<string, any> = { investee }\n const director = touched(patch, 'director', old) ? textOf(patch.director) : old!.director\n if (!director) throw new Refusal('investment-director-required', {}, 'Name who sits on the board')\n row.director = director\n if (touched(patch, 'appointedOn', old)) row.appointedOn = dayOf(patch.appointedOn, 'investment-day-invalid')\n if (touched(patch, 'resignedOn', old)) row.resignedOn = dayOf(patch.resignedOn, 'investment-day-invalid')\n const from = row.appointedOn !== undefined ? row.appointedOn : old?.appointedOn\n const to = row.resignedOn !== undefined ? row.resignedOn : old?.resignedOn\n if (from && to && to < from) throw new Refusal('investment-resigned-before-appointed', {}, 'They left before they were appointed')\n if (touched(patch, 'description', old)) row.description = textOf(patch.description)\n return row\n },\n 'investment-row-not-found',\n { investee: { id: investee.id } }\n )\n}\n\nconst REPORT_FIGURES = ['revenue', 'shareCapital', 'totalEquity', 'totalAssets'] as const\n\nexport async function saveReports(tx: EntityManager, domain: Domain, user: any, investeeId: string, patches: Patch[]) {\n const investee = await investeeOf(tx, domain, investeeId)\n return applyPatches(\n tx,\n InvestmentReport,\n domain,\n user,\n patches,\n (patch, old) => {\n const row: Record<string, any> = { investee }\n row.periodTo = touched(patch, 'periodTo', old) ? dayOf(patch.periodTo, 'investment-report-period-invalid', true) : old!.periodTo\n if (touched(patch, 'periodFrom', old)) row.periodFrom = dayOf(patch.periodFrom, 'investment-report-period-invalid')\n const from = row.periodFrom !== undefined ? row.periodFrom : old?.periodFrom\n if (from && from > row.periodTo) throw new Refusal('investment-report-period-reversed', {}, 'The period ends before it starts')\n if (touched(patch, 'kind', old)) row.kind = patch.kind ?? InvestmentReportKind.AUDITED\n for (const field of ['standard', 'auditor', 'description']) if (touched(patch, field, old)) row[field] = textOf(patch[field])\n for (const field of ['signedOn', 'receivedOn']) if (touched(patch, field, old)) row[field] = dayOf(patch[field], 'investment-day-invalid')\n /* Equity and net income may be below zero; the rest are amounts. */\n for (const field of REPORT_FIGURES) if (touched(patch, field, old)) row[field] = field === 'totalEquity' ? numberOf(patch[field]) : amountOf(patch[field], 'investment-amount-invalid')\n if (touched(patch, 'netIncome', old)) row.netIncome = numberOf(patch.netIncome)\n return row\n },\n 'investment-row-not-found',\n { investee: { id: investee.id } }\n )\n}\n\nfunction numberOf(value: any): number | null {\n if (value === null || value === undefined || value === '') return null\n const number = Number(value)\n if (!Number.isFinite(number)) throw new Refusal('investment-amount-invalid', { value }, 'Write a number')\n return number\n}\n\nexport async function saveRequests(tx: EntityManager, domain: Domain, user: any, investeeId: string, patches: Patch[]) {\n const investee = await investeeOf(tx, domain, investeeId)\n return applyPatches(\n tx,\n InvestmentRequest,\n domain,\n user,\n patches,\n (patch, old) => {\n const row: Record<string, any> = { investee }\n const title = touched(patch, 'title', old) ? textOf(patch.title) : old!.title\n if (!title) throw new Refusal('investment-request-title-required', {}, 'Say what the request is about')\n row.title = title\n if (touched(patch, 'kind', old)) row.kind = patch.kind ?? InvestmentRequestKind.WRITTEN_RESOLUTION\n for (const field of ['receivedOn', 'dueOn', 'sentOn']) if (touched(patch, field, old)) row[field] = dayOf(patch[field], 'investment-day-invalid')\n for (const field of ['signer', 'items', 'description']) if (touched(patch, field, old)) row[field] = textOf(patch[field])\n return row\n },\n 'investment-row-not-found',\n { investee: { id: investee.id } }\n )\n}\n\nexport async function saveFilings(tx: EntityManager, domain: Domain, user: any, investeeId: string, patches: Patch[]) {\n const investee = await investeeOf(tx, domain, investeeId)\n return applyPatches(\n tx,\n InvestmentFiling,\n domain,\n user,\n patches,\n (patch, old) => {\n const row: Record<string, any> = { investee }\n const name = touched(patch, 'name', old) ? textOf(patch.name) : old!.name\n if (!name) throw new Refusal('investment-filing-name-required', {}, 'Say what is to be filed')\n row.name = name\n for (const field of ['law', 'authority', 'description']) if (touched(patch, field, old)) row[field] = textOf(patch[field])\n for (const field of ['dueOn', 'filedOn']) if (touched(patch, field, old)) row[field] = dayOf(patch[field], 'investment-day-invalid')\n return row\n },\n 'investment-row-not-found',\n { investee: { id: investee.id } }\n )\n}\n\n/* ----------------------------------------------------------------------------------------------------------------- */\n/* Reading an investee's rows */\n\nconst rowsOf = <T>(tx: EntityManager, target: any, domain: Domain, investeeId: string, order: Record<string, 'ASC' | 'DESC'>) =>\n tx.getRepository<T>(target).find({ where: { domain: { id: domain.id }, investee: { id: investeeId } } as any, order: order as any })\n\nexport const holdingsOf = (tx: EntityManager, domain: Domain, investeeId: string) => rowsOf<InvestmentHolding>(tx, InvestmentHolding, domain, investeeId, { recordDate: 'ASC' })\nexport const boardSeatsOf = (tx: EntityManager, domain: Domain, investeeId: string) => rowsOf<InvestmentBoardSeat>(tx, InvestmentBoardSeat, domain, investeeId, { appointedOn: 'ASC' })\n\n/** The reports with our part of each one's equity and how many documents each has. */\nexport async function reportsOf(tx: EntityManager, domain: Domain, investeeId: string) {\n const [reports, holdings] = await Promise.all([rowsOf<InvestmentReport>(tx, InvestmentReport, domain, investeeId, { periodTo: 'DESC' }), holdingsOf(tx, domain, investeeId)])\n const counts = await documentCounts(tx, domain, 'InvestmentReport', reports.map(one => one.id))\n for (const one of reports) {\n one.ourShare = ourShareOf(one, holdings) ?? undefined\n one.documents = counts.get(one.id) ?? 0\n }\n return reports\n}\n\nexport async function requestsOf(tx: EntityManager, domain: Domain, investeeId: string) {\n const requests = await rowsOf<InvestmentRequest>(tx, InvestmentRequest, domain, investeeId, { receivedOn: 'DESC' })\n const counts = await documentCounts(tx, domain, 'InvestmentRequest', requests.map(one => one.id))\n for (const one of requests) one.documents = counts.get(one.id) ?? 0\n return requests\n}\n\nexport async function filingsOf(tx: EntityManager, domain: Domain, investeeId: string) {\n const filings = await rowsOf<InvestmentFiling>(tx, InvestmentFiling, domain, investeeId, { dueOn: 'DESC' })\n const counts = await documentCounts(tx, domain, 'InvestmentFiling', filings.map(one => one.id))\n for (const one of filings) one.documents = counts.get(one.id) ?? 0\n return filings\n}\n\n/* ----------------------------------------------------------------------------------------------------------------- */\n/* What is late or due (decision 11) - an application's management check counts these */\n\n/** Each investee's report that is late, computed from its year end and reporting months. */\nexport async function lateInvestmentReports(tx: EntityManager, domain: Domain, today: string) {\n const investees = await tx.getRepository(Investee).find({ where: { domain: { id: domain.id }, active: true } as any })\n if (!investees.length) return []\n const ids = investees.map(one => one.id)\n const [holdings, reports] = await Promise.all([\n tx.getRepository(InvestmentHolding).find({ where: { domain: { id: domain.id }, investee: { id: In(ids) } } as any, relations: ['investee'] }),\n tx.getRepository(InvestmentReport).find({ where: { domain: { id: domain.id }, investee: { id: In(ids) } } as any, relations: ['investee'] })\n ])\n return investees.flatMap(investee => {\n const late = lateReport(\n investee,\n reports.filter(one => one.investeeId === investee.id),\n holdings.filter(one => one.investeeId === investee.id),\n today\n )\n return late ? [{ investeeId: investee.id, investeeName: investee.name, ...late }] : []\n })\n}\n\n/** Requests not answered whose due day has passed or is near. */\nexport async function dueInvestmentRequests(tx: EntityManager, domain: Domain, today: string) {\n const open = await tx.getRepository(InvestmentRequest).find({ where: { domain: { id: domain.id }, sentOn: IsNull(), dueOn: Not(IsNull()) } as any, relations: ['investee'] })\n return open.filter(one => requestDue(one, today)).sort((a, b) => a.dueOn!.localeCompare(b.dueOn!))\n}\n\n/** Filings not made whose due day has passed or is near. */\nexport async function dueInvestmentFilings(tx: EntityManager, domain: Domain, today: string) {\n const open = await tx.getRepository(InvestmentFiling).find({ where: { domain: { id: domain.id }, filedOn: IsNull(), dueOn: Not(IsNull()) } as any, relations: ['investee'] })\n return open.filter(one => filingDue(one, today)).sort((a, b) => a.dueOn!.localeCompare(b.dueOn!))\n}\n"]}
|