@things-factory/invoice 10.1.256 → 10.1.257
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/document/document-defaults.d.ts +11 -2
- package/dist-client/document/document-defaults.js +7 -15
- package/dist-client/document/document-defaults.js.map +1 -1
- package/dist-client/document/document-party.d.ts +5 -2
- package/dist-client/document/document-party.js +21 -21
- package/dist-client/document/document-party.js.map +1 -1
- package/dist-client/entity360/invoice-360.d.ts +3 -4
- package/dist-client/entity360/invoice-360.js +17 -38
- package/dist-client/entity360/invoice-360.js.map +1 -1
- package/dist-client/index.d.ts +1 -1
- package/dist-client/index.js +1 -1
- package/dist-client/index.js.map +1 -1
- package/dist-client/invoice-files.d.ts +4 -10
- package/dist-client/invoice-files.js +19 -58
- package/dist-client/invoice-files.js.map +1 -1
- package/dist-client/pages/invoice-editor.d.ts +42 -19
- package/dist-client/pages/invoice-editor.js +150 -179
- package/dist-client/pages/invoice-editor.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/viewparts/document-defaults-setting-let.d.ts +7 -2
- package/dist-client/viewparts/document-defaults-setting-let.js +30 -35
- package/dist-client/viewparts/document-defaults-setting-let.js.map +1 -1
- package/package.json +12 -12
|
@@ -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 {
|
|
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.
|
|
73
|
-
/**
|
|
74
|
-
this.
|
|
75
|
-
|
|
76
|
-
this.
|
|
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
|
-
|
|
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.
|
|
119
|
-
this.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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.
|
|
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.
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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.
|
|
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) =>
|
|
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.
|
|
336
|
+
this.put({ items: linesOf(this.grist), taxBasis });
|
|
278
337
|
this.configure();
|
|
279
|
-
this.updateComplete.then(() => (this.
|
|
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.
|
|
284
|
-
this.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
navigate(`invoice-editor?id=${saved.id}`);
|
|
382
|
-
|
|
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.
|
|
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.
|
|
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, "
|
|
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, "
|
|
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, "
|
|
465
|
+
], InvoiceEditor.prototype, "defaultsReadable", void 0);
|
|
495
466
|
__decorate([
|
|
496
467
|
query('ox-grist'),
|
|
497
468
|
__metadata("design:type", DataGrist)
|