@things-factory/saas 10.1.240 → 10.1.241

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.
Files changed (76) hide show
  1. package/README.md +11 -11
  2. package/dist-client/entity360/saas-account-360.d.ts +0 -1
  3. package/dist-client/entity360/saas-account-360.js +18 -71
  4. package/dist-client/entity360/saas-account-360.js.map +1 -1
  5. package/dist-client/index.d.ts +0 -3
  6. package/dist-client/index.js +0 -3
  7. package/dist-client/index.js.map +1 -1
  8. package/dist-client/pages/saas-account-list.d.ts +1 -1
  9. package/dist-client/pages/saas-account-list.js +5 -14
  10. package/dist-client/pages/saas-account-list.js.map +1 -1
  11. package/dist-client/pages/saas-account-page.d.ts +3 -5
  12. package/dist-client/pages/saas-account-page.js +30 -55
  13. package/dist-client/pages/saas-account-page.js.map +1 -1
  14. package/dist-client/route.d.ts +1 -1
  15. package/dist-client/route.js +0 -3
  16. package/dist-client/route.js.map +1 -1
  17. package/dist-client/tsconfig.tsbuildinfo +1 -1
  18. package/dist-client/words.d.ts +0 -13
  19. package/dist-client/words.js +0 -17
  20. package/dist-client/words.js.map +1 -1
  21. package/dist-server/service/checks.d.ts +1 -9
  22. package/dist-server/service/checks.js +2 -18
  23. package/dist-server/service/checks.js.map +1 -1
  24. package/dist-server/service/index.d.ts +1 -6
  25. package/dist-server/service/index.js +1 -9
  26. package/dist-server/service/index.js.map +1 -1
  27. package/dist-server/service/saas-account.d.ts +0 -4
  28. package/dist-server/service/saas-account.js +1 -16
  29. package/dist-server/service/saas-account.js.map +1 -1
  30. package/dist-server/service/saas-mutation.d.ts +1 -10
  31. package/dist-server/service/saas-mutation.js +1 -86
  32. package/dist-server/service/saas-mutation.js.map +1 -1
  33. package/dist-server/service/saas-query.d.ts +2 -51
  34. package/dist-server/service/saas-query.js +2 -65
  35. package/dist-server/service/saas-query.js.map +1 -1
  36. package/dist-server/service/saas-rules.d.ts +3 -141
  37. package/dist-server/service/saas-rules.js +10 -213
  38. package/dist-server/service/saas-rules.js.map +1 -1
  39. package/dist-server/service/saas-service.d.ts +2 -125
  40. package/dist-server/service/saas-service.js +50 -438
  41. package/dist-server/service/saas-service.js.map +1 -1
  42. package/dist-server/service/saas-subscription.d.ts +4 -5
  43. package/dist-server/service/saas-subscription.js +11 -21
  44. package/dist-server/service/saas-subscription.js.map +1 -1
  45. package/dist-server/service/saas-sync.d.ts +0 -35
  46. package/dist-server/service/saas-sync.js +0 -60
  47. package/dist-server/service/saas-sync.js.map +1 -1
  48. package/dist-server/service/saas-type.d.ts +0 -93
  49. package/dist-server/service/saas-type.js +3 -351
  50. package/dist-server/service/saas-type.js.map +1 -1
  51. package/dist-server/tsconfig.tsbuildinfo +1 -1
  52. package/package.json +10 -10
  53. package/translations/en.json +3 -81
  54. package/translations/es.json +3 -81
  55. package/translations/ja.json +3 -81
  56. package/translations/ko.json +3 -81
  57. package/translations/ms.json +3 -81
  58. package/translations/zh.json +3 -81
  59. package/dist-client/components/saas-bill-editor.d.ts +0 -59
  60. package/dist-client/components/saas-bill-editor.js +0 -211
  61. package/dist-client/components/saas-bill-editor.js.map +0 -1
  62. package/dist-client/components/saas-candidates.d.ts +0 -33
  63. package/dist-client/components/saas-candidates.js +0 -157
  64. package/dist-client/components/saas-candidates.js.map +0 -1
  65. package/dist-client/pages/saas-cost-trend.d.ts +0 -23
  66. package/dist-client/pages/saas-cost-trend.js +0 -219
  67. package/dist-client/pages/saas-cost-trend.js.map +0 -1
  68. package/dist-server/service/saas-bill.d.ts +0 -42
  69. package/dist-server/service/saas-bill.js +0 -165
  70. package/dist-server/service/saas-bill.js.map +0 -1
  71. package/dist-server/service/saas-hidden-vendor.d.ts +0 -19
  72. package/dist-server/service/saas-hidden-vendor.js +0 -53
  73. package/dist-server/service/saas-hidden-vendor.js.map +0 -1
  74. package/dist-server/service/saas-trend-type.d.ts +0 -9
  75. package/dist-server/service/saas-trend-type.js +0 -37
  76. package/dist-server/service/saas-trend-type.js.map +0 -1
@@ -1,219 +0,0 @@
1
- import { __decorate, __metadata } from "tslib";
2
- import gql from 'graphql-tag';
3
- import { css, html, nothing } from 'lit';
4
- import { customElement, state } from 'lit/decorators.js';
5
- import { client } from '@operato/graphql';
6
- import { i18next, localize } from '@operato/i18n';
7
- import { navigate, PageView } from '@operato/shell';
8
- import { ScrollbarStyles } from '@operato/styles';
9
- import { amountText, changeText, shiftMonth, thisMonth } from '../words.js';
10
- const SERIES = 'key accountId accountName subscriptionId name currency amounts total monthOverMonth yearOverYear';
11
- /**
12
- * 비용 추이 — what the subscriptions cost month by month, from their bills (ADR-0177 decision 9): everything, each account
13
- * with its subscriptions under it, each department, in each bill's own currency. What the latest bills are worth telling
14
- * comes first. Nothing here is stored; it is read from the bills each time.
15
- */
16
- let SaasCostTrend = class SaasCostTrend extends localize(i18next)(PageView) {
17
- constructor() {
18
- super(...arguments);
19
- this.from = shiftMonth(thisMonth(), -12);
20
- this.to = shiftMonth(thisMonth(), -1);
21
- }
22
- static { this.styles = [
23
- ScrollbarStyles,
24
- css `
25
- :host {
26
- display: flex;
27
- flex-direction: column;
28
- overflow: auto;
29
- padding: var(--spacing-large, 12px);
30
- gap: var(--spacing-large, 12px);
31
- font-size: var(--ui-t-body, 14px);
32
- }
33
- .range {
34
- display: flex;
35
- flex-wrap: wrap;
36
- align-items: center;
37
- gap: var(--spacing-medium, 8px);
38
- color: var(--md-sys-color-on-surface-variant);
39
- }
40
- .range input {
41
- height: 32px;
42
- padding: 0 8px;
43
- border: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));
44
- border-radius: var(--md-sys-shape-corner-small, 4px);
45
- background: var(--md-sys-color-surface);
46
- color: var(--md-sys-color-on-surface);
47
- font: inherit;
48
- }
49
- h3 {
50
- margin: 0;
51
- font-size: var(--ui-t-title, 16px);
52
- font-weight: 600;
53
- }
54
- ul.alerts {
55
- margin: 0;
56
- padding-left: 20px;
57
- color: var(--md-sys-color-error);
58
- }
59
- .muted {
60
- color: var(--md-sys-color-on-surface-variant);
61
- }
62
- .scroll {
63
- overflow-x: auto;
64
- }
65
- table {
66
- border-collapse: collapse;
67
- white-space: nowrap;
68
- }
69
- th,
70
- td {
71
- padding: 6px 8px;
72
- border-bottom: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));
73
- text-align: right;
74
- }
75
- th:first-child,
76
- td:first-child {
77
- position: sticky;
78
- left: 0;
79
- text-align: left;
80
- background: var(--md-sys-color-surface);
81
- }
82
- th {
83
- color: var(--md-sys-color-on-surface-variant);
84
- font-weight: 500;
85
- }
86
- tr.account td:first-child button {
87
- border: none;
88
- background: none;
89
- color: var(--md-sys-color-primary);
90
- font: inherit;
91
- cursor: pointer;
92
- padding: 0;
93
- }
94
- tr.sub td:first-child {
95
- padding-left: 24px;
96
- color: var(--md-sys-color-on-surface-variant);
97
- }
98
- .up {
99
- color: var(--md-sys-color-error);
100
- }
101
- `
102
- ]; }
103
- get context() {
104
- return { title: i18next.t('title.saas-cost-trend') };
105
- }
106
- async pageUpdated(changes) {
107
- if (this.active && (changes.active || !this.trend))
108
- await this.read();
109
- }
110
- async read() {
111
- const response = await client.query({
112
- query: gql `
113
- query ($from: String!, $to: String!) {
114
- saasCostTrend(from: $from, to: $to) {
115
- months
116
- all { ${SERIES} }
117
- accounts { ${SERIES} }
118
- subscriptions { ${SERIES} }
119
- departments { key departmentId name currency amounts total }
120
- alerts { kind accountId accountName subscriptionName month amount limit }
121
- }
122
- }
123
- `,
124
- variables: { from: this.from, to: this.to },
125
- fetchPolicy: 'network-only'
126
- });
127
- this.trend = response.data?.saasCostTrend;
128
- }
129
- change(ratio) {
130
- return html `<span class=${ratio && ratio > 0 ? 'up' : ''}>${changeText(ratio)}</span>`;
131
- }
132
- row(label, one, kind = '') {
133
- return html `<tr class=${kind}>
134
- <td>${label}</td>
135
- <td>${one.currency}</td>
136
- ${one.amounts.map((amount) => html `<td>${amount ? amountText(amount) : ''}</td>`)}
137
- <td>${amountText(one.total)}</td>
138
- ${one.monthOverMonth !== undefined ? html `<td>${this.change(one.monthOverMonth)}</td><td>${this.change(one.yearOverYear)}</td>` : html `<td></td><td></td>`}
139
- </tr>`;
140
- }
141
- head(first) {
142
- return html `<tr>
143
- <th>${first}</th>
144
- <th>${i18next.t('field.saas-trend-currency')}</th>
145
- ${this.trend.months.map((month) => html `<th>${month}</th>`)}
146
- <th>${i18next.t('field.saas-trend-total')}</th>
147
- <th>${i18next.t('field.saas-trend-month-over-month')}</th>
148
- <th>${i18next.t('field.saas-trend-year-over-year')}</th>
149
- </tr>`;
150
- }
151
- render() {
152
- const trend = this.trend;
153
- return html `
154
- <div class="range">
155
- <input type="month" .value=${this.from} @change=${(e) => ((this.from = e.target.value), this.read())} />
156
- ~
157
- <input type="month" .value=${this.to} @change=${(e) => ((this.to = e.target.value), this.read())} />
158
- </div>
159
- ${trend
160
- ? html `
161
- ${trend.alerts.length
162
- ? html `<h3>${i18next.t('text.saas-trend-alerts', { month: trend.alerts[0].month })}</h3>
163
- <ul class="alerts">
164
- ${trend.alerts.map((alert) => html `<li>
165
- ${i18next.t(`text.saas-alert-${alert.kind}`, {
166
- account: alert.accountName,
167
- subscription: alert.subscriptionName ?? '',
168
- amount: amountText(alert.amount),
169
- limit: amountText(alert.limit)
170
- })}
171
- </li>`)}
172
- </ul>`
173
- : nothing}
174
- ${trend.all.length
175
- ? html `
176
- <h3>${i18next.t('text.saas-trend-accounts')}</h3>
177
- <div class="scroll">
178
- <table>
179
- ${this.head(i18next.t('field.saas-trend-account'))}
180
- ${trend.all.map((one) => this.row(i18next.t('text.saas-trend-all'), one))}
181
- ${trend.accounts.map((account) => html `
182
- ${this.row(html `<button @click=${() => navigate(`saas-account-page?id=${account.accountId}&tab=bills`)}>${account.name}</button>`, account, 'account')}
183
- ${trend.subscriptions
184
- .filter((sub) => sub.accountId === account.accountId && sub.currency === account.currency)
185
- .map((sub) => this.row(sub.name ?? i18next.t('text.saas-trend-rest'), sub, 'sub'))}
186
- `)}
187
- </table>
188
- </div>
189
- <h3>${i18next.t('text.saas-trend-departments')}</h3>
190
- <div class="scroll">
191
- <table>
192
- ${this.head(i18next.t('field.saas-trend-department'))}
193
- ${trend.departments.map((one) => this.row(one.name ?? i18next.t('text.saas-trend-no-department'), one))}
194
- </table>
195
- </div>
196
- `
197
- : html `<div class="muted">${i18next.t('text.saas-trend-empty')}</div>`}
198
- `
199
- : nothing}
200
- `;
201
- }
202
- };
203
- __decorate([
204
- state(),
205
- __metadata("design:type", Object)
206
- ], SaasCostTrend.prototype, "from", void 0);
207
- __decorate([
208
- state(),
209
- __metadata("design:type", Object)
210
- ], SaasCostTrend.prototype, "to", void 0);
211
- __decorate([
212
- state(),
213
- __metadata("design:type", Object)
214
- ], SaasCostTrend.prototype, "trend", void 0);
215
- SaasCostTrend = __decorate([
216
- customElement('saas-cost-trend')
217
- ], SaasCostTrend);
218
- export { SaasCostTrend };
219
- //# sourceMappingURL=saas-cost-trend.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"saas-cost-trend.js","sourceRoot":"","sources":["../../client/pages/saas-cost-trend.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\nimport { css, html, nothing } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate, PageView } from '@operato/shell'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { amountText, changeText, shiftMonth, thisMonth } from '../words.js'\n\nconst SERIES = 'key accountId accountName subscriptionId name currency amounts total monthOverMonth yearOverYear'\n\n/**\n * 비용 추이 — what the subscriptions cost month by month, from their bills (ADR-0177 decision 9): everything, each account\n * with its subscriptions under it, each department, in each bill's own currency. What the latest bills are worth telling\n * comes first. Nothing here is stored; it is read from the bills each time.\n */\n@customElement('saas-cost-trend')\nexport class SaasCostTrend extends localize(i18next)(PageView) {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n overflow: auto;\n padding: var(--spacing-large, 12px);\n gap: var(--spacing-large, 12px);\n font-size: var(--ui-t-body, 14px);\n }\n .range {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n color: var(--md-sys-color-on-surface-variant);\n }\n .range input {\n height: 32px;\n padding: 0 8px;\n border: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));\n border-radius: var(--md-sys-shape-corner-small, 4px);\n background: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface);\n font: inherit;\n }\n h3 {\n margin: 0;\n font-size: var(--ui-t-title, 16px);\n font-weight: 600;\n }\n ul.alerts {\n margin: 0;\n padding-left: 20px;\n color: var(--md-sys-color-error);\n }\n .muted {\n color: var(--md-sys-color-on-surface-variant);\n }\n .scroll {\n overflow-x: auto;\n }\n table {\n border-collapse: collapse;\n white-space: nowrap;\n }\n th,\n td {\n padding: 6px 8px;\n border-bottom: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));\n text-align: right;\n }\n th:first-child,\n td:first-child {\n position: sticky;\n left: 0;\n text-align: left;\n background: var(--md-sys-color-surface);\n }\n th {\n color: var(--md-sys-color-on-surface-variant);\n font-weight: 500;\n }\n tr.account td:first-child button {\n border: none;\n background: none;\n color: var(--md-sys-color-primary);\n font: inherit;\n cursor: pointer;\n padding: 0;\n }\n tr.sub td:first-child {\n padding-left: 24px;\n color: var(--md-sys-color-on-surface-variant);\n }\n .up {\n color: var(--md-sys-color-error);\n }\n `\n ]\n\n @state() from = shiftMonth(thisMonth(), -12)\n @state() to = shiftMonth(thisMonth(), -1)\n @state() trend?: any\n\n get context() {\n return { title: i18next.t('title.saas-cost-trend') }\n }\n\n async pageUpdated(changes: any) {\n if (this.active && (changes.active || !this.trend)) await this.read()\n }\n\n private async read() {\n const response = await client.query({\n query: gql`\n query ($from: String!, $to: String!) {\n saasCostTrend(from: $from, to: $to) {\n months\n all { ${SERIES} }\n accounts { ${SERIES} }\n subscriptions { ${SERIES} }\n departments { key departmentId name currency amounts total }\n alerts { kind accountId accountName subscriptionName month amount limit }\n }\n }\n `,\n variables: { from: this.from, to: this.to },\n fetchPolicy: 'network-only'\n })\n this.trend = response.data?.saasCostTrend\n }\n\n private change(ratio?: number | null) {\n return html`<span class=${ratio && ratio > 0 ? 'up' : ''}>${changeText(ratio)}</span>`\n }\n\n private row(label: unknown, one: any, kind = '') {\n return html`<tr class=${kind}>\n <td>${label}</td>\n <td>${one.currency}</td>\n ${one.amounts.map((amount: number) => html`<td>${amount ? amountText(amount) : ''}</td>`)}\n <td>${amountText(one.total)}</td>\n ${one.monthOverMonth !== undefined ? html`<td>${this.change(one.monthOverMonth)}</td><td>${this.change(one.yearOverYear)}</td>` : html`<td></td><td></td>`}\n </tr>`\n }\n\n private head(first: string) {\n return html`<tr>\n <th>${first}</th>\n <th>${i18next.t('field.saas-trend-currency')}</th>\n ${this.trend.months.map((month: string) => html`<th>${month}</th>`)}\n <th>${i18next.t('field.saas-trend-total')}</th>\n <th>${i18next.t('field.saas-trend-month-over-month')}</th>\n <th>${i18next.t('field.saas-trend-year-over-year')}</th>\n </tr>`\n }\n\n render() {\n const trend = this.trend\n return html`\n <div class=\"range\">\n <input type=\"month\" .value=${this.from} @change=${(e: any) => ((this.from = e.target.value), this.read())} />\n ~\n <input type=\"month\" .value=${this.to} @change=${(e: any) => ((this.to = e.target.value), this.read())} />\n </div>\n ${trend\n ? html`\n ${trend.alerts.length\n ? html`<h3>${i18next.t('text.saas-trend-alerts', { month: trend.alerts[0].month })}</h3>\n <ul class=\"alerts\">\n ${trend.alerts.map(\n (alert: any) =>\n html`<li>\n ${i18next.t(`text.saas-alert-${alert.kind}`, {\n account: alert.accountName,\n subscription: alert.subscriptionName ?? '',\n amount: amountText(alert.amount),\n limit: amountText(alert.limit)\n })}\n </li>`\n )}\n </ul>`\n : nothing}\n ${trend.all.length\n ? html`\n <h3>${i18next.t('text.saas-trend-accounts')}</h3>\n <div class=\"scroll\">\n <table>\n ${this.head(i18next.t('field.saas-trend-account'))}\n ${trend.all.map((one: any) => this.row(i18next.t('text.saas-trend-all'), one))}\n ${trend.accounts.map(\n (account: any) => html`\n ${this.row(html`<button @click=${() => navigate(`saas-account-page?id=${account.accountId}&tab=bills`)}>${account.name}</button>`, account, 'account')}\n ${trend.subscriptions\n .filter((sub: any) => sub.accountId === account.accountId && sub.currency === account.currency)\n .map((sub: any) => this.row(sub.name ?? i18next.t('text.saas-trend-rest'), sub, 'sub'))}\n `\n )}\n </table>\n </div>\n <h3>${i18next.t('text.saas-trend-departments')}</h3>\n <div class=\"scroll\">\n <table>\n ${this.head(i18next.t('field.saas-trend-department'))}\n ${trend.departments.map((one: any) => this.row(one.name ?? i18next.t('text.saas-trend-no-department'), one))}\n </table>\n </div>\n `\n : html`<div class=\"muted\">${i18next.t('text.saas-trend-empty')}</div>`}\n `\n : nothing}\n `\n }\n}\n"]}
@@ -1,42 +0,0 @@
1
- import { User } from '@things-factory/auth-base';
2
- import { Domain } from '@things-factory/shell';
3
- import { SaasAccount } from './saas-account';
4
- import { SaasSubscription } from './saas-subscription';
5
- /**
6
- * What an account was actually billed for a month (ADR-0177 decisions 5, 9) - one a month, in the account's currency,
7
- * one line a subscription and lines on none for tax and credits. Its total is the sum of its lines; the cost trend is
8
- * computed from these.
9
- */
10
- export declare class SaasBill {
11
- readonly id: string;
12
- domain?: Domain;
13
- domainId?: string;
14
- account?: SaasAccount;
15
- accountId?: string;
16
- month: string;
17
- billedOn?: string;
18
- currency: string;
19
- synced: boolean;
20
- lines?: SaasBillLine[];
21
- total?: number;
22
- description?: string;
23
- createdAt?: Date;
24
- updatedAt?: Date;
25
- deletedAt?: Date;
26
- creator?: User;
27
- creatorId?: string;
28
- updater?: User;
29
- updaterId?: string;
30
- }
31
- /** One line of a bill - what one subscription cost that month, or tax and credits on none. */
32
- export declare class SaasBillLine {
33
- readonly id: string;
34
- domain?: Domain;
35
- domainId?: string;
36
- bill?: SaasBill;
37
- billId?: string;
38
- subscription?: SaasSubscription;
39
- subscriptionId?: string;
40
- label?: string;
41
- amount: number;
42
- }
@@ -1,165 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SaasBillLine = exports.SaasBill = void 0;
4
- const tslib_1 = require("tslib");
5
- const auth_base_1 = require("@things-factory/auth-base");
6
- const shell_1 = require("@things-factory/shell");
7
- const type_graphql_1 = require("type-graphql");
8
- const typeorm_1 = require("typeorm");
9
- const saas_account_1 = require("./saas-account");
10
- const saas_subscription_1 = require("./saas-subscription");
11
- /**
12
- * What an account was actually billed for a month (ADR-0177 decisions 5, 9) - one a month, in the account's currency,
13
- * one line a subscription and lines on none for tax and credits. Its total is the sum of its lines; the cost trend is
14
- * computed from these.
15
- */
16
- let SaasBill = class SaasBill {
17
- };
18
- exports.SaasBill = SaasBill;
19
- tslib_1.__decorate([
20
- (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
21
- (0, type_graphql_1.Field)(type => type_graphql_1.ID, { description: 'Unique identifier of the bill' }),
22
- tslib_1.__metadata("design:type", String)
23
- ], SaasBill.prototype, "id", void 0);
24
- tslib_1.__decorate([
25
- (0, typeorm_1.ManyToOne)(type => shell_1.Domain),
26
- (0, type_graphql_1.Field)(type => shell_1.Domain, { nullable: true, description: 'The domain the bill belongs to' }),
27
- tslib_1.__metadata("design:type", shell_1.Domain)
28
- ], SaasBill.prototype, "domain", void 0);
29
- tslib_1.__decorate([
30
- (0, typeorm_1.RelationId)((bill) => bill.domain),
31
- tslib_1.__metadata("design:type", String)
32
- ], SaasBill.prototype, "domainId", void 0);
33
- tslib_1.__decorate([
34
- (0, typeorm_1.ManyToOne)(type => saas_account_1.SaasAccount, { onDelete: 'CASCADE' }),
35
- (0, type_graphql_1.Field)(type => saas_account_1.SaasAccount, { nullable: true, description: 'The account billed' }),
36
- tslib_1.__metadata("design:type", saas_account_1.SaasAccount)
37
- ], SaasBill.prototype, "account", void 0);
38
- tslib_1.__decorate([
39
- (0, typeorm_1.RelationId)((bill) => bill.account),
40
- tslib_1.__metadata("design:type", String)
41
- ], SaasBill.prototype, "accountId", void 0);
42
- tslib_1.__decorate([
43
- (0, typeorm_1.Column)(),
44
- (0, type_graphql_1.Field)({ description: 'The month it bills - YYYY-MM' }),
45
- tslib_1.__metadata("design:type", String)
46
- ], SaasBill.prototype, "month", void 0);
47
- tslib_1.__decorate([
48
- (0, typeorm_1.Column)({ nullable: true }),
49
- (0, type_graphql_1.Field)({ nullable: true, description: 'The day the vendor billed it - YYYY-MM-DD' }),
50
- tslib_1.__metadata("design:type", String)
51
- ], SaasBill.prototype, "billedOn", void 0);
52
- tslib_1.__decorate([
53
- (0, typeorm_1.Column)({ default: 'KRW' }),
54
- (0, type_graphql_1.Field)({ description: 'The currency it is in - the account’s when it was written' }),
55
- tslib_1.__metadata("design:type", String)
56
- ], SaasBill.prototype, "currency", void 0);
57
- tslib_1.__decorate([
58
- (0, typeorm_1.Column)({ default: false }),
59
- (0, type_graphql_1.Field)({ description: "Written by a sync from the vendor's cost report - a later sync rewrites it; a bill a person wrote is never rewritten" }),
60
- tslib_1.__metadata("design:type", Boolean)
61
- ], SaasBill.prototype, "synced", void 0);
62
- tslib_1.__decorate([
63
- (0, typeorm_1.OneToMany)(type => SaasBillLine, line => line.bill),
64
- (0, type_graphql_1.Field)(type => [SaasBillLine], { nullable: true, description: 'One line a subscription, and lines on none - tax, credits' }),
65
- tslib_1.__metadata("design:type", Array)
66
- ], SaasBill.prototype, "lines", void 0);
67
- tslib_1.__decorate([
68
- (0, type_graphql_1.Field)(type => type_graphql_1.Float, { nullable: true, description: 'The sum of its lines - filled by the query' }),
69
- tslib_1.__metadata("design:type", Number)
70
- ], SaasBill.prototype, "total", void 0);
71
- tslib_1.__decorate([
72
- (0, typeorm_1.Column)({ type: 'text', nullable: true }),
73
- (0, type_graphql_1.Field)({ nullable: true, description: 'Notes' }),
74
- tslib_1.__metadata("design:type", String)
75
- ], SaasBill.prototype, "description", void 0);
76
- tslib_1.__decorate([
77
- (0, typeorm_1.CreateDateColumn)(),
78
- (0, type_graphql_1.Field)({ nullable: true, description: 'When the bill was written' }),
79
- tslib_1.__metadata("design:type", Date)
80
- ], SaasBill.prototype, "createdAt", void 0);
81
- tslib_1.__decorate([
82
- (0, typeorm_1.UpdateDateColumn)(),
83
- (0, type_graphql_1.Field)({ nullable: true, description: 'When the bill last changed' }),
84
- tslib_1.__metadata("design:type", Date)
85
- ], SaasBill.prototype, "updatedAt", void 0);
86
- tslib_1.__decorate([
87
- (0, typeorm_1.DeleteDateColumn)(),
88
- (0, type_graphql_1.Field)({ nullable: true, description: 'When the bill was deleted' }),
89
- tslib_1.__metadata("design:type", Date)
90
- ], SaasBill.prototype, "deletedAt", void 0);
91
- tslib_1.__decorate([
92
- (0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
93
- (0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true, description: 'Who wrote it' }),
94
- tslib_1.__metadata("design:type", auth_base_1.User)
95
- ], SaasBill.prototype, "creator", void 0);
96
- tslib_1.__decorate([
97
- (0, typeorm_1.RelationId)((bill) => bill.creator),
98
- tslib_1.__metadata("design:type", String)
99
- ], SaasBill.prototype, "creatorId", void 0);
100
- tslib_1.__decorate([
101
- (0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
102
- (0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true, description: 'Who last changed it' }),
103
- tslib_1.__metadata("design:type", auth_base_1.User)
104
- ], SaasBill.prototype, "updater", void 0);
105
- tslib_1.__decorate([
106
- (0, typeorm_1.RelationId)((bill) => bill.updater),
107
- tslib_1.__metadata("design:type", String)
108
- ], SaasBill.prototype, "updaterId", void 0);
109
- exports.SaasBill = SaasBill = tslib_1.__decorate([
110
- (0, typeorm_1.Entity)(),
111
- (0, typeorm_1.Index)('ix_saas_bill_0', (bill) => [bill.domain, bill.account, bill.month]),
112
- (0, type_graphql_1.ObjectType)({ description: 'Entity for SaasBill — what an account was billed for a month' })
113
- ], SaasBill);
114
- /** One line of a bill - what one subscription cost that month, or tax and credits on none. */
115
- let SaasBillLine = class SaasBillLine {
116
- };
117
- exports.SaasBillLine = SaasBillLine;
118
- tslib_1.__decorate([
119
- (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
120
- (0, type_graphql_1.Field)(type => type_graphql_1.ID, { description: 'Unique identifier of the line' }),
121
- tslib_1.__metadata("design:type", String)
122
- ], SaasBillLine.prototype, "id", void 0);
123
- tslib_1.__decorate([
124
- (0, typeorm_1.ManyToOne)(type => shell_1.Domain),
125
- (0, type_graphql_1.Field)(type => shell_1.Domain, { nullable: true, description: 'The domain the line belongs to' }),
126
- tslib_1.__metadata("design:type", shell_1.Domain)
127
- ], SaasBillLine.prototype, "domain", void 0);
128
- tslib_1.__decorate([
129
- (0, typeorm_1.RelationId)((line) => line.domain),
130
- tslib_1.__metadata("design:type", String)
131
- ], SaasBillLine.prototype, "domainId", void 0);
132
- tslib_1.__decorate([
133
- (0, typeorm_1.ManyToOne)(type => SaasBill, bill => bill.lines, { onDelete: 'CASCADE' }),
134
- (0, type_graphql_1.Field)(type => SaasBill, { nullable: true, description: 'The bill it is a line of' }),
135
- tslib_1.__metadata("design:type", SaasBill)
136
- ], SaasBillLine.prototype, "bill", void 0);
137
- tslib_1.__decorate([
138
- (0, typeorm_1.RelationId)((line) => line.bill),
139
- tslib_1.__metadata("design:type", String)
140
- ], SaasBillLine.prototype, "billId", void 0);
141
- tslib_1.__decorate([
142
- (0, typeorm_1.ManyToOne)(type => saas_subscription_1.SaasSubscription, { nullable: true, onDelete: 'SET NULL' }),
143
- (0, type_graphql_1.Field)(type => saas_subscription_1.SaasSubscription, { nullable: true, description: 'The subscription it is for; none for tax and credits' }),
144
- tslib_1.__metadata("design:type", saas_subscription_1.SaasSubscription)
145
- ], SaasBillLine.prototype, "subscription", void 0);
146
- tslib_1.__decorate([
147
- (0, typeorm_1.RelationId)((line) => line.subscription),
148
- tslib_1.__metadata("design:type", String)
149
- ], SaasBillLine.prototype, "subscriptionId", void 0);
150
- tslib_1.__decorate([
151
- (0, typeorm_1.Column)({ nullable: true }),
152
- (0, type_graphql_1.Field)({ nullable: true, description: 'What the bill calls it - tax, a credit, a support fee' }),
153
- tslib_1.__metadata("design:type", String)
154
- ], SaasBillLine.prototype, "label", void 0);
155
- tslib_1.__decorate([
156
- (0, typeorm_1.Column)((0, shell_1.moneyColumn)()),
157
- (0, type_graphql_1.Field)(type => type_graphql_1.Float, { description: 'What it cost; a credit is below zero' }),
158
- tslib_1.__metadata("design:type", Number)
159
- ], SaasBillLine.prototype, "amount", void 0);
160
- exports.SaasBillLine = SaasBillLine = tslib_1.__decorate([
161
- (0, typeorm_1.Entity)(),
162
- (0, typeorm_1.Index)('ix_saas_bill_line_0', (line) => [line.domain, line.bill]),
163
- (0, type_graphql_1.ObjectType)({ description: 'Entity for SaasBillLine — what one subscription cost in a bill, or tax and credits' })
164
- ], SaasBillLine);
165
- //# sourceMappingURL=saas-bill.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"saas-bill.js","sourceRoot":"","sources":["../../server/service/saas-bill.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '@things-factory/auth-base'\nimport { Domain, moneyColumn } from '@things-factory/shell'\nimport { Field, Float, ID, ObjectType } from 'type-graphql'\nimport { Column, CreateDateColumn, DeleteDateColumn, Entity, Index, ManyToOne, OneToMany, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\n\nimport { SaasAccount } from './saas-account'\nimport { SaasSubscription } from './saas-subscription'\n\n/**\n * What an account was actually billed for a month (ADR-0177 decisions 5, 9) - one a month, in the account's currency,\n * one line a subscription and lines on none for tax and credits. Its total is the sum of its lines; the cost trend is\n * computed from these.\n */\n@Entity()\n@Index('ix_saas_bill_0', (bill: SaasBill) => [bill.domain, bill.account, bill.month])\n@ObjectType({ description: 'Entity for SaasBill — what an account was billed for a month' })\nexport class SaasBill {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier of the bill' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain the bill belongs to' })\n domain?: Domain\n\n @RelationId((bill: SaasBill) => bill.domain)\n domainId?: string\n\n @ManyToOne(type => SaasAccount, { onDelete: 'CASCADE' })\n @Field(type => SaasAccount, { nullable: true, description: 'The account billed' })\n account?: SaasAccount\n\n @RelationId((bill: SaasBill) => bill.account)\n accountId?: string\n\n @Column()\n @Field({ description: 'The month it bills - YYYY-MM' })\n month: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The day the vendor billed it - YYYY-MM-DD' })\n billedOn?: string\n\n @Column({ default: 'KRW' })\n @Field({ description: 'The currency it is in - the account’s when it was written' })\n currency: string\n\n @Column({ default: false })\n @Field({ description: \"Written by a sync from the vendor's cost report - a later sync rewrites it; a bill a person wrote is never rewritten\" })\n synced: boolean\n\n @OneToMany(type => SaasBillLine, line => line.bill)\n @Field(type => [SaasBillLine], { nullable: true, description: 'One line a subscription, and lines on none - tax, credits' })\n lines?: SaasBillLine[]\n\n @Field(type => Float, { nullable: true, description: 'The sum of its lines - filled by the query' })\n total?: number\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 bill was written' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When the bill last changed' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'When the bill was deleted' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who wrote it' })\n creator?: User\n\n @RelationId((bill: SaasBill) => bill.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((bill: SaasBill) => bill.updater)\n updaterId?: string\n}\n\n/** One line of a bill - what one subscription cost that month, or tax and credits on none. */\n@Entity()\n@Index('ix_saas_bill_line_0', (line: SaasBillLine) => [line.domain, line.bill])\n@ObjectType({ description: 'Entity for SaasBillLine — what one subscription cost in a bill, or tax and credits' })\nexport class SaasBillLine {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier of the line' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain the line belongs to' })\n domain?: Domain\n\n @RelationId((line: SaasBillLine) => line.domain)\n domainId?: string\n\n @ManyToOne(type => SaasBill, bill => bill.lines, { onDelete: 'CASCADE' })\n @Field(type => SaasBill, { nullable: true, description: 'The bill it is a line of' })\n bill?: SaasBill\n\n @RelationId((line: SaasBillLine) => line.bill)\n billId?: string\n\n @ManyToOne(type => SaasSubscription, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => SaasSubscription, { nullable: true, description: 'The subscription it is for; none for tax and credits' })\n subscription?: SaasSubscription\n\n @RelationId((line: SaasBillLine) => line.subscription)\n subscriptionId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'What the bill calls it - tax, a credit, a support fee' })\n label?: string\n\n @Column(moneyColumn())\n @Field(type => Float, { description: 'What it cost; a credit is below zero' })\n amount: number\n}\n"]}
@@ -1,19 +0,0 @@
1
- import { User } from '@things-factory/auth-base';
2
- import { Counterparty } from '@things-factory/biz-base';
3
- import { Domain } from '@things-factory/shell';
4
- /**
5
- * Whom a keeper said is no subscription - paid month after month but a cleaner, an accountant, a landlord - so the books
6
- * stop offering it as one (ADR-0177 개정 1): a counterparty, or the name written on lines no counterparty is linked to.
7
- * Shown again, the row goes.
8
- */
9
- export declare class SaasHiddenVendor {
10
- readonly id: string;
11
- domain?: Domain;
12
- domainId?: string;
13
- counterparty?: Counterparty;
14
- counterpartyId?: string;
15
- /** The written name's key (biz-base `nameKey`), for lines no counterparty is linked to. */
16
- nameKey?: string;
17
- createdAt?: Date;
18
- creator?: User;
19
- }
@@ -1,53 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SaasHiddenVendor = void 0;
4
- const tslib_1 = require("tslib");
5
- const auth_base_1 = require("@things-factory/auth-base");
6
- const biz_base_1 = require("@things-factory/biz-base");
7
- const shell_1 = require("@things-factory/shell");
8
- const typeorm_1 = require("typeorm");
9
- /**
10
- * Whom a keeper said is no subscription - paid month after month but a cleaner, an accountant, a landlord - so the books
11
- * stop offering it as one (ADR-0177 개정 1): a counterparty, or the name written on lines no counterparty is linked to.
12
- * Shown again, the row goes.
13
- */
14
- let SaasHiddenVendor = class SaasHiddenVendor {
15
- };
16
- exports.SaasHiddenVendor = SaasHiddenVendor;
17
- tslib_1.__decorate([
18
- (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
19
- tslib_1.__metadata("design:type", String)
20
- ], SaasHiddenVendor.prototype, "id", void 0);
21
- tslib_1.__decorate([
22
- (0, typeorm_1.ManyToOne)(type => shell_1.Domain),
23
- tslib_1.__metadata("design:type", shell_1.Domain)
24
- ], SaasHiddenVendor.prototype, "domain", void 0);
25
- tslib_1.__decorate([
26
- (0, typeorm_1.RelationId)((row) => row.domain),
27
- tslib_1.__metadata("design:type", String)
28
- ], SaasHiddenVendor.prototype, "domainId", void 0);
29
- tslib_1.__decorate([
30
- (0, typeorm_1.ManyToOne)(type => biz_base_1.Counterparty, { nullable: true, onDelete: 'CASCADE' }),
31
- tslib_1.__metadata("design:type", biz_base_1.Counterparty)
32
- ], SaasHiddenVendor.prototype, "counterparty", void 0);
33
- tslib_1.__decorate([
34
- (0, typeorm_1.RelationId)((row) => row.counterparty),
35
- tslib_1.__metadata("design:type", String)
36
- ], SaasHiddenVendor.prototype, "counterpartyId", void 0);
37
- tslib_1.__decorate([
38
- (0, typeorm_1.Column)({ nullable: true }),
39
- tslib_1.__metadata("design:type", String)
40
- ], SaasHiddenVendor.prototype, "nameKey", void 0);
41
- tslib_1.__decorate([
42
- (0, typeorm_1.CreateDateColumn)(),
43
- tslib_1.__metadata("design:type", Date)
44
- ], SaasHiddenVendor.prototype, "createdAt", void 0);
45
- tslib_1.__decorate([
46
- (0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
47
- tslib_1.__metadata("design:type", auth_base_1.User)
48
- ], SaasHiddenVendor.prototype, "creator", void 0);
49
- exports.SaasHiddenVendor = SaasHiddenVendor = tslib_1.__decorate([
50
- (0, typeorm_1.Entity)(),
51
- (0, typeorm_1.Index)('ix_saas_hidden_vendor_0', (row) => [row.domain, row.counterparty, row.nameKey])
52
- ], SaasHiddenVendor);
53
- //# sourceMappingURL=saas-hidden-vendor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"saas-hidden-vendor.js","sourceRoot":"","sources":["../../server/service/saas-hidden-vendor.ts"],"names":[],"mappings":";;;;AAAA,yDAAgD;AAChD,uDAAuD;AACvD,iDAA8C;AAC9C,qCAAgH;AAEhH;;;;GAIG;AAGI,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;CAyB5B,CAAA;AAzBY,4CAAgB;AAElB;IADR,IAAA,gCAAsB,EAAC,MAAM,CAAC;;4CACZ;AAGnB;IADC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;sCACjB,cAAM;gDAAA;AAGf;IADC,IAAA,oBAAU,EAAC,CAAC,GAAqB,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;;kDACjC;AAGjB;IADC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,uBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;sCAC1D,uBAAY;sDAAA;AAG3B;IADC,IAAA,oBAAU,EAAC,CAAC,GAAqB,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC;;wDACjC;AAIvB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDACX;AAGhB;IADC,IAAA,0BAAgB,GAAE;sCACP,IAAI;mDAAA;AAGhB;IADC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCAClC,gBAAI;iDAAA;2BAxBH,gBAAgB;IAF5B,IAAA,gBAAM,GAAE;IACR,IAAA,eAAK,EAAC,yBAAyB,EAAE,CAAC,GAAqB,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;GAC5F,gBAAgB,CAyB5B","sourcesContent":["import { User } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\nimport { Domain } from '@things-factory/shell'\nimport { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId } from 'typeorm'\n\n/**\n * Whom a keeper said is no subscription - paid month after month but a cleaner, an accountant, a landlord - so the books\n * stop offering it as one (ADR-0177 개정 1): a counterparty, or the name written on lines no counterparty is linked to.\n * Shown again, the row goes.\n */\n@Entity()\n@Index('ix_saas_hidden_vendor_0', (row: SaasHiddenVendor) => [row.domain, row.counterparty, row.nameKey])\nexport class SaasHiddenVendor {\n @PrimaryGeneratedColumn('uuid')\n readonly id: string\n\n @ManyToOne(type => Domain)\n domain?: Domain\n\n @RelationId((row: SaasHiddenVendor) => row.domain)\n domainId?: string\n\n @ManyToOne(type => Counterparty, { nullable: true, onDelete: 'CASCADE' })\n counterparty?: Counterparty\n\n @RelationId((row: SaasHiddenVendor) => row.counterparty)\n counterpartyId?: string\n\n /** The written name's key (biz-base `nameKey`), for lines no counterparty is linked to. */\n @Column({ nullable: true })\n nameKey?: string\n\n @CreateDateColumn()\n createdAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n creator?: User\n}\n"]}
@@ -1,9 +0,0 @@
1
- /** A row's last twelve months and the months to come, for a list's mini chart and a 360 (ADR-0177 decision 10). */
2
- export declare class SaasTrend {
3
- months: string[];
4
- values: (number | null)[];
5
- nextMonths: string[];
6
- expected: (number | null)[];
7
- sources: (string | null)[];
8
- currency: string;
9
- }
@@ -1,37 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SaasTrend = void 0;
4
- const tslib_1 = require("tslib");
5
- const type_graphql_1 = require("type-graphql");
6
- /** A row's last twelve months and the months to come, for a list's mini chart and a 360 (ADR-0177 decision 10). */
7
- let SaasTrend = class SaasTrend {
8
- };
9
- exports.SaasTrend = SaasTrend;
10
- tslib_1.__decorate([
11
- (0, type_graphql_1.Field)(type => [String], { description: 'The last twelve months, oldest first - YYYY-MM' }),
12
- tslib_1.__metadata("design:type", Array)
13
- ], SaasTrend.prototype, "months", void 0);
14
- tslib_1.__decorate([
15
- (0, type_graphql_1.Field)(type => [type_graphql_1.Float], { nullable: 'items', description: 'What was billed each of them; empty for a month with no bill' }),
16
- tslib_1.__metadata("design:type", Array)
17
- ], SaasTrend.prototype, "values", void 0);
18
- tslib_1.__decorate([
19
- (0, type_graphql_1.Field)(type => [String], { description: 'The months to come - YYYY-MM' }),
20
- tslib_1.__metadata("design:type", Array)
21
- ], SaasTrend.prototype, "nextMonths", void 0);
22
- tslib_1.__decorate([
23
- (0, type_graphql_1.Field)(type => [type_graphql_1.Float], { nullable: 'items', description: 'What is expected each month to come; empty when nothing falls in it' }),
24
- tslib_1.__metadata("design:type", Array)
25
- ], SaasTrend.prototype, "expected", void 0);
26
- tslib_1.__decorate([
27
- (0, type_graphql_1.Field)(type => [String], { nullable: 'items', description: "Where each past month's amount came from - bill, bank, or empty" }),
28
- tslib_1.__metadata("design:type", Array)
29
- ], SaasTrend.prototype, "sources", void 0);
30
- tslib_1.__decorate([
31
- (0, type_graphql_1.Field)({ description: 'The currency of the amounts' }),
32
- tslib_1.__metadata("design:type", String)
33
- ], SaasTrend.prototype, "currency", void 0);
34
- exports.SaasTrend = SaasTrend = tslib_1.__decorate([
35
- (0, type_graphql_1.ObjectType)({ description: 'An account’s months - what was billed in the last twelve, what is expected in the next three' })
36
- ], SaasTrend);
37
- //# sourceMappingURL=saas-trend-type.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"saas-trend-type.js","sourceRoot":"","sources":["../../server/service/saas-trend-type.ts"],"names":[],"mappings":";;;;AAAA,+CAAuD;AAEvD,mHAAmH;AAE5G,IAAM,SAAS,GAAf,MAAM,SAAS;CAOrB,CAAA;AAPY,8BAAS;AACwE;IAA3F,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;;yCAAiB;AACgB;IAA3H,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,oBAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAAC;;yCAA0B;AAC3E;IAAzE,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;;6CAAqB;AACqC;IAAlI,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,oBAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,qEAAqE,EAAE,CAAC;;2CAA4B;AAC9B;IAA/H,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,iEAAiE,EAAE,CAAC;;0CAA2B;AACnG;IAAtD,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;;2CAAiB;oBAN5D,SAAS;IADrB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,8FAA8F,EAAE,CAAC;GAC/G,SAAS,CAOrB","sourcesContent":["import { Field, Float, ObjectType } from 'type-graphql'\n\n/** A row's last twelve months and the months to come, for a list's mini chart and a 360 (ADR-0177 decision 10). */\n@ObjectType({ description: 'An account’s months - what was billed in the last twelve, what is expected in the next three' })\nexport class SaasTrend {\n @Field(type => [String], { description: 'The last twelve months, oldest first - YYYY-MM' }) months: string[]\n @Field(type => [Float], { nullable: 'items', description: 'What was billed each of them; empty for a month with no bill' }) values: (number | null)[]\n @Field(type => [String], { description: 'The months to come - YYYY-MM' }) nextMonths: string[]\n @Field(type => [Float], { nullable: 'items', description: 'What is expected each month to come; empty when nothing falls in it' }) expected: (number | null)[]\n @Field(type => [String], { nullable: 'items', description: \"Where each past month's amount came from - bill, bank, or empty\" }) sources: (string | null)[]\n @Field({ description: 'The currency of the amounts' }) currency: string\n}\n"]}