@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
package/README.md CHANGED
@@ -3,18 +3,18 @@
3
3
  What the company subscribes to online and pays for again and again — not only SaaS: cloud (AWS, Azure, GCP), domains and
4
4
  certificates too — and the software licenses it owns (ADR-0177 in operato-application).
5
5
 
6
- - **Account** — one account we hold with a vendor, the unit a bill comes from (an AWS organization's payer account, a
7
- Microsoft 365 tenant, an Adobe Admin Console, a registrar). An account under another (an AWS member account) names its
8
- purpose and admins only; the money is the parent's. No password, key or recovery code is kept — only where it is kept.
6
+ - **Account** — one account we hold with a vendor (an AWS organization's payer account, a Microsoft 365 tenant, an Adobe
7
+ Admin Console, a registrar). An account under another (an AWS member account) names its purpose and admins only. No
8
+ password, key or recovery code is kept — only where it is kept.
9
9
  - **Subscription** — one thing under an account we decide on and can cancel: a plan with seats, a fixed fee, metered
10
- usage with a monthly budget, a commitment, or a license we own (which points at the asset register and carries no
11
- amount). What metered usage is made of (EC2, tokens) is the vendor's bill, not a row here.
12
- - **Seat** — who holds one place of a subscription.
13
- - **Bill** — what an account was actually billed for a month, one line per subscription. The cost trend is computed from
14
- the bills, never stored.
10
+ usage, a commitment, or a license we own (which points at the asset register). Its quantity and unit price are a
11
+ reference only.
12
+ - **Seat** — who holds one place of a subscription. A Microsoft 365 or Google Workspace tenant keeps them in step
13
+ (`syncSaasLicenses`).
15
14
 
16
- A subscription's money still to come is read by the application from these rows (`saasExpected`), on the day rule the
17
- recurring cash items use (`@things-factory/accounting` `recurring-schedule`).
15
+ No money is kept here (ADR-0177 amendment 7): what a subscription costs month by month, and what is still to come, is a
16
+ recurring cash item's (`@things-factory/accounting`), compared by a person with the reference the quantities and unit
17
+ prices make.
18
18
 
19
19
  Who may do what: `saas.keeping` keeps everything, `saas.reading` reads it, and an account's admins — whatever their
20
- seat — keep their own accounts' subscriptions, seats and bills (`saas.administering`).
20
+ seat — keep their own accounts' subscriptions and seats (`saas.administering`).
@@ -5,7 +5,6 @@ export declare class SaasAccount360 extends LitElement {
5
5
  static styles: import("lit").CSSResult;
6
6
  subject?: Entity360Subject;
7
7
  answer?: Entity360DoorAnswer<any>;
8
- payments?: any[] | null;
9
8
  updated(changed: Map<string, unknown>): void;
10
9
  private read;
11
10
  render(): import("lit-html").TemplateResult<1>;
@@ -8,9 +8,10 @@ import { entity360Name, readEntity360Door, registerEntity360Kind, registerEntity
8
8
  import { navigate } from '@operato/shell';
9
9
  import { amountText, wordOf } from '../words.js';
10
10
  /*
11
- * A subscription account's 360 (ADR-0177 decision 11) - one account read deep: what it is and how it is paid, its last
12
- * twelve months against the next three expected, its bills month by month, what is subscribed under it and what moved on
13
- * it. Reading first; the account's screen opens from a quiet button beside the heading.
11
+ * A subscription account's 360 (ADR-0177 decision 11) - one account read deep: what it is and how it is paid, what is
12
+ * subscribed under it with the reference its quantities and unit prices make, and what moved on it. No months and no
13
+ * bank lines - the money is a recurring item's (개정 7). Reading first; the account's screen opens from a quiet button
14
+ * beside the heading.
14
15
  */
15
16
  export function registerSaasAccount360() {
16
17
  registerEntity360Kind({
@@ -26,12 +27,10 @@ export function registerSaasAccount360() {
26
27
  const ACCOUNT = gql `
27
28
  query ($id: String!) {
28
29
  saasAccount(id: $id) {
29
- id name identifier purpose currency billingDay status closedOn
30
+ id name identifier purpose currency status closedOn
30
31
  counterparty { id name } paymentAccount { id name } parent { id name } admins { id name }
31
- trend { months values sources nextMonths expected currency }
32
32
  }
33
- saasSubscriptions(accountId: $id) { id name billing quantity unitPrice amount budget everyMonths renewalOn autoRenew status seatsHeld }
34
- saasBills(accountId: $id) { id month billedOn total lines { subscription { id name } label amount } }
33
+ saasSubscriptions(accountId: $id) { id name billing quantity unitPrice referenceAmount renewalOn autoRenew status seatsHeld }
35
34
  saasChanges(accountId: $id) { subject field fromValue toValue at changedBy }
36
35
  }
37
36
  `;
@@ -75,13 +74,6 @@ let SaasAccount360 = class SaasAccount360 extends LitElement {
75
74
  td:first-child {
76
75
  text-align: left;
77
76
  }
78
- td.expected {
79
- color: var(--ui-muted);
80
- font-style: italic;
81
- }
82
- ox-grist-sparkline-renderer {
83
- height: 48px;
84
- }
85
77
  .quiet {
86
78
  border: none;
87
79
  background: none;
@@ -110,21 +102,6 @@ let SaasAccount360 = class SaasAccount360 extends LitElement {
110
102
  if (this.subject?.id !== id)
111
103
  return;
112
104
  this.answer = readEntity360Door(response, data => (data?.saasAccount ? data : null));
113
- this.payments = undefined;
114
- try {
115
- const paid = await client.query({
116
- query: gql `query ($id: String!) { saasPayments(accountId: $id) { id transactionDate amount currency description } }`,
117
- variables: { id },
118
- fetchPolicy: 'no-cache',
119
- errorPolicy: 'all'
120
- });
121
- if (this.subject?.id === id)
122
- this.payments = paid.errors?.length ? null : paid.data?.saasPayments ?? [];
123
- }
124
- catch {
125
- if (this.subject?.id === id)
126
- this.payments = null;
127
- }
128
105
  }
129
106
  render() {
130
107
  const state = this.answer?.state ?? 'loading';
@@ -133,9 +110,9 @@ let SaasAccount360 = class SaasAccount360 extends LitElement {
133
110
  const section = (heading, body, action) => html `<ox-entity360-section .heading=${heading} .state=${state} .message=${this.answer?.message ?? ''}>${action ? html `<span slot="actions">${action}</span>` : nothing}${one ? body : nothing}</ox-entity360-section>`;
134
111
  if (!one)
135
112
  return section(i18next.t('title.saas-account-page'), nothing);
136
- const trend = one.trend;
137
113
  const subs = data.saasSubscriptions ?? [];
138
- const bills = data.saasBills ?? [];
114
+ const live = subs.filter(sub => sub.status !== 'CANCELLED');
115
+ const reference = live.map(sub => sub.referenceAmount).filter((value) => value !== null && value !== undefined);
139
116
  const changes = data.saasChanges ?? [];
140
117
  const nextRenewal = subs
141
118
  .filter(sub => sub.status === 'ACTIVE' && sub.renewalOn)
@@ -150,7 +127,7 @@ let SaasAccount360 = class SaasAccount360 extends LitElement {
150
127
  <dt>${i18next.t('field.saas-account-payment-account')}</dt>
151
128
  <dd>${one.paymentAccount?.name ?? '—'}</dd>
152
129
  <dt>${i18next.t('field.saas-account-currency')}</dt>
153
- <dd>${one.currency}${one.billingDay ? ` · ${i18next.t('text.saas-account-billing-day', { day: one.billingDay })}` : ''}</dd>
130
+ <dd>${one.currency}</dd>
154
131
  <dt>${i18next.t('text.saas-360-next-renewal')}</dt>
155
132
  <dd>${nextRenewal ?? '—'}</dd>
156
133
  <dt>${i18next.t('field.saas-account-admins')}</dt>
@@ -158,46 +135,20 @@ let SaasAccount360 = class SaasAccount360 extends LitElement {
158
135
  <dt>${i18next.t('field.saas-account-status')}</dt>
159
136
  <dd>${wordOf('saas-account-status', one.status)}${one.closedOn ? ` · ${one.closedOn}` : ''}</dd>
160
137
  </dl>`, open)}
161
- ${trend
162
- ? section(i18next.t('text.saas-360-months'), html `${[...trend.values, ...trend.expected].some((value) => value !== null)
163
- ? html `<ox-grist-sparkline-renderer .value=${trend} .column=${{ type: 'sparkline', name: 'trend', record: {} }}></ox-grist-sparkline-renderer>`
164
- : nothing}
165
- <table>
166
- <tr><th>${i18next.t('field.saas-bill-month')}</th><th>${trend.currency}</th></tr>
167
- ${trend.months.map((month, i) => html `<tr>
168
- <td>${month} ${trend.sources?.[i] ? html `<span class="muted">${i18next.t(`text.saas-360-source-${trend.sources[i]}`)}</span>` : nothing}</td>
169
- <td>${trend.values[i] === null ? '—' : amountText(trend.values[i])}</td>
170
- </tr>`)}
171
- ${trend.nextMonths.map((month, i) => html `<tr><td>${month}</td><td class="expected">${trend.expected[i] === null ? '' : amountText(trend.expected[i])}</td></tr>`)}
172
- </table>`)
173
- : nothing}
174
- ${this.payments
175
- ? section(i18next.t('text.saas-360-payments', { vendor: one.counterparty?.name ?? '' }), this.payments.length
176
- ? html `<table>
177
- ${this.payments.map(line => html `<tr>
178
- <td>${entity360Name({ __typename: 'Transaction', id: line.id, transactionDate: line.transactionDate, direction: 'EXPENSE' }, line.transactionDate)}</td>
179
- <td class="muted">${line.description ?? ''}</td>
180
- <td>${amountText(line.amount)} ${line.currency !== one.currency ? line.currency : ''}</td>
181
- </tr>`)}
182
- </table>`
183
- : html `<div class="muted">${i18next.t(one.counterparty ? 'text.saas-360-payments-empty' : 'text.saas-360-payments-no-vendor')}</div>`)
184
- : nothing}
185
- ${section(i18next.t('text.saas-tab-bills'), bills.length
186
- ? html `<table>
187
- ${bills.map(bill => html `<tr>
188
- <td>${bill.month}${bill.billedOn ? html ` <span class="muted">${bill.billedOn}</span>` : nothing}</td>
189
- <td class="muted">${(bill.lines ?? []).map((line) => `${line.subscription?.name ?? line.label ?? ''} ${amountText(line.amount)}`).join(' · ')}</td>
190
- <td>${amountText(bill.total)}</td>
191
- </tr>`)}
192
- </table>`
193
- : html `<div class="muted">${i18next.t('text.saas-bills-empty')}</div>`)}
194
138
  ${section(i18next.t('text.saas-tab-subscriptions'), subs.length
195
139
  ? html `<table>
196
140
  ${subs.map(sub => html `<tr>
197
141
  <td>${sub.name} <span class="muted">${wordOf('saas-billing', sub.billing)} · ${wordOf('saas-subscription-status', sub.status)}</span></td>
198
- <td class="muted">${sub.billing === 'SEAT' ? i18next.t('text.saas-360-seats', { held: sub.seatsHeld ?? 0, quantity: sub.quantity ?? 0 }) : sub.billing === 'USAGE' ? i18next.t('text.saas-360-budget', { budget: amountText(sub.budget) }) : ''}</td>
199
- <td>${sub.billing === 'SEAT' ? amountText((sub.quantity ?? 0) * (sub.unitPrice ?? 0)) : amountText(sub.amount)}</td>
142
+ <td class="muted">${sub.billing === 'SEAT' ? i18next.t('text.saas-360-seats', { held: sub.seatsHeld ?? 0, quantity: sub.quantity ?? 0 }) : ''}</td>
143
+ <td>${amountText(sub.referenceAmount)}</td>
200
144
  </tr>`)}
145
+ ${reference.length
146
+ ? html `<tr>
147
+ <td class="muted">${i18next.t('text.saas-360-reference')}</td>
148
+ <td></td>
149
+ <td>${amountText(reference.reduce((sum, value) => sum + value, 0))} ${one.currency}</td>
150
+ </tr>`
151
+ : nothing}
201
152
  </table>`
202
153
  : html `<div class="muted">${i18next.t('text.saas-subscriptions-empty')}</div>`)}
203
154
  ${section(i18next.t('text.saas-360-changes'), changes.length
@@ -220,10 +171,6 @@ __decorate([
220
171
  state(),
221
172
  __metadata("design:type", Object)
222
173
  ], SaasAccount360.prototype, "answer", void 0);
223
- __decorate([
224
- state(),
225
- __metadata("design:type", Object)
226
- ], SaasAccount360.prototype, "payments", void 0);
227
174
  SaasAccount360 = __decorate([
228
175
  customElement('saas-account-360')
229
176
  ], SaasAccount360);
@@ -1 +1 @@
1
- {"version":3,"file":"saas-account-360.js","sourceRoot":"","sources":["../../client/entity360/saas-account-360.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next } from '@operato/i18n'\nimport { entity360Name, readEntity360Door, registerEntity360Kind, registerEntity360Section, type Entity360DoorAnswer, type Entity360Subject } from '@operato/layout'\nimport { navigate } from '@operato/shell'\n\nimport { amountText, wordOf } from '../words.js'\n\n/*\n * A subscription account's 360 (ADR-0177 decision 11) - one account read deep: what it is and how it is paid, its last\n * twelve months against the next three expected, its bills month by month, what is subscribed under it and what moved on\n * it. Reading first; the account's screen opens from a quiet button beside the heading.\n */\nexport function registerSaasAccount360() {\n registerEntity360Kind({\n kind: 'saas-account',\n typenames: ['SaasAccount'],\n /* Not yet a record the assistant's conversation follows - the module registers no subject describer for it. */\n assistantSubject: false,\n title: ({ value }) => `${i18next.t('title.saas-account-page')} · ${value.name || ''}`,\n context: ({ value }) => [value.identifier, value.currency].filter(Boolean).join(' · ')\n })\n registerEntity360Section('saas-account', { name: 'overview', order: 10, render: subject => html`<saas-account-360 .subject=${subject}></saas-account-360>` })\n}\n\nconst ACCOUNT = gql`\n query ($id: String!) {\n saasAccount(id: $id) {\n id name identifier purpose currency billingDay status closedOn\n counterparty { id name } paymentAccount { id name } parent { id name } admins { id name }\n trend { months values sources nextMonths expected currency }\n }\n saasSubscriptions(accountId: $id) { id name billing quantity unitPrice amount budget everyMonths renewalOn autoRenew status seatsHeld }\n saasBills(accountId: $id) { id month billedOn total lines { subscription { id name } label amount } }\n saasChanges(accountId: $id) { subject field fromValue toValue at changedBy }\n }\n`\n\n@customElement('saas-account-360')\nexport class SaasAccount360 extends LitElement {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-large, 12px);\n font-size: var(--ui-t-body, 13px);\n line-height: 1.5;\n }\n dl {\n margin: 0;\n display: grid;\n grid-template-columns: max-content 1fr;\n gap: var(--spacing-small, 4px) var(--spacing-large, 12px);\n }\n dt,\n th,\n .muted {\n color: var(--ui-muted);\n }\n dd {\n margin: 0;\n overflow-wrap: anywhere;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n font-variant-numeric: tabular-nums;\n }\n th,\n td {\n padding: 4px 6px;\n border-bottom: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n text-align: right;\n font-weight: normal;\n }\n th:first-child,\n td:first-child {\n text-align: left;\n }\n td.expected {\n color: var(--ui-muted);\n font-style: italic;\n }\n ox-grist-sparkline-renderer {\n height: 48px;\n }\n .quiet {\n border: none;\n background: none;\n padding: 0;\n color: var(--md-sys-color-primary);\n font: inherit;\n cursor: pointer;\n }\n `\n\n @property({ attribute: false }) subject?: Entity360Subject\n @state() answer?: Entity360DoorAnswer<any>\n /* What left the bank for the vendor - the bank's door, read apart so a reader without it still sees the rest. */\n @state() payments?: any[] | null\n\n updated(changed: Map<string, unknown>) {\n if (changed.has('subject')) this.read()\n }\n\n private async read() {\n const id = this.subject?.id\n this.answer = undefined\n if (!id) return\n let response: any\n try {\n response = await client.query({ query: ACCOUNT, variables: { id }, fetchPolicy: 'no-cache', errorPolicy: 'all' } as any)\n } catch (e: any) {\n response = { data: null, errors: e?.graphQLErrors?.length ? e.graphQLErrors : [{ message: e?.message ?? String(e) }] }\n }\n if (this.subject?.id !== id) return\n this.answer = readEntity360Door(response, data => (data?.saasAccount ? data : null))\n this.payments = undefined\n try {\n const paid: any = await client.query({\n query: gql`query ($id: String!) { saasPayments(accountId: $id) { id transactionDate amount currency description } }`,\n variables: { id },\n fetchPolicy: 'no-cache',\n errorPolicy: 'all'\n } as any)\n if (this.subject?.id === id) this.payments = paid.errors?.length ? null : paid.data?.saasPayments ?? []\n } catch {\n if (this.subject?.id === id) this.payments = null\n }\n }\n\n render() {\n const state = this.answer?.state ?? 'loading'\n const data = this.answer?.data\n const one = data?.saasAccount\n const section = (heading: string, body: unknown, action?: unknown) =>\n html`<ox-entity360-section .heading=${heading} .state=${state} .message=${this.answer?.message ?? ''}>${action ? html`<span slot=\"actions\">${action}</span>` : nothing}${one ? body : nothing}</ox-entity360-section>`\n if (!one) return section(i18next.t('title.saas-account-page'), nothing)\n\n const trend = one.trend\n const subs: any[] = data.saasSubscriptions ?? []\n const bills: any[] = data.saasBills ?? []\n const changes: any[] = data.saasChanges ?? []\n const nextRenewal = subs\n .filter(sub => sub.status === 'ACTIVE' && sub.renewalOn)\n .map(sub => sub.renewalOn)\n .sort()[0]\n const open = html`<button class=\"quiet\" @click=${() => navigate(`saas-account-page?id=${one.id}`)}>${i18next.t('button.saas-open-account')}</button>`\n const party = (value?: { id: string; name: string }) => (value ? entity360Name({ __typename: 'Counterparty', ...value }, value.name) : '—')\n\n return html`\n ${section(\n one.name,\n html`<dl>\n <dt>${i18next.t('field.saas-account-counterparty')}</dt>\n <dd>${party(one.counterparty)}</dd>\n <dt>${i18next.t('field.saas-account-payment-account')}</dt>\n <dd>${one.paymentAccount?.name ?? '—'}</dd>\n <dt>${i18next.t('field.saas-account-currency')}</dt>\n <dd>${one.currency}${one.billingDay ? ` · ${i18next.t('text.saas-account-billing-day', { day: one.billingDay })}` : ''}</dd>\n <dt>${i18next.t('text.saas-360-next-renewal')}</dt>\n <dd>${nextRenewal ?? '—'}</dd>\n <dt>${i18next.t('field.saas-account-admins')}</dt>\n <dd>${(one.admins ?? []).map((admin: any) => admin.name).join(', ') || '—'}</dd>\n <dt>${i18next.t('field.saas-account-status')}</dt>\n <dd>${wordOf('saas-account-status', one.status)}${one.closedOn ? ` · ${one.closedOn}` : ''}</dd>\n </dl>`,\n open\n )}\n ${trend\n ? section(\n i18next.t('text.saas-360-months'),\n html`${[...trend.values, ...trend.expected].some((value: number | null) => value !== null)\n ? html`<ox-grist-sparkline-renderer .value=${trend} .column=${{ type: 'sparkline', name: 'trend', record: {} }}></ox-grist-sparkline-renderer>`\n : nothing}\n <table>\n <tr><th>${i18next.t('field.saas-bill-month')}</th><th>${trend.currency}</th></tr>\n ${trend.months.map(\n (month: string, i: number) =>\n html`<tr>\n <td>${month} ${trend.sources?.[i] ? html`<span class=\"muted\">${i18next.t(`text.saas-360-source-${trend.sources[i]}`)}</span>` : nothing}</td>\n <td>${trend.values[i] === null ? '—' : amountText(trend.values[i])}</td>\n </tr>`\n )}\n ${trend.nextMonths.map((month: string, i: number) => html`<tr><td>${month}</td><td class=\"expected\">${trend.expected[i] === null ? '' : amountText(trend.expected[i])}</td></tr>`)}\n </table>`\n )\n : nothing}\n ${this.payments\n ? section(\n i18next.t('text.saas-360-payments', { vendor: one.counterparty?.name ?? '' }),\n this.payments.length\n ? html`<table>\n ${this.payments.map(\n line => html`<tr>\n <td>${entity360Name({ __typename: 'Transaction', id: line.id, transactionDate: line.transactionDate, direction: 'EXPENSE' }, line.transactionDate)}</td>\n <td class=\"muted\">${line.description ?? ''}</td>\n <td>${amountText(line.amount)} ${line.currency !== one.currency ? line.currency : ''}</td>\n </tr>`\n )}\n </table>`\n : html`<div class=\"muted\">${i18next.t(one.counterparty ? 'text.saas-360-payments-empty' : 'text.saas-360-payments-no-vendor')}</div>`\n )\n : nothing}\n ${section(\n i18next.t('text.saas-tab-bills'),\n bills.length\n ? html`<table>\n ${bills.map(\n bill => html`<tr>\n <td>${bill.month}${bill.billedOn ? html` <span class=\"muted\">${bill.billedOn}</span>` : nothing}</td>\n <td class=\"muted\">${(bill.lines ?? []).map((line: any) => `${line.subscription?.name ?? line.label ?? ''} ${amountText(line.amount)}`).join(' · ')}</td>\n <td>${amountText(bill.total)}</td>\n </tr>`\n )}\n </table>`\n : html`<div class=\"muted\">${i18next.t('text.saas-bills-empty')}</div>`\n )}\n ${section(\n i18next.t('text.saas-tab-subscriptions'),\n subs.length\n ? html`<table>\n ${subs.map(\n sub => html`<tr>\n <td>${sub.name} <span class=\"muted\">${wordOf('saas-billing', sub.billing)} · ${wordOf('saas-subscription-status', sub.status)}</span></td>\n <td class=\"muted\">${sub.billing === 'SEAT' ? i18next.t('text.saas-360-seats', { held: sub.seatsHeld ?? 0, quantity: sub.quantity ?? 0 }) : sub.billing === 'USAGE' ? i18next.t('text.saas-360-budget', { budget: amountText(sub.budget) }) : ''}</td>\n <td>${sub.billing === 'SEAT' ? amountText((sub.quantity ?? 0) * (sub.unitPrice ?? 0)) : amountText(sub.amount)}</td>\n </tr>`\n )}\n </table>`\n : html`<div class=\"muted\">${i18next.t('text.saas-subscriptions-empty')}</div>`\n )}\n ${section(\n i18next.t('text.saas-360-changes'),\n changes.length\n ? html`<table>\n ${changes.map(\n change => html`<tr>\n <td>${String(change.at).slice(0, 10)} <span class=\"muted\">${change.changedBy ?? ''}</span></td>\n <td>${change.subject} · ${i18next.t(`text.saas-change-field-${change.field}`, { defaultValue: change.field })}</td>\n <td>${change.fromValue ?? '—'} → ${change.toValue ?? '—'}</td>\n </tr>`\n )}\n </table>`\n : html`<div class=\"muted\">${i18next.t('text.saas-360-changes-empty')}</div>`\n )}\n `\n }\n}\n"]}
1
+ {"version":3,"file":"saas-account-360.js","sourceRoot":"","sources":["../../client/entity360/saas-account-360.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next } from '@operato/i18n'\nimport { entity360Name, readEntity360Door, registerEntity360Kind, registerEntity360Section, type Entity360DoorAnswer, type Entity360Subject } from '@operato/layout'\nimport { navigate } from '@operato/shell'\n\nimport { amountText, wordOf } from '../words.js'\n\n/*\n * A subscription account's 360 (ADR-0177 decision 11) - one account read deep: what it is and how it is paid, what is\n * subscribed under it with the reference its quantities and unit prices make, and what moved on it. No months and no\n * bank lines - the money is a recurring item's (개정 7). Reading first; the account's screen opens from a quiet button\n * beside the heading.\n */\nexport function registerSaasAccount360() {\n registerEntity360Kind({\n kind: 'saas-account',\n typenames: ['SaasAccount'],\n /* Not yet a record the assistant's conversation follows - the module registers no subject describer for it. */\n assistantSubject: false,\n title: ({ value }) => `${i18next.t('title.saas-account-page')} · ${value.name || ''}`,\n context: ({ value }) => [value.identifier, value.currency].filter(Boolean).join(' · ')\n })\n registerEntity360Section('saas-account', { name: 'overview', order: 10, render: subject => html`<saas-account-360 .subject=${subject}></saas-account-360>` })\n}\n\nconst ACCOUNT = gql`\n query ($id: String!) {\n saasAccount(id: $id) {\n id name identifier purpose currency status closedOn\n counterparty { id name } paymentAccount { id name } parent { id name } admins { id name }\n }\n saasSubscriptions(accountId: $id) { id name billing quantity unitPrice referenceAmount renewalOn autoRenew status seatsHeld }\n saasChanges(accountId: $id) { subject field fromValue toValue at changedBy }\n }\n`\n\n@customElement('saas-account-360')\nexport class SaasAccount360 extends LitElement {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-large, 12px);\n font-size: var(--ui-t-body, 13px);\n line-height: 1.5;\n }\n dl {\n margin: 0;\n display: grid;\n grid-template-columns: max-content 1fr;\n gap: var(--spacing-small, 4px) var(--spacing-large, 12px);\n }\n dt,\n th,\n .muted {\n color: var(--ui-muted);\n }\n dd {\n margin: 0;\n overflow-wrap: anywhere;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n font-variant-numeric: tabular-nums;\n }\n th,\n td {\n padding: 4px 6px;\n border-bottom: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n text-align: right;\n font-weight: normal;\n }\n th:first-child,\n td:first-child {\n text-align: left;\n }\n .quiet {\n border: none;\n background: none;\n padding: 0;\n color: var(--md-sys-color-primary);\n font: inherit;\n cursor: pointer;\n }\n `\n\n @property({ attribute: false }) subject?: Entity360Subject\n @state() answer?: Entity360DoorAnswer<any>\n\n updated(changed: Map<string, unknown>) {\n if (changed.has('subject')) this.read()\n }\n\n private async read() {\n const id = this.subject?.id\n this.answer = undefined\n if (!id) return\n let response: any\n try {\n response = await client.query({ query: ACCOUNT, variables: { id }, fetchPolicy: 'no-cache', errorPolicy: 'all' } as any)\n } catch (e: any) {\n response = { data: null, errors: e?.graphQLErrors?.length ? e.graphQLErrors : [{ message: e?.message ?? String(e) }] }\n }\n if (this.subject?.id !== id) return\n this.answer = readEntity360Door(response, data => (data?.saasAccount ? data : null))\n }\n\n render() {\n const state = this.answer?.state ?? 'loading'\n const data = this.answer?.data\n const one = data?.saasAccount\n const section = (heading: string, body: unknown, action?: unknown) =>\n html`<ox-entity360-section .heading=${heading} .state=${state} .message=${this.answer?.message ?? ''}>${action ? html`<span slot=\"actions\">${action}</span>` : nothing}${one ? body : nothing}</ox-entity360-section>`\n if (!one) return section(i18next.t('title.saas-account-page'), nothing)\n\n const subs: any[] = data.saasSubscriptions ?? []\n const live = subs.filter(sub => sub.status !== 'CANCELLED')\n const reference = live.map(sub => sub.referenceAmount).filter((value): value is number => value !== null && value !== undefined)\n const changes: any[] = data.saasChanges ?? []\n const nextRenewal = subs\n .filter(sub => sub.status === 'ACTIVE' && sub.renewalOn)\n .map(sub => sub.renewalOn)\n .sort()[0]\n const open = html`<button class=\"quiet\" @click=${() => navigate(`saas-account-page?id=${one.id}`)}>${i18next.t('button.saas-open-account')}</button>`\n const party = (value?: { id: string; name: string }) => (value ? entity360Name({ __typename: 'Counterparty', ...value }, value.name) : '—')\n\n return html`\n ${section(\n one.name,\n html`<dl>\n <dt>${i18next.t('field.saas-account-counterparty')}</dt>\n <dd>${party(one.counterparty)}</dd>\n <dt>${i18next.t('field.saas-account-payment-account')}</dt>\n <dd>${one.paymentAccount?.name ?? '—'}</dd>\n <dt>${i18next.t('field.saas-account-currency')}</dt>\n <dd>${one.currency}</dd>\n <dt>${i18next.t('text.saas-360-next-renewal')}</dt>\n <dd>${nextRenewal ?? '—'}</dd>\n <dt>${i18next.t('field.saas-account-admins')}</dt>\n <dd>${(one.admins ?? []).map((admin: any) => admin.name).join(', ') || '—'}</dd>\n <dt>${i18next.t('field.saas-account-status')}</dt>\n <dd>${wordOf('saas-account-status', one.status)}${one.closedOn ? ` · ${one.closedOn}` : ''}</dd>\n </dl>`,\n open\n )}\n ${section(\n i18next.t('text.saas-tab-subscriptions'),\n subs.length\n ? html`<table>\n ${subs.map(\n sub => html`<tr>\n <td>${sub.name} <span class=\"muted\">${wordOf('saas-billing', sub.billing)} · ${wordOf('saas-subscription-status', sub.status)}</span></td>\n <td class=\"muted\">${sub.billing === 'SEAT' ? i18next.t('text.saas-360-seats', { held: sub.seatsHeld ?? 0, quantity: sub.quantity ?? 0 }) : ''}</td>\n <td>${amountText(sub.referenceAmount)}</td>\n </tr>`\n )}\n ${reference.length\n ? html`<tr>\n <td class=\"muted\">${i18next.t('text.saas-360-reference')}</td>\n <td></td>\n <td>${amountText(reference.reduce((sum, value) => sum + value, 0))} ${one.currency}</td>\n </tr>`\n : nothing}\n </table>`\n : html`<div class=\"muted\">${i18next.t('text.saas-subscriptions-empty')}</div>`\n )}\n ${section(\n i18next.t('text.saas-360-changes'),\n changes.length\n ? html`<table>\n ${changes.map(\n change => html`<tr>\n <td>${String(change.at).slice(0, 10)} <span class=\"muted\">${change.changedBy ?? ''}</span></td>\n <td>${change.subject} · ${i18next.t(`text.saas-change-field-${change.field}`, { defaultValue: change.field })}</td>\n <td>${change.fromValue ?? '—'} → ${change.toValue ?? '—'}</td>\n </tr>`\n )}\n </table>`\n : html`<div class=\"muted\">${i18next.t('text.saas-360-changes-empty')}</div>`\n )}\n `\n }\n}\n"]}
@@ -1,4 +1 @@
1
- export { SaasBillEditor } from './components/saas-bill-editor.js';
2
- export { SaasCandidates } from './components/saas-candidates.js';
3
1
  export { registerSaasAccount360 } from './entity360/saas-account-360.js';
4
- export { trendTitle } from './words.js';
@@ -1,5 +1,2 @@
1
- export { SaasBillEditor } from './components/saas-bill-editor.js';
2
- export { SaasCandidates } from './components/saas-candidates.js';
3
1
  export { registerSaasAccount360 } from './entity360/saas-account-360.js';
4
- export { trendTitle } from './words.js';
5
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAA;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAA;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA","sourcesContent":["export { SaasBillEditor } from './components/saas-bill-editor.js'\nexport { SaasCandidates } from './components/saas-candidates.js'\nexport { registerSaasAccount360 } from './entity360/saas-account-360.js'\nexport { trendTitle } from './words.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAA","sourcesContent":["export { registerSaasAccount360 } from './entity360/saas-account-360.js'\n"]}
@@ -5,7 +5,7 @@ import { PageView } from '@operato/shell';
5
5
  declare const SaasAccountList_base: (new (...args: any[]) => import("lit").LitElement) & typeof PageView;
6
6
  /**
7
7
  * 구독 서비스 — the accounts the domain holds with vendors (ADR-0177 decision 2), typed like a spreadsheet and saved together.
8
- * An account opens to its subscriptions, their seats, its admins and its bills.
8
+ * An account opens to its subscriptions, their seats and its admins. Its money is a recurring item's (개정 7).
9
9
  */
10
10
  export declare class SaasAccountList extends SaasAccountList_base {
11
11
  static styles: import("lit").CSSResult[];
@@ -12,18 +12,16 @@ import { navigate, PageView } from '@operato/shell';
12
12
  import { CommonButtonStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles';
13
13
  import { isMobileDevice } from '@operato/utils';
14
14
  /* The register grids' save, the asset module's - this module saves its grids the same way. */
15
- import { saveGrid } from '@things-factory/asset/dist-client/grid.js';
16
- import { SaasCandidates } from '../components/saas-candidates.js';
17
- import { ACCOUNT_STATUSES, fieldWord, filterOptionsOf, optionsOf, picker, trendTitle } from '../words.js';
18
- const FIELDS = ['name', 'counterparty', 'identifier', 'parent', 'purpose', 'billingEmail', 'paymentAccount', 'currency', 'billingDay', 'secretLocation', 'increaseAlertRate', 'status', 'closedOn', 'description'];
15
+ import { saveGrid } from '@things-factory/asset/dist-client';
16
+ import { ACCOUNT_STATUSES, fieldWord, filterOptionsOf, optionsOf, picker } from '../words.js';
17
+ const FIELDS = ['name', 'counterparty', 'identifier', 'parent', 'purpose', 'billingEmail', 'paymentAccount', 'currency', 'secretLocation', 'status', 'closedOn', 'description'];
19
18
  const ACCOUNT_FIELDS = `
20
19
  id name counterparty { id name } identifier parent { id name } purpose admins { id name } billingEmail
21
- paymentAccount { id name } currency billingDay secretLocation increaseAlertRate status closedOn description
22
- trend { months values nextMonths expected currency }
20
+ paymentAccount { id name } currency secretLocation status closedOn description
23
21
  `;
24
22
  /**
25
23
  * 구독 서비스 — the accounts the domain holds with vendors (ADR-0177 decision 2), typed like a spreadsheet and saved together.
26
- * An account opens to its subscriptions, their seats, its admins and its bills.
24
+ * An account opens to its subscriptions, their seats and its admins. Its money is a recurring item's (개정 7).
27
25
  */
28
26
  let SaasAccountList = class SaasAccountList extends localize(i18next)(PageView) {
29
27
  constructor() {
@@ -41,9 +39,6 @@ let SaasAccountList = class SaasAccountList extends localize(i18next)(PageView)
41
39
  actions: this.mine
42
40
  ? []
43
41
  : [
44
- { title: i18next.t('button.saas-cost-trend'), icon: 'monitoring', action: () => navigate('saas-cost-trend') },
45
- /* What the bank and card lines already say is subscribed to (ADR-0177 개정 1). */
46
- { title: i18next.t('button.saas-candidates'), icon: 'manage_search', action: () => SaasCandidates.open({ made: () => this.grist.fetch() }) },
47
42
  { title: i18next.t('button.add'), icon: 'add', action: () => this.grist.addRecordToTop({ currency: 'KRW', status: 'ACTIVE' }) },
48
43
  { title: i18next.t('button.delete'), action: () => this.save(true), ...CommonButtonStyles.delete },
49
44
  { title: i18next.t('button.save'), action: () => this.save(), ...CommonButtonStyles.save }
@@ -83,8 +78,6 @@ let SaasAccountList = class SaasAccountList extends localize(i18next)(PageView)
83
78
  { type: 'gutter', gutterName: 'button', icon: 'open_in_new', handlers: { click: (_c, _d, _col, record) => open(record) } },
84
79
  /* The name opens the account's 360 (ADR-0067); a new row is typed into. */
85
80
  text('name', 180, { record: { mandatory: true, renderer: (value, _c, record) => (record?.id ? entity360Name({ __typename: 'SaasAccount', ...record }, value) : value ?? '') } }),
86
- /* The last twelve months billed, the next three expected dashed (ADR-0177 decision 10). */
87
- { type: 'sparkline', name: 'trend', header: fieldWord('saas-account', 'trend'), width: 120, record: { options: { title: (value) => trendTitle(value) } } },
88
81
  object('counterparty', 150, 'counterpartyChoices', [{ name: 'name', header: fieldWord('saas-account', 'counterparty') }]),
89
82
  text('identifier', 150),
90
83
  object('parent', 140, 'saasAccounts', [{ name: 'name', header: fieldWord('saas-account', 'parent') }]),
@@ -98,8 +91,6 @@ let SaasAccountList = class SaasAccountList extends localize(i18next)(PageView)
98
91
  },
99
92
  object('paymentAccount', 150, 'bankAccounts', [{ name: 'name', header: fieldWord('saas-account', 'paymentAccount') }]),
100
93
  text('currency', 60, { record: { align: 'center' } }),
101
- { type: 'integer', name: 'billingDay', header: fieldWord('saas-account', 'billingDay'), width: 70, record: { ...editable, align: 'right' } },
102
- { type: 'float', name: 'increaseAlertRate', header: fieldWord('saas-account', 'increaseAlertRate'), width: 80, hidden: true, record: { ...editable, align: 'right' } },
103
94
  text('billingEmail', 160, { hidden: true }),
104
95
  text('secretLocation', 160),
105
96
  {
@@ -1 +1 @@
1
- {"version":3,"file":"saas-account-list.js","sourceRoot":"","sources":["../../client/pages/saas-account-list.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist/ox-grist.js'\nimport '@operato/data-grist/ox-filters-form.js'\n\nimport gql from 'graphql-tag'\nimport { html } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\n\nimport { DataGrist, FetchOption, getEditor } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { entity360Name } from '@operato/layout'\nimport { navigate, PageView } from '@operato/shell'\nimport { CommonButtonStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\n/* The register grids' save, the asset module's - this module saves its grids the same way. */\nimport { saveGrid } from '@things-factory/asset/dist-client/grid.js'\n\nimport { SaasCandidates } from '../components/saas-candidates.js'\nimport { ACCOUNT_STATUSES, fieldWord, filterOptionsOf, optionsOf, picker, trendTitle } from '../words.js'\n\nconst FIELDS = ['name', 'counterparty', 'identifier', 'parent', 'purpose', 'billingEmail', 'paymentAccount', 'currency', 'billingDay', 'secretLocation', 'increaseAlertRate', 'status', 'closedOn', 'description']\nconst ACCOUNT_FIELDS = `\n id name counterparty { id name } identifier parent { id name } purpose admins { id name } billingEmail\n paymentAccount { id name } currency billingDay secretLocation increaseAlertRate status closedOn description\n trend { months values nextMonths expected currency }\n`\n\n/**\n * 구독 서비스 — the accounts the domain holds with vendors (ADR-0177 decision 2), typed like a spreadsheet and saved together.\n * An account opens to its subscriptions, their seats, its admins and its bills.\n */\n@customElement('saas-account-list')\nexport class SaasAccountList extends localize(i18next)(PageView) {\n static styles = [WorkSurfacePageStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles]\n\n /** Only the accounts the signed-in person administers, opened and not kept here. */\n protected mine = false\n\n @state() gristConfig: any\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n const title = i18next.t(this.mine ? 'title.my-saas-account-list' : 'title.saas-account-list')\n return {\n title,\n search: { handler: (search: string) => (this.grist.searchText = search), value: this.grist?.searchText || '' },\n filter: { handler: () => this.grist.toggleHeadroom() },\n actions: this.mine\n ? []\n : [\n { title: i18next.t('button.saas-cost-trend'), icon: 'monitoring', action: () => navigate('saas-cost-trend') },\n /* What the bank and card lines already say is subscribed to (ADR-0177 개정 1). */\n { title: i18next.t('button.saas-candidates'), icon: 'manage_search', action: () => SaasCandidates.open({ made: () => this.grist.fetch() }) },\n { title: i18next.t('button.add'), icon: 'add', action: () => this.grist.addRecordToTop({ currency: 'KRW', status: 'ACTIVE' }) },\n { title: i18next.t('button.delete'), action: () => this.save(true), ...CommonButtonStyles.delete },\n { title: i18next.t('button.save'), action: () => this.save(), ...CommonButtonStyles.save }\n ],\n exportable: { name: title, data: () => this.grist.exportRecords() }\n }\n }\n\n render() {\n return html`<ox-grist class=\"page-fill\" search-mode=\"term\" .mode=${isMobileDevice() ? 'CARD' : 'GRID'} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\"><ox-filters-form without-search autofocus></ox-filters-form></div>\n </div>\n </ox-grist>`\n }\n\n async pageInitialized() {\n const editable = { editable: !this.mine }\n const text = (name: string, width: number, extra: any = {}) => ({ type: 'string', name, header: fieldWord('saas-account', name), width, sortable: true, ...extra, record: { ...editable, ...extra.record } })\n const object = (name: string, width: number, queryName: string, columns: { name: string; header?: string }[]) => ({\n type: 'object',\n name,\n header: fieldWord('saas-account', name),\n width,\n record: { ...editable, editor: getEditor('resource-object'), options: picker(queryName, columns) }\n })\n const open = (record: any) => record?.id && navigate(`saas-account-page?id=${record.id}`)\n this.gristConfig = {\n rows: { appendable: !this.mine, selectable: { multiple: !this.mine } },\n pagination: { limit: 100, pages: [50, 100] },\n emptyNote: {\n title: i18next.t(this.mine ? 'text.my-saas-account-empty' : 'text.saas-account-empty'),\n description: i18next.t(this.mine ? 'text.my-saas-account-empty-description' : 'text.saas-account-empty-description')\n },\n list: { fields: ['name', 'identifier'], details: ['purpose', 'admins'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n ...(this.mine ? [] : [{ type: 'gutter', gutterName: 'row-selector', multiple: true }]),\n { type: 'gutter', gutterName: 'button', icon: 'open_in_new', handlers: { click: (_c: any, _d: any, _col: any, record: any) => open(record) } },\n /* The name opens the account's 360 (ADR-0067); a new row is typed into. */\n text('name', 180, { record: { mandatory: true, renderer: (value: string, _c: any, record: any) => (record?.id ? entity360Name({ __typename: 'SaasAccount', ...record }, value) : value ?? '') } }),\n /* The last twelve months billed, the next three expected dashed (ADR-0177 decision 10). */\n { type: 'sparkline', name: 'trend', header: fieldWord('saas-account', 'trend'), width: 120, record: { options: { title: (value: any) => trendTitle(value) } } },\n object('counterparty', 150, 'counterpartyChoices', [{ name: 'name', header: fieldWord('saas-account', 'counterparty') }]),\n text('identifier', 150),\n object('parent', 140, 'saasAccounts', [{ name: 'name', header: fieldWord('saas-account', 'parent') }]),\n text('purpose', 120),\n {\n type: 'string',\n name: 'admins',\n header: fieldWord('saas-account', 'admins'),\n width: 160,\n record: { renderer: (value: any[]) => (value ?? []).map(one => one.name).join(', ') }\n },\n object('paymentAccount', 150, 'bankAccounts', [{ name: 'name', header: fieldWord('saas-account', 'paymentAccount') }]),\n text('currency', 60, { record: { align: 'center' } }),\n { type: 'integer', name: 'billingDay', header: fieldWord('saas-account', 'billingDay'), width: 70, record: { ...editable, align: 'right' } },\n { type: 'float', name: 'increaseAlertRate', header: fieldWord('saas-account', 'increaseAlertRate'), width: 80, hidden: true, record: { ...editable, align: 'right' } },\n text('billingEmail', 160, { hidden: true }),\n text('secretLocation', 160),\n {\n type: 'select',\n name: 'status',\n header: fieldWord('saas-account', 'status'),\n width: 80,\n sortable: true,\n record: { ...editable, options: optionsOf('saas-account-status', ACCOUNT_STATUSES) },\n filter: { type: 'select', operator: 'eq', options: filterOptionsOf('saas-account-status', ACCOUNT_STATUSES) }\n },\n text('closedOn', 100, { record: { align: 'center' } }),\n text('description', 200)\n ],\n sorters: [{ name: 'name' }]\n }\n }\n\n async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [], search }: FetchOption) {\n if (this.mine) {\n const response = await client.query({ query: gql`query { rows: mySaasAccounts { ${ACCOUNT_FIELDS} } }`, fetchPolicy: 'network-only' })\n if (response.errors?.length) throw response\n return { total: response.data.rows.length, records: response.data.rows }\n }\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!], $search: String) {\n responses: saasAccounts(filters: $filters, pagination: $pagination, sortings: $sortings, search: $search) {\n items { ${ACCOUNT_FIELDS} }\n total\n }\n }\n `,\n variables: { filters, pagination: { page, limit }, sortings, search: search?.trim() || undefined },\n fetchPolicy: 'network-only'\n })\n if (response.errors?.length) throw response\n return { total: response.data.responses.total, records: response.data.responses.items }\n }\n\n private async save(deleting = false) {\n /* A picked row goes as its id. */\n const shape = ({ counterparty, parent, paymentAccount, ...patch }: any) => ({\n ...patch,\n ...(counterparty === undefined ? {} : { counterpartyId: counterparty?.id ?? null }),\n ...(parent === undefined ? {} : { parentId: parent?.id ?? null }),\n ...(paymentAccount === undefined ? {} : { paymentAccountId: paymentAccount?.id ?? null })\n })\n if (await saveGrid(this.grist, FIELDS, 'updateMultipleSaasAccount', 'SaasAccountPatch', {}, deleting, shape)) this.grist.fetch()\n }\n}\n\n/** 내가 관리하는 구독 서비스 — the accounts the signed-in person is named an admin of, whatever their seat (ADR-0177 Q4). */\n@customElement('my-saas-account-list')\nexport class MySaasAccountList extends SaasAccountList {\n protected mine = true\n}\n"]}
1
+ {"version":3,"file":"saas-account-list.js","sourceRoot":"","sources":["../../client/pages/saas-account-list.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist/ox-grist.js'\nimport '@operato/data-grist/ox-filters-form.js'\n\nimport gql from 'graphql-tag'\nimport { html } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\n\nimport { DataGrist, FetchOption, getEditor } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { entity360Name } from '@operato/layout'\nimport { navigate, PageView } from '@operato/shell'\nimport { CommonButtonStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\n/* The register grids' save, the asset module's - this module saves its grids the same way. */\nimport { saveGrid } from '@things-factory/asset/dist-client'\n\nimport { ACCOUNT_STATUSES, fieldWord, filterOptionsOf, optionsOf, picker } from '../words.js'\n\nconst FIELDS = ['name', 'counterparty', 'identifier', 'parent', 'purpose', 'billingEmail', 'paymentAccount', 'currency', 'secretLocation', 'status', 'closedOn', 'description']\nconst ACCOUNT_FIELDS = `\n id name counterparty { id name } identifier parent { id name } purpose admins { id name } billingEmail\n paymentAccount { id name } currency secretLocation status closedOn description\n`\n\n/**\n * 구독 서비스 — the accounts the domain holds with vendors (ADR-0177 decision 2), typed like a spreadsheet and saved together.\n * An account opens to its subscriptions, their seats and its admins. Its money is a recurring item's (개정 7).\n */\n@customElement('saas-account-list')\nexport class SaasAccountList extends localize(i18next)(PageView) {\n static styles = [WorkSurfacePageStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles]\n\n /** Only the accounts the signed-in person administers, opened and not kept here. */\n protected mine = false\n\n @state() gristConfig: any\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n const title = i18next.t(this.mine ? 'title.my-saas-account-list' : 'title.saas-account-list')\n return {\n title,\n search: { handler: (search: string) => (this.grist.searchText = search), value: this.grist?.searchText || '' },\n filter: { handler: () => this.grist.toggleHeadroom() },\n actions: this.mine\n ? []\n : [\n { title: i18next.t('button.add'), icon: 'add', action: () => this.grist.addRecordToTop({ currency: 'KRW', status: 'ACTIVE' }) },\n { title: i18next.t('button.delete'), action: () => this.save(true), ...CommonButtonStyles.delete },\n { title: i18next.t('button.save'), action: () => this.save(), ...CommonButtonStyles.save }\n ],\n exportable: { name: title, data: () => this.grist.exportRecords() }\n }\n }\n\n render() {\n return html`<ox-grist class=\"page-fill\" search-mode=\"term\" .mode=${isMobileDevice() ? 'CARD' : 'GRID'} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\"><ox-filters-form without-search autofocus></ox-filters-form></div>\n </div>\n </ox-grist>`\n }\n\n async pageInitialized() {\n const editable = { editable: !this.mine }\n const text = (name: string, width: number, extra: any = {}) => ({ type: 'string', name, header: fieldWord('saas-account', name), width, sortable: true, ...extra, record: { ...editable, ...extra.record } })\n const object = (name: string, width: number, queryName: string, columns: { name: string; header?: string }[]) => ({\n type: 'object',\n name,\n header: fieldWord('saas-account', name),\n width,\n record: { ...editable, editor: getEditor('resource-object'), options: picker(queryName, columns) }\n })\n const open = (record: any) => record?.id && navigate(`saas-account-page?id=${record.id}`)\n this.gristConfig = {\n rows: { appendable: !this.mine, selectable: { multiple: !this.mine } },\n pagination: { limit: 100, pages: [50, 100] },\n emptyNote: {\n title: i18next.t(this.mine ? 'text.my-saas-account-empty' : 'text.saas-account-empty'),\n description: i18next.t(this.mine ? 'text.my-saas-account-empty-description' : 'text.saas-account-empty-description')\n },\n list: { fields: ['name', 'identifier'], details: ['purpose', 'admins'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n ...(this.mine ? [] : [{ type: 'gutter', gutterName: 'row-selector', multiple: true }]),\n { type: 'gutter', gutterName: 'button', icon: 'open_in_new', handlers: { click: (_c: any, _d: any, _col: any, record: any) => open(record) } },\n /* The name opens the account's 360 (ADR-0067); a new row is typed into. */\n text('name', 180, { record: { mandatory: true, renderer: (value: string, _c: any, record: any) => (record?.id ? entity360Name({ __typename: 'SaasAccount', ...record }, value) : value ?? '') } }),\n object('counterparty', 150, 'counterpartyChoices', [{ name: 'name', header: fieldWord('saas-account', 'counterparty') }]),\n text('identifier', 150),\n object('parent', 140, 'saasAccounts', [{ name: 'name', header: fieldWord('saas-account', 'parent') }]),\n text('purpose', 120),\n {\n type: 'string',\n name: 'admins',\n header: fieldWord('saas-account', 'admins'),\n width: 160,\n record: { renderer: (value: any[]) => (value ?? []).map(one => one.name).join(', ') }\n },\n object('paymentAccount', 150, 'bankAccounts', [{ name: 'name', header: fieldWord('saas-account', 'paymentAccount') }]),\n text('currency', 60, { record: { align: 'center' } }),\n text('billingEmail', 160, { hidden: true }),\n text('secretLocation', 160),\n {\n type: 'select',\n name: 'status',\n header: fieldWord('saas-account', 'status'),\n width: 80,\n sortable: true,\n record: { ...editable, options: optionsOf('saas-account-status', ACCOUNT_STATUSES) },\n filter: { type: 'select', operator: 'eq', options: filterOptionsOf('saas-account-status', ACCOUNT_STATUSES) }\n },\n text('closedOn', 100, { record: { align: 'center' } }),\n text('description', 200)\n ],\n sorters: [{ name: 'name' }]\n }\n }\n\n async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [], search }: FetchOption) {\n if (this.mine) {\n const response = await client.query({ query: gql`query { rows: mySaasAccounts { ${ACCOUNT_FIELDS} } }`, fetchPolicy: 'network-only' })\n if (response.errors?.length) throw response\n return { total: response.data.rows.length, records: response.data.rows }\n }\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!], $search: String) {\n responses: saasAccounts(filters: $filters, pagination: $pagination, sortings: $sortings, search: $search) {\n items { ${ACCOUNT_FIELDS} }\n total\n }\n }\n `,\n variables: { filters, pagination: { page, limit }, sortings, search: search?.trim() || undefined },\n fetchPolicy: 'network-only'\n })\n if (response.errors?.length) throw response\n return { total: response.data.responses.total, records: response.data.responses.items }\n }\n\n private async save(deleting = false) {\n /* A picked row goes as its id. */\n const shape = ({ counterparty, parent, paymentAccount, ...patch }: any) => ({\n ...patch,\n ...(counterparty === undefined ? {} : { counterpartyId: counterparty?.id ?? null }),\n ...(parent === undefined ? {} : { parentId: parent?.id ?? null }),\n ...(paymentAccount === undefined ? {} : { paymentAccountId: paymentAccount?.id ?? null })\n })\n if (await saveGrid(this.grist, FIELDS, 'updateMultipleSaasAccount', 'SaasAccountPatch', {}, deleting, shape)) this.grist.fetch()\n }\n}\n\n/** 내가 관리하는 구독 서비스 — the accounts the signed-in person is named an admin of, whatever their seat (ADR-0177 Q4). */\n@customElement('my-saas-account-list')\nexport class MySaasAccountList extends SaasAccountList {\n protected mine = true\n}\n"]}
@@ -1,11 +1,10 @@
1
1
  import '@operato/data-grist/ox-grist.js';
2
- import '../components/saas-bill-editor.js';
3
2
  import { PageView } from '@operato/shell';
4
- type Tab = 'subscriptions' | 'seats' | 'bills' | 'admins';
3
+ type Tab = 'subscriptions' | 'seats' | 'admins';
5
4
  declare const SaasAccountPage_base: (new (...args: any[]) => import("lit").LitElement) & typeof PageView;
6
5
  /**
7
- * 구독 서비스 한 계정 — tab by tab its subscriptions, the seats of one of them, its bills month by month and who administers
8
- * it (ADR-0177 decisions 2-6). The keepers keep all of it; an admin keeps what is under their own account, not the account
6
+ * 구독 서비스 한 계정 — tab by tab its subscriptions, the seats of one of them and who administers it (ADR-0177 decisions
7
+ * 2-6; its money is a recurring item's, 개정 7). The keepers keep all of it; an admin keeps what is under their own account, not the account
9
8
  * or its admins.
10
9
  */
11
10
  export declare class SaasAccountPage extends SaasAccountPage_base {
@@ -46,7 +45,6 @@ export declare class SaasAccountPage extends SaasAccountPage_base {
46
45
  total: any;
47
46
  records: any;
48
47
  }>;
49
- private openBill;
50
48
  private save;
51
49
  /** The admins are a field of the account - the grid's rows, less the deleted, saved as its list. */
52
50
  private saveAdmins;