@things-factory/saas 10.1.240 → 10.1.242
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/README.md +11 -11
- package/dist-client/entity360/saas-account-360.d.ts +0 -1
- package/dist-client/entity360/saas-account-360.js +18 -71
- package/dist-client/entity360/saas-account-360.js.map +1 -1
- package/dist-client/index.d.ts +0 -3
- package/dist-client/index.js +0 -3
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/saas-account-list.d.ts +1 -1
- package/dist-client/pages/saas-account-list.js +5 -14
- package/dist-client/pages/saas-account-list.js.map +1 -1
- package/dist-client/pages/saas-account-page.d.ts +3 -5
- package/dist-client/pages/saas-account-page.js +30 -55
- package/dist-client/pages/saas-account-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +0 -3
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/words.d.ts +0 -13
- package/dist-client/words.js +0 -17
- package/dist-client/words.js.map +1 -1
- package/dist-server/service/checks.d.ts +1 -9
- package/dist-server/service/checks.js +2 -18
- package/dist-server/service/checks.js.map +1 -1
- package/dist-server/service/index.d.ts +1 -6
- package/dist-server/service/index.js +1 -9
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/saas-account.d.ts +0 -4
- package/dist-server/service/saas-account.js +1 -16
- package/dist-server/service/saas-account.js.map +1 -1
- package/dist-server/service/saas-mutation.d.ts +1 -10
- package/dist-server/service/saas-mutation.js +1 -86
- package/dist-server/service/saas-mutation.js.map +1 -1
- package/dist-server/service/saas-query.d.ts +2 -51
- package/dist-server/service/saas-query.js +2 -65
- package/dist-server/service/saas-query.js.map +1 -1
- package/dist-server/service/saas-rules.d.ts +3 -141
- package/dist-server/service/saas-rules.js +10 -213
- package/dist-server/service/saas-rules.js.map +1 -1
- package/dist-server/service/saas-service.d.ts +2 -125
- package/dist-server/service/saas-service.js +50 -438
- package/dist-server/service/saas-service.js.map +1 -1
- package/dist-server/service/saas-subscription.d.ts +4 -5
- package/dist-server/service/saas-subscription.js +11 -21
- package/dist-server/service/saas-subscription.js.map +1 -1
- package/dist-server/service/saas-sync.d.ts +0 -35
- package/dist-server/service/saas-sync.js +0 -60
- package/dist-server/service/saas-sync.js.map +1 -1
- package/dist-server/service/saas-type.d.ts +0 -93
- package/dist-server/service/saas-type.js +3 -351
- package/dist-server/service/saas-type.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/translations/en.json +3 -81
- package/translations/es.json +3 -81
- package/translations/ja.json +3 -81
- package/translations/ko.json +3 -81
- package/translations/ms.json +3 -81
- package/translations/zh.json +3 -81
- package/dist-client/components/saas-bill-editor.d.ts +0 -59
- package/dist-client/components/saas-bill-editor.js +0 -211
- package/dist-client/components/saas-bill-editor.js.map +0 -1
- package/dist-client/components/saas-candidates.d.ts +0 -33
- package/dist-client/components/saas-candidates.js +0 -157
- package/dist-client/components/saas-candidates.js.map +0 -1
- package/dist-client/pages/saas-cost-trend.d.ts +0 -23
- package/dist-client/pages/saas-cost-trend.js +0 -219
- package/dist-client/pages/saas-cost-trend.js.map +0 -1
- package/dist-server/service/saas-bill.d.ts +0 -42
- package/dist-server/service/saas-bill.js +0 -165
- package/dist-server/service/saas-bill.js.map +0 -1
- package/dist-server/service/saas-hidden-vendor.d.ts +0 -19
- package/dist-server/service/saas-hidden-vendor.js +0 -53
- package/dist-server/service/saas-hidden-vendor.js.map +0 -1
- package/dist-server/service/saas-trend-type.d.ts +0 -9
- package/dist-server/service/saas-trend-type.js +0 -37
- package/dist-server/service/saas-trend-type.js.map +0 -1
package/dist-client/words.d.ts
CHANGED
|
@@ -15,13 +15,8 @@ export declare const filterOptionsOf: (set: string, codes: string[]) => {
|
|
|
15
15
|
}[];
|
|
16
16
|
/** An amount as a bill writes it - grouped, cents only when it has them. */
|
|
17
17
|
export declare const amountText: (value?: number | null) => string;
|
|
18
|
-
/** A change as a signed percent - `+12.5%`; blank when nothing to compare. */
|
|
19
|
-
export declare const changeText: (ratio?: number | null) => string;
|
|
20
18
|
/** A grid column's header - `field.<set>-<name in kebab>`. */
|
|
21
19
|
export declare const fieldWord: (set: string, name: string) => string;
|
|
22
|
-
/** This month, `YYYY-MM`, and a number of months from one. */
|
|
23
|
-
export declare const thisMonth: () => string;
|
|
24
|
-
export declare function shiftMonth(key: string, by: number): string;
|
|
25
20
|
/** The resource picker of a grid column - which one and nothing about it, searched by its name. */
|
|
26
21
|
export declare const picker: (queryName: string, columns: {
|
|
27
22
|
name: string;
|
|
@@ -37,11 +32,3 @@ export declare const picker: (queryName: string, columns: {
|
|
|
37
32
|
hidden: boolean;
|
|
38
33
|
})[];
|
|
39
34
|
};
|
|
40
|
-
/** A row's months in words - what a mini chart draws, for its tooltip: `2026-08 900 · 2026-09 — · … → 2026-10 1,050`. */
|
|
41
|
-
export declare function trendTitle(trend?: {
|
|
42
|
-
months: string[];
|
|
43
|
-
values: (number | null)[];
|
|
44
|
-
nextMonths: string[];
|
|
45
|
-
expected: (number | null)[];
|
|
46
|
-
currency: string;
|
|
47
|
-
} | null): string;
|
package/dist-client/words.js
CHANGED
|
@@ -12,28 +12,11 @@ export const optionsOf = (set, codes) => codes.map(value => ({ value, display: w
|
|
|
12
12
|
export const filterOptionsOf = (set, codes) => [{ value: '', display: '' }, ...optionsOf(set, codes)];
|
|
13
13
|
/** An amount as a bill writes it - grouped, cents only when it has them. */
|
|
14
14
|
export const amountText = (value) => value === null || value === undefined || value === '' ? '' : Number(value).toLocaleString(undefined, { maximumFractionDigits: 2 });
|
|
15
|
-
/** A change as a signed percent - `+12.5%`; blank when nothing to compare. */
|
|
16
|
-
export const changeText = (ratio) => (ratio === null || ratio === undefined ? '' : `${ratio > 0 ? '+' : ''}${(ratio * 100).toFixed(1)}%`);
|
|
17
15
|
/** A grid column's header - `field.<set>-<name in kebab>`. */
|
|
18
16
|
export const fieldWord = (set, name) => i18next.t(`field.${set}-${name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}`);
|
|
19
|
-
/** This month, `YYYY-MM`, and a number of months from one. */
|
|
20
|
-
export const thisMonth = () => new Date().toISOString().slice(0, 7);
|
|
21
|
-
export function shiftMonth(key, by) {
|
|
22
|
-
const [year, month] = key.split('-').map(Number);
|
|
23
|
-
const index = year * 12 + (month - 1) + by;
|
|
24
|
-
return `${Math.floor(index / 12)}-${String((index % 12) + 1).padStart(2, '0')}`;
|
|
25
|
-
}
|
|
26
17
|
/** The resource picker of a grid column - which one and nothing about it, searched by its name. */
|
|
27
18
|
export const picker = (queryName, columns) => ({
|
|
28
19
|
queryName,
|
|
29
20
|
select: [{ name: 'id', hidden: true }, ...columns.map(one => ({ ...one, width: one.name === 'name' ? 200 : 130 }))]
|
|
30
21
|
});
|
|
31
|
-
/** A row's months in words - what a mini chart draws, for its tooltip: `2026-08 900 · 2026-09 — · … → 2026-10 1,050`. */
|
|
32
|
-
export function trendTitle(trend) {
|
|
33
|
-
if (!trend)
|
|
34
|
-
return '';
|
|
35
|
-
const past = trend.months.map((month, i) => `${month} ${trend.values[i] === null ? '—' : amountText(trend.values[i])}`).join(' · ');
|
|
36
|
-
const next = trend.nextMonths.map((month, i) => `${month} ${trend.expected[i] === null ? '—' : amountText(trend.expected[i])}`).join(' · ');
|
|
37
|
-
return `${past} → ${i18next.t('text.saas-trend-expected')} ${next} (${trend.currency})`;
|
|
38
|
-
}
|
|
39
22
|
//# sourceMappingURL=words.js.map
|
package/dist-client/words.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"words.js","sourceRoot":"","sources":["../client/words.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,mEAAmE;AACnE,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,CAAA;AACzE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,QAAQ,EAAE,YAAY,EAAE,WAAW,CAAC,CAAA;AAC1E,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;AAEpD,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;AACrE,+DAA+D;AAC/D,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,GAAW,EAAE,IAAoB,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;AAClH,yCAAyC;AACzC,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,KAAe,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;AACvH,+DAA+D;AAC/D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,GAAW,EAAE,KAAe,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;AAEvH,4EAA4E;AAC5E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAqB,EAAE,EAAE,CAClD,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAM,EAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE,EAAE,qBAAqB,EAAE,CAAC,EAAE,CAAC,CAAA;AAE7I,
|
|
1
|
+
{"version":3,"file":"words.js","sourceRoot":"","sources":["../client/words.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,mEAAmE;AACnE,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,CAAA;AACzE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,QAAQ,EAAE,YAAY,EAAE,WAAW,CAAC,CAAA;AAC1E,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;AAEpD,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;AACrE,+DAA+D;AAC/D,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,GAAW,EAAE,IAAoB,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;AAClH,yCAAyC;AACzC,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,KAAe,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;AACvH,+DAA+D;AAC/D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,GAAW,EAAE,KAAe,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;AAEvH,4EAA4E;AAC5E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAqB,EAAE,EAAE,CAClD,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAM,EAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE,EAAE,qBAAqB,EAAE,CAAC,EAAE,CAAC,CAAA;AAE7I,8DAA8D;AAC9D,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;AAE9I,mGAAmG;AACnG,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,SAAiB,EAAE,OAA4C,EAAE,EAAE,CAAC,CAAC;IAC1F,SAAS;IACT,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;CACpH,CAAC,CAAA","sourcesContent":["import { i18next } from '@operato/i18n'\n\n/* What the subscriptions' codes read as, in the reader's words. */\nexport const BILLINGS = ['SEAT', 'FIXED', 'USAGE', 'COMMITMENT', 'OWNED']\nexport const SUBSCRIPTION_STATUSES = ['ACTIVE', 'CANCELLING', 'CANCELLED']\nexport const ACCOUNT_STATUSES = ['ACTIVE', 'CLOSED']\n\nconst kebab = (code: string) => code.toLowerCase().replace(/_/g, '-')\n/** The word for a code of a set - `text.saas-billing-seat`. */\nexport const wordOf = (set: string, code?: string | null) => (code ? i18next.t(`text.${set}-${kebab(code)}`) : '')\n/** A grid's select options for a set. */\nexport const optionsOf = (set: string, codes: string[]) => codes.map(value => ({ value, display: wordOf(set, value) }))\n/** A grid's filter options for a set, with the blank first. */\nexport const filterOptionsOf = (set: string, codes: string[]) => [{ value: '', display: '' }, ...optionsOf(set, codes)]\n\n/** An amount as a bill writes it - grouped, cents only when it has them. */\nexport const amountText = (value?: number | null) =>\n value === null || value === undefined || value === ('' as any) ? '' : Number(value).toLocaleString(undefined, { maximumFractionDigits: 2 })\n\n/** A grid column's header - `field.<set>-<name in kebab>`. */\nexport const fieldWord = (set: string, name: string) => i18next.t(`field.${set}-${name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}`)\n\n/** The resource picker of a grid column - which one and nothing about it, searched by its name. */\nexport const picker = (queryName: string, columns: { name: string; header?: string }[]) => ({\n queryName,\n select: [{ name: 'id', hidden: true }, ...columns.map(one => ({ ...one, width: one.name === 'name' ? 200 : 130 }))]\n})\n"]}
|
|
@@ -17,7 +17,7 @@ export declare function saasSeatsOfLeavers(tx: EntityManager, domain: Domain, to
|
|
|
17
17
|
/** Accounts in use with nobody left to administer them - no admin named, or every admin retired. */
|
|
18
18
|
export declare function saasAccountsWithoutAdmin(tx: EntityManager, domain: Domain, today: string): Promise<SaasAccount[]>;
|
|
19
19
|
/**
|
|
20
|
-
* Seat plans in use whose seats held today are not as many as
|
|
20
|
+
* Seat plans in use whose seats held today are not as many as bought. A plan with no price is left out - a
|
|
21
21
|
* free or trial plan a tenant hands out (1,000,000 Power BI Free) costs nothing unused (ADR-0177 개정 2, the user 10-31).
|
|
22
22
|
*/
|
|
23
23
|
export declare function saasSeatCountsDiffer(tx: EntityManager, domain: Domain, today: string): Promise<{
|
|
@@ -25,11 +25,3 @@ export declare function saasSeatCountsDiffer(tx: EntityManager, domain: Domain,
|
|
|
25
25
|
account: SaasAccount;
|
|
26
26
|
held: number;
|
|
27
27
|
}[]>;
|
|
28
|
-
/**
|
|
29
|
-
* Accounts in use that were to be billed last month and have no bill for it (decision 9) - the trend holds only while the
|
|
30
|
-
* bills are written every month.
|
|
31
|
-
*/
|
|
32
|
-
export declare function saasBillsMissing(tx: EntityManager, domain: Domain, today: string): Promise<{
|
|
33
|
-
account: SaasAccount;
|
|
34
|
-
month: string;
|
|
35
|
-
}[]>;
|
|
@@ -4,16 +4,14 @@ exports.saasRenewalsDue = saasRenewalsDue;
|
|
|
4
4
|
exports.saasSeatsOfLeavers = saasSeatsOfLeavers;
|
|
5
5
|
exports.saasAccountsWithoutAdmin = saasAccountsWithoutAdmin;
|
|
6
6
|
exports.saasSeatCountsDiffer = saasSeatCountsDiffer;
|
|
7
|
-
exports.saasBillsMissing = saasBillsMissing;
|
|
8
7
|
const organization_1 = require("@things-factory/organization");
|
|
9
8
|
const typeorm_1 = require("typeorm");
|
|
10
9
|
const saas_account_1 = require("./saas-account");
|
|
11
|
-
const saas_bill_1 = require("./saas-bill");
|
|
12
10
|
const saas_rules_1 = require("./saas-rules");
|
|
13
11
|
const saas_seat_1 = require("./saas-seat");
|
|
14
12
|
const saas_subscription_1 = require("./saas-subscription");
|
|
15
13
|
/*
|
|
16
|
-
* The subscriptions' part of an application's management check (ADR-0177 decisions 6, 7
|
|
14
|
+
* The subscriptions' part of an application's management check (ADR-0177 decisions 6, 7): judged here, counted there.
|
|
17
15
|
* Each takes the day it judges on.
|
|
18
16
|
*/
|
|
19
17
|
/** The employees who have retired by a day. */
|
|
@@ -56,7 +54,7 @@ async function saasAccountsWithoutAdmin(tx, domain, today) {
|
|
|
56
54
|
return accounts.filter(account => !(account.admins ?? []).some(admin => !retired.has(admin.id)));
|
|
57
55
|
}
|
|
58
56
|
/**
|
|
59
|
-
* Seat plans in use whose seats held today are not as many as
|
|
57
|
+
* Seat plans in use whose seats held today are not as many as bought. A plan with no price is left out - a
|
|
60
58
|
* free or trial plan a tenant hands out (1,000,000 Power BI Free) costs nothing unused (ADR-0177 개정 2, the user 10-31).
|
|
61
59
|
*/
|
|
62
60
|
async function saasSeatCountsDiffer(tx, domain, today) {
|
|
@@ -69,18 +67,4 @@ async function saasSeatCountsDiffer(tx, domain, today) {
|
|
|
69
67
|
.map(sub => ({ subscription: sub, account: accounts.find(one => one.id === sub.accountId), held: seats.filter(seat => seat.subscriptionId === sub.id && (0, saas_rules_1.seatHeld)(seat, today)).length }))
|
|
70
68
|
.filter(one => one.held !== Number(one.subscription.quantity ?? 0));
|
|
71
69
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Accounts in use that were to be billed last month and have no bill for it (decision 9) - the trend holds only while the
|
|
74
|
-
* bills are written every month.
|
|
75
|
-
*/
|
|
76
|
-
async function saasBillsMissing(tx, domain, today) {
|
|
77
|
-
const { accounts, subs } = await inUse(tx, domain);
|
|
78
|
-
const month = (0, saas_rules_1.shiftMonth)(today.slice(0, 7), -1);
|
|
79
|
-
const [year, m] = month.split('-').map(Number);
|
|
80
|
-
const due = accounts.filter(account => !account.parentId && subs.some(sub => sub.accountId === account.id && sub.billing !== saas_rules_1.SaasBilling.OWNED && (0, saas_rules_1.billedIn)(sub, year, m, account.billingDay)));
|
|
81
|
-
if (!due.length)
|
|
82
|
-
return [];
|
|
83
|
-
const billed = new Set((await tx.getRepository(saas_bill_1.SaasBill).find({ where: { domain: { id: domain.id }, month, account: { id: (0, typeorm_1.In)(due.map(one => one.id)) } } })).map(one => one.accountId));
|
|
84
|
-
return due.filter(one => !billed.has(one.id)).map(account => ({ account, month }));
|
|
85
|
-
}
|
|
86
70
|
//# sourceMappingURL=checks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checks.js","sourceRoot":"","sources":["../../server/service/checks.ts"],"names":[],"mappings":"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","sourcesContent":["import { Employee } from '@things-factory/organization'\nimport { Domain } from '@things-factory/shell'\nimport { EntityManager, In } from 'typeorm'\n\nimport { SaasAccount } from './saas-account'\nimport { SaasBill } from './saas-bill'\nimport { billedIn, renewalDue, SaasAccountStatus, SaasBilling, SaasSubscriptionStatus, seatHeld, shiftMonth } from './saas-rules'\nimport { SaasSeat } from './saas-seat'\nimport { SaasSubscription } from './saas-subscription'\n\n/*\n * The subscriptions' part of an application's management check (ADR-0177 decisions 6, 7, 9): judged here, counted there.\n * Each takes the day it judges on.\n */\n\n/** The employees who have retired by a day. */\nasync function retiredOf(tx: EntityManager, domain: Domain, ids: string[], today: string): Promise<Set<string>> {\n if (!ids.length) return new Set()\n const employees = await tx.getRepository(Employee).find({ where: { domain: { id: domain.id }, id: In(ids) } as any })\n return new Set(employees.filter(one => one.retiredOn && one.retiredOn <= today).map(one => one.id))\n}\n\nasync function inUse(tx: EntityManager, domain: Domain) {\n const accounts = await tx.getRepository(SaasAccount).find({ where: { domain: { id: domain.id }, status: SaasAccountStatus.ACTIVE } as any, relations: ['admins'] })\n const subs = accounts.length\n ? await tx.getRepository(SaasSubscription).find({ where: { domain: { id: domain.id }, account: { id: In(accounts.map(one => one.id)) }, status: In([SaasSubscriptionStatus.ACTIVE, SaasSubscriptionStatus.CANCELLING]) } as any })\n : []\n return { accounts, subs }\n}\n\n/** Subscriptions in use whose renewal is near - from their notice days before it to the day (decision 7). */\nexport async function saasRenewalsDue(tx: EntityManager, domain: Domain, today: string) {\n const { accounts, subs } = await inUse(tx, domain)\n return subs.filter(sub => renewalDue(sub, today)).map(sub => ({ subscription: sub, account: accounts.find(one => one.id === sub.accountId)! }))\n}\n\n/** Seats still held by people who have retired. */\nexport async function saasSeatsOfLeavers(tx: EntityManager, domain: Domain, today: string) {\n const { accounts, subs } = await inUse(tx, domain)\n if (!subs.length) return []\n const seats = (await tx.getRepository(SaasSeat).find({ where: { domain: { id: domain.id }, subscription: { id: In(subs.map(one => one.id)) } } as any, relations: ['employee'] })).filter(\n seat => seat.employeeId && seatHeld(seat, today)\n )\n const retired = await retiredOf(tx, domain, [...new Set(seats.map(one => one.employeeId!))], today)\n return seats\n .filter(seat => retired.has(seat.employeeId!))\n .map(seat => {\n const subscription = subs.find(one => one.id === seat.subscriptionId)!\n return { seat, subscription, account: accounts.find(one => one.id === subscription.accountId)! }\n })\n}\n\n/** Accounts in use with nobody left to administer them - no admin named, or every admin retired. */\nexport async function saasAccountsWithoutAdmin(tx: EntityManager, domain: Domain, today: string) {\n const { accounts } = await inUse(tx, domain)\n const retired = await retiredOf(tx, domain, [...new Set(accounts.flatMap(one => (one.admins ?? []).map(admin => admin.id)))], today)\n return accounts.filter(account => !(account.admins ?? []).some(admin => !retired.has(admin.id)))\n}\n\n/**\n * Seat plans in use whose seats held today are not as many as they are billed for. A plan with no price is left out - a\n * free or trial plan a tenant hands out (1,000,000 Power BI Free) costs nothing unused (ADR-0177 개정 2, the user 10-31).\n */\nexport async function saasSeatCountsDiffer(tx: EntityManager, domain: Domain, today: string) {\n const { accounts, subs } = await inUse(tx, domain)\n const plans = subs.filter(one => one.billing === SaasBilling.SEAT && one.startOn <= today && one.unitPrice !== null && one.unitPrice !== undefined)\n if (!plans.length) return []\n const seats = await tx.getRepository(SaasSeat).find({ where: { domain: { id: domain.id }, subscription: { id: In(plans.map(one => one.id)) } } as any })\n return plans\n .map(sub => ({ subscription: sub, account: accounts.find(one => one.id === sub.accountId)!, held: seats.filter(seat => seat.subscriptionId === sub.id && seatHeld(seat, today)).length }))\n .filter(one => one.held !== Number(one.subscription.quantity ?? 0))\n}\n\n/**\n * Accounts in use that were to be billed last month and have no bill for it (decision 9) - the trend holds only while the\n * bills are written every month.\n */\nexport async function saasBillsMissing(tx: EntityManager, domain: Domain, today: string) {\n const { accounts, subs } = await inUse(tx, domain)\n const month = shiftMonth(today.slice(0, 7), -1)\n const [year, m] = month.split('-').map(Number)\n const due = accounts.filter(account => !account.parentId && subs.some(sub => sub.accountId === account.id && sub.billing !== SaasBilling.OWNED && billedIn(sub, year, m, account.billingDay)))\n if (!due.length) return []\n const billed = new Set(\n (await tx.getRepository(SaasBill).find({ where: { domain: { id: domain.id }, month, account: { id: In(due.map(one => one.id)) } } as any })).map(one => one.accountId)\n )\n return due.filter(one => !billed.has(one.id)).map(account => ({ account, month }))\n}\n"]}
|
|
1
|
+
{"version":3,"file":"checks.js","sourceRoot":"","sources":["../../server/service/checks.ts"],"names":[],"mappings":"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","sourcesContent":["import { Employee } from '@things-factory/organization'\nimport { Domain } from '@things-factory/shell'\nimport { EntityManager, In } from 'typeorm'\n\nimport { SaasAccount } from './saas-account'\nimport { renewalDue, SaasAccountStatus, SaasBilling, SaasSubscriptionStatus, seatHeld } from './saas-rules'\nimport { SaasSeat } from './saas-seat'\nimport { SaasSubscription } from './saas-subscription'\n\n/*\n * The subscriptions' part of an application's management check (ADR-0177 decisions 6, 7): judged here, counted there.\n * Each takes the day it judges on.\n */\n\n/** The employees who have retired by a day. */\nasync function retiredOf(tx: EntityManager, domain: Domain, ids: string[], today: string): Promise<Set<string>> {\n if (!ids.length) return new Set()\n const employees = await tx.getRepository(Employee).find({ where: { domain: { id: domain.id }, id: In(ids) } as any })\n return new Set(employees.filter(one => one.retiredOn && one.retiredOn <= today).map(one => one.id))\n}\n\nasync function inUse(tx: EntityManager, domain: Domain) {\n const accounts = await tx.getRepository(SaasAccount).find({ where: { domain: { id: domain.id }, status: SaasAccountStatus.ACTIVE } as any, relations: ['admins'] })\n const subs = accounts.length\n ? await tx.getRepository(SaasSubscription).find({ where: { domain: { id: domain.id }, account: { id: In(accounts.map(one => one.id)) }, status: In([SaasSubscriptionStatus.ACTIVE, SaasSubscriptionStatus.CANCELLING]) } as any })\n : []\n return { accounts, subs }\n}\n\n/** Subscriptions in use whose renewal is near - from their notice days before it to the day (decision 7). */\nexport async function saasRenewalsDue(tx: EntityManager, domain: Domain, today: string) {\n const { accounts, subs } = await inUse(tx, domain)\n return subs.filter(sub => renewalDue(sub, today)).map(sub => ({ subscription: sub, account: accounts.find(one => one.id === sub.accountId)! }))\n}\n\n/** Seats still held by people who have retired. */\nexport async function saasSeatsOfLeavers(tx: EntityManager, domain: Domain, today: string) {\n const { accounts, subs } = await inUse(tx, domain)\n if (!subs.length) return []\n const seats = (await tx.getRepository(SaasSeat).find({ where: { domain: { id: domain.id }, subscription: { id: In(subs.map(one => one.id)) } } as any, relations: ['employee'] })).filter(\n seat => seat.employeeId && seatHeld(seat, today)\n )\n const retired = await retiredOf(tx, domain, [...new Set(seats.map(one => one.employeeId!))], today)\n return seats\n .filter(seat => retired.has(seat.employeeId!))\n .map(seat => {\n const subscription = subs.find(one => one.id === seat.subscriptionId)!\n return { seat, subscription, account: accounts.find(one => one.id === subscription.accountId)! }\n })\n}\n\n/** Accounts in use with nobody left to administer them - no admin named, or every admin retired. */\nexport async function saasAccountsWithoutAdmin(tx: EntityManager, domain: Domain, today: string) {\n const { accounts } = await inUse(tx, domain)\n const retired = await retiredOf(tx, domain, [...new Set(accounts.flatMap(one => (one.admins ?? []).map(admin => admin.id)))], today)\n return accounts.filter(account => !(account.admins ?? []).some(admin => !retired.has(admin.id)))\n}\n\n/**\n * Seat plans in use whose seats held today are not as many as bought. A plan with no price is left out - a\n * free or trial plan a tenant hands out (1,000,000 Power BI Free) costs nothing unused (ADR-0177 개정 2, the user 10-31).\n */\nexport async function saasSeatCountsDiffer(tx: EntityManager, domain: Domain, today: string) {\n const { accounts, subs } = await inUse(tx, domain)\n const plans = subs.filter(one => one.billing === SaasBilling.SEAT && one.startOn <= today && one.unitPrice !== null && one.unitPrice !== undefined)\n if (!plans.length) return []\n const seats = await tx.getRepository(SaasSeat).find({ where: { domain: { id: domain.id }, subscription: { id: In(plans.map(one => one.id)) } } as any })\n return plans\n .map(sub => ({ subscription: sub, account: accounts.find(one => one.id === sub.accountId)!, held: seats.filter(seat => seat.subscriptionId === sub.id && seatHeld(seat, today)).length }))\n .filter(one => one.held !== Number(one.subscription.quantity ?? 0))\n}\n"]}
|
|
@@ -2,22 +2,17 @@ export * from './saas-rules';
|
|
|
2
2
|
export * from './saas-account';
|
|
3
3
|
export * from './saas-subscription';
|
|
4
4
|
export * from './saas-seat';
|
|
5
|
-
export * from './saas-bill';
|
|
6
|
-
export * from './saas-hidden-vendor';
|
|
7
5
|
export * from './saas-type';
|
|
8
|
-
export * from './saas-trend-type';
|
|
9
6
|
export * from './saas-access';
|
|
10
7
|
export * from './saas-service';
|
|
11
8
|
export * from './saas-sync';
|
|
12
9
|
export * from './checks';
|
|
13
10
|
import { SaasAccount } from './saas-account';
|
|
14
|
-
import { SaasBill, SaasBillLine } from './saas-bill';
|
|
15
|
-
import { SaasHiddenVendor } from './saas-hidden-vendor';
|
|
16
11
|
import { SaasMutation } from './saas-mutation';
|
|
17
12
|
import { SaasAccountQuery } from './saas-query';
|
|
18
13
|
import { SaasSeat } from './saas-seat';
|
|
19
14
|
import { SaasSubscription } from './saas-subscription';
|
|
20
|
-
export declare const entities: (typeof SaasAccount | typeof SaasSubscription | typeof SaasSeat
|
|
15
|
+
export declare const entities: (typeof SaasAccount | typeof SaasSubscription | typeof SaasSeat)[];
|
|
21
16
|
export declare const schema: {
|
|
22
17
|
resolverClasses: (typeof SaasMutation | typeof SaasAccountQuery)[];
|
|
23
18
|
};
|
|
@@ -7,18 +7,13 @@ tslib_1.__exportStar(require("./saas-rules"), exports);
|
|
|
7
7
|
tslib_1.__exportStar(require("./saas-account"), exports);
|
|
8
8
|
tslib_1.__exportStar(require("./saas-subscription"), exports);
|
|
9
9
|
tslib_1.__exportStar(require("./saas-seat"), exports);
|
|
10
|
-
tslib_1.__exportStar(require("./saas-bill"), exports);
|
|
11
|
-
tslib_1.__exportStar(require("./saas-hidden-vendor"), exports);
|
|
12
10
|
tslib_1.__exportStar(require("./saas-type"), exports);
|
|
13
|
-
tslib_1.__exportStar(require("./saas-trend-type"), exports);
|
|
14
11
|
tslib_1.__exportStar(require("./saas-access"), exports);
|
|
15
12
|
tslib_1.__exportStar(require("./saas-service"), exports);
|
|
16
13
|
tslib_1.__exportStar(require("./saas-sync"), exports);
|
|
17
14
|
tslib_1.__exportStar(require("./checks"), exports);
|
|
18
15
|
/* IMPORT ENTITIES AND RESOLVERS */
|
|
19
16
|
const saas_account_1 = require("./saas-account");
|
|
20
|
-
const saas_bill_1 = require("./saas-bill");
|
|
21
|
-
const saas_hidden_vendor_1 = require("./saas-hidden-vendor");
|
|
22
17
|
const saas_mutation_1 = require("./saas-mutation");
|
|
23
18
|
const saas_query_1 = require("./saas-query");
|
|
24
19
|
const saas_seat_1 = require("./saas-seat");
|
|
@@ -27,10 +22,7 @@ exports.entities = [
|
|
|
27
22
|
/* ENTITIES */
|
|
28
23
|
saas_account_1.SaasAccount,
|
|
29
24
|
saas_subscription_1.SaasSubscription,
|
|
30
|
-
saas_seat_1.SaasSeat
|
|
31
|
-
saas_bill_1.SaasBill,
|
|
32
|
-
saas_bill_1.SaasBillLine,
|
|
33
|
-
saas_hidden_vendor_1.SaasHiddenVendor
|
|
25
|
+
saas_seat_1.SaasSeat
|
|
34
26
|
];
|
|
35
27
|
exports.schema = {
|
|
36
28
|
resolverClasses: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,uDAA4B;AAC5B,yDAA8B;AAC9B,8DAAmC;AACnC,sDAA2B;AAC3B,sDAA2B;AAC3B
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,uDAA4B;AAC5B,yDAA8B;AAC9B,8DAAmC;AACnC,sDAA2B;AAC3B,sDAA2B;AAC3B,wDAA6B;AAC7B,yDAA8B;AAC9B,sDAA2B;AAC3B,mDAAwB;AAExB,mCAAmC;AACnC,iDAA4C;AAC5C,mDAA8C;AAC9C,6CAA+C;AAC/C,2CAAsC;AACtC,2DAAsD;AAEzC,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,0BAAW;IACX,oCAAgB;IAChB,oBAAQ;CACT,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,sBAAsB;QACtB,6BAAgB;QAChB,4BAAY;KACb;CACF,CAAA","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './saas-rules'\nexport * from './saas-account'\nexport * from './saas-subscription'\nexport * from './saas-seat'\nexport * from './saas-type'\nexport * from './saas-access'\nexport * from './saas-service'\nexport * from './saas-sync'\nexport * from './checks'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport { SaasAccount } from './saas-account'\nimport { SaasMutation } from './saas-mutation'\nimport { SaasAccountQuery } from './saas-query'\nimport { SaasSeat } from './saas-seat'\nimport { SaasSubscription } from './saas-subscription'\n\nexport const entities = [\n /* ENTITIES */\n SaasAccount,\n SaasSubscription,\n SaasSeat\n]\n\nexport const schema = {\n resolverClasses: [\n /* RESOLVER CLASSES */\n SaasAccountQuery,\n SaasMutation\n ]\n}\n"]}
|
|
@@ -4,7 +4,6 @@ import { Counterparty } from '@things-factory/biz-base';
|
|
|
4
4
|
import { Employee } from '@things-factory/organization';
|
|
5
5
|
import { Domain } from '@things-factory/shell';
|
|
6
6
|
import { SaasAccountStatus } from './saas-rules';
|
|
7
|
-
import { SaasTrend } from './saas-trend-type';
|
|
8
7
|
/**
|
|
9
8
|
* One account the domain holds with a vendor - the unit a bill comes from (ADR-0177 decision 2): an AWS organization's
|
|
10
9
|
* payer account, a Microsoft 365 tenant, a registrar. An account under another (an AWS member account) names its purpose
|
|
@@ -26,13 +25,10 @@ export declare class SaasAccount {
|
|
|
26
25
|
paymentAccount?: BankAccount;
|
|
27
26
|
paymentAccountId?: string;
|
|
28
27
|
currency: string;
|
|
29
|
-
billingDay?: number;
|
|
30
28
|
secretLocation?: string;
|
|
31
|
-
increaseAlertRate?: number;
|
|
32
29
|
status: SaasAccountStatus;
|
|
33
30
|
closedOn?: string;
|
|
34
31
|
description?: string;
|
|
35
|
-
trend?: SaasTrend;
|
|
36
32
|
createdAt?: Date;
|
|
37
33
|
updatedAt?: Date;
|
|
38
34
|
deletedAt?: Date;
|
|
@@ -10,7 +10,6 @@ const shell_1 = require("@things-factory/shell");
|
|
|
10
10
|
const type_graphql_1 = require("type-graphql");
|
|
11
11
|
const typeorm_1 = require("typeorm");
|
|
12
12
|
const saas_rules_1 = require("./saas-rules");
|
|
13
|
-
const saas_trend_type_1 = require("./saas-trend-type");
|
|
14
13
|
/**
|
|
15
14
|
* One account the domain holds with a vendor - the unit a bill comes from (ADR-0177 decision 2): an AWS organization's
|
|
16
15
|
* payer account, a Microsoft 365 tenant, a registrar. An account under another (an AWS member account) names its purpose
|
|
@@ -88,24 +87,14 @@ tslib_1.__decorate([
|
|
|
88
87
|
], SaasAccount.prototype, "paymentAccountId", void 0);
|
|
89
88
|
tslib_1.__decorate([
|
|
90
89
|
(0, typeorm_1.Column)({ default: 'KRW' }),
|
|
91
|
-
(0, type_graphql_1.Field)({ description: 'The currency
|
|
90
|
+
(0, type_graphql_1.Field)({ description: 'The currency its prices are in - ISO 4217' }),
|
|
92
91
|
tslib_1.__metadata("design:type", String)
|
|
93
92
|
], SaasAccount.prototype, "currency", void 0);
|
|
94
|
-
tslib_1.__decorate([
|
|
95
|
-
(0, typeorm_1.Column)({ type: 'int', nullable: true }),
|
|
96
|
-
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'The day of the month the bill is paid, 1-31; past the month’s end is its last day' }),
|
|
97
|
-
tslib_1.__metadata("design:type", Number)
|
|
98
|
-
], SaasAccount.prototype, "billingDay", void 0);
|
|
99
93
|
tslib_1.__decorate([
|
|
100
94
|
(0, typeorm_1.Column)({ nullable: true }),
|
|
101
95
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Where its password, keys and recovery codes are kept - never the secrets themselves' }),
|
|
102
96
|
tslib_1.__metadata("design:type", String)
|
|
103
97
|
], SaasAccount.prototype, "secretLocation", void 0);
|
|
104
|
-
tslib_1.__decorate([
|
|
105
|
-
(0, typeorm_1.Column)({ type: 'float', nullable: true }),
|
|
106
|
-
(0, type_graphql_1.Field)(type => type_graphql_1.Float, { nullable: true, description: 'A bill up more than this over the month before is told (0.2 for 20%); 20% when empty' }),
|
|
107
|
-
tslib_1.__metadata("design:type", Number)
|
|
108
|
-
], SaasAccount.prototype, "increaseAlertRate", void 0);
|
|
109
98
|
tslib_1.__decorate([
|
|
110
99
|
(0, typeorm_1.Column)({ default: saas_rules_1.SaasAccountStatus.ACTIVE }),
|
|
111
100
|
(0, type_graphql_1.Field)(type => saas_rules_1.SaasAccountStatus, { description: 'In use or closed' }),
|
|
@@ -121,10 +110,6 @@ tslib_1.__decorate([
|
|
|
121
110
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Notes' }),
|
|
122
111
|
tslib_1.__metadata("design:type", String)
|
|
123
112
|
], SaasAccount.prototype, "description", void 0);
|
|
124
|
-
tslib_1.__decorate([
|
|
125
|
-
(0, type_graphql_1.Field)(type => saas_trend_type_1.SaasTrend, { nullable: true, description: 'Its last twelve months billed and the next three expected - filled by the list queries' }),
|
|
126
|
-
tslib_1.__metadata("design:type", saas_trend_type_1.SaasTrend)
|
|
127
|
-
], SaasAccount.prototype, "trend", void 0);
|
|
128
113
|
tslib_1.__decorate([
|
|
129
114
|
(0, typeorm_1.CreateDateColumn)(),
|
|
130
115
|
(0, type_graphql_1.Field)({ nullable: true, description: 'When the account was registered' }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"saas-account.js","sourceRoot":"","sources":["../../server/service/saas-account.ts"],"names":[],"mappings":"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","sourcesContent":["import { BankAccount } from '@things-factory/accounting'\nimport { User } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\nimport { Employee } from '@things-factory/organization'\nimport { Domain } from '@things-factory/shell'\nimport { Field, Float, ID, Int, ObjectType } from 'type-graphql'\nimport {\n Column,\n CreateDateColumn,\n DeleteDateColumn,\n Entity,\n Index,\n JoinTable,\n ManyToMany,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\n\nimport { SaasAccountStatus } from './saas-rules'\nimport { SaasTrend } from './saas-trend-type'\n\n/**\n * One account the domain holds with a vendor - the unit a bill comes from (ADR-0177 decision 2): an AWS organization's\n * payer account, a Microsoft 365 tenant, a registrar. An account under another (an AWS member account) names its purpose\n * and admins only; the money is the parent's. No password, key or recovery code is kept - only where it is kept.\n */\n@Entity()\n@Index('ix_saas_account_0', (account: SaasAccount) => [account.domain, account.status])\n@ObjectType({ description: 'Entity for SaasAccount — an account the domain holds with a vendor of something it subscribes to' })\nexport class SaasAccount {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier of the account' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain the account belongs to' })\n domain?: Domain\n\n @RelationId((account: SaasAccount) => account.domain)\n domainId?: string\n\n @Column()\n @Field({ description: 'The service - AWS, Microsoft 365, Adobe Creative Cloud, a registrar' })\n name: string\n\n @ManyToOne(type => Counterparty, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => Counterparty, { nullable: true, description: 'The vendor it is paid to' })\n counterparty?: Counterparty\n\n @RelationId((account: SaasAccount) => account.counterparty)\n counterpartyId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: \"The vendor's name for the account - an AWS account number, a tenant domain, an organization id\" })\n identifier?: string\n\n @ManyToOne(type => SaasAccount, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => SaasAccount, { nullable: true, description: 'The account it is under - an AWS member account under its payer; the money is the parent’s' })\n parent?: SaasAccount\n\n @RelationId((account: SaasAccount) => account.parent)\n parentId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'What it is used for - production, development, a customer' })\n purpose?: string\n\n @ManyToMany(type => Employee)\n @JoinTable({ name: 'saas_account_admins' })\n @Field(type => [Employee], { nullable: true, description: 'Who administers it at the vendor - they keep its subscriptions, seats and bills' })\n admins?: Employee[]\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Where the vendor sends the bills' })\n billingEmail?: string\n\n @ManyToOne(type => BankAccount, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => BankAccount, { nullable: true, description: 'What it is paid from - a bank account or a card (accounting, ADR-0178)' })\n paymentAccount?: BankAccount\n\n @RelationId((account: SaasAccount) => account.paymentAccount)\n paymentAccountId?: string\n\n @Column({ default: 'KRW' })\n @Field({ description: 'The currency it is billed in - ISO 4217' })\n currency: string\n\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'The day of the month the bill is paid, 1-31; past the month’s end is its last day' })\n billingDay?: number\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Where its password, keys and recovery codes are kept - never the secrets themselves' })\n secretLocation?: string\n\n @Column({ type: 'float', nullable: true })\n @Field(type => Float, { nullable: true, description: 'A bill up more than this over the month before is told (0.2 for 20%); 20% when empty' })\n increaseAlertRate?: number\n\n @Column({ default: SaasAccountStatus.ACTIVE })\n @Field(type => SaasAccountStatus, { description: 'In use or closed' })\n status: SaasAccountStatus\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The day it was closed - YYYY-MM-DD' })\n closedOn?: string\n\n @Column({ type: 'text', nullable: true })\n @Field({ nullable: true, description: 'Notes' })\n description?: string\n\n @Field(type => SaasTrend, { nullable: true, description: 'Its last twelve months billed and the next three expected - filled by the list queries' })\n trend?: SaasTrend\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'When the account was registered' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When the account last changed' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'When the account was deleted' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who registered it' })\n creator?: User\n\n @RelationId((account: SaasAccount) => account.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who last changed it' })\n updater?: User\n\n @RelationId((account: SaasAccount) => account.updater)\n updaterId?: string\n}\n"]}
|
|
1
|
+
{"version":3,"file":"saas-account.js","sourceRoot":"","sources":["../../server/service/saas-account.ts"],"names":[],"mappings":"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","sourcesContent":["import { BankAccount } from '@things-factory/accounting'\nimport { User } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\nimport { Employee } from '@things-factory/organization'\nimport { Domain } from '@things-factory/shell'\nimport { Field, Float, ID, Int, ObjectType } from 'type-graphql'\nimport {\n Column,\n CreateDateColumn,\n DeleteDateColumn,\n Entity,\n Index,\n JoinTable,\n ManyToMany,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\n\nimport { SaasAccountStatus } from './saas-rules'\n\n/**\n * One account the domain holds with a vendor - the unit a bill comes from (ADR-0177 decision 2): an AWS organization's\n * payer account, a Microsoft 365 tenant, a registrar. An account under another (an AWS member account) names its purpose\n * and admins only; the money is the parent's. No password, key or recovery code is kept - only where it is kept.\n */\n@Entity()\n@Index('ix_saas_account_0', (account: SaasAccount) => [account.domain, account.status])\n@ObjectType({ description: 'Entity for SaasAccount — an account the domain holds with a vendor of something it subscribes to' })\nexport class SaasAccount {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier of the account' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain the account belongs to' })\n domain?: Domain\n\n @RelationId((account: SaasAccount) => account.domain)\n domainId?: string\n\n @Column()\n @Field({ description: 'The service - AWS, Microsoft 365, Adobe Creative Cloud, a registrar' })\n name: string\n\n @ManyToOne(type => Counterparty, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => Counterparty, { nullable: true, description: 'The vendor it is paid to' })\n counterparty?: Counterparty\n\n @RelationId((account: SaasAccount) => account.counterparty)\n counterpartyId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: \"The vendor's name for the account - an AWS account number, a tenant domain, an organization id\" })\n identifier?: string\n\n @ManyToOne(type => SaasAccount, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => SaasAccount, { nullable: true, description: 'The account it is under - an AWS member account under its payer; the money is the parent’s' })\n parent?: SaasAccount\n\n @RelationId((account: SaasAccount) => account.parent)\n parentId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'What it is used for - production, development, a customer' })\n purpose?: string\n\n @ManyToMany(type => Employee)\n @JoinTable({ name: 'saas_account_admins' })\n @Field(type => [Employee], { nullable: true, description: 'Who administers it at the vendor - they keep its subscriptions, seats and bills' })\n admins?: Employee[]\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Where the vendor sends the bills' })\n billingEmail?: string\n\n @ManyToOne(type => BankAccount, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => BankAccount, { nullable: true, description: 'What it is paid from - a bank account or a card (accounting, ADR-0178)' })\n paymentAccount?: BankAccount\n\n @RelationId((account: SaasAccount) => account.paymentAccount)\n paymentAccountId?: string\n\n @Column({ default: 'KRW' })\n @Field({ description: 'The currency its prices are in - ISO 4217' })\n currency: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Where its password, keys and recovery codes are kept - never the secrets themselves' })\n secretLocation?: string\n\n @Column({ default: SaasAccountStatus.ACTIVE })\n @Field(type => SaasAccountStatus, { description: 'In use or closed' })\n status: SaasAccountStatus\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The day it was closed - YYYY-MM-DD' })\n closedOn?: string\n\n @Column({ type: 'text', nullable: true })\n @Field({ nullable: true, description: 'Notes' })\n description?: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'When the account was registered' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When the account last changed' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'When the account was deleted' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who registered it' })\n creator?: User\n\n @RelationId((account: SaasAccount) => account.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who last changed it' })\n updater?: User\n\n @RelationId((account: SaasAccount) => account.updater)\n updaterId?: string\n}\n"]}
|
|
@@ -1,19 +1,10 @@
|
|
|
1
1
|
import { SaasAccount } from './saas-account';
|
|
2
|
-
import { SaasBill } from './saas-bill';
|
|
3
2
|
import { SaasSeat } from './saas-seat';
|
|
4
3
|
import { SaasSubscription } from './saas-subscription';
|
|
5
|
-
import { SaasAccountPatch,
|
|
4
|
+
import { SaasAccountPatch, SaasLicensesInput, SaasSeatPatch, SaasSubscriptionPatch } from './saas-type';
|
|
6
5
|
export declare class SaasMutation {
|
|
7
6
|
syncSaasLicenses(licenses: SaasLicensesInput, context: ResolverContext): Promise<import("./saas-sync").LicenseSyncResult>;
|
|
8
|
-
syncSaasCosts(costs: SaasCostsInput[], context: ResolverContext): Promise<{
|
|
9
|
-
results: import("./saas-sync").CostSyncResult[];
|
|
10
|
-
unmatched: string[];
|
|
11
|
-
}>;
|
|
12
7
|
updateMultipleSaasAccount(patches: SaasAccountPatch[], context: ResolverContext): Promise<SaasAccount[]>;
|
|
13
|
-
createSaasAccountsFromCandidates(keys: string[], context: ResolverContext): Promise<SaasAccount[]>;
|
|
14
|
-
hideSaasCandidates(keys: string[], hide: boolean, context: ResolverContext): Promise<boolean>;
|
|
15
8
|
updateMultipleSaasSubscription(accountId: string, patches: SaasSubscriptionPatch[], context: ResolverContext): Promise<SaasSubscription[]>;
|
|
16
9
|
updateMultipleSaasSeat(subscriptionId: string, patches: SaasSeatPatch[], context: ResolverContext): Promise<SaasSeat[]>;
|
|
17
|
-
saveSaasBill(accountId: string, bill: SaasBillInput, context: ResolverContext): Promise<SaasBill>;
|
|
18
|
-
deleteSaasBill(id: string, context: ResolverContext): Promise<boolean>;
|
|
19
10
|
}
|
|
@@ -2,25 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SaasMutation = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const auth_base_1 = require("@things-factory/auth-base");
|
|
6
5
|
const shell_1 = require("@things-factory/shell");
|
|
7
6
|
const type_graphql_1 = require("type-graphql");
|
|
8
7
|
const saas_access_1 = require("./saas-access");
|
|
9
8
|
const saas_account_1 = require("./saas-account");
|
|
10
|
-
const saas_bill_1 = require("./saas-bill");
|
|
11
9
|
const saas_service_1 = require("./saas-service");
|
|
12
10
|
const saas_seat_1 = require("./saas-seat");
|
|
13
11
|
const saas_subscription_1 = require("./saas-subscription");
|
|
14
12
|
const saas_sync_1 = require("./saas-sync");
|
|
15
13
|
const saas_type_1 = require("./saas-type");
|
|
16
|
-
/** The bank's fact (ADR-0139) is read only by whoever reads bank lines. */
|
|
17
|
-
async function mustReadBank(state) {
|
|
18
|
-
const { domain, user, unsafeIP, prohibitedPrivileges } = state;
|
|
19
|
-
if (!(await (0, auth_base_1.checkPermission)({ category: 'bank', privilege: 'query', owner: false }, user, domain, unsafeIP, prohibitedPrivileges)))
|
|
20
|
-
throw new shell_1.Refusal('saas-candidates-need-bank', {}, 'Making accounts from the books needs reading the bank lines');
|
|
21
|
-
}
|
|
22
14
|
/*
|
|
23
|
-
* The accounts are the keepers'. What is under an account - its subscriptions
|
|
15
|
+
* The accounts are the keepers'. What is under an account - its subscriptions and their seats - is also its
|
|
24
16
|
* admins', whatever their seat (ADR-0177 Q4): those doors ask `saas-self`, and the service keeps an admin to their own.
|
|
25
17
|
*/
|
|
26
18
|
let SaasMutation = class SaasMutation {
|
|
@@ -29,25 +21,10 @@ let SaasMutation = class SaasMutation {
|
|
|
29
21
|
const { tx, domain, user } = context.state;
|
|
30
22
|
return (0, saas_sync_1.syncSaasLicenses)(tx, domain, user, licenses, new Date().toISOString().slice(0, 10));
|
|
31
23
|
}
|
|
32
|
-
/* A scenario's step (ADR-0177 개정 3): the bills of the closed months as the vendor's cost report says. */
|
|
33
|
-
syncSaasCosts(costs, context) {
|
|
34
|
-
const { tx, domain, user } = context.state;
|
|
35
|
-
return (0, saas_sync_1.syncSaasCostsOf)(tx, domain, user, costs, new Date().toISOString().slice(0, 10));
|
|
36
|
-
}
|
|
37
24
|
updateMultipleSaasAccount(patches, context) {
|
|
38
25
|
const { tx, domain, user } = context.state;
|
|
39
26
|
return (0, saas_service_1.saveSaasAccounts)(tx, domain, user, patches);
|
|
40
27
|
}
|
|
41
|
-
/* The candidates are read from the bank and card lines - a keeper who also reads them (checked in the service). */
|
|
42
|
-
async createSaasAccountsFromCandidates(keys, context) {
|
|
43
|
-
const { tx, domain, user } = context.state;
|
|
44
|
-
await mustReadBank(context.state);
|
|
45
|
-
return (0, saas_service_1.createSaasAccountsFromCandidates)(tx, domain, user, keys, new Date().toISOString().slice(0, 10));
|
|
46
|
-
}
|
|
47
|
-
async hideSaasCandidates(keys, hide, context) {
|
|
48
|
-
const { tx, domain, user } = context.state;
|
|
49
|
-
return (0, saas_service_1.hideSaasCandidates)(tx, domain, user, keys, hide);
|
|
50
|
-
}
|
|
51
28
|
async updateMultipleSaasSubscription(accountId, patches, context) {
|
|
52
29
|
const { tx, domain, user } = context.state;
|
|
53
30
|
return (0, saas_service_1.saveSaasSubscriptions)(tx, domain, user, await (0, saas_access_1.scopeOf)(context.state, 'mutation', tx), accountId, patches);
|
|
@@ -56,14 +33,6 @@ let SaasMutation = class SaasMutation {
|
|
|
56
33
|
const { tx, domain, user } = context.state;
|
|
57
34
|
return (0, saas_service_1.saveSaasSeats)(tx, domain, user, await (0, saas_access_1.scopeOf)(context.state, 'mutation', tx), subscriptionId, patches);
|
|
58
35
|
}
|
|
59
|
-
async saveSaasBill(accountId, bill, context) {
|
|
60
|
-
const { tx, domain, user } = context.state;
|
|
61
|
-
return (0, saas_service_1.saveSaasBill)(tx, domain, user, await (0, saas_access_1.scopeOf)(context.state, 'mutation', tx), accountId, bill);
|
|
62
|
-
}
|
|
63
|
-
async deleteSaasBill(id, context) {
|
|
64
|
-
const { tx, domain } = context.state;
|
|
65
|
-
return (0, saas_service_1.deleteSaasBill)(tx, domain, await (0, saas_access_1.scopeOf)(context.state, 'mutation', tx), id);
|
|
66
|
-
}
|
|
67
36
|
};
|
|
68
37
|
exports.SaasMutation = SaasMutation;
|
|
69
38
|
tslib_1.__decorate([
|
|
@@ -76,16 +45,6 @@ tslib_1.__decorate([
|
|
|
76
45
|
tslib_1.__metadata("design:paramtypes", [saas_type_1.SaasLicensesInput, Object]),
|
|
77
46
|
tslib_1.__metadata("design:returntype", void 0)
|
|
78
47
|
], SaasMutation.prototype, "syncSaasLicenses", null);
|
|
79
|
-
tslib_1.__decorate([
|
|
80
|
-
(0, type_graphql_1.Directive)('@transaction'),
|
|
81
|
-
(0, type_graphql_1.Directive)('@privilege(category: "saas-sync", privilege: "mutation", domainOwnerGranted: true)'),
|
|
82
|
-
(0, type_graphql_1.Mutation)(type => saas_type_1.SaasCostSyncs, { description: "To write accounts' bills of the closed months from their vendors' cost reports - an entry an account; never over a bill a person wrote; vendor accounts no account names are passed over" }),
|
|
83
|
-
tslib_1.__param(0, (0, type_graphql_1.Arg)('costs', type => [saas_type_1.SaasCostsInput])),
|
|
84
|
-
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
85
|
-
tslib_1.__metadata("design:type", Function),
|
|
86
|
-
tslib_1.__metadata("design:paramtypes", [Array, Object]),
|
|
87
|
-
tslib_1.__metadata("design:returntype", void 0)
|
|
88
|
-
], SaasMutation.prototype, "syncSaasCosts", null);
|
|
89
48
|
tslib_1.__decorate([
|
|
90
49
|
(0, type_graphql_1.Directive)('@transaction'),
|
|
91
50
|
(0, type_graphql_1.Directive)('@privilege(category: "saas", privilege: "mutation", domainOwnerGranted: true)'),
|
|
@@ -97,27 +56,6 @@ tslib_1.__decorate([
|
|
|
97
56
|
tslib_1.__metadata("design:paramtypes", [Array, Object]),
|
|
98
57
|
tslib_1.__metadata("design:returntype", void 0)
|
|
99
58
|
], SaasMutation.prototype, "updateMultipleSaasAccount", null);
|
|
100
|
-
tslib_1.__decorate([
|
|
101
|
-
(0, type_graphql_1.Directive)('@transaction'),
|
|
102
|
-
(0, type_graphql_1.Directive)('@privilege(category: "saas", privilege: "mutation", domainOwnerGranted: true)'),
|
|
103
|
-
(0, type_graphql_1.Mutation)(type => [saas_account_1.SaasAccount], { description: 'To make an account, with one usage subscription at the monthly average, of each candidate (by its key)' }),
|
|
104
|
-
tslib_1.__param(0, (0, type_graphql_1.Arg)('keys', type => [String])),
|
|
105
|
-
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
106
|
-
tslib_1.__metadata("design:type", Function),
|
|
107
|
-
tslib_1.__metadata("design:paramtypes", [Array, Object]),
|
|
108
|
-
tslib_1.__metadata("design:returntype", Promise)
|
|
109
|
-
], SaasMutation.prototype, "createSaasAccountsFromCandidates", null);
|
|
110
|
-
tslib_1.__decorate([
|
|
111
|
-
(0, type_graphql_1.Directive)('@transaction'),
|
|
112
|
-
(0, type_graphql_1.Directive)('@privilege(category: "saas", privilege: "mutation", domainOwnerGranted: true)'),
|
|
113
|
-
(0, type_graphql_1.Mutation)(type => Boolean, { description: 'To hide candidates (by their keys), or with hide false to show them again' }),
|
|
114
|
-
tslib_1.__param(0, (0, type_graphql_1.Arg)('keys', type => [String])),
|
|
115
|
-
tslib_1.__param(1, (0, type_graphql_1.Arg)('hide')),
|
|
116
|
-
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
117
|
-
tslib_1.__metadata("design:type", Function),
|
|
118
|
-
tslib_1.__metadata("design:paramtypes", [Array, Boolean, Object]),
|
|
119
|
-
tslib_1.__metadata("design:returntype", Promise)
|
|
120
|
-
], SaasMutation.prototype, "hideSaasCandidates", null);
|
|
121
59
|
tslib_1.__decorate([
|
|
122
60
|
(0, type_graphql_1.Directive)('@transaction'),
|
|
123
61
|
(0, type_graphql_1.Directive)('@privilege(category: "saas-self", privilege: "mutation", domainOwnerGranted: true)'),
|
|
@@ -142,29 +80,6 @@ tslib_1.__decorate([
|
|
|
142
80
|
tslib_1.__metadata("design:paramtypes", [String, Array, Object]),
|
|
143
81
|
tslib_1.__metadata("design:returntype", Promise)
|
|
144
82
|
], SaasMutation.prototype, "updateMultipleSaasSeat", null);
|
|
145
|
-
tslib_1.__decorate([
|
|
146
|
-
(0, type_graphql_1.Directive)('@transaction'),
|
|
147
|
-
(0, type_graphql_1.Directive)('@privilege(category: "saas-self", privilege: "mutation", domainOwnerGranted: true)'),
|
|
148
|
-
(0, type_graphql_1.Directive)('@auditTarget(kind: "SaasAccount", arg: "accountId")'),
|
|
149
|
-
(0, type_graphql_1.Mutation)(type => saas_bill_1.SaasBill, { description: "To write an account's bill for a month, its lines whole" }),
|
|
150
|
-
tslib_1.__param(0, (0, type_graphql_1.Arg)('accountId')),
|
|
151
|
-
tslib_1.__param(1, (0, type_graphql_1.Arg)('bill', type => saas_type_1.SaasBillInput)),
|
|
152
|
-
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
153
|
-
tslib_1.__metadata("design:type", Function),
|
|
154
|
-
tslib_1.__metadata("design:paramtypes", [String, saas_type_1.SaasBillInput, Object]),
|
|
155
|
-
tslib_1.__metadata("design:returntype", Promise)
|
|
156
|
-
], SaasMutation.prototype, "saveSaasBill", null);
|
|
157
|
-
tslib_1.__decorate([
|
|
158
|
-
(0, type_graphql_1.Directive)('@transaction'),
|
|
159
|
-
(0, type_graphql_1.Directive)('@privilege(category: "saas-self", privilege: "mutation", domainOwnerGranted: true)'),
|
|
160
|
-
(0, type_graphql_1.Directive)('@auditTarget(kind: "SaasBill", arg: "id")'),
|
|
161
|
-
(0, type_graphql_1.Mutation)(type => Boolean, { description: 'To delete a bill' }),
|
|
162
|
-
tslib_1.__param(0, (0, type_graphql_1.Arg)('id')),
|
|
163
|
-
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
164
|
-
tslib_1.__metadata("design:type", Function),
|
|
165
|
-
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
166
|
-
tslib_1.__metadata("design:returntype", Promise)
|
|
167
|
-
], SaasMutation.prototype, "deleteSaasBill", null);
|
|
168
83
|
exports.SaasMutation = SaasMutation = tslib_1.__decorate([
|
|
169
84
|
(0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals),
|
|
170
85
|
(0, type_graphql_1.Resolver)(saas_account_1.SaasAccount)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"saas-mutation.js","sourceRoot":"","sources":["../../server/service/saas-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { checkPermission } from '@things-factory/auth-base'\nimport { Refusal, SpeakRefusals } from '@things-factory/shell'\nimport { Arg, Ctx, Directive, Mutation, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { scopeOf } from './saas-access'\nimport { SaasAccount } from './saas-account'\nimport { SaasBill } from './saas-bill'\nimport { createSaasAccountsFromCandidates, deleteSaasBill, hideSaasCandidates, saveSaasAccounts, saveSaasBill, saveSaasSeats, saveSaasSubscriptions } from './saas-service'\nimport { SaasSeat } from './saas-seat'\nimport { SaasSubscription } from './saas-subscription'\nimport { syncSaasCostsOf, syncSaasLicenses } from './saas-sync'\nimport { SaasAccountPatch, SaasBillInput, SaasCostSyncs, SaasCostsInput, SaasLicenseSync, SaasLicensesInput, SaasSeatPatch, SaasSubscriptionPatch } from './saas-type'\n\n/** The bank's fact (ADR-0139) is read only by whoever reads bank lines. */\nasync function mustReadBank(state: any) {\n const { domain, user, unsafeIP, prohibitedPrivileges } = state\n if (!(await checkPermission({ category: 'bank', privilege: 'query', owner: false } as any, user, domain, unsafeIP, prohibitedPrivileges)))\n throw new Refusal('saas-candidates-need-bank', {}, 'Making accounts from the books needs reading the bank lines')\n}\n\n/*\n * The accounts are the keepers'. What is under an account - its subscriptions, their seats, its bills - is also its\n * admins', whatever their seat (ADR-0177 Q4): those doors ask `saas-self`, and the service keeps an admin to their own.\n */\n@UseMiddleware(SpeakRefusals)\n@Resolver(SaasAccount)\nexport class SaasMutation {\n /* A scenario's step (ADR-0177 개정 2): seats and quantities only, so its own category - the system admin runs it. */\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas-sync\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(type => SaasLicenseSync, { description: \"To keep an account's seats and quantities as its vendor tenant says - what it bought and who holds each\" })\n syncSaasLicenses(@Arg('licenses', type => SaasLicensesInput) licenses: SaasLicensesInput, @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return syncSaasLicenses(tx, domain, user, licenses, new Date().toISOString().slice(0, 10))\n }\n\n /* A scenario's step (ADR-0177 개정 3): the bills of the closed months as the vendor's cost report says. */\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas-sync\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(type => SaasCostSyncs, { description: \"To write accounts' bills of the closed months from their vendors' cost reports - an entry an account; never over a bill a person wrote; vendor accounts no account names are passed over\" })\n syncSaasCosts(@Arg('costs', type => [SaasCostsInput]) costs: SaasCostsInput[], @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return syncSaasCostsOf(tx, domain, user, costs, new Date().toISOString().slice(0, 10))\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@auditTarget(kind: \"SaasAccount\", arg: \"patches\")')\n @Mutation(type => [SaasAccount], { description: 'To save the accounts grid - new, changed and deleted rows together' })\n updateMultipleSaasAccount(@Arg('patches', type => [SaasAccountPatch]) patches: SaasAccountPatch[], @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return saveSaasAccounts(tx, domain, user, patches)\n }\n\n /* The candidates are read from the bank and card lines - a keeper who also reads them (checked in the service). */\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(type => [SaasAccount], { description: 'To make an account, with one usage subscription at the monthly average, of each candidate (by its key)' })\n async createSaasAccountsFromCandidates(@Arg('keys', type => [String]) keys: string[], @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n await mustReadBank(context.state)\n return createSaasAccountsFromCandidates(tx, domain, user, keys, new Date().toISOString().slice(0, 10))\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(type => Boolean, { description: 'To hide candidates (by their keys), or with hide false to show them again' })\n async hideSaasCandidates(@Arg('keys', type => [String]) keys: string[], @Arg('hide') hide: boolean, @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return hideSaasCandidates(tx, domain, user, keys, hide)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas-self\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@auditTarget(kind: \"SaasAccount\", arg: \"accountId\")')\n @Mutation(type => [SaasSubscription], { description: \"To save an account's subscriptions grid\" })\n async updateMultipleSaasSubscription(@Arg('accountId') accountId: string, @Arg('patches', type => [SaasSubscriptionPatch]) patches: SaasSubscriptionPatch[], @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return saveSaasSubscriptions(tx, domain, user, await scopeOf(context.state, 'mutation', tx), accountId, patches)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas-self\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@auditTarget(kind: \"SaasSubscription\", arg: \"subscriptionId\")')\n @Mutation(type => [SaasSeat], { description: \"To save a subscription's seats grid\" })\n async updateMultipleSaasSeat(@Arg('subscriptionId') subscriptionId: string, @Arg('patches', type => [SaasSeatPatch]) patches: SaasSeatPatch[], @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return saveSaasSeats(tx, domain, user, await scopeOf(context.state, 'mutation', tx), subscriptionId, patches)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas-self\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@auditTarget(kind: \"SaasAccount\", arg: \"accountId\")')\n @Mutation(type => SaasBill, { description: \"To write an account's bill for a month, its lines whole\" })\n async saveSaasBill(@Arg('accountId') accountId: string, @Arg('bill', type => SaasBillInput) bill: SaasBillInput, @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return saveSaasBill(tx, domain, user, await scopeOf(context.state, 'mutation', tx), accountId, bill)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas-self\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@auditTarget(kind: \"SaasBill\", arg: \"id\")')\n @Mutation(type => Boolean, { description: 'To delete a bill' })\n async deleteSaasBill(@Arg('id') id: string, @Ctx() context: ResolverContext) {\n const { tx, domain } = context.state\n return deleteSaasBill(tx, domain, await scopeOf(context.state, 'mutation', tx), id)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"saas-mutation.js","sourceRoot":"","sources":["../../server/service/saas-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { SpeakRefusals } from '@things-factory/shell'\nimport { Arg, Ctx, Directive, Mutation, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { scopeOf } from './saas-access'\nimport { SaasAccount } from './saas-account'\nimport { saveSaasAccounts, saveSaasSeats, saveSaasSubscriptions } from './saas-service'\nimport { SaasSeat } from './saas-seat'\nimport { SaasSubscription } from './saas-subscription'\nimport { syncSaasLicenses } from './saas-sync'\nimport { SaasAccountPatch, SaasLicenseSync, SaasLicensesInput, SaasSeatPatch, SaasSubscriptionPatch } from './saas-type'\n\n/*\n * The accounts are the keepers'. What is under an account - its subscriptions and their seats - is also its\n * admins', whatever their seat (ADR-0177 Q4): those doors ask `saas-self`, and the service keeps an admin to their own.\n */\n@UseMiddleware(SpeakRefusals)\n@Resolver(SaasAccount)\nexport class SaasMutation {\n /* A scenario's step (ADR-0177 개정 2): seats and quantities only, so its own category - the system admin runs it. */\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas-sync\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(type => SaasLicenseSync, { description: \"To keep an account's seats and quantities as its vendor tenant says - what it bought and who holds each\" })\n syncSaasLicenses(@Arg('licenses', type => SaasLicensesInput) licenses: SaasLicensesInput, @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return syncSaasLicenses(tx, domain, user, licenses, new Date().toISOString().slice(0, 10))\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@auditTarget(kind: \"SaasAccount\", arg: \"patches\")')\n @Mutation(type => [SaasAccount], { description: 'To save the accounts grid - new, changed and deleted rows together' })\n updateMultipleSaasAccount(@Arg('patches', type => [SaasAccountPatch]) patches: SaasAccountPatch[], @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return saveSaasAccounts(tx, domain, user, patches)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas-self\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@auditTarget(kind: \"SaasAccount\", arg: \"accountId\")')\n @Mutation(type => [SaasSubscription], { description: \"To save an account's subscriptions grid\" })\n async updateMultipleSaasSubscription(@Arg('accountId') accountId: string, @Arg('patches', type => [SaasSubscriptionPatch]) patches: SaasSubscriptionPatch[], @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return saveSaasSubscriptions(tx, domain, user, await scopeOf(context.state, 'mutation', tx), accountId, patches)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"saas-self\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@auditTarget(kind: \"SaasSubscription\", arg: \"subscriptionId\")')\n @Mutation(type => [SaasSeat], { description: \"To save a subscription's seats grid\" })\n async updateMultipleSaasSeat(@Arg('subscriptionId') subscriptionId: string, @Arg('patches', type => [SaasSeatPatch]) patches: SaasSeatPatch[], @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return saveSaasSeats(tx, domain, user, await scopeOf(context.state, 'mutation', tx), subscriptionId, patches)\n }\n}\n"]}
|