@things-factory/invoice 10.1.256 → 10.1.258

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.
@@ -5,18 +5,19 @@ import gql from 'graphql-tag';
5
5
  import { css, html, nothing } from 'lit';
6
6
  import { customElement, query, state } from 'lit/decorators.js';
7
7
  import { DataGrist } from '@operato/data-grist';
8
- import { client } from '@operato/graphql';
8
+ import { client, MutationController, QueryController } from '@operato/graphql';
9
9
  import { i18next, localize } from '@operato/i18n';
10
10
  import { notify } from '@operato/layout';
11
11
  import { OxPrompt } from '@operato/popup/ox-prompt.js';
12
- import { navigate, PageView } from '@operato/shell';
12
+ import { navigate, PageView, DraftController } from '@operato/shell';
13
13
  import { ButtonStyles, CommonGristStyles, DocumentPageStyles, FormFieldStyles, ScrollbarStyles } from '@operato/styles';
14
+ import { hasPrivilege } from '@things-factory/auth-base/dist-client';
14
15
  import { daysAfter, nextInvoiceNumber } from '@things-factory/invoice/dist-shared/document-start.js';
15
16
  import { documentAmount } from '../document/document-amount';
16
17
  import { DocumentFormStyles, renderDocumentHead, renderDocumentTotals } from '../document/document-form';
17
18
  import { documentLinesConfig, linesOf, onLineChange, reckonLines } from '../document/document-lines';
18
19
  import { partyOfName } from '../document/document-party';
19
- import { documentDefaults, headFromDefaults, OUR_SIDE_FIELDS, oursFromLast } from '../document/document-defaults';
20
+ import { DOCUMENT_DEFAULTS, DOCUMENT_DEFAULTS_READER, headFromDefaults, OUR_SIDE_FIELDS, oursFromLast } from '../document/document-defaults';
20
21
  import { openDocumentPreview } from '../document/document-preview';
21
22
  import { invoiceStatusWord } from '../entity360/invoice-360';
22
23
  import { invoiceLinkSection } from '../invoice-attach';
@@ -62,18 +63,36 @@ const PAYMENT_DAYS = 30;
62
63
  let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
63
64
  constructor() {
64
65
  super(...arguments);
65
- this.invoice = null;
66
- this.form = blank();
67
- this.loading = false;
68
- this.error = null;
69
- this.busy = false;
70
- this.voidReason = '';
71
66
  /** The totals of the lines as typed - reckoned as the invoice will keep them, shown before it is saved. */
72
- this.amounts = {};
73
- /** The company's words a new invoice starts with, and the payment terms it offers (ADR-0170). */
74
- this.defaults = null;
75
- /** The number a new invoice was offered - it follows the direction while the person leaves it. */
76
- this.suggestedNumber = '';
67
+ this.typedAmounts = null;
68
+ /** Whether this person may read the company's words - unknown, and nothing read, until asked. */
69
+ this.defaultsReadable = false;
70
+ /* The company's words, the invoice the address names, and the last sales invoice a new one follows (ADR-0190). */
71
+ this.companyDefaults = new QueryController(this, DOCUMENT_DEFAULTS, { variables: () => (this.defaultsReadable ? {} : undefined) });
72
+ this.record = new QueryController(this, gql(`query ($id: String!) { invoice(id: $id) { ${FIELDS} } }`), {
73
+ variables: () => (this.invoiceId ? { id: this.invoiceId } : undefined)
74
+ });
75
+ this.last = new QueryController(this, gql `
76
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
77
+ invoices(filters: $filters, pagination: $pagination, sortings: $sortings) { items { number ${OUR_SIDE_FIELDS.join(' ')} } }
78
+ }
79
+ `, {
80
+ variables: () => this.address && !this.address.id
81
+ ? { filters: [{ name: 'direction', operator: 'eq', value: 'SALES' }], pagination: { page: 1, limit: 1 }, sortings: [{ name: 'createdAt', desc: true }] }
82
+ : undefined
83
+ });
84
+ /* What is typed over the invoice read, or over what a new one starts with; the lines are the grid's. */
85
+ this.draft = new DraftController(this, { read: () => this.formRead, key: () => this.address });
86
+ /* The reason an issued one is voided with. */
87
+ this.voiding = new DraftController(this, { read: () => ({ reason: '' }), key: () => this.address });
88
+ /* Every change of the invoice reads it again once it is done. */
89
+ this.saves = {
90
+ create: new MutationController(this, gql `mutation ($invoice: NewInvoice!) { invoice: createInvoice(invoice: $invoice) { id } }`),
91
+ update: this.saving(gql `mutation ($id: String!, $invoice: NewInvoice!) { invoice: updateInvoice(id: $id, invoice: $invoice) { id } }`),
92
+ issue: this.saving(gql `mutation ($id: String!) { invoice: issueInvoice(id: $id) { id } }`),
93
+ void: this.saving(gql `mutation ($id: String!, $reason: String!) { invoice: voidInvoice(id: $id, reason: $reason) { id } }`)
94
+ };
95
+ this.relined = false;
77
96
  }
78
97
  static { this.styles = [
79
98
  FormFieldStyles,
@@ -88,6 +107,49 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
88
107
  }
89
108
  `
90
109
  ]; }
110
+ saving(mutation) {
111
+ return new MutationController(this, mutation, { refetch: () => [this.record] });
112
+ }
113
+ get invoiceId() {
114
+ return this.address?.id ?? '';
115
+ }
116
+ /** The company's words a new invoice starts with, and the payment terms it offers (ADR-0170) - none until read. */
117
+ get defaults() {
118
+ return this.companyDefaults.data?.documentDefaults ?? null;
119
+ }
120
+ get invoice() {
121
+ return (this.invoiceId && this.record.data?.invoice) || null;
122
+ }
123
+ /** What the form shows unedited: the invoice read, or what a new one starts with. */
124
+ get formRead() {
125
+ if (!this.address)
126
+ return undefined;
127
+ return this.address.id ? (this.invoice ?? undefined) : this.start;
128
+ }
129
+ /** The form as it shows: each field's edit, else what was read. */
130
+ get form() {
131
+ return { ...blank(), ...this.formRead, ...this.draft.edits };
132
+ }
133
+ /** The totals of the lines as typed, else as the invoice keeps them. */
134
+ get amounts() {
135
+ return this.typedAmounts ?? this.invoice ?? {};
136
+ }
137
+ get voidReason() {
138
+ return this.voiding.value('reason') ?? '';
139
+ }
140
+ /** A failed read, or an invoice the address names that is not there. */
141
+ get error() {
142
+ if (this.record.error)
143
+ return this.record.error;
144
+ return this.invoiceId && this.record.data && !this.record.data.invoice ? new Error(i18next.t('text.invoice-not-found')) : null;
145
+ }
146
+ get loading() {
147
+ return this.record.loading;
148
+ }
149
+ /** Runs one request at a time. */
150
+ get busy() {
151
+ return Object.values(this.saves).some(save => save.pending);
152
+ }
91
153
  get received() {
92
154
  return this.form.taxBasis === 'RECEIVED';
93
155
  }
@@ -111,28 +173,34 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
111
173
  actions: this.error ? [] : actions
112
174
  };
113
175
  }
114
- async pageUpdated(changes, lifecycle) {
176
+ pageUpdated(changes, lifecycle) {
115
177
  if (!this.active || !(changes.active || changes.params))
116
178
  return;
117
179
  const id = lifecycle?.params?.id;
118
- if (!this.defaults)
119
- this.defaults = await documentDefaults();
120
- if (!id) {
121
- this.invoice = null;
122
- this.form = blank();
123
- this.amounts = {};
124
- this.configure();
125
- this.updateContext();
126
- /* Started, not waited for - the page's title stands meanwhile. */
127
- this.prefill();
180
+ if (!this.defaultsReadable)
181
+ hasPrivilege(DOCUMENT_DEFAULTS_READER).then(may => (this.defaultsReadable = !!may));
182
+ this.address = id ? { id } : {};
183
+ }
184
+ willUpdate(changes) {
185
+ super.willUpdate(changes);
186
+ const source = this.invoiceId ? this.invoice : JSON.stringify(this.address ?? null);
187
+ if (source === this.lined)
128
188
  return;
129
- }
130
- if (id !== this.invoice?.id)
131
- await this.load(id);
189
+ this.lined = source;
190
+ this.typedAmounts = null;
191
+ this.configure();
192
+ this.relined = true;
193
+ }
194
+ updated(changes) {
195
+ super.updated(changes);
196
+ if (!this.relined)
197
+ return;
198
+ this.relined = false;
199
+ this.grist?.fetch();
132
200
  }
133
201
  render() {
134
202
  const links = this.invoice ? invoiceLinkSection() : undefined;
135
- return html `<ox-page-state .loading=${this.loading} .error=${this.error} @retry=${() => this.invoice && this.load(this.invoice.id)}>
203
+ return html `<ox-page-state .loading=${this.loading} .error=${this.error} @retry=${() => this.record.refetch()}>
136
204
  <div class="page">
137
205
  <section class="page-section">
138
206
  <header class="page-section-header"><h3>${i18next.t('text.invoice-section-head')}</h3></header>
@@ -146,8 +214,8 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
146
214
  <ox-grist
147
215
  mode="GRID"
148
216
  .config=${this.gristConfig}
149
- .fetchHandler=${() => ({ total: this.form.items.length, records: this.form.items })}
150
- @record-change=${(e) => (this.amounts = onLineChange(this.grist, e, this.form.currency, this.received, this.form.discountAmount))}
217
+ .fetchHandler=${() => ({ total: (this.form.items ?? []).length, records: (this.form.items ?? []).map((item) => ({ ...item })) })}
218
+ @record-change=${(e) => (this.typedAmounts = onLineChange(this.grist, e, this.form.currency, this.received, this.form.discountAmount))}
151
219
  ></ox-grist>
152
220
  </section>
153
221
  <section class="page-section">
@@ -174,7 +242,7 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
174
242
  const editable = this.editable && !this.busy;
175
243
  return html `<label
176
244
  >${i18next.t('field.invoice-due-date')}
177
- <input type="date" .value=${this.form.dueDate ?? ''} ?disabled=${!editable} @input=${(e) => (this.form = { ...this.form, dueDate: e.target.value })}
245
+ <input type="date" .value=${this.form.dueDate ?? ''} ?disabled=${!editable} @input=${(e) => this.put({ dueDate: e.target.value })}
178
246
  /></label>
179
247
  ${this.form.direction === 'PURCHASE'
180
248
  ? html `<label
@@ -201,61 +269,52 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
201
269
  }
202
270
  /**
203
271
  * A party typed by name and not picked is the counterparty of that name when exactly one is - case and spaces aside;
204
- * otherwise it stays unlinked and the editor says so.
272
+ * otherwise it stays unlinked and the editor says so. Looked up for the save and shown nowhere: the invoice read after
273
+ * it shows the link.
205
274
  */
206
275
  async linkByName() {
207
276
  if (this.form.counterpartyId)
208
- return;
209
- const party = await partyOfName(this.form.direction === 'PURCHASE' ? this.form.supplierName : this.form.buyerName);
210
- if (party)
211
- this.form = { ...this.form, counterpartyId: party.id, counterparty: party };
277
+ return null;
278
+ return partyOfName(this.form.direction === 'PURCHASE' ? this.form.supplierName : this.form.buyerName);
212
279
  }
213
280
  /**
214
281
  * What a new invoice starts with, so a person writes only what is new: the number after the last sales invoice's, the
215
282
  * due date the company's days after the issue, the company's own words (ADR-0170 decision 6), and what they leave
216
283
  * blank of our side as the last sales invoice wrote it.
217
284
  */
218
- async prefill() {
219
- let last;
220
- try {
221
- const response = await client.query({
222
- query: gql `
223
- query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
224
- invoices(filters: $filters, pagination: $pagination, sortings: $sortings) { items { number ${OUR_SIDE_FIELDS.join(' ')} } }
225
- }
226
- `,
227
- variables: { filters: [{ name: 'direction', operator: 'eq', value: 'SALES' }], pagination: { page: 1, limit: 1 }, sortings: [{ name: 'createdAt', desc: true }] },
228
- fetchPolicy: 'no-cache'
229
- });
230
- last = response.data?.invoices?.items?.[0];
231
- }
232
- catch {
233
- /* Nothing to follow - the first number of the year, and our side from the company's words alone. */
234
- }
235
- /* An invoice opened meanwhile is not a new one. */
236
- if (this.invoice)
237
- return;
238
- this.suggestedNumber = nextInvoiceNumber(last?.number, Number(this.form.issueDate.slice(0, 4)));
239
- this.form = { ...this.form, ...headFromDefaults(this.form, this.defaults) };
240
- this.form = {
241
- ...this.form,
242
- ...oursFromLast(this.form, last),
243
- number: this.form.number || this.suggestedNumber,
244
- dueDate: this.form.dueDate || daysAfter(this.form.issueDate, this.paymentDays)
285
+ get start() {
286
+ const form = blank();
287
+ const head = { ...form, ...headFromDefaults(form, this.defaults) };
288
+ return {
289
+ ...head,
290
+ ...oursFromLast(head, this.last.data?.invoices?.items?.[0]),
291
+ number: this.suggestedNumber,
292
+ dueDate: daysAfter(form.issueDate, this.paymentDays)
245
293
  };
246
294
  }
295
+ /** The number a new invoice is offered once the last one is read - it follows the direction while the person leaves it. */
296
+ get suggestedNumber() {
297
+ if (!this.last.answeredAt && !this.last.error)
298
+ return '';
299
+ const last = this.last.data?.invoices?.items?.[0];
300
+ return nextInvoiceNumber(last?.number, Number(today().slice(0, 4)));
301
+ }
247
302
  /** Days an invoice is due - the company's, or thirty. */
248
303
  get paymentDays() {
249
304
  return this.defaults?.paymentDays ?? PAYMENT_DAYS;
250
305
  }
251
306
  chooseParty(party) {
252
307
  const side = this.form.direction === 'PURCHASE' ? 'supplier' : 'buyer';
253
- this.form = {
254
- ...this.form,
308
+ this.put({
255
309
  counterpartyId: party?.id ?? null,
256
310
  counterparty: party,
257
311
  ...(party ? { [`${side}Name`]: party.name, [`${side}RegistrationNumber`]: party.businessNumber ?? this.form[`${side}RegistrationNumber`] } : {})
258
- };
312
+ });
313
+ }
314
+ /** Edits of several fields at once. */
315
+ put(patch) {
316
+ for (const [name, value] of Object.entries(patch))
317
+ this.draft.set(name, value);
259
318
  }
260
319
  renderVoid() {
261
320
  return html `<section class="page-section">
@@ -268,20 +327,20 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
268
327
  <div class="form">
269
328
  <label class="wide"
270
329
  >${i18next.t('field.invoice-void-reason')}
271
- <input .value=${this.voidReason} ?disabled=${this.busy} @input=${(e) => (this.voidReason = e.target.value)}
330
+ <input .value=${this.voidReason} ?disabled=${this.busy} @input=${(e) => this.voiding.set('reason', e.target.value)}
272
331
  /></label>
273
332
  </div>
274
333
  </section>`;
275
334
  }
276
335
  changeTaxBasis(taxBasis) {
277
- this.form = { ...this.form, items: linesOf(this.grist), taxBasis };
336
+ this.put({ items: linesOf(this.grist), taxBasis });
278
337
  this.configure();
279
- this.updateComplete.then(() => (this.amounts = reckonLines(this.grist, this.form.currency, this.received, this.form.discountAmount)));
338
+ this.updateComplete.then(() => (this.typedAmounts = reckonLines(this.grist, this.form.currency, this.received, this.form.discountAmount)));
280
339
  }
281
340
  /** The discount of the whole invoice as typed, and the totals after it. */
282
341
  discount(value) {
283
- this.form = { ...this.form, discountAmount: value };
284
- this.amounts = reckonLines(this.grist, this.form.currency, this.received, value);
342
+ this.put({ discountAmount: value });
343
+ this.typedAmounts = reckonLines(this.grist, this.form.currency, this.received, value);
285
344
  }
286
345
  /**
287
346
  * A change of the head; a new currency reckons the lines in its minor unit; the due date follows a moved issue date
@@ -298,58 +357,15 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
298
357
  : patch.direction === 'SALES' && !String(this.form.number ?? '').trim()
299
358
  ? { number: this.suggestedNumber }
300
359
  : {};
301
- this.form = { ...this.form, ...patch, ...number, ...(following ? { dueDate: daysAfter(patch.issueDate, this.paymentDays) } : {}) };
360
+ this.put({ ...patch, ...number, ...(following ? { dueDate: daysAfter(patch.issueDate, this.paymentDays) } : {}) });
302
361
  if (patch.currency && patch.currency !== currency) {
303
362
  this.configure();
304
- this.amounts = reckonLines(this.grist, this.form.currency, this.received, this.form.discountAmount);
363
+ this.typedAmounts = reckonLines(this.grist, this.form.currency, this.received, this.form.discountAmount);
305
364
  }
306
365
  }
307
366
  configure() {
308
367
  this.gristConfig = documentLinesConfig({ editable: this.editable, received: this.form.taxBasis === 'RECEIVED', currency: this.form.currency || 'KRW' });
309
368
  }
310
- async load(id) {
311
- this.loading = true;
312
- this.error = null;
313
- try {
314
- const response = await client.query({ query: gql(`query ($id: String!) { invoice(id: $id) { ${FIELDS} } }`), variables: { id }, fetchPolicy: 'no-cache' });
315
- if (response.errors?.length)
316
- throw response;
317
- if (!response.data.invoice)
318
- throw new Error(i18next.t('text.invoice-not-found'));
319
- this.show(response.data.invoice);
320
- }
321
- catch (error) {
322
- this.error = error;
323
- }
324
- finally {
325
- this.loading = false;
326
- this.updateContext();
327
- }
328
- }
329
- show(invoice) {
330
- this.invoice = invoice;
331
- this.form = { ...invoice, items: invoice.items.map((item) => ({ ...item })) };
332
- this.amounts = invoice;
333
- this.voidReason = '';
334
- this.configure();
335
- this.grist?.fetch();
336
- }
337
- /** Runs one request at a time; a refusal is said in the server's words. */
338
- async run(work) {
339
- if (this.busy)
340
- return undefined;
341
- this.busy = true;
342
- try {
343
- return await work();
344
- }
345
- catch (error) {
346
- notify({ level: 'error', message: error?.graphQLErrors?.[0]?.message ?? error?.errors?.[0]?.message ?? error?.message ?? String(error) });
347
- return undefined;
348
- }
349
- finally {
350
- this.busy = false;
351
- }
352
- }
353
369
  input() {
354
370
  const input = Object.fromEntries(HEAD.map(key => [key, this.form[key] === '' ? null : this.form[key] ?? null]));
355
371
  input.taxBasis = input.direction === 'PURCHASE' ? input.taxBasis || 'CALCULATED' : 'CALCULATED';
@@ -357,29 +373,24 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
357
373
  input.discountAmount = input.discountAmount === null ? 0 : Number(input.discountAmount);
358
374
  return input;
359
375
  }
376
+ /** A new one is opened once it is made; the edits sent are cleared once the save answered. Answers the invoice's id. */
360
377
  async save() {
361
- const id = this.invoice?.id;
362
378
  if (this.busy)
363
379
  return undefined;
364
- await this.linkByName();
365
- const saved = await this.run(async () => {
366
- const response = await client.mutate({
367
- mutation: gql(id
368
- ? `mutation ($id: String!, $invoice: NewInvoice!) { invoice: updateInvoice(id: $id, invoice: $invoice) { ${FIELDS} } }`
369
- : `mutation ($invoice: NewInvoice!) { invoice: createInvoice(invoice: $invoice) { ${FIELDS} } }`),
370
- variables: { ...(id ? { id } : {}), invoice: this.input() }
371
- });
372
- if (response.errors?.length)
373
- throw response;
374
- return response.data.invoice;
380
+ const id = this.invoiceId;
381
+ const linked = await this.linkByName();
382
+ const saved = await this.draft.submit(() => {
383
+ const invoice = { ...this.input(), ...(linked ? { counterpartyId: linked.id } : {}) };
384
+ return id ? this.saves.update.run({ id, invoice }) : this.saves.create.run({ invoice });
375
385
  });
376
386
  if (!saved)
377
387
  return undefined;
378
- this.show(saved);
379
- this.updateContext();
380
- if (!id)
381
- navigate(`invoice-editor?id=${saved.id}`);
382
- return saved;
388
+ if (!id) {
389
+ /* The lines went with it: the grid keeps them as saved while the new invoice opens. */
390
+ this.grist?.commit();
391
+ navigate(`invoice-editor?id=${saved.invoice.id}`);
392
+ }
393
+ return saved.invoice.id;
383
394
  }
384
395
  /** Asked first - to whom and how much, fixed after - then save what is on screen and issue it. */
385
396
  async issue() {
@@ -399,7 +410,7 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
399
410
  const saved = await this.save();
400
411
  if (!saved)
401
412
  return;
402
- const issued = await this.mutate(`mutation ($id: String!) { invoice: issueInvoice(id: $id) { ${FIELDS} } }`, { id: saved.id });
413
+ const issued = await this.saves.issue.run({ id: saved });
403
414
  if (issued)
404
415
  notify({ level: 'info', message: i18next.t('text.invoice-issued') });
405
416
  }
@@ -412,23 +423,7 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
412
423
  });
413
424
  if (!confirmed)
414
425
  return;
415
- await this.mutate(`mutation ($id: String!, $reason: String!) { invoice: voidInvoice(id: $id, reason: $reason) { ${FIELDS} } }`, {
416
- id: this.invoice.id,
417
- reason: this.voidReason.trim()
418
- });
419
- }
420
- async mutate(mutation, variables) {
421
- const changed = await this.run(async () => {
422
- const response = await client.mutate({ mutation: gql(mutation), variables });
423
- if (response.errors?.length)
424
- throw response;
425
- return response.data.invoice;
426
- });
427
- if (changed) {
428
- this.show(changed);
429
- this.updateContext();
430
- }
431
- return changed;
426
+ await this.voiding.submit(sent => this.saves.void.run({ id: this.invoiceId, reason: (sent.reason ?? '').trim() }));
432
427
  }
433
428
  /**
434
429
  * The saved invoice as it goes out (ADR-0170 decision 10) - the server's page in the language chosen there, the
@@ -455,27 +450,7 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
455
450
  __decorate([
456
451
  state(),
457
452
  __metadata("design:type", Object)
458
- ], InvoiceEditor.prototype, "invoice", void 0);
459
- __decorate([
460
- state(),
461
- __metadata("design:type", Object)
462
- ], InvoiceEditor.prototype, "form", void 0);
463
- __decorate([
464
- state(),
465
- __metadata("design:type", Object)
466
- ], InvoiceEditor.prototype, "loading", void 0);
467
- __decorate([
468
- state(),
469
- __metadata("design:type", Object)
470
- ], InvoiceEditor.prototype, "error", void 0);
471
- __decorate([
472
- state(),
473
- __metadata("design:type", Object)
474
- ], InvoiceEditor.prototype, "busy", void 0);
475
- __decorate([
476
- state(),
477
- __metadata("design:type", Object)
478
- ], InvoiceEditor.prototype, "voidReason", void 0);
453
+ ], InvoiceEditor.prototype, "address", void 0);
479
454
  __decorate([
480
455
  state(),
481
456
  __metadata("design:type", Object)
@@ -483,15 +458,11 @@ __decorate([
483
458
  __decorate([
484
459
  state(),
485
460
  __metadata("design:type", Object)
486
- ], InvoiceEditor.prototype, "amounts", void 0);
487
- __decorate([
488
- state(),
489
- __metadata("design:type", Object)
490
- ], InvoiceEditor.prototype, "defaults", void 0);
461
+ ], InvoiceEditor.prototype, "typedAmounts", void 0);
491
462
  __decorate([
492
463
  state(),
493
464
  __metadata("design:type", Object)
494
- ], InvoiceEditor.prototype, "suggestedNumber", void 0);
465
+ ], InvoiceEditor.prototype, "defaultsReadable", void 0);
495
466
  __decorate([
496
467
  query('ox-grist'),
497
468
  __metadata("design:type", DataGrist)