@things-factory/sales 10.1.255 → 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/entity360/opportunity-360.d.ts +3 -4
- package/dist-client/entity360/opportunity-360.js +16 -38
- package/dist-client/entity360/opportunity-360.js.map +1 -1
- package/dist-client/pages/opportunity-editor.d.ts +25 -33
- package/dist-client/pages/opportunity-editor.js +108 -175
- package/dist-client/pages/opportunity-editor.js.map +1 -1
- package/dist-client/pages/opportunity-list.d.ts +2 -3
- package/dist-client/pages/opportunity-list.js +6 -12
- package/dist-client/pages/opportunity-list.js.map +1 -1
- package/dist-client/pages/quotation-editor.d.ts +45 -26
- package/dist-client/pages/quotation-editor.js +225 -248
- package/dist-client/pages/quotation-editor.js.map +1 -1
- package/dist-client/pages/quotation-list.d.ts +7 -3
- package/dist-client/pages/quotation-list.js +26 -27
- package/dist-client/pages/quotation-list.js.map +1 -1
- package/dist-client/pages/sales-product-list.d.ts +7 -2
- package/dist-client/pages/sales-product-list.js +36 -55
- package/dist-client/pages/sales-product-list.js.map +1 -1
- package/dist-client/sales-product-pick.d.ts +4 -2
- package/dist-client/sales-product-pick.js +36 -32
- package/dist-client/sales-product-pick.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/index.d.ts +1 -0
- package/dist-server/index.js +1 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/work-doors.d.ts +1 -0
- package/dist-server/work-doors.js +13 -0
- package/dist-server/work-doors.js.map +1 -0
- package/package.json +10 -10
- package/translations/en.json +4 -1
- package/translations/es.json +4 -1
- package/translations/ja.json +4 -1
- package/translations/ko.json +4 -1
- package/translations/ms.json +4 -1
- package/translations/zh.json +4 -1
|
@@ -6,7 +6,7 @@ import gql from 'graphql-tag';
|
|
|
6
6
|
import { html } from 'lit';
|
|
7
7
|
import { customElement, query, state } from 'lit/decorators.js';
|
|
8
8
|
import { DataGrist } from '@operato/data-grist';
|
|
9
|
-
import { client } from '@operato/graphql';
|
|
9
|
+
import { client, MutationController } from '@operato/graphql';
|
|
10
10
|
import { i18next, localize } from '@operato/i18n';
|
|
11
11
|
import { notify } from '@operato/layout';
|
|
12
12
|
import { OxPrompt } from '@operato/popup/ox-prompt.js';
|
|
@@ -29,7 +29,17 @@ let SalesProductList = class SalesProductList extends localize(i18next)(PageView
|
|
|
29
29
|
super(...arguments);
|
|
30
30
|
/* In use or out of use - none chosen is every product. */
|
|
31
31
|
this.inUse = '';
|
|
32
|
-
|
|
32
|
+
/* A save or a delete reads the list again, and the chips count with it. */
|
|
33
|
+
this.saving = new MutationController(this, gql `
|
|
34
|
+
mutation ($patches: [SalesProductPatch!]!) {
|
|
35
|
+
updateMultipleSalesProduct(patches: $patches) { id }
|
|
36
|
+
}
|
|
37
|
+
`);
|
|
38
|
+
this.deleting = new MutationController(this, gql `
|
|
39
|
+
mutation ($ids: [String!]!) {
|
|
40
|
+
deleteSalesProducts(ids: $ids)
|
|
41
|
+
}
|
|
42
|
+
`);
|
|
33
43
|
}
|
|
34
44
|
static { this.styles = [WorkSurfacePageStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles]; }
|
|
35
45
|
get context() {
|
|
@@ -61,12 +71,14 @@ let SalesProductList = class SalesProductList extends localize(i18next)(PageView
|
|
|
61
71
|
.mode=${isMobileDevice() ? 'CARD' : 'GRID'}
|
|
62
72
|
.config=${this.gristConfig}
|
|
63
73
|
.fetchHandler=${this.fetchHandler.bind(this)}
|
|
74
|
+
.countHandler=${this.countHandler.bind(this)}
|
|
75
|
+
@counts-change=${() => this.requestUpdate()}
|
|
64
76
|
>
|
|
65
77
|
<div slot="headroom" class="header">
|
|
66
78
|
<div class="filters">
|
|
67
79
|
<ox-filters-form without-search autofocus></ox-filters-form>
|
|
68
80
|
<ox-input-select-buttons
|
|
69
|
-
.options=${['active', 'inactive'].map(value => ({ value, display: i18next.t(`text.sales-product-${value}`), count: this.
|
|
81
|
+
.options=${['active', 'inactive'].map(value => ({ value, display: i18next.t(`text.sales-product-${value}`), count: this.grist?.counts?.active?.[value] ?? 0 }))}
|
|
70
82
|
.value=${this.inUse || undefined}
|
|
71
83
|
@change=${(e) => ((this.inUse = e.detail || ''), this.grist.fetch())}
|
|
72
84
|
></ox-input-select-buttons>
|
|
@@ -127,9 +139,24 @@ let SalesProductList = class SalesProductList extends localize(i18next)(PageView
|
|
|
127
139
|
sorters: [{ name: 'code' }]
|
|
128
140
|
};
|
|
129
141
|
}
|
|
142
|
+
/* The in-use chips' numbers, on the list's filters before the chip is added; a chip keeps its number until the next land. */
|
|
143
|
+
async countHandler({ filters, search, signal }) {
|
|
144
|
+
const response = await client.query({
|
|
145
|
+
query: gql `
|
|
146
|
+
query ($filters: [Filter!], $search: String) {
|
|
147
|
+
salesProductActiveCounts(filters: $filters, search: $search) { value count }
|
|
148
|
+
}
|
|
149
|
+
`,
|
|
150
|
+
variables: { filters, search: search?.trim() || undefined },
|
|
151
|
+
fetchPolicy: 'no-cache',
|
|
152
|
+
context: { fetchOptions: { signal }, queryDeduplication: false }
|
|
153
|
+
});
|
|
154
|
+
if (response.errors?.length)
|
|
155
|
+
throw response.errors[0];
|
|
156
|
+
return { active: Object.fromEntries(response.data.salesProductActiveCounts.map((row) => [row.value, row.count])) };
|
|
157
|
+
}
|
|
130
158
|
async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [], search }) {
|
|
131
159
|
search = search?.trim() || undefined;
|
|
132
|
-
this.countActive(filters, search);
|
|
133
160
|
if (this.inUse)
|
|
134
161
|
filters = [...filters, { name: 'active', operator: this.inUse === 'active' ? 'is_true' : 'is_not_true', value: null }];
|
|
135
162
|
const response = await client.query({
|
|
@@ -148,21 +175,6 @@ let SalesProductList = class SalesProductList extends localize(i18next)(PageView
|
|
|
148
175
|
throw response;
|
|
149
176
|
return { total: response.data.responses.total, records: response.data.responses.items };
|
|
150
177
|
}
|
|
151
|
-
/* The in-use chips' numbers, on the list's filters before the chip is added. */
|
|
152
|
-
async countActive(filters, search) {
|
|
153
|
-
const response = await client.query({
|
|
154
|
-
query: gql `
|
|
155
|
-
query ($filters: [Filter!], $search: String) {
|
|
156
|
-
salesProductActiveCounts(filters: $filters, search: $search) { value count }
|
|
157
|
-
}
|
|
158
|
-
`,
|
|
159
|
-
variables: { filters, search },
|
|
160
|
-
fetchPolicy: 'no-cache'
|
|
161
|
-
});
|
|
162
|
-
if (response.errors?.length)
|
|
163
|
-
return console.error(response.errors);
|
|
164
|
-
this.activeCounts = new Map(response.data.salesProductActiveCounts.map((row) => [row.value, row.count]));
|
|
165
|
-
}
|
|
166
178
|
/** New, changed and deleted rows together; a refusal is said in the server's words and nothing is kept. */
|
|
167
179
|
async save() {
|
|
168
180
|
const patches = this.grist.dirtyRecords.map((record) => {
|
|
@@ -171,23 +183,10 @@ let SalesProductList = class SalesProductList extends localize(i18next)(PageView
|
|
|
171
183
|
});
|
|
172
184
|
if (!patches.length)
|
|
173
185
|
return;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
updateMultipleSalesProduct(patches: $patches) { id }
|
|
179
|
-
}
|
|
180
|
-
`,
|
|
181
|
-
variables: { patches }
|
|
182
|
-
});
|
|
183
|
-
if (response.errors?.length)
|
|
184
|
-
throw response;
|
|
185
|
-
notify({ level: 'info', message: i18next.t('text.sales-product-saved') });
|
|
186
|
-
this.grist.fetch();
|
|
187
|
-
}
|
|
188
|
-
catch (error) {
|
|
189
|
-
notify({ level: 'error', message: error?.graphQLErrors?.[0]?.message ?? error?.errors?.[0]?.message ?? error?.message ?? String(error) });
|
|
190
|
-
}
|
|
186
|
+
if (!(await this.saving.run({ patches })))
|
|
187
|
+
return;
|
|
188
|
+
notify({ level: 'info', message: i18next.t('text.sales-product-saved') });
|
|
189
|
+
this.grist.fetch();
|
|
191
190
|
}
|
|
192
191
|
async delete() {
|
|
193
192
|
const ids = this.grist.selected.map((record) => record.id).filter(Boolean);
|
|
@@ -201,22 +200,8 @@ let SalesProductList = class SalesProductList extends localize(i18next)(PageView
|
|
|
201
200
|
});
|
|
202
201
|
if (!confirmed)
|
|
203
202
|
return;
|
|
204
|
-
|
|
205
|
-
const response = await client.mutate({
|
|
206
|
-
mutation: gql `
|
|
207
|
-
mutation ($ids: [String!]!) {
|
|
208
|
-
deleteSalesProducts(ids: $ids)
|
|
209
|
-
}
|
|
210
|
-
`,
|
|
211
|
-
variables: { ids }
|
|
212
|
-
});
|
|
213
|
-
if (response.errors?.length)
|
|
214
|
-
throw response;
|
|
203
|
+
if (await this.deleting.run({ ids }))
|
|
215
204
|
this.grist.fetch();
|
|
216
|
-
}
|
|
217
|
-
catch (error) {
|
|
218
|
-
notify({ level: 'error', message: error?.graphQLErrors?.[0]?.message ?? error?.errors?.[0]?.message ?? error?.message ?? String(error) });
|
|
219
|
-
}
|
|
220
205
|
}
|
|
221
206
|
};
|
|
222
207
|
__decorate([
|
|
@@ -227,10 +212,6 @@ __decorate([
|
|
|
227
212
|
state(),
|
|
228
213
|
__metadata("design:type", Object)
|
|
229
214
|
], SalesProductList.prototype, "inUse", void 0);
|
|
230
|
-
__decorate([
|
|
231
|
-
state(),
|
|
232
|
-
__metadata("design:type", Object)
|
|
233
|
-
], SalesProductList.prototype, "activeCounts", void 0);
|
|
234
215
|
__decorate([
|
|
235
216
|
query('ox-grist'),
|
|
236
217
|
__metadata("design:type", DataGrist)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sales-product-list.js","sourceRoot":"","sources":["../../client/pages/sales-product-list.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist/ox-grist.js'\nimport '@operato/data-grist/ox-filters-form.js'\nimport '@operato/input/ox-input-select-buttons.js'\n\nimport gql from 'graphql-tag'\nimport { html } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\n\nimport { DataGrist, FetchOption } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { PageView } from '@operato/shell'\nimport { CommonButtonStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\nimport { documentNumber } from '@things-factory/invoice/dist-client'\n\nimport { salesProductKindWord } from '../sales-product-words'\n\nconst KINDS = ['GOODS', 'SERVICE', 'LABOR']\n\n/** The fields a row writes - all of them for a new row, the changed ones otherwise. */\nconst FIELDS = ['code', 'name', 'kind', 'grade', 'spec', 'unit', 'unitPrice', 'currency', 'taxKind', 'active', 'description']\n\n/**\n * 상품 — what the domain sells and the price it offers it at: goods, a service, or labor by the grade of the people\n * (ADR-0171). Typed like a spreadsheet and saved together. A quotation line chosen from a product copies it at that\n * moment; changing a price here changes no written quotation. The headroom narrows it to the products in use or out of\n * use, each chip with how many it holds under the other conditions.\n */\n@customElement('sales-product-list')\nexport class SalesProductList extends localize(i18next)(PageView) {\n static styles = [WorkSurfacePageStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles]\n\n @state() gristConfig: any\n /* In use or out of use - none chosen is every product. */\n @state() private inUse = ''\n @state() private activeCounts = new Map<string, number>()\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return {\n title: i18next.t('title.sales-product-list'),\n search: {\n handler: (search: string) => {\n this.grist.searchText = search\n },\n value: this.grist?.searchText || ''\n },\n filter: {\n handler: () => {\n this.grist.toggleHeadroom()\n }\n },\n actions: [\n { title: i18next.t('button.add'), icon: 'add', action: () => this.grist.addRecordToTop({ kind: 'GOODS', currency: 'KRW', taxKind: 'TAXABLE', active: true }) },\n { title: i18next.t('button.delete'), action: () => this.delete(), ...CommonButtonStyles.delete },\n { title: i18next.t('button.save'), action: () => this.save(), ...CommonButtonStyles.save }\n ],\n exportable: { name: i18next.t('title.sales-product-list'), data: () => this.grist.exportRecords() }\n }\n }\n\n render() {\n return html`<ox-grist\n class=\"page-fill\"\n search-mode=\"term\"\n .mode=${isMobileDevice() ? 'CARD' : 'GRID'}\n .config=${this.gristConfig}\n .fetchHandler=${this.fetchHandler.bind(this)}\n >\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\">\n <ox-filters-form without-search autofocus></ox-filters-form>\n <ox-input-select-buttons\n .options=${['active', 'inactive'].map(value => ({ value, display: i18next.t(`text.sales-product-${value}`), count: this.activeCounts.get(value) ?? 0 }))}\n .value=${this.inUse || undefined}\n @change=${(e: CustomEvent) => ((this.inUse = (e.detail as string) || ''), this.grist.fetch())}\n ></ox-input-select-buttons>\n </div>\n </div>\n </ox-grist>`\n }\n\n async pageInitialized() {\n const text = (name: string, header: string, width: number, extra: any = {}) => ({ type: 'string', name, header, width, sortable: true, ...extra, record: { editable: true, ...extra.record } })\n this.gristConfig = {\n rows: { appendable: true, selectable: { multiple: true } },\n pagination: { limit: 100, pages: [50, 100, 500] },\n emptyNote: { title: i18next.t('text.sales-product-empty'), description: i18next.t('text.sales-product-empty-description') },\n list: { fields: ['name', 'code'], details: ['kind', 'unit', 'unitPrice'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n { type: 'gutter', gutterName: 'row-selector', multiple: true },\n text('code', i18next.t('field.sales-product-code'), 110, { record: { mandatory: true } }),\n text('name', i18next.t('field.sales-product-name'), 200, { record: { mandatory: true } }),\n {\n type: 'select',\n name: 'kind',\n header: i18next.t('field.sales-product-kind'),\n width: 80,\n sortable: true,\n record: { editable: true, options: KINDS.map(kind => ({ value: kind, display: salesProductKindWord(kind) })) },\n filter: { type: 'select', operator: 'eq', options: [{ display: '', value: '' }, ...KINDS.map(kind => ({ display: salesProductKindWord(kind), value: kind }))] }\n },\n text('grade', i18next.t('field.sales-product-grade'), 110),\n text('spec', i18next.t('field.sales-product-spec'), 160),\n text('unit', i18next.t('field.sales-product-unit'), 70, { record: { align: 'center' } }),\n {\n type: 'float',\n name: 'unitPrice',\n header: i18next.t('field.sales-product-unit-price'),\n width: 130,\n sortable: true,\n record: { editable: true, align: 'right', renderer: (value: number, _c: any, record: any) => documentNumber(value, record?.currency || 'KRW') }\n },\n text('currency', i18next.t('field.sales-product-currency'), 70, { record: { align: 'center' } }),\n {\n type: 'select',\n name: 'taxKind',\n header: i18next.t('field.sales-product-tax-kind'),\n width: 80,\n record: {\n editable: true,\n options: [\n { value: 'TAXABLE', display: i18next.t('text.tax-kind-taxable') },\n { value: 'ZERO_RATED', display: i18next.t('text.tax-kind-zero-rated') },\n { value: 'EXEMPT', display: i18next.t('text.tax-kind-exempt') }\n ]\n }\n },\n { type: 'checkbox', name: 'active', header: i18next.t('field.sales-product-active'), width: 60, sortable: true, record: { editable: true } },\n text('description', i18next.t('field.sales-product-description'), 220)\n ],\n sorters: [{ name: 'code' }]\n }\n }\n\n async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [], search }: FetchOption) {\n search = search?.trim() || undefined\n this.countActive(filters, search)\n if (this.inUse) filters = [...filters, { name: 'active', operator: this.inUse === 'active' ? 'is_true' : 'is_not_true', value: null }]\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!], $search: String) {\n responses: salesProducts(filters: $filters, pagination: $pagination, sortings: $sortings, search: $search) {\n items { id code name kind grade spec unit unitPrice currency taxKind active description }\n total\n }\n }\n `,\n variables: { filters, pagination: { page, limit }, sortings, search },\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 /* The in-use chips' numbers, on the list's filters before the chip is added. */\n private async countActive(filters: any[], search?: string) {\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $search: String) {\n salesProductActiveCounts(filters: $filters, search: $search) { value count }\n }\n `,\n variables: { filters, search },\n fetchPolicy: 'no-cache'\n })\n if (response.errors?.length) return console.error(response.errors)\n this.activeCounts = new Map(response.data.salesProductActiveCounts.map((row: { value: string; count: number }) => [row.value, row.count]))\n }\n\n /** New, changed and deleted rows together; a refusal is said in the server's words and nothing is kept. */\n private async save() {\n const patches = this.grist.dirtyRecords.map((record: any) => {\n const changed = record.__dirty__ === '+' ? FIELDS.filter(field => record[field] !== undefined) : Object.keys(record.__dirtyfields__ ?? {}).filter(field => FIELDS.includes(field))\n return { ...(record.id ? { id: record.id } : {}), cuFlag: record.__dirty__, ...Object.fromEntries(changed.map(field => [field, record[field] === '' ? null : record[field]])) }\n })\n if (!patches.length) return\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation ($patches: [SalesProductPatch!]!) {\n updateMultipleSalesProduct(patches: $patches) { id }\n }\n `,\n variables: { patches }\n })\n if (response.errors?.length) throw response\n notify({ level: 'info', message: i18next.t('text.sales-product-saved') })\n this.grist.fetch()\n } catch (error: any) {\n notify({ level: 'error', message: error?.graphQLErrors?.[0]?.message ?? error?.errors?.[0]?.message ?? error?.message ?? String(error) })\n }\n }\n\n private async delete() {\n const ids = this.grist.selected.map((record: any) => record.id).filter(Boolean)\n if (!ids.length) return\n const confirmed = await OxPrompt.open({\n title: i18next.t('button.delete'),\n text: i18next.t('text.sales-product-delete-confirm', { count: ids.length }),\n confirmButton: { text: i18next.t('button.delete'), destructive: true },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n if (!confirmed) return\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation ($ids: [String!]!) {\n deleteSalesProducts(ids: $ids)\n }\n `,\n variables: { ids }\n })\n if (response.errors?.length) throw response\n this.grist.fetch()\n } catch (error: any) {\n notify({ level: 'error', message: error?.graphQLErrors?.[0]?.message ?? error?.errors?.[0]?.message ?? error?.message ?? String(error) })\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sales-product-list.js","sourceRoot":"","sources":["../../client/pages/sales-product-list.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist/ox-grist.js'\nimport '@operato/data-grist/ox-filters-form.js'\nimport '@operato/input/ox-input-select-buttons.js'\n\nimport gql from 'graphql-tag'\nimport { html } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\n\nimport { DataGrist, FetchOption } from '@operato/data-grist'\nimport { client, MutationController } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { PageView } from '@operato/shell'\nimport { CommonButtonStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\nimport { documentNumber } from '@things-factory/invoice/dist-client'\n\nimport { salesProductKindWord } from '../sales-product-words'\n\nconst KINDS = ['GOODS', 'SERVICE', 'LABOR']\n\n/** The fields a row writes - all of them for a new row, the changed ones otherwise. */\nconst FIELDS = ['code', 'name', 'kind', 'grade', 'spec', 'unit', 'unitPrice', 'currency', 'taxKind', 'active', 'description']\n\n/**\n * 상품 — what the domain sells and the price it offers it at: goods, a service, or labor by the grade of the people\n * (ADR-0171). Typed like a spreadsheet and saved together. A quotation line chosen from a product copies it at that\n * moment; changing a price here changes no written quotation. The headroom narrows it to the products in use or out of\n * use, each chip with how many it holds under the other conditions.\n */\n@customElement('sales-product-list')\nexport class SalesProductList extends localize(i18next)(PageView) {\n static styles = [WorkSurfacePageStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles]\n\n @state() gristConfig: any\n /* In use or out of use - none chosen is every product. */\n @state() private inUse = ''\n /* A save or a delete reads the list again, and the chips count with it. */\n private saving = new MutationController(\n this,\n gql`\n mutation ($patches: [SalesProductPatch!]!) {\n updateMultipleSalesProduct(patches: $patches) { id }\n }\n `\n )\n private deleting = new MutationController(\n this,\n gql`\n mutation ($ids: [String!]!) {\n deleteSalesProducts(ids: $ids)\n }\n `\n )\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return {\n title: i18next.t('title.sales-product-list'),\n search: {\n handler: (search: string) => {\n this.grist.searchText = search\n },\n value: this.grist?.searchText || ''\n },\n filter: {\n handler: () => {\n this.grist.toggleHeadroom()\n }\n },\n actions: [\n { title: i18next.t('button.add'), icon: 'add', action: () => this.grist.addRecordToTop({ kind: 'GOODS', currency: 'KRW', taxKind: 'TAXABLE', active: true }) },\n { title: i18next.t('button.delete'), action: () => this.delete(), ...CommonButtonStyles.delete },\n { title: i18next.t('button.save'), action: () => this.save(), ...CommonButtonStyles.save }\n ],\n exportable: { name: i18next.t('title.sales-product-list'), data: () => this.grist.exportRecords() }\n }\n }\n\n render() {\n return html`<ox-grist\n class=\"page-fill\"\n search-mode=\"term\"\n .mode=${isMobileDevice() ? 'CARD' : 'GRID'}\n .config=${this.gristConfig}\n .fetchHandler=${this.fetchHandler.bind(this)}\n .countHandler=${this.countHandler.bind(this)}\n @counts-change=${() => this.requestUpdate()}\n >\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\">\n <ox-filters-form without-search autofocus></ox-filters-form>\n <ox-input-select-buttons\n .options=${['active', 'inactive'].map(value => ({ value, display: i18next.t(`text.sales-product-${value}`), count: this.grist?.counts?.active?.[value] ?? 0 }))}\n .value=${this.inUse || undefined}\n @change=${(e: CustomEvent) => ((this.inUse = (e.detail as string) || ''), this.grist.fetch())}\n ></ox-input-select-buttons>\n </div>\n </div>\n </ox-grist>`\n }\n\n async pageInitialized() {\n const text = (name: string, header: string, width: number, extra: any = {}) => ({ type: 'string', name, header, width, sortable: true, ...extra, record: { editable: true, ...extra.record } })\n this.gristConfig = {\n rows: { appendable: true, selectable: { multiple: true } },\n pagination: { limit: 100, pages: [50, 100, 500] },\n emptyNote: { title: i18next.t('text.sales-product-empty'), description: i18next.t('text.sales-product-empty-description') },\n list: { fields: ['name', 'code'], details: ['kind', 'unit', 'unitPrice'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n { type: 'gutter', gutterName: 'row-selector', multiple: true },\n text('code', i18next.t('field.sales-product-code'), 110, { record: { mandatory: true } }),\n text('name', i18next.t('field.sales-product-name'), 200, { record: { mandatory: true } }),\n {\n type: 'select',\n name: 'kind',\n header: i18next.t('field.sales-product-kind'),\n width: 80,\n sortable: true,\n record: { editable: true, options: KINDS.map(kind => ({ value: kind, display: salesProductKindWord(kind) })) },\n filter: { type: 'select', operator: 'eq', options: [{ display: '', value: '' }, ...KINDS.map(kind => ({ display: salesProductKindWord(kind), value: kind }))] }\n },\n text('grade', i18next.t('field.sales-product-grade'), 110),\n text('spec', i18next.t('field.sales-product-spec'), 160),\n text('unit', i18next.t('field.sales-product-unit'), 70, { record: { align: 'center' } }),\n {\n type: 'float',\n name: 'unitPrice',\n header: i18next.t('field.sales-product-unit-price'),\n width: 130,\n sortable: true,\n record: { editable: true, align: 'right', renderer: (value: number, _c: any, record: any) => documentNumber(value, record?.currency || 'KRW') }\n },\n text('currency', i18next.t('field.sales-product-currency'), 70, { record: { align: 'center' } }),\n {\n type: 'select',\n name: 'taxKind',\n header: i18next.t('field.sales-product-tax-kind'),\n width: 80,\n record: {\n editable: true,\n options: [\n { value: 'TAXABLE', display: i18next.t('text.tax-kind-taxable') },\n { value: 'ZERO_RATED', display: i18next.t('text.tax-kind-zero-rated') },\n { value: 'EXEMPT', display: i18next.t('text.tax-kind-exempt') }\n ]\n }\n },\n { type: 'checkbox', name: 'active', header: i18next.t('field.sales-product-active'), width: 60, sortable: true, record: { editable: true } },\n text('description', i18next.t('field.sales-product-description'), 220)\n ],\n sorters: [{ name: 'code' }]\n }\n }\n\n /* The in-use chips' numbers, on the list's filters before the chip is added; a chip keeps its number until the next land. */\n async countHandler({ filters, search, signal }: FetchOption) {\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $search: String) {\n salesProductActiveCounts(filters: $filters, search: $search) { value count }\n }\n `,\n variables: { filters, search: search?.trim() || undefined },\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal }, queryDeduplication: false }\n })\n if (response.errors?.length) throw response.errors[0]\n return { active: Object.fromEntries(response.data.salesProductActiveCounts.map((row: { value: string; count: number }) => [row.value, row.count])) }\n }\n\n async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [], search }: FetchOption) {\n search = search?.trim() || undefined\n if (this.inUse) filters = [...filters, { name: 'active', operator: this.inUse === 'active' ? 'is_true' : 'is_not_true', value: null }]\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!], $search: String) {\n responses: salesProducts(filters: $filters, pagination: $pagination, sortings: $sortings, search: $search) {\n items { id code name kind grade spec unit unitPrice currency taxKind active description }\n total\n }\n }\n `,\n variables: { filters, pagination: { page, limit }, sortings, search },\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 /** New, changed and deleted rows together; a refusal is said in the server's words and nothing is kept. */\n private async save() {\n const patches = this.grist.dirtyRecords.map((record: any) => {\n const changed = record.__dirty__ === '+' ? FIELDS.filter(field => record[field] !== undefined) : Object.keys(record.__dirtyfields__ ?? {}).filter(field => FIELDS.includes(field))\n return { ...(record.id ? { id: record.id } : {}), cuFlag: record.__dirty__, ...Object.fromEntries(changed.map(field => [field, record[field] === '' ? null : record[field]])) }\n })\n if (!patches.length) return\n if (!(await this.saving.run({ patches }))) return\n notify({ level: 'info', message: i18next.t('text.sales-product-saved') })\n this.grist.fetch()\n }\n\n private async delete() {\n const ids = this.grist.selected.map((record: any) => record.id).filter(Boolean)\n if (!ids.length) return\n const confirmed = await OxPrompt.open({\n title: i18next.t('button.delete'),\n text: i18next.t('text.sales-product-delete-confirm', { count: ids.length }),\n confirmButton: { text: i18next.t('button.delete'), destructive: true },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n if (!confirmed) return\n if (await this.deleting.run({ ids })) this.grist.fetch()\n }\n}\n"]}
|
|
@@ -38,10 +38,12 @@ export declare class SalesProductPick extends SalesProductPick_base {
|
|
|
38
38
|
currency: string;
|
|
39
39
|
disabled: boolean;
|
|
40
40
|
private search;
|
|
41
|
-
private
|
|
41
|
+
private open;
|
|
42
|
+
private term;
|
|
42
43
|
private active;
|
|
43
44
|
private timer?;
|
|
44
|
-
private
|
|
45
|
+
private choices;
|
|
46
|
+
private get found();
|
|
45
47
|
render(): import("lit-html").TemplateResult<1>;
|
|
46
48
|
private findSoon;
|
|
47
49
|
private find;
|
|
@@ -2,7 +2,7 @@ import { __decorate, __metadata } from "tslib";
|
|
|
2
2
|
import gql from 'graphql-tag';
|
|
3
3
|
import { css, html, LitElement, nothing } from 'lit';
|
|
4
4
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
5
|
-
import {
|
|
5
|
+
import { QueryController } from '@operato/graphql';
|
|
6
6
|
import { i18next, localize } from '@operato/i18n';
|
|
7
7
|
import { defaultTaxRate, documentNumber } from '@things-factory/invoice/dist-client';
|
|
8
8
|
/**
|
|
@@ -40,9 +40,25 @@ let SalesProductPick = class SalesProductPick extends localize(i18next)(LitEleme
|
|
|
40
40
|
this.currency = 'KRW';
|
|
41
41
|
this.disabled = false;
|
|
42
42
|
this.search = '';
|
|
43
|
-
|
|
43
|
+
/* The choices are open, for what was typed when typing last paused. */
|
|
44
|
+
this.open = false;
|
|
45
|
+
this.term = '';
|
|
44
46
|
this.active = 0;
|
|
45
|
-
|
|
47
|
+
/* The products offered for the term (ADR-0190); the last ones stay while the next term is read. */
|
|
48
|
+
this.choices = new QueryController(this, CHOICES, {
|
|
49
|
+
variables: () => this.open && !this.disabled
|
|
50
|
+
? {
|
|
51
|
+
filters: [
|
|
52
|
+
{ name: 'active', operator: 'eq', value: true },
|
|
53
|
+
{ name: 'currency', operator: 'eq', value: this.currency || 'KRW' }
|
|
54
|
+
],
|
|
55
|
+
search: this.term.trim() || undefined,
|
|
56
|
+
pagination: { page: 1, limit: 30 },
|
|
57
|
+
sortings: [{ name: 'code' }]
|
|
58
|
+
}
|
|
59
|
+
: undefined,
|
|
60
|
+
keepPrevious: true
|
|
61
|
+
});
|
|
46
62
|
}
|
|
47
63
|
static { this.styles = css `
|
|
48
64
|
:host {
|
|
@@ -106,6 +122,11 @@ let SalesProductPick = class SalesProductPick extends localize(i18next)(LitEleme
|
|
|
106
122
|
cursor: default;
|
|
107
123
|
}
|
|
108
124
|
`; }
|
|
125
|
+
get found() {
|
|
126
|
+
if (!this.open || this.choices.error)
|
|
127
|
+
return null;
|
|
128
|
+
return this.choices.data?.products?.items ?? null;
|
|
129
|
+
}
|
|
109
130
|
render() {
|
|
110
131
|
return html `
|
|
111
132
|
<input
|
|
@@ -116,7 +137,7 @@ let SalesProductPick = class SalesProductPick extends localize(i18next)(LitEleme
|
|
|
116
137
|
@focus=${() => this.find()}
|
|
117
138
|
@input=${(e) => ((this.search = e.target.value), this.findSoon())}
|
|
118
139
|
@keydown=${(e) => this.key(e)}
|
|
119
|
-
@blur=${() => setTimeout(() => (this.
|
|
140
|
+
@blur=${() => setTimeout(() => (this.open = false), 150)}
|
|
120
141
|
/>
|
|
121
142
|
${this.found
|
|
122
143
|
? html `<ul role="listbox">
|
|
@@ -134,33 +155,12 @@ let SalesProductPick = class SalesProductPick extends localize(i18next)(LitEleme
|
|
|
134
155
|
clearTimeout(this.timer);
|
|
135
156
|
this.timer = setTimeout(() => this.find(), 250);
|
|
136
157
|
}
|
|
137
|
-
|
|
158
|
+
find() {
|
|
138
159
|
if (this.disabled)
|
|
139
160
|
return;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
query: CHOICES,
|
|
144
|
-
variables: {
|
|
145
|
-
filters: [
|
|
146
|
-
{ name: 'active', operator: 'eq', value: true },
|
|
147
|
-
{ name: 'currency', operator: 'eq', value: this.currency || 'KRW' }
|
|
148
|
-
],
|
|
149
|
-
search: this.search.trim() || undefined,
|
|
150
|
-
pagination: { page: 1, limit: 30 },
|
|
151
|
-
sortings: [{ name: 'code' }]
|
|
152
|
-
},
|
|
153
|
-
fetchPolicy: 'no-cache'
|
|
154
|
-
});
|
|
155
|
-
/* Only the answer to the last thing typed is shown. */
|
|
156
|
-
if (asked !== this.asked)
|
|
157
|
-
return;
|
|
158
|
-
this.found = response.data?.products?.items ?? [];
|
|
159
|
-
this.active = 0;
|
|
160
|
-
}
|
|
161
|
-
catch {
|
|
162
|
-
this.found = null;
|
|
163
|
-
}
|
|
161
|
+
this.open = true;
|
|
162
|
+
this.term = this.search;
|
|
163
|
+
this.active = 0;
|
|
164
164
|
}
|
|
165
165
|
key(e) {
|
|
166
166
|
if (!this.found?.length || e.isComposing)
|
|
@@ -172,14 +172,14 @@ let SalesProductPick = class SalesProductPick extends localize(i18next)(LitEleme
|
|
|
172
172
|
else if (e.key === 'Enter')
|
|
173
173
|
this.choose(this.found[this.active]);
|
|
174
174
|
else if (e.key === 'Escape')
|
|
175
|
-
this.
|
|
175
|
+
this.open = false;
|
|
176
176
|
else
|
|
177
177
|
return;
|
|
178
178
|
e.preventDefault();
|
|
179
179
|
}
|
|
180
180
|
choose(product) {
|
|
181
181
|
this.search = '';
|
|
182
|
-
this.
|
|
182
|
+
this.open = false;
|
|
183
183
|
this.dispatchEvent(new CustomEvent('product', { detail: product, bubbles: true, composed: true }));
|
|
184
184
|
}
|
|
185
185
|
};
|
|
@@ -198,7 +198,11 @@ __decorate([
|
|
|
198
198
|
__decorate([
|
|
199
199
|
state(),
|
|
200
200
|
__metadata("design:type", Object)
|
|
201
|
-
], SalesProductPick.prototype, "
|
|
201
|
+
], SalesProductPick.prototype, "open", void 0);
|
|
202
|
+
__decorate([
|
|
203
|
+
state(),
|
|
204
|
+
__metadata("design:type", Object)
|
|
205
|
+
], SalesProductPick.prototype, "term", void 0);
|
|
202
206
|
__decorate([
|
|
203
207
|
state(),
|
|
204
208
|
__metadata("design:type", Object)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sales-product-pick.js","sourceRoot":"","sources":["../client/sales-product-pick.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, localize } from '@operato/i18n'\nimport { defaultTaxRate, documentNumber } from '@things-factory/invoice/dist-client'\n\n/** A product as a quotation line takes it - what it is called and sold in, its price and taxation (ADR-0171). */\nexport type PickedProduct = {\n id: string\n code: string\n name: string\n kind: string\n grade?: string | null\n spec?: string | null\n unit: string\n unitPrice: number\n currency: string\n taxKind: string\n}\n\n/**\n * The line a product makes: its name (and specification), one of its unit at its price, taxed as it is. A copy - the\n * line keeps no tie to the product, so the person changes its price or anything else, and a later price of the\n * product never reaches a written quotation (the user, 2026-10-28).\n */\nexport function lineOfProduct(product: PickedProduct) {\n return {\n description: product.spec ? `${product.name} · ${product.spec}` : product.name,\n quantity: 1,\n unit: product.unit,\n unitPrice: product.unitPrice,\n discount: 0,\n taxKind: product.taxKind,\n taxRate: defaultTaxRate(product.taxKind)\n }\n}\n\nconst CHOICES = gql`\n query ($filters: [Filter!], $search: String, $pagination: Pagination, $sortings: [Sorting!]) {\n products: salesProducts(filters: $filters, search: $search, pagination: $pagination, sortings: $sortings) {\n items { id code name kind grade spec unit unitPrice currency taxKind }\n }\n }\n`\n\n/**\n * 상품에서 추가 — one field that finds the domain's products still offered, in the document's currency, as it is typed;\n * a click takes one and fires `product` with it. The editor makes a line of it - a copy, which the person then changes\n * like any line. The interaction is the counterparty picker's (invoice `document-party-select`).\n */\n@customElement('sales-product-pick')\nexport class SalesProductPick extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n position: relative;\n display: block;\n }\n input {\n box-sizing: border-box;\n width: 100%;\n height: var(--form-element-height-medium, 32px);\n padding: 0 var(--spacing-medium, 8px);\n border: var(--input-field-border, 1px solid var(--md-sys-color-outline));\n border-radius: var(--input-field-border-radius, var(--md-sys-shape-corner-small, 4px));\n background: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n font: inherit;\n font-size: var(--ui-t-body, 13px);\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n }\n input:focus-visible {\n outline: none;\n border-color: var(--md-sys-color-primary);\n }\n ul {\n position: absolute;\n z-index: 10;\n left: 0;\n right: 0;\n top: calc(100% + 2px);\n max-height: 260px;\n overflow-y: auto;\n margin: 0;\n padding: var(--spacing-small, 4px) 0;\n list-style: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--ui-r, 10px);\n background: var(--md-sys-color-surface-container-lowest);\n box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);\n }\n li {\n display: flex;\n justify-content: space-between;\n gap: var(--spacing-large, 12px);\n padding: 6px var(--spacing-large, 12px);\n cursor: pointer;\n font-size: var(--ui-t-body, 13px);\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n }\n li:hover,\n li[active] {\n background: var(--md-sys-color-surface-container-high);\n }\n li small {\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n }\n li small,\n li.none {\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n li.none {\n cursor: default;\n }\n `\n\n /** The document's currency - only products priced in it are offered. */\n @property() currency = 'KRW'\n @property({ type: Boolean }) disabled = false\n\n @state() private search = ''\n @state() private found: PickedProduct[] | null = null\n @state() private active = 0\n\n private timer?: ReturnType<typeof setTimeout>\n private asked = 0\n\n render() {\n return html`\n <input\n .value=${this.search}\n placeholder=${i18next.t('text.sales-product-search')}\n aria-label=${i18next.t('field.sales-product-pick')}\n ?disabled=${this.disabled}\n @focus=${() => this.find()}\n @input=${(e: any) => ((this.search = e.target.value), this.findSoon())}\n @keydown=${(e: KeyboardEvent) => this.key(e)}\n @blur=${() => setTimeout(() => (this.found = null), 150)}\n />\n ${this.found\n ? html`<ul role=\"listbox\">\n ${this.found.length\n ? this.found.map(\n (one, index) =>\n html`<li role=\"option\" ?active=${index === this.active} @mousedown=${(e: Event) => (e.preventDefault(), this.choose(one))}>\n <span>${one.name}${one.spec ? ` · ${one.spec}` : ''}</span>\n <small>${[one.grade, one.unit, documentNumber(one.unitPrice, one.currency)].filter(Boolean).join(' · ')}</small>\n </li>`\n )\n : html`<li class=\"none\">${i18next.t('text.sales-product-none')}</li>`}\n </ul>`\n : nothing}\n `\n }\n\n private findSoon() {\n clearTimeout(this.timer)\n this.timer = setTimeout(() => this.find(), 250)\n }\n\n private async find() {\n if (this.disabled) return\n const asked = ++this.asked\n try {\n const response = await client.query({\n query: CHOICES,\n variables: {\n filters: [\n { name: 'active', operator: 'eq', value: true },\n { name: 'currency', operator: 'eq', value: this.currency || 'KRW' }\n ],\n search: this.search.trim() || undefined,\n pagination: { page: 1, limit: 30 },\n sortings: [{ name: 'code' }]\n },\n fetchPolicy: 'no-cache'\n })\n /* Only the answer to the last thing typed is shown. */\n if (asked !== this.asked) return\n this.found = response.data?.products?.items ?? []\n this.active = 0\n } catch {\n this.found = null\n }\n }\n\n private key(e: KeyboardEvent) {\n if (!this.found?.length || e.isComposing) return\n if (e.key === 'ArrowDown') this.active = Math.min(this.active + 1, this.found.length - 1)\n else if (e.key === 'ArrowUp') this.active = Math.max(this.active - 1, 0)\n else if (e.key === 'Enter') this.choose(this.found[this.active])\n else if (e.key === 'Escape') this.found = null\n else return\n e.preventDefault()\n }\n\n private choose(product: PickedProduct) {\n this.search = ''\n this.found = null\n this.dispatchEvent(new CustomEvent('product', { detail: product, bubbles: true, composed: true }))\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sales-product-pick.js","sourceRoot":"","sources":["../client/sales-product-pick.ts"],"names":[],"mappings":";AAAA,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AAgBpF;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,OAAsB;IAClD,OAAO;QACL,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI;QAC9E,QAAQ,EAAE,CAAC;QACX,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,QAAQ,EAAE,CAAC;QACX,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC;KACzC,CAAA;AACH,CAAC;AAED,MAAM,OAAO,GAAG,GAAG,CAAA;;;;;;CAMlB,CAAA;AAED;;;;GAIG;AAEI,IAAM,gBAAgB,GAAtB,MAAM,gBAAiB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;IAA5D;;QAgEL,wEAAwE;QAC5D,aAAQ,GAAG,KAAK,CAAA;QACC,aAAQ,GAAG,KAAK,CAAA;QAE5B,WAAM,GAAG,EAAE,CAAA;QAC5B,uEAAuE;QACtD,SAAI,GAAG,KAAK,CAAA;QACZ,SAAI,GAAG,EAAE,CAAA;QACT,WAAM,GAAG,CAAC,CAAA;QAI3B,mGAAmG;QAC3F,YAAO,GAAG,IAAI,eAAe,CACnC,IAAI,EACJ,OAAO,EACP;YACE,SAAS,EAAE,GAAG,EAAE,CACd,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ;gBACzB,CAAC,CAAC;oBACE,OAAO,EAAE;wBACP,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;wBAC/C,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK,EAAE;qBACpE;oBACD,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,SAAS;oBACrC,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;oBAClC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;iBAC7B;gBACH,CAAC,CAAC,SAAS;YACf,YAAY,EAAE,IAAI;SACnB,CACF,CAAA;IA8DH,CAAC;aA5JQ,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DlB,AA7DY,CA6DZ;IAmCD,IAAY,KAAK;QACf,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QACjD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,IAAI,IAAI,CAAA;IACnD,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAA;;iBAEE,IAAI,CAAC,MAAM;sBACN,OAAO,CAAC,CAAC,CAAC,2BAA2B,CAAC;qBACvC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC;oBACtC,IAAI,CAAC,QAAQ;iBAChB,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE;iBACjB,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;mBAC3D,CAAC,CAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;gBACpC,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,EAAE,GAAG,CAAC;;QAExD,IAAI,CAAC,KAAK;YACV,CAAC,CAAC,IAAI,CAAA;cACA,IAAI,CAAC,KAAK,CAAC,MAAM;gBACjB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CACZ,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CACb,IAAI,CAAA,6BAA6B,KAAK,KAAK,IAAI,CAAC,MAAM,eAAe,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;8BAC/G,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;+BAC1C,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;0BACnG,CACT;gBACH,CAAC,CAAC,IAAI,CAAA,oBAAoB,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,OAAO;gBACnE;YACR,CAAC,CAAC,OAAO;KACZ,CAAA;IACH,CAAC;IAEO,QAAQ;QACd,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACxB,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAA;IACjD,CAAC;IAEO,IAAI;QACV,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAM;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAA;QACvB,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;IACjB,CAAC;IAEO,GAAG,CAAC,CAAgB;QAC1B,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC,WAAW;YAAE,OAAM;QAChD,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW;YAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;aACpF,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;aACnE,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO;YAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;aAC3D,IAAI,CAAC,CAAC,GAAG,KAAK,QAAQ;YAAE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAA;;YACzC,OAAM;QACX,CAAC,CAAC,cAAc,EAAE,CAAA;IACpB,CAAC;IAEO,MAAM,CAAC,OAAsB;QACnC,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA;QAChB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAA;QACjB,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACpG,CAAC;;AA3FW;IAAX,QAAQ,EAAE;;kDAAiB;AACC;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;;kDAAiB;AAE5B;IAAhB,KAAK,EAAE;;gDAAoB;AAEX;IAAhB,KAAK,EAAE;;8CAAqB;AACZ;IAAhB,KAAK,EAAE;;8CAAkB;AACT;IAAhB,KAAK,EAAE;;gDAAmB;AAxEhB,gBAAgB;IAD5B,aAAa,CAAC,oBAAoB,CAAC;GACvB,gBAAgB,CA6J5B","sourcesContent":["import gql from 'graphql-tag'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { QueryController } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { defaultTaxRate, documentNumber } from '@things-factory/invoice/dist-client'\n\n/** A product as a quotation line takes it - what it is called and sold in, its price and taxation (ADR-0171). */\nexport type PickedProduct = {\n id: string\n code: string\n name: string\n kind: string\n grade?: string | null\n spec?: string | null\n unit: string\n unitPrice: number\n currency: string\n taxKind: string\n}\n\n/**\n * The line a product makes: its name (and specification), one of its unit at its price, taxed as it is. A copy - the\n * line keeps no tie to the product, so the person changes its price or anything else, and a later price of the\n * product never reaches a written quotation (the user, 2026-10-28).\n */\nexport function lineOfProduct(product: PickedProduct) {\n return {\n description: product.spec ? `${product.name} · ${product.spec}` : product.name,\n quantity: 1,\n unit: product.unit,\n unitPrice: product.unitPrice,\n discount: 0,\n taxKind: product.taxKind,\n taxRate: defaultTaxRate(product.taxKind)\n }\n}\n\nconst CHOICES = gql`\n query ($filters: [Filter!], $search: String, $pagination: Pagination, $sortings: [Sorting!]) {\n products: salesProducts(filters: $filters, search: $search, pagination: $pagination, sortings: $sortings) {\n items { id code name kind grade spec unit unitPrice currency taxKind }\n }\n }\n`\n\n/**\n * 상품에서 추가 — one field that finds the domain's products still offered, in the document's currency, as it is typed;\n * a click takes one and fires `product` with it. The editor makes a line of it - a copy, which the person then changes\n * like any line. The interaction is the counterparty picker's (invoice `document-party-select`).\n */\n@customElement('sales-product-pick')\nexport class SalesProductPick extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n position: relative;\n display: block;\n }\n input {\n box-sizing: border-box;\n width: 100%;\n height: var(--form-element-height-medium, 32px);\n padding: 0 var(--spacing-medium, 8px);\n border: var(--input-field-border, 1px solid var(--md-sys-color-outline));\n border-radius: var(--input-field-border-radius, var(--md-sys-shape-corner-small, 4px));\n background: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n font: inherit;\n font-size: var(--ui-t-body, 13px);\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n }\n input:focus-visible {\n outline: none;\n border-color: var(--md-sys-color-primary);\n }\n ul {\n position: absolute;\n z-index: 10;\n left: 0;\n right: 0;\n top: calc(100% + 2px);\n max-height: 260px;\n overflow-y: auto;\n margin: 0;\n padding: var(--spacing-small, 4px) 0;\n list-style: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--ui-r, 10px);\n background: var(--md-sys-color-surface-container-lowest);\n box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);\n }\n li {\n display: flex;\n justify-content: space-between;\n gap: var(--spacing-large, 12px);\n padding: 6px var(--spacing-large, 12px);\n cursor: pointer;\n font-size: var(--ui-t-body, 13px);\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n }\n li:hover,\n li[active] {\n background: var(--md-sys-color-surface-container-high);\n }\n li small {\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n }\n li small,\n li.none {\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n li.none {\n cursor: default;\n }\n `\n\n /** The document's currency - only products priced in it are offered. */\n @property() currency = 'KRW'\n @property({ type: Boolean }) disabled = false\n\n @state() private search = ''\n /* The choices are open, for what was typed when typing last paused. */\n @state() private open = false\n @state() private term = ''\n @state() private active = 0\n\n private timer?: ReturnType<typeof setTimeout>\n\n /* The products offered for the term (ADR-0190); the last ones stay while the next term is read. */\n private choices = new QueryController(\n this,\n CHOICES,\n {\n variables: () =>\n this.open && !this.disabled\n ? {\n filters: [\n { name: 'active', operator: 'eq', value: true },\n { name: 'currency', operator: 'eq', value: this.currency || 'KRW' }\n ],\n search: this.term.trim() || undefined,\n pagination: { page: 1, limit: 30 },\n sortings: [{ name: 'code' }]\n }\n : undefined,\n keepPrevious: true\n }\n )\n\n private get found(): PickedProduct[] | null {\n if (!this.open || this.choices.error) return null\n return this.choices.data?.products?.items ?? null\n }\n\n render() {\n return html`\n <input\n .value=${this.search}\n placeholder=${i18next.t('text.sales-product-search')}\n aria-label=${i18next.t('field.sales-product-pick')}\n ?disabled=${this.disabled}\n @focus=${() => this.find()}\n @input=${(e: any) => ((this.search = e.target.value), this.findSoon())}\n @keydown=${(e: KeyboardEvent) => this.key(e)}\n @blur=${() => setTimeout(() => (this.open = false), 150)}\n />\n ${this.found\n ? html`<ul role=\"listbox\">\n ${this.found.length\n ? this.found.map(\n (one, index) =>\n html`<li role=\"option\" ?active=${index === this.active} @mousedown=${(e: Event) => (e.preventDefault(), this.choose(one))}>\n <span>${one.name}${one.spec ? ` · ${one.spec}` : ''}</span>\n <small>${[one.grade, one.unit, documentNumber(one.unitPrice, one.currency)].filter(Boolean).join(' · ')}</small>\n </li>`\n )\n : html`<li class=\"none\">${i18next.t('text.sales-product-none')}</li>`}\n </ul>`\n : nothing}\n `\n }\n\n private findSoon() {\n clearTimeout(this.timer)\n this.timer = setTimeout(() => this.find(), 250)\n }\n\n private find() {\n if (this.disabled) return\n this.open = true\n this.term = this.search\n this.active = 0\n }\n\n private key(e: KeyboardEvent) {\n if (!this.found?.length || e.isComposing) return\n if (e.key === 'ArrowDown') this.active = Math.min(this.active + 1, this.found.length - 1)\n else if (e.key === 'ArrowUp') this.active = Math.max(this.active - 1, 0)\n else if (e.key === 'Enter') this.choose(this.found[this.active])\n else if (e.key === 'Escape') this.open = false\n else return\n e.preventDefault()\n }\n\n private choose(product: PickedProduct) {\n this.search = ''\n this.open = false\n this.dispatchEvent(new CustomEvent('product', { detail: product, bubbles: true, composed: true }))\n }\n}\n"]}
|