@things-factory/invoice 10.1.227 → 10.1.229
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-form.js +1 -1
- package/dist-client/document/document-form.js.map +1 -1
- package/dist-client/document/document-lines.js +3 -3
- package/dist-client/document/document-lines.js.map +1 -1
- package/dist-client/document/document-party.js +1 -1
- package/dist-client/document/document-party.js.map +1 -1
- package/dist-client/document/document-preview.js +1 -1
- package/dist-client/document/document-preview.js.map +1 -1
- package/dist-client/entity360/invoice-360.js +2 -1
- package/dist-client/entity360/invoice-360.js.map +1 -1
- package/dist-client/pages/invoice-editor.js +3 -3
- package/dist-client/pages/invoice-editor.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/index.js +2 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/service/invoice/invoice-subject.d.ts +9 -0
- package/dist-server/service/invoice/invoice-subject.js +26 -0
- package/dist-server/service/invoice/invoice-subject.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-shared/document-print.js +1 -1
- package/dist-shared/document-print.js.map +1 -1
- package/package.json +9 -9
- package/shared/document-print.ts +1 -1
- package/translations/en.json +1 -7
- package/translations/es.json +10 -11
- package/translations/ja.json +10 -11
- package/translations/ko.json +1 -7
- package/translations/ms.json +10 -11
- package/translations/zh.json +10 -11
- package/tsconfig.shared.tsbuildinfo +1 -1
|
@@ -201,7 +201,7 @@ export function renderDocumentHead(value, editable, change, dates = nothing, num
|
|
|
201
201
|
*/
|
|
202
202
|
export function renderDocumentTotals(value, discount) {
|
|
203
203
|
const currency = value.currency || 'KRW';
|
|
204
|
-
const kind = (taxKind) => taxKind === 'ZERO_RATED' ? i18next.t('text.
|
|
204
|
+
const kind = (taxKind) => taxKind === 'ZERO_RATED' ? i18next.t('text.tax-kind-zero-rated') : taxKind === 'EXEMPT' ? i18next.t('text.tax-kind-exempt') : i18next.t('text.tax-kind-taxable');
|
|
205
205
|
const discounted = Number(value.discountAmount) > 0;
|
|
206
206
|
return html `${discount?.editable
|
|
207
207
|
? html `<div class="form discount">
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-form.js","sourceRoot":"","sources":["../../client/document/document-form.ts"],"names":[],"mappings":"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","sourcesContent":["import { i18next } from '@operato/i18n'\nimport { css, html, nothing, type TemplateResult } from 'lit'\n\nimport { currencyChoices, documentAmount } from './document-amount'\n\n/*\n * The parts every commercial document editor draws — the head, the totals by tax kind and rate. Invoice and quotation\n * editors put them in their own sections; the quotation module imports them from here (ADR-0120 decision 1).\n */\n\nexport type DocumentValue = Record<string, any>\n\ntype HeadField = { name: string; label: string; wide?: boolean; prose?: boolean }\n\n/** The head fields in the order a person fills them: the document, each party as one group, then the terms. */\nexport const HEAD_FIELDS: HeadField[] = [\n { name: 'number', label: 'field.invoice-number' },\n { name: 'issueDate', label: 'field.invoice-issue-date' },\n { name: 'supplierName', label: 'field.invoice-party-name' },\n { name: 'supplierRegistrationNumber', label: 'field.invoice-party-registration-number' },\n { name: 'supplierAddress', label: 'field.invoice-party-address', prose: true },\n { name: 'buyerName', label: 'field.invoice-party-name' },\n { name: 'buyerRegistrationNumber', label: 'field.invoice-party-registration-number' },\n { name: 'buyerAddress', label: 'field.invoice-party-address', prose: true },\n { name: 'purchaseOrder', label: 'field.invoice-purchase-order' },\n { name: 'sourceReference', label: 'field.invoice-source-reference' },\n { name: 'paymentTerms', label: 'field.invoice-payment-terms', wide: true, prose: true },\n { name: 'bankDetails', label: 'field.invoice-bank-details', wide: true, prose: true },\n { name: 'notes', label: 'field.invoice-notes', wide: true, prose: true }\n]\n\nconst field = (name: string) => HEAD_FIELDS.find(one => one.name === name)!\n\nexport const DocumentFormStyles = css`\n /*\n * The lines' grid is as wide as its columns. With the grid styles the page is a flex item, and a flex item is at least\n * as wide as what it holds: the page grew past the window, the head spread into three cut-off columns and the\n * page scrolled sideways. The page and its sections may be narrower than the grid; the grid scrolls inside.\n */\n .page,\n .page-section,\n ox-grist {\n min-width: 0;\n }\n .form {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));\n gap: var(--spacing-large) var(--spacing-huge);\n }\n .form label {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-small);\n font-size: var(--ui-t-micro);\n color: var(--ui-muted);\n }\n .form .wide {\n grid-column: 1 / -1;\n }\n /* The two parties side by side, each its name, number and address together - not interleaved in one grid. */\n .parties {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));\n gap: var(--spacing-large) var(--spacing-huge);\n margin: var(--spacing-large) 0;\n }\n .parties fieldset {\n display: grid;\n gap: var(--spacing-large);\n margin: 0;\n padding: var(--spacing-large) var(--spacing-large) var(--spacing-large);\n border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n border-radius: var(--ui-r, 10px);\n }\n .parties legend {\n padding: 0 var(--spacing-small);\n font-size: var(--ui-t-small, 11.5px);\n font-weight: 600;\n color: var(--ui-ink);\n }\n .form input,\n .form select,\n .form textarea {\n box-sizing: border-box;\n width: 100%;\n height: var(--form-element-height-medium);\n font-size: var(--ui-t-body);\n color: var(--ui-ink);\n }\n /* A searchable choice stands as the inputs beside it - the same box, not an underline. */\n .form ox-select {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n height: var(--form-element-height-medium);\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-size: var(--ui-t-body);\n color: var(--ui-ink);\n }\n /* Whether the party is one of the counterparties - said quietly under the picker. */\n .form .quiet {\n font-size: var(--ui-t-micro);\n color: var(--ui-muted);\n }\n /* The payment terms the company offers, a click away above the field. */\n .form .terms {\n display: flex;\n flex-wrap: wrap;\n gap: var(--spacing-small);\n }\n .form .terms button[selected] {\n border-color: var(--md-sys-color-primary);\n color: var(--md-sys-color-primary);\n }\n .form textarea {\n height: auto;\n min-height: calc(var(--form-element-height-medium) * 2);\n }\n table.totals {\n width: 100%;\n border-collapse: collapse;\n font-variant-numeric: tabular-nums;\n }\n table.totals th,\n table.totals td {\n padding: var(--spacing-small) var(--spacing-medium);\n border-bottom: 1px solid var(--ui-line-soft);\n text-align: right;\n font-size: var(--ui-t-body);\n }\n table.totals th:first-child,\n table.totals td:first-child {\n text-align: left;\n }\n table.totals th {\n color: var(--ui-muted);\n font-weight: 400;\n }\n table.totals tfoot td {\n font-weight: 600;\n }\n /* The discount of the whole document, typed over the totals it lowers. */\n .discount {\n margin-bottom: var(--spacing-medium);\n }\n .discount input {\n max-width: 240px;\n text-align: right;\n }\n .discount .problem {\n color: var(--md-sys-color-error);\n }\n`\n\n/** The head of a document. `dates` holds the fields a kind adds — the due date, the last day of an offer. */\n/** `numberLabel` names the number as the kind does - 「청구서 번호」, 「견적 번호」. */\n/** `terms` are the payment terms the company offers (ADR-0170) - one click puts one in. */\n/**\n * `party` is the counterparty picker, drawn inside the group it fills - the buyer's of a sale, the supplier's of a\n * purchase (ADR-0170 decision 11).\n */\nexport function renderDocumentHead(\n value: DocumentValue,\n editable: boolean,\n change: (patch: DocumentValue) => void,\n dates: TemplateResult | typeof nothing = nothing,\n numberLabel = 'field.invoice-number',\n { terms = [], party: picker = nothing }: { terms?: string[]; party?: TemplateResult | typeof nothing } = {}\n) {\n const offered = (field: HeadField) =>\n field.name === 'paymentTerms' && editable && terms.length\n ? html`<span class=\"terms\">${terms.map(term => html`<button small ?selected=${value.paymentTerms === term} @click=${() => change({ paymentTerms: term })}>${term.split('\\n')[0]}</button>`)}</span>`\n : nothing\n const input = (field: HeadField) =>\n html`<label class=${field.wide ? 'wide' : ''}\n >${i18next.t(field.label)} ${offered(field)}\n ${field.prose\n ? html`<textarea .value=${value[field.name] ?? ''} ?disabled=${!editable} @input=${(e: any) => change({ [field.name]: e.target.value })}></textarea>`\n : html`<input\n type=${field.name === 'issueDate' ? 'date' : 'text'}\n .value=${value[field.name] ?? ''}\n ?disabled=${!editable}\n @input=${(e: any) => change({ [field.name]: e.target.value })}\n />`}</label\n >`\n\n const other = value.direction === 'PURCHASE' ? 'supplier' : 'buyer'\n const party = (side: 'supplier' | 'buyer', legend: string) =>\n html`<fieldset class=\"form\">\n <legend>${i18next.t(legend)}</legend>\n ${side === other ? picker : nothing} ${['Name', 'RegistrationNumber', 'Address'].map(part => input(field(`${side}${part}`)))}\n </fieldset>`\n\n return html`<div class=\"form\">\n ${input({ ...field('number'), label: numberLabel })} ${input(field('issueDate'))}\n <label\n >${i18next.t('field.invoice-direction')}\n <select .value=${value.direction} ?disabled=${!editable} @change=${(e: any) => change({ direction: e.target.value })}>\n <option value=\"SALES\" ?selected=${value.direction === 'SALES'}>${i18next.t('text.invoice-direction-sales')}</option>\n <option value=\"PURCHASE\" ?selected=${value.direction === 'PURCHASE'}>${i18next.t('text.invoice-direction-purchase')}</option>\n </select></label\n >\n <label\n >${i18next.t('field.invoice-currency')}\n <select ?disabled=${!editable} @change=${(e: any) => change({ currency: e.target.value })}>\n ${currencyChoices().map(code => html`<option value=${code} ?selected=${value.currency === code}>${code}</option>`)}\n </select></label\n >\n <label\n >${i18next.t('field.invoice-language')}\n <select ?disabled=${!editable} @change=${(e: any) => change({ language: e.target.value })}>\n <option value=\"ko\" ?selected=${value.language === 'ko'}>${i18next.t('text.invoice-language-ko')}</option>\n <option value=\"en\" ?selected=${value.language === 'en'}>${i18next.t('text.invoice-language-en')}</option>\n </select></label\n >\n ${dates}\n </div>\n <div class=\"parties\">${party('supplier', 'field.invoice-supplier-name')} ${party('buyer', 'field.invoice-buyer-name')}</div>\n <div class=\"form\">${['purchaseOrder', 'sourceReference', 'paymentTerms', 'bankDetails', 'notes'].map(name => input(field(name)))}</div>`\n}\n\n/**\n * Supply and tax of each tax kind and rate, then the document totals. With a discount of the whole document the lines'\n * supply and the discount come first, and each group's supply is the one after its part (ADR-0170 decision 8).\n * `discount` puts the discount's input over the table while a draft is written; `change` takes what is typed.\n */\nexport function renderDocumentTotals(value: DocumentValue, discount?: { value: unknown; editable: boolean; change: (value: string) => void }) {\n const currency = value.currency || 'KRW'\n const kind = (taxKind: string) =>\n taxKind === 'ZERO_RATED' ? i18next.t('text.invoice-tax-kind-zero-rated') : taxKind === 'EXEMPT' ? i18next.t('text.invoice-tax-kind-exempt') : i18next.t('text.invoice-tax-kind-taxable')\n const discounted = Number(value.discountAmount) > 0\n return html`${discount?.editable\n ? html`<div class=\"form discount\">\n <label\n >${i18next.t('field.invoice-document-discount')}\n <input type=\"number\" min=\"0\" .value=${String(discount.value ?? '')} @input=${(e: any) => discount.change(e.target.value)} />\n ${value.discountProblem ? html`<span class=\"problem\">${i18next.t('error.invoice-document-discount-invalid')}</span>` : nothing}\n </label>\n </div>`\n : nothing}\n <table class=\"totals\">\n <thead>\n <tr>\n <th>${i18next.t('field.invoice-tax-kind')}</th>\n <th>${i18next.t('field.invoice-tax-rate')}</th>\n <th>${i18next.t('field.invoice-supply-amount')}</th>\n <th>${i18next.t('field.invoice-tax-amount')}</th>\n </tr>\n </thead>\n <tbody>\n ${discounted\n ? html`<tr>\n <td colspan=\"2\">${i18next.t('field.invoice-line-sum')}</td>\n <td>${documentAmount(value.lineSupplyAmount, currency)}</td>\n <td></td>\n </tr>\n <tr>\n <td colspan=\"2\">${i18next.t('field.invoice-discount')}</td>\n <td>− ${documentAmount(value.discountAmount, currency)}</td>\n <td></td>\n </tr>`\n : nothing}\n ${(value.taxGroups ?? []).map(\n (group: any) =>\n html`<tr>\n <td>${kind(group.taxKind)}</td>\n <td>${group.taxRate}%</td>\n <td>${documentAmount(group.supplyAmount, currency)}</td>\n <td>${documentAmount(group.taxAmount, currency)}</td>\n </tr>`\n )}\n </tbody>\n <tfoot>\n <tr>\n <td colspan=\"2\">${i18next.t('field.invoice-sum')}</td>\n <td>${documentAmount(value.supplyAmount, currency)}</td>\n <td>${documentAmount(value.taxAmount, currency)}</td>\n </tr>\n <tr>\n <td colspan=\"3\">${i18next.t('field.invoice-total-amount')}</td>\n <td>${documentAmount(value.totalAmount, currency)}</td>\n </tr>\n </tfoot>\n </table>`\n}\n"]}
|
|
1
|
+
{"version":3,"file":"document-form.js","sourceRoot":"","sources":["../../client/document/document-form.ts"],"names":[],"mappings":"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","sourcesContent":["import { i18next } from '@operato/i18n'\nimport { css, html, nothing, type TemplateResult } from 'lit'\n\nimport { currencyChoices, documentAmount } from './document-amount'\n\n/*\n * The parts every commercial document editor draws — the head, the totals by tax kind and rate. Invoice and quotation\n * editors put them in their own sections; the quotation module imports them from here (ADR-0120 decision 1).\n */\n\nexport type DocumentValue = Record<string, any>\n\ntype HeadField = { name: string; label: string; wide?: boolean; prose?: boolean }\n\n/** The head fields in the order a person fills them: the document, each party as one group, then the terms. */\nexport const HEAD_FIELDS: HeadField[] = [\n { name: 'number', label: 'field.invoice-number' },\n { name: 'issueDate', label: 'field.invoice-issue-date' },\n { name: 'supplierName', label: 'field.invoice-party-name' },\n { name: 'supplierRegistrationNumber', label: 'field.invoice-party-registration-number' },\n { name: 'supplierAddress', label: 'field.invoice-party-address', prose: true },\n { name: 'buyerName', label: 'field.invoice-party-name' },\n { name: 'buyerRegistrationNumber', label: 'field.invoice-party-registration-number' },\n { name: 'buyerAddress', label: 'field.invoice-party-address', prose: true },\n { name: 'purchaseOrder', label: 'field.invoice-purchase-order' },\n { name: 'sourceReference', label: 'field.invoice-source-reference' },\n { name: 'paymentTerms', label: 'field.invoice-payment-terms', wide: true, prose: true },\n { name: 'bankDetails', label: 'field.invoice-bank-details', wide: true, prose: true },\n { name: 'notes', label: 'field.invoice-notes', wide: true, prose: true }\n]\n\nconst field = (name: string) => HEAD_FIELDS.find(one => one.name === name)!\n\nexport const DocumentFormStyles = css`\n /*\n * The lines' grid is as wide as its columns. With the grid styles the page is a flex item, and a flex item is at least\n * as wide as what it holds: the page grew past the window, the head spread into three cut-off columns and the\n * page scrolled sideways. The page and its sections may be narrower than the grid; the grid scrolls inside.\n */\n .page,\n .page-section,\n ox-grist {\n min-width: 0;\n }\n .form {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));\n gap: var(--spacing-large) var(--spacing-huge);\n }\n .form label {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-small);\n font-size: var(--ui-t-micro);\n color: var(--ui-muted);\n }\n .form .wide {\n grid-column: 1 / -1;\n }\n /* The two parties side by side, each its name, number and address together - not interleaved in one grid. */\n .parties {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));\n gap: var(--spacing-large) var(--spacing-huge);\n margin: var(--spacing-large) 0;\n }\n .parties fieldset {\n display: grid;\n gap: var(--spacing-large);\n margin: 0;\n padding: var(--spacing-large) var(--spacing-large) var(--spacing-large);\n border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n border-radius: var(--ui-r, 10px);\n }\n .parties legend {\n padding: 0 var(--spacing-small);\n font-size: var(--ui-t-small, 11.5px);\n font-weight: 600;\n color: var(--ui-ink);\n }\n .form input,\n .form select,\n .form textarea {\n box-sizing: border-box;\n width: 100%;\n height: var(--form-element-height-medium);\n font-size: var(--ui-t-body);\n color: var(--ui-ink);\n }\n /* A searchable choice stands as the inputs beside it - the same box, not an underline. */\n .form ox-select {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n height: var(--form-element-height-medium);\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-size: var(--ui-t-body);\n color: var(--ui-ink);\n }\n /* Whether the party is one of the counterparties - said quietly under the picker. */\n .form .quiet {\n font-size: var(--ui-t-micro);\n color: var(--ui-muted);\n }\n /* The payment terms the company offers, a click away above the field. */\n .form .terms {\n display: flex;\n flex-wrap: wrap;\n gap: var(--spacing-small);\n }\n .form .terms button[selected] {\n border-color: var(--md-sys-color-primary);\n color: var(--md-sys-color-primary);\n }\n .form textarea {\n height: auto;\n min-height: calc(var(--form-element-height-medium) * 2);\n }\n table.totals {\n width: 100%;\n border-collapse: collapse;\n font-variant-numeric: tabular-nums;\n }\n table.totals th,\n table.totals td {\n padding: var(--spacing-small) var(--spacing-medium);\n border-bottom: 1px solid var(--ui-line-soft);\n text-align: right;\n font-size: var(--ui-t-body);\n }\n table.totals th:first-child,\n table.totals td:first-child {\n text-align: left;\n }\n table.totals th {\n color: var(--ui-muted);\n font-weight: 400;\n }\n table.totals tfoot td {\n font-weight: 600;\n }\n /* The discount of the whole document, typed over the totals it lowers. */\n .discount {\n margin-bottom: var(--spacing-medium);\n }\n .discount input {\n max-width: 240px;\n text-align: right;\n }\n .discount .problem {\n color: var(--md-sys-color-error);\n }\n`\n\n/** The head of a document. `dates` holds the fields a kind adds — the due date, the last day of an offer. */\n/** `numberLabel` names the number as the kind does - 「청구서 번호」, 「견적 번호」. */\n/** `terms` are the payment terms the company offers (ADR-0170) - one click puts one in. */\n/**\n * `party` is the counterparty picker, drawn inside the group it fills - the buyer's of a sale, the supplier's of a\n * purchase (ADR-0170 decision 11).\n */\nexport function renderDocumentHead(\n value: DocumentValue,\n editable: boolean,\n change: (patch: DocumentValue) => void,\n dates: TemplateResult | typeof nothing = nothing,\n numberLabel = 'field.invoice-number',\n { terms = [], party: picker = nothing }: { terms?: string[]; party?: TemplateResult | typeof nothing } = {}\n) {\n const offered = (field: HeadField) =>\n field.name === 'paymentTerms' && editable && terms.length\n ? html`<span class=\"terms\">${terms.map(term => html`<button small ?selected=${value.paymentTerms === term} @click=${() => change({ paymentTerms: term })}>${term.split('\\n')[0]}</button>`)}</span>`\n : nothing\n const input = (field: HeadField) =>\n html`<label class=${field.wide ? 'wide' : ''}\n >${i18next.t(field.label)} ${offered(field)}\n ${field.prose\n ? html`<textarea .value=${value[field.name] ?? ''} ?disabled=${!editable} @input=${(e: any) => change({ [field.name]: e.target.value })}></textarea>`\n : html`<input\n type=${field.name === 'issueDate' ? 'date' : 'text'}\n .value=${value[field.name] ?? ''}\n ?disabled=${!editable}\n @input=${(e: any) => change({ [field.name]: e.target.value })}\n />`}</label\n >`\n\n const other = value.direction === 'PURCHASE' ? 'supplier' : 'buyer'\n const party = (side: 'supplier' | 'buyer', legend: string) =>\n html`<fieldset class=\"form\">\n <legend>${i18next.t(legend)}</legend>\n ${side === other ? picker : nothing} ${['Name', 'RegistrationNumber', 'Address'].map(part => input(field(`${side}${part}`)))}\n </fieldset>`\n\n return html`<div class=\"form\">\n ${input({ ...field('number'), label: numberLabel })} ${input(field('issueDate'))}\n <label\n >${i18next.t('field.invoice-direction')}\n <select .value=${value.direction} ?disabled=${!editable} @change=${(e: any) => change({ direction: e.target.value })}>\n <option value=\"SALES\" ?selected=${value.direction === 'SALES'}>${i18next.t('text.invoice-direction-sales')}</option>\n <option value=\"PURCHASE\" ?selected=${value.direction === 'PURCHASE'}>${i18next.t('text.invoice-direction-purchase')}</option>\n </select></label\n >\n <label\n >${i18next.t('field.invoice-currency')}\n <select ?disabled=${!editable} @change=${(e: any) => change({ currency: e.target.value })}>\n ${currencyChoices().map(code => html`<option value=${code} ?selected=${value.currency === code}>${code}</option>`)}\n </select></label\n >\n <label\n >${i18next.t('field.invoice-language')}\n <select ?disabled=${!editable} @change=${(e: any) => change({ language: e.target.value })}>\n <option value=\"ko\" ?selected=${value.language === 'ko'}>${i18next.t('text.invoice-language-ko')}</option>\n <option value=\"en\" ?selected=${value.language === 'en'}>${i18next.t('text.invoice-language-en')}</option>\n </select></label\n >\n ${dates}\n </div>\n <div class=\"parties\">${party('supplier', 'field.invoice-supplier-name')} ${party('buyer', 'field.invoice-buyer-name')}</div>\n <div class=\"form\">${['purchaseOrder', 'sourceReference', 'paymentTerms', 'bankDetails', 'notes'].map(name => input(field(name)))}</div>`\n}\n\n/**\n * Supply and tax of each tax kind and rate, then the document totals. With a discount of the whole document the lines'\n * supply and the discount come first, and each group's supply is the one after its part (ADR-0170 decision 8).\n * `discount` puts the discount's input over the table while a draft is written; `change` takes what is typed.\n */\nexport function renderDocumentTotals(value: DocumentValue, discount?: { value: unknown; editable: boolean; change: (value: string) => void }) {\n const currency = value.currency || 'KRW'\n const kind = (taxKind: string) =>\n taxKind === 'ZERO_RATED' ? i18next.t('text.tax-kind-zero-rated') : taxKind === 'EXEMPT' ? i18next.t('text.tax-kind-exempt') : i18next.t('text.tax-kind-taxable')\n const discounted = Number(value.discountAmount) > 0\n return html`${discount?.editable\n ? html`<div class=\"form discount\">\n <label\n >${i18next.t('field.invoice-document-discount')}\n <input type=\"number\" min=\"0\" .value=${String(discount.value ?? '')} @input=${(e: any) => discount.change(e.target.value)} />\n ${value.discountProblem ? html`<span class=\"problem\">${i18next.t('error.invoice-document-discount-invalid')}</span>` : nothing}\n </label>\n </div>`\n : nothing}\n <table class=\"totals\">\n <thead>\n <tr>\n <th>${i18next.t('field.invoice-tax-kind')}</th>\n <th>${i18next.t('field.invoice-tax-rate')}</th>\n <th>${i18next.t('field.invoice-supply-amount')}</th>\n <th>${i18next.t('field.invoice-tax-amount')}</th>\n </tr>\n </thead>\n <tbody>\n ${discounted\n ? html`<tr>\n <td colspan=\"2\">${i18next.t('field.invoice-line-sum')}</td>\n <td>${documentAmount(value.lineSupplyAmount, currency)}</td>\n <td></td>\n </tr>\n <tr>\n <td colspan=\"2\">${i18next.t('field.invoice-discount')}</td>\n <td>− ${documentAmount(value.discountAmount, currency)}</td>\n <td></td>\n </tr>`\n : nothing}\n ${(value.taxGroups ?? []).map(\n (group: any) =>\n html`<tr>\n <td>${kind(group.taxKind)}</td>\n <td>${group.taxRate}%</td>\n <td>${documentAmount(group.supplyAmount, currency)}</td>\n <td>${documentAmount(group.taxAmount, currency)}</td>\n </tr>`\n )}\n </tbody>\n <tfoot>\n <tr>\n <td colspan=\"2\">${i18next.t('field.invoice-sum')}</td>\n <td>${documentAmount(value.supplyAmount, currency)}</td>\n <td>${documentAmount(value.taxAmount, currency)}</td>\n </tr>\n <tr>\n <td colspan=\"3\">${i18next.t('field.invoice-total-amount')}</td>\n <td>${documentAmount(value.totalAmount, currency)}</td>\n </tr>\n </tfoot>\n </table>`\n}\n"]}
|
|
@@ -31,9 +31,9 @@ export function documentLinesConfig({ editable, received, currency }) {
|
|
|
31
31
|
record: {
|
|
32
32
|
editable,
|
|
33
33
|
options: [
|
|
34
|
-
{ value: 'TAXABLE', display: i18next.t('text.
|
|
35
|
-
{ value: 'ZERO_RATED', display: i18next.t('text.
|
|
36
|
-
{ value: 'EXEMPT', display: i18next.t('text.
|
|
34
|
+
{ value: 'TAXABLE', display: i18next.t('text.tax-kind-taxable') },
|
|
35
|
+
{ value: 'ZERO_RATED', display: i18next.t('text.tax-kind-zero-rated') },
|
|
36
|
+
{ value: 'EXEMPT', display: i18next.t('text.tax-kind-exempt') }
|
|
37
37
|
]
|
|
38
38
|
}
|
|
39
39
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-lines.js","sourceRoot":"","sources":["../../client/document/document-lines.ts"],"names":[],"mappings":"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","sourcesContent":["import { DataGrist } from '@operato/data-grist'\nimport { i18next } from '@operato/i18n'\n\nimport { defaultTaxRate, documentAmounts, type DocumentAmounts } from '@things-factory/invoice/dist-shared/document-amounts.js'\n\nimport { documentAmount, documentNumber } from './document-amount'\n\n/** The grid of a document's lines — typed or pasted like a spreadsheet while the document is a draft. */\nexport function documentLinesConfig({ editable, received, currency }: { editable: boolean; received?: boolean; currency: string }) {\n /* Prices and discounts read with thousands marks; the widths keep the supply in view at about 900px, the unit's column too. */\n const number = (name: string, header: string, width = 100) => ({\n type: 'float',\n name,\n header,\n width,\n record: { editable, align: 'right', renderer: (value: number) => documentNumber(value, currency) }\n })\n return {\n rows: { appendable: editable, selectable: { multiple: true }, accumulator: true },\n pagination: { infinite: true },\n emptyNote: { title: i18next.t('text.invoice-lines-empty') },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n ...(editable ? [{ type: 'gutter', gutterName: 'row-selector', multiple: true }] : []),\n { type: 'string', name: 'description', header: i18next.t('field.invoice-line-description'), width: 220, record: { editable } },\n number('quantity', i18next.t('field.invoice-quantity'), 60),\n { type: 'string', name: 'unit', header: i18next.t('field.invoice-unit'), width: 60, record: { editable, align: 'center' } },\n number('unitPrice', i18next.t('field.invoice-unit-price'), 110),\n number('discount', i18next.t('field.invoice-discount'), 80),\n {\n type: 'select',\n name: 'taxKind',\n header: i18next.t('field.invoice-tax-kind'),\n width: 70,\n record: {\n editable,\n options: [\n { value: 'TAXABLE', display: i18next.t('text.invoice-tax-kind-taxable') },\n { value: 'ZERO_RATED', display: i18next.t('text.invoice-tax-kind-zero-rated') },\n { value: 'EXEMPT', display: i18next.t('text.invoice-tax-kind-exempt') }\n ]\n }\n },\n number('taxRate', i18next.t('field.invoice-tax-rate-percent'), 56),\n ...(received ? [number('receivedTaxAmount', i18next.t('field.invoice-received-tax-amount'), 120)] : []),\n {\n type: 'float',\n name: 'supplyAmount',\n header: i18next.t('field.invoice-supply-amount'),\n width: 130,\n record: { editable: false, align: 'right', renderer: (value: number) => documentAmount(value, currency) },\n accumulator: 'sum'\n }\n ]\n }\n}\n\n/** The lines on the grid, as the save mutation takes them; blank rows are left out. */\nexport function linesOf(grist: DataGrist | null | undefined) {\n const records = (grist?.dirtyData?.records ?? []) as any[]\n return records\n .filter(record => record.__dirty__ !== '-' && String(record.description ?? '').trim())\n .map(record => ({\n description: String(record.description).trim(),\n quantity: Number(record.quantity ?? 1),\n unit: String(record.unit ?? '').trim() || null,\n unitPrice: Number(record.unitPrice ?? 0),\n discount: Number(record.discount ?? 0),\n taxKind: record.taxKind || 'TAXABLE',\n taxRate: Number(record.taxRate ?? 0),\n ...(record.receivedTaxAmount === undefined || record.receivedTaxAmount === null || record.receivedTaxAmount === ''\n ? {}\n : { receivedTaxAmount: Number(record.receivedTaxAmount) })\n }))\n}\n\nconst blank = (value: unknown) => value === undefined || value === null || value === ''\n\n/**\n * What a line typed into is missing, filled - a quantity of one, no discount, taxable at the standard rate - and its\n * rate following its tax kind when the kind is what changed (taxable 10 %, zero-rated and exempt none). A select shows\n * its first option for an empty value, so 「과세」 looked chosen and choosing it changed nothing; the line now holds it.\n */\nexport function settleLine(record: any, changed?: string) {\n if (!record || record.__dirty__ === '-') return\n if (blank(record.quantity)) record.quantity = 1\n if (blank(record.discount)) record.discount = 0\n if (!record.taxKind) record.taxKind = 'TAXABLE'\n if (changed === 'taxKind' || blank(record.taxRate)) record.taxRate = defaultTaxRate(record.taxKind)\n}\n\n/**\n * The lines on the grid reckoned as the document will keep them (shared/document-amounts.ts): each row's supply is\n * written onto it for the grid to show, and the document's tax groups and totals - after `discountAmount`, the\n * discount of the whole document - are returned for the totals table.\n */\nexport function reckonLines(grist: DataGrist | null | undefined, currency: string, received = false, discountAmount: unknown = 0): DocumentAmounts {\n const records = ((grist?.dirtyData?.records ?? []) as any[]).filter(record => record.__dirty__ !== '-' && String(record.description ?? '').trim())\n const amounts = documentAmounts(records, currency || 'KRW', received, discountAmount)\n records.forEach((record, index) => (record.supplyAmount = amounts.lines[index].supplyAmount ?? null))\n return amounts\n}\n\n/**\n * A change on the grid as an editor takes it: the changed line settled, every line reckoned, the grid redrawn with\n * the supplies. Returns the totals to show.\n */\nexport function onLineChange(grist: DataGrist | null | undefined, event: CustomEvent, currency: string, received = false, discountAmount: unknown = 0): DocumentAmounts {\n settleLine(event.detail?.after, event.detail?.column?.name)\n const amounts = reckonLines(grist, currency, received, discountAmount)\n grist?.refresh()\n return amounts\n}\n"]}
|
|
1
|
+
{"version":3,"file":"document-lines.js","sourceRoot":"","sources":["../../client/document/document-lines.ts"],"names":[],"mappings":"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","sourcesContent":["import { DataGrist } from '@operato/data-grist'\nimport { i18next } from '@operato/i18n'\n\nimport { defaultTaxRate, documentAmounts, type DocumentAmounts } from '@things-factory/invoice/dist-shared/document-amounts.js'\n\nimport { documentAmount, documentNumber } from './document-amount'\n\n/** The grid of a document's lines — typed or pasted like a spreadsheet while the document is a draft. */\nexport function documentLinesConfig({ editable, received, currency }: { editable: boolean; received?: boolean; currency: string }) {\n /* Prices and discounts read with thousands marks; the widths keep the supply in view at about 900px, the unit's column too. */\n const number = (name: string, header: string, width = 100) => ({\n type: 'float',\n name,\n header,\n width,\n record: { editable, align: 'right', renderer: (value: number) => documentNumber(value, currency) }\n })\n return {\n rows: { appendable: editable, selectable: { multiple: true }, accumulator: true },\n pagination: { infinite: true },\n emptyNote: { title: i18next.t('text.invoice-lines-empty') },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n ...(editable ? [{ type: 'gutter', gutterName: 'row-selector', multiple: true }] : []),\n { type: 'string', name: 'description', header: i18next.t('field.invoice-line-description'), width: 220, record: { editable } },\n number('quantity', i18next.t('field.invoice-quantity'), 60),\n { type: 'string', name: 'unit', header: i18next.t('field.invoice-unit'), width: 60, record: { editable, align: 'center' } },\n number('unitPrice', i18next.t('field.invoice-unit-price'), 110),\n number('discount', i18next.t('field.invoice-discount'), 80),\n {\n type: 'select',\n name: 'taxKind',\n header: i18next.t('field.invoice-tax-kind'),\n width: 70,\n record: {\n editable,\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 number('taxRate', i18next.t('field.invoice-tax-rate-percent'), 56),\n ...(received ? [number('receivedTaxAmount', i18next.t('field.invoice-received-tax-amount'), 120)] : []),\n {\n type: 'float',\n name: 'supplyAmount',\n header: i18next.t('field.invoice-supply-amount'),\n width: 130,\n record: { editable: false, align: 'right', renderer: (value: number) => documentAmount(value, currency) },\n accumulator: 'sum'\n }\n ]\n }\n}\n\n/** The lines on the grid, as the save mutation takes them; blank rows are left out. */\nexport function linesOf(grist: DataGrist | null | undefined) {\n const records = (grist?.dirtyData?.records ?? []) as any[]\n return records\n .filter(record => record.__dirty__ !== '-' && String(record.description ?? '').trim())\n .map(record => ({\n description: String(record.description).trim(),\n quantity: Number(record.quantity ?? 1),\n unit: String(record.unit ?? '').trim() || null,\n unitPrice: Number(record.unitPrice ?? 0),\n discount: Number(record.discount ?? 0),\n taxKind: record.taxKind || 'TAXABLE',\n taxRate: Number(record.taxRate ?? 0),\n ...(record.receivedTaxAmount === undefined || record.receivedTaxAmount === null || record.receivedTaxAmount === ''\n ? {}\n : { receivedTaxAmount: Number(record.receivedTaxAmount) })\n }))\n}\n\nconst blank = (value: unknown) => value === undefined || value === null || value === ''\n\n/**\n * What a line typed into is missing, filled - a quantity of one, no discount, taxable at the standard rate - and its\n * rate following its tax kind when the kind is what changed (taxable 10 %, zero-rated and exempt none). A select shows\n * its first option for an empty value, so 「과세」 looked chosen and choosing it changed nothing; the line now holds it.\n */\nexport function settleLine(record: any, changed?: string) {\n if (!record || record.__dirty__ === '-') return\n if (blank(record.quantity)) record.quantity = 1\n if (blank(record.discount)) record.discount = 0\n if (!record.taxKind) record.taxKind = 'TAXABLE'\n if (changed === 'taxKind' || blank(record.taxRate)) record.taxRate = defaultTaxRate(record.taxKind)\n}\n\n/**\n * The lines on the grid reckoned as the document will keep them (shared/document-amounts.ts): each row's supply is\n * written onto it for the grid to show, and the document's tax groups and totals - after `discountAmount`, the\n * discount of the whole document - are returned for the totals table.\n */\nexport function reckonLines(grist: DataGrist | null | undefined, currency: string, received = false, discountAmount: unknown = 0): DocumentAmounts {\n const records = ((grist?.dirtyData?.records ?? []) as any[]).filter(record => record.__dirty__ !== '-' && String(record.description ?? '').trim())\n const amounts = documentAmounts(records, currency || 'KRW', received, discountAmount)\n records.forEach((record, index) => (record.supplyAmount = amounts.lines[index].supplyAmount ?? null))\n return amounts\n}\n\n/**\n * A change on the grid as an editor takes it: the changed line settled, every line reckoned, the grid redrawn with\n * the supplies. Returns the totals to show.\n */\nexport function onLineChange(grist: DataGrist | null | undefined, event: CustomEvent, currency: string, received = false, discountAmount: unknown = 0): DocumentAmounts {\n settleLine(event.detail?.after, event.detail?.column?.name)\n const amounts = reckonLines(grist, currency, received, discountAmount)\n grist?.refresh()\n return amounts\n}\n"]}
|
|
@@ -125,7 +125,7 @@ let DocumentPartySelect = class DocumentPartySelect extends localize(i18next)(Li
|
|
|
125
125
|
<input
|
|
126
126
|
.value=${this.search}
|
|
127
127
|
placeholder=${this.party?.name ?? i18next.t('text.invoice-counterparty-search')}
|
|
128
|
-
aria-label=${i18next.t('field.
|
|
128
|
+
aria-label=${i18next.t('field.counterparty')}
|
|
129
129
|
?disabled=${this.disabled}
|
|
130
130
|
@focus=${() => this.find()}
|
|
131
131
|
@input=${(e) => ((this.search = e.target.value), this.findSoon())}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-party.js","sourceRoot":"","sources":["../../client/document/document-party.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport 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 { ButtonStyles } from '@operato/styles'\nimport { partyNamed } from '@things-factory/invoice/dist-shared/document-start.js'\n\n/** A counterparty as a document's party - which one, its name and registration number (`counterpartyChoices`). */\nexport type DocumentParty = { id: string; name: string; businessNumber?: string | null }\n\nconst CHOICES = gql`\n query ($search: String, $pagination: Pagination) {\n counterparties: counterpartyChoices(search: $search, pagination: $pagination) {\n items { id name businessNumber }\n }\n }\n`\n\n/**\n * The counterparty a party's name is - the one choice of that name, case and spaces aside (ADR-0170 decision 11); null\n * when none or more than one is, or the choices cannot be read. Searched by the name's longest word, so 「(주) 하티오랩」\n * finds 「(주)하티오랩」.\n */\nexport async function partyOfName(name: unknown): Promise<DocumentParty | null> {\n const word = String(name ?? '')\n .split(/\\s+/)\n .reduce((longest, one) => (one.length > longest.length ? one : longest), '')\n if (!word) return null\n try {\n const response = await client.query({ query: CHOICES, variables: { search: word, pagination: { page: 1, limit: 100 } }, fetchPolicy: 'no-cache' })\n return partyNamed(name, (response.data?.counterparties?.items ?? []) as DocumentParty[]) ?? null\n } catch {\n return null\n }\n}\n\n/**\n * Picking a counterparty for a document - one field that finds as it is typed and lists what it found under it; a\n * click takes one and fires `party` with it (or with null when cleared). Every editor of a commercial document picks\n * its party here - invoice and quotation (ADR-0120 decision 1). Reads `counterpartyChoices`, which every seat may.\n */\n@customElement('document-party-select')\nexport class DocumentPartySelect extends localize(i18next)(LitElement) {\n static styles = [\n ButtonStyles,\n css`\n :host {\n position: relative;\n display: block;\n }\n .field {\n display: flex;\n gap: var(--spacing-small, 4px);\n align-items: center;\n }\n input {\n flex: 1;\n min-width: 0;\n box-sizing: border-box;\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 }\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\n /** The party chosen, shown as the field's placeholder until something is typed. */\n @property({ attribute: false }) party: DocumentParty | null = null\n @property({ type: Boolean }) disabled = false\n\n @state() private search = ''\n @state() private found: DocumentParty[] | 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 <div class=\"field\">\n <input\n .value=${this.search}\n placeholder=${this.party?.name ?? i18next.t('text.invoice-counterparty-search')}\n aria-label=${i18next.t('field.invoice-counterparty')}\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.party && !this.disabled\n ? html`<button quiet icon-only title=${i18next.t('button.invoice-counterparty-clear')} aria-label=${i18next.t('button.invoice-counterparty-clear')} @click=${() => this.choose(null)}>\n <md-icon>close</md-icon>\n </button>`\n : nothing}\n </div>\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}</span>${one.businessNumber ? html`<small>${one.businessNumber}</small>` : nothing}\n </li>`\n )\n : html`<li class=\"none\">${i18next.t('text.invoice-counterparty-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({ query: CHOICES, variables: { search: this.search.trim() || undefined, pagination: { page: 1, limit: 30 } }, fetchPolicy: 'no-cache' })\n /* Only the answer to the last thing typed is shown. */\n if (asked !== this.asked) return\n this.found = response.data?.counterparties?.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(party: DocumentParty | null) {\n this.search = ''\n this.found = null\n this.dispatchEvent(new CustomEvent('party', { detail: party, bubbles: true, composed: true }))\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"document-party.js","sourceRoot":"","sources":["../../client/document/document-party.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport 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 { ButtonStyles } from '@operato/styles'\nimport { partyNamed } from '@things-factory/invoice/dist-shared/document-start.js'\n\n/** A counterparty as a document's party - which one, its name and registration number (`counterpartyChoices`). */\nexport type DocumentParty = { id: string; name: string; businessNumber?: string | null }\n\nconst CHOICES = gql`\n query ($search: String, $pagination: Pagination) {\n counterparties: counterpartyChoices(search: $search, pagination: $pagination) {\n items { id name businessNumber }\n }\n }\n`\n\n/**\n * The counterparty a party's name is - the one choice of that name, case and spaces aside (ADR-0170 decision 11); null\n * when none or more than one is, or the choices cannot be read. Searched by the name's longest word, so 「(주) 하티오랩」\n * finds 「(주)하티오랩」.\n */\nexport async function partyOfName(name: unknown): Promise<DocumentParty | null> {\n const word = String(name ?? '')\n .split(/\\s+/)\n .reduce((longest, one) => (one.length > longest.length ? one : longest), '')\n if (!word) return null\n try {\n const response = await client.query({ query: CHOICES, variables: { search: word, pagination: { page: 1, limit: 100 } }, fetchPolicy: 'no-cache' })\n return partyNamed(name, (response.data?.counterparties?.items ?? []) as DocumentParty[]) ?? null\n } catch {\n return null\n }\n}\n\n/**\n * Picking a counterparty for a document - one field that finds as it is typed and lists what it found under it; a\n * click takes one and fires `party` with it (or with null when cleared). Every editor of a commercial document picks\n * its party here - invoice and quotation (ADR-0120 decision 1). Reads `counterpartyChoices`, which every seat may.\n */\n@customElement('document-party-select')\nexport class DocumentPartySelect extends localize(i18next)(LitElement) {\n static styles = [\n ButtonStyles,\n css`\n :host {\n position: relative;\n display: block;\n }\n .field {\n display: flex;\n gap: var(--spacing-small, 4px);\n align-items: center;\n }\n input {\n flex: 1;\n min-width: 0;\n box-sizing: border-box;\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 }\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\n /** The party chosen, shown as the field's placeholder until something is typed. */\n @property({ attribute: false }) party: DocumentParty | null = null\n @property({ type: Boolean }) disabled = false\n\n @state() private search = ''\n @state() private found: DocumentParty[] | 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 <div class=\"field\">\n <input\n .value=${this.search}\n placeholder=${this.party?.name ?? i18next.t('text.invoice-counterparty-search')}\n aria-label=${i18next.t('field.counterparty')}\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.party && !this.disabled\n ? html`<button quiet icon-only title=${i18next.t('button.invoice-counterparty-clear')} aria-label=${i18next.t('button.invoice-counterparty-clear')} @click=${() => this.choose(null)}>\n <md-icon>close</md-icon>\n </button>`\n : nothing}\n </div>\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}</span>${one.businessNumber ? html`<small>${one.businessNumber}</small>` : nothing}\n </li>`\n )\n : html`<li class=\"none\">${i18next.t('text.invoice-counterparty-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({ query: CHOICES, variables: { search: this.search.trim() || undefined, pagination: { page: 1, limit: 30 } }, fetchPolicy: 'no-cache' })\n /* Only the answer to the last thing typed is shown. */\n if (asked !== this.asked) return\n this.found = response.data?.counterparties?.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(party: DocumentParty | null) {\n this.search = ''\n this.found = null\n this.dispatchEvent(new CustomEvent('party', { detail: party, bubbles: true, composed: true }))\n }\n}\n"]}
|
|
@@ -138,7 +138,7 @@ let DocumentPreview = class DocumentPreview extends localize(i18next)(LitElement
|
|
|
138
138
|
<div class="frame">
|
|
139
139
|
<!-- No script runs in the frame; an empty sandbox also kept the page from drawing in the app's browser. -->
|
|
140
140
|
<iframe
|
|
141
|
-
title=${i18next.t('title.
|
|
141
|
+
title=${i18next.t('title.preview')}
|
|
142
142
|
sandbox="allow-same-origin"
|
|
143
143
|
scrolling="no"
|
|
144
144
|
.srcdoc=${this.page}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-preview.js","sourceRoot":"","sources":["../../client/document/document-preview.ts"],"names":[],"mappings":";AAAA,OAAO,4BAA4B,CAAA;AACnC,OAAO,6BAA6B,CAAA;AACpC,OAAO,iCAAiC,CAAA;AAExC,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,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAE/D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAsB,MAAM,kBAAkB,CAAA;AAWrF,MAAM,OAAO,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;AAE/F,0DAA0D;AAC1D,SAAS,gBAAgB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAgB;IACrE,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAA;IACjF,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC1F,IAAI,CAAC,KAAK,EAAE,CAAA;IACZ,UAAU,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAA;AAClD,CAAC;AAED;;;;GAIG;AAEI,IAAM,eAAe,GAArB,MAAM,eAAgB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;IAA3D;;QAsDO,aAAQ,GAAkB,eAAe,CAAC,CAAC,CAAC,CAAA;QAEvC,SAAI,GAAG,EAAE,CAAA;QACT,SAAI,GAAG,KAAK,CAAA;IAqE/B,CAAC;aA7HQ,WAAM,GAAG;QACd,YAAY;QACZ,eAAe;QACf,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8CF;KACF,AAlDY,CAkDZ;IAQD,YAAY;QACV,IAAI,CAAC,IAAI,EAAE,CAAA;IACb,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAA;QACnG,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,KAAK,EAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC5G,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,KAAK,CAAA;QACnB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,QAAQ;QACpB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC;YACH,gBAAgB,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;QAC3D,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,KAAK,EAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC5G,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,KAAK,CAAA;QACnB,CAAC;IACH,CAAC;IAED,4FAA4F;IACpF,GAAG,CAAC,KAAwB;QAClC,MAAM,MAAM,GAAG,KAAK,CAAC,eAAe,EAAE,eAAe,EAAE,YAAY,CAAA;QACnE,IAAI,MAAM;YAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAA;IAChD,CAAC;IAED,MAAM;QACJ,MAAM,IAAI,GAAG,CAAC,QAAuB,EAAE,EAAE,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAA;QAC7I,OAAO,IAAI,CAAA;;gBAEC,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;;mBAEtC,IAAI,CAAC,QAAQ;uBACT,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;oBAC5C,CAAC,CAAc,EAAE,EAAE;YAC3B,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,CAAA;YAC1D,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQ;gBAAE,OAAM;YAC3C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAA;YACpB,IAAI,CAAC,IAAI,EAAE,CAAA;QACb,CAAC;;+BAEoB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;6CACL,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAA,qBAAqB,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;;;4BAG9F,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,WAAW,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,4BAA4B,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC;mCACtH,IAAI,CAAC,IAAI,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,qCAAqC,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;;;;;kBAKvI,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;;;oBAGhC,IAAI,CAAC,IAAI;kBACX,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAA2B,CAAC;;UAE7D,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA,wBAAwB,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO;;KAExF,CAAA;IACH,CAAC;;AAxE+B;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;+CAA4B;AAC/C;IAAX,QAAQ,EAAE;;iDAA6C;AAEvC;IAAhB,KAAK,EAAE;;6CAAkB;AACT;IAAhB,KAAK,EAAE;;6CAAqB;AAzDlB,eAAe;IAD3B,aAAa,CAAC,kBAAkB,CAAC;GACrB,eAAe,CA8H3B;;AAED,iGAAiG;AACjG,MAAM,UAAU,mBAAmB,CAAC,MAA0B,EAAE,QAAmC,EAAE,KAAa;IAChH,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,QAAQ,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,CAAA;IAC/E,OAAO,SAAS,CAAC,IAAI,CAAA,6BAA6B,MAAM,cAAc,GAAG,sBAAsB,EAAE;QAC/F,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,OAAO;QACb,KAAK;KACN,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/input/ox-select.js'\nimport '@operato/popup/ox-popup-list.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { notify, openPopup } from '@operato/layout'\nimport { ButtonStyles, ScrollbarStyles } from '@operato/styles'\n\nimport { PRINT_LANGUAGES, printDocument, type PrintLanguage } from './document-print'\n\n/** The two forms the server makes a document in (`DocumentFileFormat`): the page to preview and print, and the PDF. */\nexport type DocumentFileFormat = 'HTML' | 'PDF'\n\n/** A file the server made - its name, media type and bytes in base64 (`DocumentFile`). */\nexport type DocumentFile = { fileName: string; mimeType: string; content: string }\n\n/** How a kind's editor asks the server for its document in a form and a language - the invoice's door, the quotation's. */\nexport type DocumentFileLoader = (format: DocumentFileFormat, language: PrintLanguage) => Promise<DocumentFile>\n\nconst bytesOf = (content: string) => Uint8Array.from(atob(content), char => char.charCodeAt(0))\n\n/** Hand a file the server made to the browser to save. */\nfunction saveDocumentFile({ fileName, mimeType, content }: DocumentFile) {\n const url = URL.createObjectURL(new Blob([bytesOf(content)], { type: mimeType }))\n const link = Object.assign(document.createElement('a'), { href: url, download: fileName })\n link.click()\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n}\n\n/**\n * A saved invoice or quotation as it goes out, before it does (ADR-0170 decision 10): the server's page of A4 in a\n * frame, the language of the print to change - the document's own first - and the PDF to download or the same page to\n * print, in that language. Every commercial document's editor opens it with its own loader.\n */\n@customElement('document-preview')\nexport class DocumentPreview extends localize(i18next)(LitElement) {\n static styles = [\n ButtonStyles,\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--ui-bg, var(--md-sys-color-surface));\n }\n .bar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-bottom: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n .bar .spacer {\n flex: 1;\n }\n ox-select {\n min-width: 120px;\n }\n /* The frame grows to its page and this box scrolls it - with the house scrollbar, not the frame's own. */\n .frame {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: auto;\n background: #e9edf1;\n }\n iframe {\n display: block;\n width: 100%;\n border: 0;\n overflow: hidden;\n }\n .loading {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n background: rgba(233, 237, 241, 0.6);\n font-size: var(--ui-t-body, 13px);\n }\n `\n ]\n\n @property({ attribute: false }) loader!: DocumentFileLoader\n @property() language: PrintLanguage = PRINT_LANGUAGES[0]\n\n @state() private page = ''\n @state() private busy = false\n\n firstUpdated() {\n this.show()\n }\n\n private async show() {\n this.busy = true\n try {\n this.page = new TextDecoder().decode(bytesOf((await this.loader('HTML', this.language)).content))\n } catch (error: any) {\n notify({ level: 'error', message: error?.graphQLErrors?.[0]?.message ?? error?.message ?? String(error) })\n } finally {\n this.busy = false\n }\n }\n\n private async download() {\n this.busy = true\n try {\n saveDocumentFile(await this.loader('PDF', this.language))\n } catch (error: any) {\n notify({ level: 'error', message: error?.graphQLErrors?.[0]?.message ?? error?.message ?? String(error) })\n } finally {\n this.busy = false\n }\n }\n\n /** As tall as its page - read from its document, which is ours (same origin, no script). */\n private fit(frame: HTMLIFrameElement) {\n const height = frame.contentDocument?.documentElement?.scrollHeight\n if (height) frame.style.height = `${height}px`\n }\n\n render() {\n const word = (language: PrintLanguage) => (language === 'en' ? i18next.t('text.invoice-language-en') : i18next.t('text.invoice-language-ko'))\n return html`\n <div class=\"bar\">\n <span>${i18next.t('title.invoice-print-language')}</span>\n <ox-select\n .value=${this.language}\n aria-label=${i18next.t('title.invoice-print-language')}\n @change=${(e: CustomEvent) => {\n const next = PRINT_LANGUAGES.find(one => one === e.detail)\n if (!next || next === this.language) return\n this.language = next\n this.show()\n }}\n >\n <span slot=\"label\">${word(this.language)}</span>\n <ox-popup-list align-left nowrap>${PRINT_LANGUAGES.map(one => html`<div option value=${one}>${word(one)}</div>`)}</ox-popup-list>\n </ox-select>\n <span class=\"spacer\"></span>\n <button ?disabled=${this.busy || !this.page} @click=${() => printDocument(this.page)}><md-icon>print</md-icon>${i18next.t('button.invoice-print')}</button>\n <button raised ?disabled=${this.busy} @click=${() => this.download()}><md-icon>picture_as_pdf</md-icon>${i18next.t('button.invoice-download-pdf')}</button>\n </div>\n <div class=\"frame\">\n <!-- No script runs in the frame; an empty sandbox also kept the page from drawing in the app's browser. -->\n <iframe\n title=${i18next.t('title.invoice-preview')}\n sandbox=\"allow-same-origin\"\n scrolling=\"no\"\n .srcdoc=${this.page}\n @load=${(e: Event) => this.fit(e.target as HTMLIFrameElement)}\n ></iframe>\n ${this.busy ? html`<div class=\"loading\">${i18next.t('text.loading')}</div>` : nothing}\n </div>\n `\n }\n}\n\n/** Open the preview of a saved document, in its own language when the print has words for it. */\nexport function openDocumentPreview(loader: DocumentFileLoader, language: string | null | undefined, title: string) {\n const own = PRINT_LANGUAGES.find(one => one === language) ?? PRINT_LANGUAGES[0]\n return openPopup(html`<document-preview .loader=${loader} .language=${own}></document-preview>`, {\n backdrop: true,\n size: 'large',\n title\n })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"document-preview.js","sourceRoot":"","sources":["../../client/document/document-preview.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/input/ox-select.js'\nimport '@operato/popup/ox-popup-list.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { notify, openPopup } from '@operato/layout'\nimport { ButtonStyles, ScrollbarStyles } from '@operato/styles'\n\nimport { PRINT_LANGUAGES, printDocument, type PrintLanguage } from './document-print'\n\n/** The two forms the server makes a document in (`DocumentFileFormat`): the page to preview and print, and the PDF. */\nexport type DocumentFileFormat = 'HTML' | 'PDF'\n\n/** A file the server made - its name, media type and bytes in base64 (`DocumentFile`). */\nexport type DocumentFile = { fileName: string; mimeType: string; content: string }\n\n/** How a kind's editor asks the server for its document in a form and a language - the invoice's door, the quotation's. */\nexport type DocumentFileLoader = (format: DocumentFileFormat, language: PrintLanguage) => Promise<DocumentFile>\n\nconst bytesOf = (content: string) => Uint8Array.from(atob(content), char => char.charCodeAt(0))\n\n/** Hand a file the server made to the browser to save. */\nfunction saveDocumentFile({ fileName, mimeType, content }: DocumentFile) {\n const url = URL.createObjectURL(new Blob([bytesOf(content)], { type: mimeType }))\n const link = Object.assign(document.createElement('a'), { href: url, download: fileName })\n link.click()\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n}\n\n/**\n * A saved invoice or quotation as it goes out, before it does (ADR-0170 decision 10): the server's page of A4 in a\n * frame, the language of the print to change - the document's own first - and the PDF to download or the same page to\n * print, in that language. Every commercial document's editor opens it with its own loader.\n */\n@customElement('document-preview')\nexport class DocumentPreview extends localize(i18next)(LitElement) {\n static styles = [\n ButtonStyles,\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--ui-bg, var(--md-sys-color-surface));\n }\n .bar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-bottom: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n .bar .spacer {\n flex: 1;\n }\n ox-select {\n min-width: 120px;\n }\n /* The frame grows to its page and this box scrolls it - with the house scrollbar, not the frame's own. */\n .frame {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: auto;\n background: #e9edf1;\n }\n iframe {\n display: block;\n width: 100%;\n border: 0;\n overflow: hidden;\n }\n .loading {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n background: rgba(233, 237, 241, 0.6);\n font-size: var(--ui-t-body, 13px);\n }\n `\n ]\n\n @property({ attribute: false }) loader!: DocumentFileLoader\n @property() language: PrintLanguage = PRINT_LANGUAGES[0]\n\n @state() private page = ''\n @state() private busy = false\n\n firstUpdated() {\n this.show()\n }\n\n private async show() {\n this.busy = true\n try {\n this.page = new TextDecoder().decode(bytesOf((await this.loader('HTML', this.language)).content))\n } catch (error: any) {\n notify({ level: 'error', message: error?.graphQLErrors?.[0]?.message ?? error?.message ?? String(error) })\n } finally {\n this.busy = false\n }\n }\n\n private async download() {\n this.busy = true\n try {\n saveDocumentFile(await this.loader('PDF', this.language))\n } catch (error: any) {\n notify({ level: 'error', message: error?.graphQLErrors?.[0]?.message ?? error?.message ?? String(error) })\n } finally {\n this.busy = false\n }\n }\n\n /** As tall as its page - read from its document, which is ours (same origin, no script). */\n private fit(frame: HTMLIFrameElement) {\n const height = frame.contentDocument?.documentElement?.scrollHeight\n if (height) frame.style.height = `${height}px`\n }\n\n render() {\n const word = (language: PrintLanguage) => (language === 'en' ? i18next.t('text.invoice-language-en') : i18next.t('text.invoice-language-ko'))\n return html`\n <div class=\"bar\">\n <span>${i18next.t('title.invoice-print-language')}</span>\n <ox-select\n .value=${this.language}\n aria-label=${i18next.t('title.invoice-print-language')}\n @change=${(e: CustomEvent) => {\n const next = PRINT_LANGUAGES.find(one => one === e.detail)\n if (!next || next === this.language) return\n this.language = next\n this.show()\n }}\n >\n <span slot=\"label\">${word(this.language)}</span>\n <ox-popup-list align-left nowrap>${PRINT_LANGUAGES.map(one => html`<div option value=${one}>${word(one)}</div>`)}</ox-popup-list>\n </ox-select>\n <span class=\"spacer\"></span>\n <button ?disabled=${this.busy || !this.page} @click=${() => printDocument(this.page)}><md-icon>print</md-icon>${i18next.t('button.invoice-print')}</button>\n <button raised ?disabled=${this.busy} @click=${() => this.download()}><md-icon>picture_as_pdf</md-icon>${i18next.t('button.invoice-download-pdf')}</button>\n </div>\n <div class=\"frame\">\n <!-- No script runs in the frame; an empty sandbox also kept the page from drawing in the app's browser. -->\n <iframe\n title=${i18next.t('title.preview')}\n sandbox=\"allow-same-origin\"\n scrolling=\"no\"\n .srcdoc=${this.page}\n @load=${(e: Event) => this.fit(e.target as HTMLIFrameElement)}\n ></iframe>\n ${this.busy ? html`<div class=\"loading\">${i18next.t('text.loading')}</div>` : nothing}\n </div>\n `\n }\n}\n\n/** Open the preview of a saved document, in its own language when the print has words for it. */\nexport function openDocumentPreview(loader: DocumentFileLoader, language: string | null | undefined, title: string) {\n const own = PRINT_LANGUAGES.find(one => one === language) ?? PRINT_LANGUAGES[0]\n return openPopup(html`<document-preview .loader=${loader} .language=${own}></document-preview>`, {\n backdrop: true,\n size: 'large',\n title\n })\n}\n"]}
|
|
@@ -18,6 +18,7 @@ export function registerInvoiceKind() {
|
|
|
18
18
|
registerEntity360Kind({
|
|
19
19
|
kind: 'invoice',
|
|
20
20
|
typenames: ['Invoice'],
|
|
21
|
+
assistantSubject: true,
|
|
21
22
|
title: ({ value }) => `${i18next.t('title.invoice')} · ${counterpartOf(value) || value.number || ''}`,
|
|
22
23
|
context: ({ value }) => [value.number, value.issueDate, value.status ? invoiceStatusWord(value.status) : ''].filter(Boolean).join(' · ')
|
|
23
24
|
});
|
|
@@ -95,7 +96,7 @@ let InvoiceOverviewSection = class InvoiceOverviewSection extends LitElement {
|
|
|
95
96
|
? html `<dl>
|
|
96
97
|
<dt>${i18next.t('field.invoice-number')}</dt>
|
|
97
98
|
<dd>${one.number} · ${invoiceStatusWord(one.status)}</dd>
|
|
98
|
-
<dt>${i18next.t('field.
|
|
99
|
+
<dt>${i18next.t('field.counterparty')}</dt>
|
|
99
100
|
<dd>${one.counterpartyId ? counterpartCell(one) : '—'}</dd>
|
|
100
101
|
<dt>${i18next.t('field.invoice-supplier-name')}</dt>
|
|
101
102
|
<dd>${one.supplierName}</dd>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoice-360.js","sourceRoot":"","sources":["../../client/entity360/invoice-360.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"invoice-360.js","sourceRoot":"","sources":["../../client/entity360/invoice-360.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next } from '@operato/i18n'\nimport { entity360Name, readEntity360Door, registerEntity360Kind, registerEntity360Section, type Entity360DoorAnswer, type Entity360Subject } from '@operato/layout'\n\nimport { DocumentFormStyles, renderDocumentTotals } from '../document/document-form'\nimport { invoice360Query } from '../invoice-attach'\nimport '../invoice-files'\n\n/** The other party: the buyer of a sale, the supplier of a purchase. */\nexport const counterpartOf = (value: { direction?: string; supplierName?: string; buyerName?: string }) =>\n (value.direction === 'PURCHASE' ? value.supplierName : value.buyerName) || ''\n\n/** The other party as printed - a link to its counterparty's 360 when the invoice names one, else the printed name. */\nexport const counterpartCell = (value: { counterpartyId?: string | null; direction?: string; supplierName?: string; buyerName?: string }) =>\n value.counterpartyId ? entity360Name({ __typename: 'Counterparty', id: value.counterpartyId, name: counterpartOf(value) }, counterpartOf(value)) : counterpartOf(value)\n\nexport const invoiceStatusWord = (status?: string) =>\n status === 'ISSUED' ? i18next.t('text.invoice-status-issued') : status === 'VOID' ? i18next.t('text.invoice-status-void') : i18next.t('text.invoice-status-draft')\n\n/** An invoice's 360 (ADR-0067). The module draws the invoice; an application hangs its own sections on 'invoice'. */\nexport function registerInvoiceKind() {\n registerEntity360Kind({\n kind: 'invoice',\n typenames: ['Invoice'],\n assistantSubject: true,\n title: ({ value }) => `${i18next.t('title.invoice')} · ${counterpartOf(value) || value.number || ''}`,\n context: ({ value }) => [value.number, value.issueDate, value.status ? invoiceStatusWord(value.status) : ''].filter(Boolean).join(' · ')\n })\n registerEntity360Section('invoice', {\n name: 'overview',\n order: 10,\n render: subject => html`<invoice-overview-section .subject=${subject}></invoice-overview-section>`\n })\n registerEntity360Section('invoice', {\n name: 'files',\n order: 20,\n render: subject => html`<ox-entity360-section .heading=${i18next.t('text.invoice-section-files')} .state=${'ready'}><invoice-files .invoiceId=${subject.id}></invoice-files></ox-entity360-section>`\n })\n}\n\n@customElement('invoice-overview-section')\nexport class InvoiceOverviewSection extends LitElement {\n static styles = [\n DocumentFormStyles,\n css`\n :host {\n font-size: var(--ui-t-body);\n line-height: 1.5;\n }\n dl {\n margin: 0 0 var(--spacing-large);\n display: grid;\n grid-template-columns: max-content 1fr;\n gap: var(--spacing-small) var(--spacing-large);\n }\n dt {\n color: var(--ui-muted);\n }\n dd {\n margin: 0;\n overflow-wrap: anywhere;\n font-variant-numeric: tabular-nums;\n }\n `\n ]\n\n @property({ attribute: false }) subject?: Entity360Subject\n @state() answer?: Entity360DoorAnswer<any>\n\n updated(changed: Map<string, unknown>) {\n if (changed.has('subject')) this.read()\n }\n\n private async read() {\n const id = this.subject?.id\n this.answer = undefined\n if (!id) return\n let response: any\n try {\n response = await client.query({\n query: gql(`\n query ($id: String!) {\n invoice: ${invoice360Query()}(id: $id) {\n id number status direction currency issueDate dueDate counterpartyId supplierName buyerName purchaseOrder\n supplyAmount taxAmount totalAmount taxGroups { taxKind taxRate supplyAmount taxAmount }\n }\n }\n `),\n variables: { id },\n fetchPolicy: 'no-cache',\n errorPolicy: 'all'\n } as any)\n } catch (e: any) {\n response = { data: null, errors: e?.graphQLErrors?.length ? e.graphQLErrors : [{ message: e?.message ?? String(e) }] }\n }\n if (this.subject?.id !== id) return\n this.answer = readEntity360Door(response, data => data?.invoice)\n }\n\n render() {\n const one = this.answer?.data\n return html`<ox-entity360-section .heading=${i18next.t('title.invoice')} .state=${this.answer?.state ?? 'loading'} .message=${this.answer?.message ?? ''}>\n ${one\n ? html`<dl>\n <dt>${i18next.t('field.invoice-number')}</dt>\n <dd>${one.number} · ${invoiceStatusWord(one.status)}</dd>\n <dt>${i18next.t('field.counterparty')}</dt>\n <dd>${one.counterpartyId ? counterpartCell(one) : '—'}</dd>\n <dt>${i18next.t('field.invoice-supplier-name')}</dt>\n <dd>${one.supplierName}</dd>\n <dt>${i18next.t('field.invoice-buyer-name')}</dt>\n <dd>${one.buyerName}</dd>\n <dt>${i18next.t('field.invoice-issue-date')}</dt>\n <dd>${one.issueDate ?? '—'}</dd>\n <dt>${i18next.t('field.invoice-due-date')}</dt>\n <dd>${one.dueDate ?? '—'}</dd>\n </dl>\n ${renderDocumentTotals(one)}`\n : ''}\n </ox-entity360-section>`\n }\n}\n"]}
|
|
@@ -101,7 +101,7 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
|
|
|
101
101
|
actions.push({ title: i18next.t('button.invoice-issue'), icon: 'send', emphasis: { raised: true }, action: () => this.issue() });
|
|
102
102
|
}
|
|
103
103
|
if (this.invoice)
|
|
104
|
-
actions.push({ title: i18next.t('button.
|
|
104
|
+
actions.push({ title: i18next.t('button.preview'), icon: 'preview', action: () => this.preview() });
|
|
105
105
|
return {
|
|
106
106
|
title: this.invoice ? `${i18next.t('title.invoice')} ${this.invoice.number}` : i18next.t('title.invoice-new'),
|
|
107
107
|
...(this.invoice ? { description: invoiceStatusWord(this.invoice.status) } : {}),
|
|
@@ -194,7 +194,7 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
|
|
|
194
194
|
renderParty(editable) {
|
|
195
195
|
const name = this.form.direction === 'PURCHASE' ? this.form.supplierName : this.form.buyerName;
|
|
196
196
|
return html `<label
|
|
197
|
-
>${i18next.t('field.
|
|
197
|
+
>${i18next.t('field.counterparty')}
|
|
198
198
|
<document-party-select .party=${this.form.counterparty ?? null} ?disabled=${!editable} @party=${(e) => this.chooseParty(e.detail)}></document-party-select>
|
|
199
199
|
${this.invoice && !this.form.counterpartyId && String(name ?? '').trim() ? html `<span class="quiet">${i18next.t('text.invoice-counterparty-unlinked')}</span>` : nothing}
|
|
200
200
|
</label>`;
|
|
@@ -449,7 +449,7 @@ let InvoiceEditor = class InvoiceEditor extends localize(i18next)(PageView) {
|
|
|
449
449
|
if (!response.data?.invoiceDocumentFile)
|
|
450
450
|
throw response.errors?.[0] ?? new Error(i18next.t('text.invoice-not-found'));
|
|
451
451
|
return response.data.invoiceDocumentFile;
|
|
452
|
-
}, language, `${i18next.t('title.
|
|
452
|
+
}, language, `${i18next.t('title.preview')} · ${number}`);
|
|
453
453
|
}
|
|
454
454
|
};
|
|
455
455
|
__decorate([
|