import { SolidTemplateElement, Helpers, store } from 'https://cdn.skypack.dev/@startinblox/core@0.17'; import { openInvoiceWindow, cleanInvoiceBeforeDuplicate } from './utils/functions.js'; // const base_url = import.meta.url.replace(/\/[^\/]*$/, ''); const base_url = "https://cdn.skypack.dev/@startinblox/component-invoicing@1.3"; Helpers.importCSS(`${base_url}/css/main.css?min`); export class SolidInvoicing extends SolidTemplateElement { constructor() { super(); this.setTranslationsPath(`${base_url}/locales`); if (window.hubl) { this.localize = (key) => { return window.hubl.intl.t("invoices." + key) || this.strings[key] || key; } } // populate callbacks this.customerPopulateCallback = null; this.customerElement = null; this.freelancerPopulateCallback = null; this.freelancerElement = null; } static get propsDefinition() { return { dataSrc: 'data-src', routerPrefix: 'router-prefix', uploadUrl: 'upload-dir', logo: 'logo-dir' } } connectedCallback() { this.addEventListener('click', (e) => { // Display invoice list if(e.target && e.target.closest('.show-list')){ let invoice = e.target.closest('.invoices-list'); invoice.classList.toggle('invoices-list-visible') } // Display invoice details if(e.target && e.target.closest('[name="invoice-title"]')){ let invoice = e.target.closest('[name="invoice-title"]'); invoice.closest('solid-display').classList.toggle('invoice-visible') } // Close dialog if(e.target && e.target.closest('.close-button')){ let routeName = e.target.closest('#clients-invoices') ? 'invoice-list' : 'freelance-invoice-list' window.dispatchEvent( new CustomEvent('requestNavigation', { detail: { route: routeName } }), ); } // Print invoice if (e.target && e.target.closest('.print')) { const printWindow = openInvoiceWindow('print', base_url); setTimeout(() => { printWindow.print(); printWindow.close(); }, 1000); return true; } // Download invoice if (e.target && e.target.closest('.download')) { openInvoiceWindow('download', base_url); return true; } // Duplicate invoice if (e.target && e.target.closest('.duplication-button')) { const form = this.querySelector('.duplicate-form').component; form.getFormValue().then((value) => { let resources = value; resources["@context"] = form.context; const resourceId = this.querySelector('.new-customer-invoice-form')?.component.resourceId; if (!resourceId) { console.warn('No URI found to post a new invoice.'); return; } store.post(cleanInvoiceBeforeDuplicate(resources), resourceId).then(() => { this.dispatchEvent( new CustomEvent('requestNavigation', { bubbles: true, detail: { route: "invoice-list" }, }), ); }); }); } // Close duplicate dialog if (e.target && e.target.closest('.close-duplication-dialog')) { this.dispatchEvent( new CustomEvent('requestNavigation', { bubbles: true, detail: { route: "invoice-list" }, }), ); } }); // Reactivity: subscribe batches to invoices changes this.addEventListener('widgetRendered', (e) => { if (e.target.tagName === 'WIDGET-TASKS') { const batchDisplay = e.target.closest('solid-display'); if (!batchDisplay) return; const invoiceSrc = batchDisplay.closest('.invoice-details')?.dataset.src; if (!invoiceSrc) return; store.subscribeResourceTo(batchDisplay.dataset.src, invoiceSrc); } }); } /** * Get total amount of a list of invoices * @param {Element} displayElement - solid-display of the invoices * @param {boolean} includeBusinessProvision - calculate business provision * @returns {{amount: number, businessProvisionAmount: number, businessProvision: number}} amount informations of invoices */ async getTotalAmount(displayElement, includeBusinessProvision = false) { return new Promise(async (resolve, reject) => { if (!displayElement) reject('Element not found'); const invoices = displayElement.component.resource; if (!invoices) resolve(0); const amount = await Promise.all(invoices['ldp:contains'].map(invoice => invoice.htAmount)) .then(a => a.reduce((acc, current) => parseFloat(acc) + (parseFloat(current) || 0), 0)); let businessProvisionAmount = null; let businessProvision = null; if (includeBusinessProvision) { businessProvision = await this.getProjectBusinessProvision(); businessProvisionAmount = amount * (businessProvision / 100) } resolve({ amount, businessProvisionAmount, businessProvision }) }) } /** * Calculate the business provision of the project * @returns {Promise<number>} business provision */ async getProjectBusinessProvision() { const projectId = this.getAttribute('data-src'); if (!projectId) return; let project = store.get(projectId); if (!project) { const context = this.querySelector('#customer-invoices')?.context; project = await store.getData(projectId, context || {}) } const businessProvidersList = await project['businessprovider']; let businessProvision = 0; if (businessProvidersList) { for (const businessProvider of businessProvidersList['ldp:contains']) { businessProvision += await businessProvider['fee']; } } return businessProvision; } /** * Generate recap and show values */ generateRecap() { const customerInvoices = this.querySelector('#customer-invoices') const freelancerInvoices = this.querySelector('#freelancer-invoices') const recapFreelancerInvoices = this.querySelector('#recap-freelancer-invoices') const recapCustomerInvoices = this.querySelector('#recap-customer-invoices') const recapFees = this.querySelector('#recap-fees') const recapBusinessProvision = this.querySelector('#recap-business-provision') const infoBusinessProvision = this.querySelector('#info-business-provision') const recapTotal = this.querySelector('#recap-total') Promise.all([this.getTotalAmount(customerInvoices, true), this.getTotalAmount(freelancerInvoices, false)]) .then(([customerTotal, freelancerTotal]) => { if (customerTotal) { // customer amounts recapCustomerInvoices.innerText = customerTotal.amount.toFixed(2); recapFees.innerText = (customerTotal.amount * 0.05).toFixed(2); recapBusinessProvision.innerText = (customerTotal.businessProvisionAmount).toFixed(2); infoBusinessProvision.innerText = `${customerTotal.businessProvision}%` } if (freelancerTotal) { // freelancer amounts recapFreelancerInvoices.innerText = freelancerTotal.amount.toFixed(2); } const recapAmount = (customerTotal.amount || 0) - (freelancerTotal.amount || 0); recapTotal.innerText = recapAmount.toFixed(2); if (recapAmount > 0) { recapTotal.classList.remove('icon-dislike'); recapTotal.classList.add('icon-like'); } else { recapTotal.classList.remove('icon-like'); recapTotal.classList.add('icon-dislike'); } }) .catch(() => { console.warn('An error occured while generating the recap'); }) } /** * Attach populate listeners on invoices displays */ attachPopulateListeners() { this.detachPopulateListeners(); // to prevent memory leak, detach listeners this.customerElement = this.querySelector('#customer-invoices') this.freelancerElement = this.querySelector('#freelancer-invoices') // Customer callback this.customerPopulateCallback = () => { // if freelancer populated, generate recap if (this.freelancerElement && this.freelancerElement.component.resource) this.generateRecap(); } // Freelancer callback this.freelancerPopulateCallback = () => { // if customer populated, generate recap if (this.customerElement && this.customerElement.component.resource) this.generateRecap(); } // Attach callbacks to events this.customerElement.addEventListener('populate', this.customerPopulateCallback) this.freelancerElement.addEventListener('populate', this.freelancerPopulateCallback) } /** * Detach listeners */ detachPopulateListeners() { if (this.customerElement && this.customerPopulateCallback) { this.customerElement.removeEventListener('populate', this.customerPopulateCallback); } if (this.freelancerElement && this.freelancerPopulateCallback) { this.freelancerElement.removeEventListener('populate', this.freelancerPopulateCallback); } } /** * Sent after the rendering of the component */ renderCallback() { this.attachPopulateListeners(); } template({dataSrc, routerPrefix, uploadUrl, logo}) { let prefix = routerPrefix ? `route-prefix="${routerPrefix}"`: '' return ` <div id="solid-invoicing" class="solid-invoicing segment full padding-large sm-padding-xsmall sm-padding-top-xlarge"> <!-- widgets ==================================== --> <solid-widget name="widget-money"> <template> <div>\${value?value:0} € </div> </template> </solid-widget> <solid-widget name="widget-tva-rate"> <template> <div>${this.localize('widget.tva_rate')} \${value} %</div> </template> </solid-widget> <solid-widget name="widget-invoice-number"> <template> <div class="text-color-heading">N°\${value}</div> </template> </solid-widget> <solid-widget name="widget-batches"> <template> <solid-display class="batches-list" data-src="\${value}" fields="batch-header(title, amount), batch-body(tasks), batch-footer(ht-total(ht-label, htAmount))" value-amount="${this.localize('value.amount')}" value-ht-label="${this.localize('label.value_ht')}" widget-tasks="widget-tasks" widget-htAmount="widget-money" ></solid-display> </template> </solid-widget> <solid-widget name="widget-tasks"> <template> <solid-display class="tasks-list" data-src="\${value}" fields="price-line(title, htAmount)" widget-title="solid-display-div-multiline" widget-htAmount="widget-money" ></solid-display> </template> </solid-widget> <solid-widget name="widget-form-batches"> <template> <solid-form data-src="\${value}" data-holder naked fields="batch-header(title), batch-body(tasks)" widget-tasks="widget-form-tasks" widget-title="solid-form-placeholder-text" maxlength-title="70" label-title="${this.localize('label.title_batch')}" label-tasks="" multiple-tasks required-title > </solid-form> </template> </solid-widget> <solid-widget name="widget-form-tasks"> <template> <solid-form data-src="\${value}" class="tasks-list" data-holder naked fields="price-line(title, htAmount)" widget-htAmount="solid-form-placeholder-number" widget-title="solid-form-placeholder-textarea" label-title="${this.localize('label.title_task')}" label-htAmount="${this.localize('label.ht_amount')}" required-title required-htAmount ></solid-form> </template> </solid-widget> <solid-widget name="widget-render-title"> <template> <div><strong>${this.localize('widget.render_title')}\${value}</strong></div> </template> </solid-widget> <solid-widget name="widget-render-metadata"> <template> <div> <div class="invoice-render__fees"> <p>\${value}</p> <p>${this.localize('widget.render_metadata_paymentdate')}</p> <p>${this.localize('widget.render_metadata_late')}</p> <p>${this.localize('widget.render_metadata_indemnity')}</p> </div> <div class="invoice-render__rib"> <div><strong>${this.localize('widget.render_metadata_bank_transfert')}</strong></div> <table> <tr> <td>BIC</td> <td>CMCIFR2A</td> </tr> <tr> <td>IBAN</td> <td>FR76 1027 8060 8200 0205 5370 166</td> </tr> </table> </div> </div> </template> </solid-widget> <solid-widget name="widget-embed-drive"> <template> <embed src="\${value}" width="560px" height="800px"/> </template> </solid-widget> <solid-widget name="widget-nomenclature"> <template> <div> <b>${this.localize('title.nomenclature')}</b> : ${this.localize('text.nomenclature')} </div> </template> </solid-widget> <solid-widget name="widget-state"> <template> \${value == 'edited' ? '<div class="segment full button text-xsmall text-bold text-center reversed color-secondary bordered">${this.localize('option.edited')}</div>' : '' } \${value == 'pending' ? '<div class="segment full button text-xsmall text-bold text-center color-secondary bordered">${this.localize('option.pending')}</div>' : '' } \${value == 'sent' ? '<div class="segment full button text-xsmall text-bold text-center color-third bordered">${this.localize('option.sent')}</div>' : '' } \${value == 'paid' ? '<div class="segment full button text-xsmall text-bold text-center reversed paid bordered">${this.localize('option.paid')}</div>' : '' } \${value == 'late' ? '<div class="segment full button text-xsmall text-bold text-center reversed bordered late">${this.localize('option.late')}</div>' : '' } </template> </solid-widget> <solid-widget name="close-button"> <template> <button class="close-button icon icon-close"> </button> </template> </solid-widget> <!-- /widgets ==================================== --> <header class="segment full main-menu"> <solid-router class="segment full margin-bottom-medium" default-route="invoices-summary" ${prefix}> <solid-route name="invoices-summary" class="segment half padding-right-medium"> <div class="segment full border-bottom padding-bottom-small text-uppercase text-large">${this.localize('header.summary')}</div> </solid-route> <solid-route name="invoices-details" class="segment half padding-left-medium"> <div class="segment full border-bottom padding-bottom-small text-uppercase text-large">${this.localize('header.list')}</div> </solid-route> </solid-router> </header> <section id="invoices-summary" data-view="invoices-summary"> <div class="segment block half sm-full padding-top-medium padding-bottom-medium padding-right-medium"> <div class="segment full padding-large shadow"> <div class="text-color-heading text-uppercase text-xxlarge text-bold margin-bottom-xsmall whitespace-normal">${this.localize('summary.totalTitle')}</div> <div class="total-help whitespace-normal text-large">${this.localize('summary.totalTitleHelp')}</div> <div class="separation-line margin-top-medium margin-bottom-xsmall border-bottom border-color-third"></div> <span id="recap-total" class="icon margin-top-medium text-xxlarge segment text-bold"></span> </div> </div> <div class="segment full sm-whitespace-normal"> <div class="segment quarter sm-full padding-right-medium"> <div class="segment full padding-medium shadow total-block"> <div class="title-block text-xlarge text-color-heading text-uppercase text-bold margin-bottom-small whitespace-normal">${this.localize('summary.totalCustomerTitle')}</div> <div class="separation-line margin-top-small margin-bottom-xsmall border-bottom border-color-third"></div> <span class="total-block-amount text-xxlarge" id="recap-customer-invoices"></span> </div> </div> <div class="segment quarter sm-full padding-right-medium"> <div class="segment full padding-medium shadow total-block"> <div class="title-block text-xlarge text-color-heading text-uppercase text-bold margin-bottom-small whitespace-normal">${this.localize('summary.totalFreelancerTitle')}</div> <div class="separation-line margin-top-small margin-bottom-xsmall border-bottom border-color-third"></div> <span class="total-block-amount text-xxlarge" id="recap-freelancer-invoices"></span> </div> </div> <div class="segment quarter sm-full padding-right-medium"> <div class="segment full padding-medium shadow total-block"> <div class="title-block text-xlarge text-color-heading text-uppercase text-bold margin-bottom-small whitespace-normal">${this.localize('summary.totalFeesTitle')}</div> <div class="separation-line margin-top-small margin-bottom-xsmall border-bottom border-color-third"></div> <span class="total-block-amount text-xxlarge" id="recap-fees"></span> </div> </div> <div class="segment quarter sm-full padding-right-medium"> <div class="segment full padding-medium shadow total-block bp-block"> <div class="title-block text-xlarge text-color-heading text-uppercase text-bold whitespace-normal">${this.localize('summary.totalBusinessProvisionsTitle')}</div> <div class="whitespace-normal text-large" id="info-business-provision"></div> <div class="separation-line margin-top-xsmall margin-bottom-xsmall border-bottom border-color-third"></div> <span class="total-block-amount text-xxlarge" id="recap-business-provision"></span> </div> </div> </div> </section> <section id="invoices-details" data-view="invoices-details" hidden=""> <!-- customer ==================================== --> <section id="clients-invoices" class="invoices-list invoices-list--clients"> <header class="segment"> <solid-router default-route="invoice-list" ${prefix}> <solid-route name="invoice-list"></solid-route> <solid-route name="add-invoice" class="segment full button text-xsmall text-bold text-uppercase text-center reversed color-third bordered icon icon-plus">${this.localize('button.invoice_create')}</solid-route> <solid-route name="show-invoice" use-id></solid-route> <solid-route name="edit-invoice" use-id></solid-route> <solid-route name="duplicate-invoice" use-id></solid-route> </solid-router> <div class="show-list segment half sm-full cursor-pointer sm-margin-top-medium text-bold text-large text-uppercase text-color-heading padding-xsmall icon icon-arrow-down">${this.localize('title.customer_invoices')}</div> </header> <dialog data-view="add-invoice" class="invoice-form padding-xxlarge sm-padding-medium"> <close-button></close-button> <h3 class="text-color-heading">${this.localize('title.invoice_create')}</h3> <solid-form class="new-customer-invoice-form segment full whitespace-normal" data-src="${dataSrc}" nested-field="customerInvoices" fields="first-line(title, identifier), second-line(state, invoicingDate), batches, last-line(tvaRate, additionalText)" label-title="${this.localize('label.invoice_title')}" label-identifier="${this.localize('label.invoice_identifier')}" label-state="${this.localize('label.invoice_state')}" label-invoicingDate="${this.localize('label.invoice_invoicing_date')}" label-tvaRate="${this.localize('label.invoice_tva_rate')}" label-additionalText="${this.localize('label.invoice_additional_text')}" placeholder-additionalText="${this.localize('placholder.invoice_additional_text')}" label-batches="" widget-state="solid-form-dropdown-label" widget-batches="widget-form-batches" widget-invoicingDate="solid-form-date-label" widget-tvaRate="solid-form-number-label" widget-additionalText="solid-form-placeholder-label-text" enum-state="${this.localize('option.edited')} = edited, ${this.localize('option.pending')} = pending, ${this.localize('option.sent')} = sent, ${this.localize('option.late')} = late, ${this.localize('option.paid')} = paid" class-first-line="segment full sm-whitespace-normal" class-second-line="segment full sm-whitespace-normal" class-last-line="segment half sm-full" class-title="segment half sm-full" class-identifier="segment half sm-full" class-state="segment half sm-full" class-invoicingDate="segment half sm-full" class-state="custom-select" multiple-batches next="invoice-list" required-title required-identifier required-state required-invoicingDate required-tvaRate submit-button="${this.localize('button.edit_submit_form')}" class-submit-button="submit sm-full button text-xsmall text-bold text-uppercase text-center icon icon-check" ></solid-form> </dialog> <dialog data-view="edit-invoice" class="invoice-form padding-xxlarge sm-padding-medium"> <close-button></close-button> <h3>${this.localize('title.invoice_modify')}</h3> <solid-form bind-resources class="segment full whitespace-normal" fields="first-line(title, identifier), second-line(state, invoicingDate), batches, last-line(tvaRate, additionalText)" label-title="${this.localize('label.invoice_title')}" label-identifier="${this.localize('label.invoice_identifier')}" label-state="${this.localize('label.invoice_state')}" label-invoicingDate="${this.localize('label.invoice_invoicing_date')}" label-tvaRate="${this.localize('label.invoice_tva_rate')}" label-additionalText="${this.localize('label.invoice_additional_text')}" label-batches="" widget-state="solid-form-dropdown-label" widget-batches="widget-form-batches" widget-invoicingDate="solid-form-date-label" widget-tvaRate="solid-form-number-label" widget-additionalText="solid-form-placeholder-label-text" enum-state="${this.localize('option.edited')} = edited, ${this.localize('option.pending')} = pending, ${this.localize('option.sent')} = sent, ${this.localize('option.late')} = late, ${this.localize('option.paid')} = paid" class-first-line="segment full sm-whitespace-normal" class-second-line="segment full sm-whitespace-normal" class-last-line="segment half sm-full" class-title="segment half sm-full" class-identifier="segment half sm-full" class-state="segment half sm-full" class-invoicingDate="segment half sm-full" class-state="custom-select" multiple-batches next="invoice-list" required-title required-identifier required-invoicingDate required-tvaRate submit-button="${this.localize('button.edit_submit_form')}" class-submit-button="submit sm-full button text-xsmall text-bold text-uppercase text-center icon icon-check" ></solid-form> <solid-delete class="segment sm-full button text-xsmall text-bold text-uppercase text-center icon icon-trash" bind-resources next="invoice-list" confirmation-message="${this.localize('confirm.delete_invoice')}" confirmation-type="dialog" confirmation-submit-text="${this.localize('validate.delete_invoice')}" confirmation-cancel-text="${this.localize('cancel.delete_invoice')}" confirmation-submit-class="segment sm-full button text-xsmall text-bold reversed text-uppercase text-center color-secondary bordered" confirmation-cancel-class="cancel segment sm-full button text-xsmall text-bold text-uppercase text-center color-secondary bordered" data-label="${this.localize('button.delete_submit_form')}" ></solid-delete> </dialog> <dialog data-view="duplicate-invoice" class="invoice-form padding-xxlarge sm-padding-medium" id="duplicate"> <close-button></close-button> <h3>${this.localize('title.invoice_duplicate')}</h3> <h4>${this.localize('question.invoice_duplicate')}</h4> <solid-form class="duplicate-form" bind-resources fields="first-line(title, identifier), second-line(state, invoicingDate), batches, last-line(tvaRate, additionalText)" widget-state="solid-form-dropdown-label" widget-batches="widget-form-batches" widget-invoicingDate="solid-form-date-label" widget-tvaRate="solid-form-number-label" enum-state="${this.localize('option.edited')} = edited, ${this.localize('option.pending')} = pending, ${this.localize('option.sent')} = sent, ${this.localize('option.late')} = late, ${this.localize('option.paid')} = paid" class-state="custom-select" class-title="invoice-title" multiple-batches naked ></solid-form> <div class="popup-buttons"> <button class="popup-buttons__cancel close-duplication-dialog">${this.localize('button.duplicate_cancel')}</button> <button class="popup-buttons__validate duplication-button">${this.localize('button.duplicate_confirm')}</button> </div> </dialog> <solid-display id="customer-invoices" data-view="invoice-list" class="invoices-list__item invoice-list-display" data-src="${dataSrc}" nested-field="customerInvoices" fields="invoice-title(invoice-main(invoice-header(title, identifier), invoice-footer(invoicingDate, htAmount)), invoice-aside(state)), invoice-hidden(actions(duplicate, edit, show, print), batches, invoice-totals(totals-header, ht-total(ht-label, htAmount), tva-total(tvaRate, tvaAmount), ttc-total(ttc-label, ttcAmount)))" value-totals-header="${this.localize('value.totals_header')}" value-ht-label="${this.localize('label.value_ht')}" value-ttc-label="${this.localize('label.value_ttc')}" widget-batches="widget-batches" widget-htAmount="widget-money" widget-state="widget-state" widget-tvaAmount="widget-money" widget-ttcAmount="widget-money" widget-tvaRate="widget-tva-rate" widget-identifier="widget-invoice-number" label-edit="${this.localize('label.edit')}" label-show="${this.localize('label.show')}" label-duplicate="${this.localize('label.duplicate')}" action-edit="edit-invoice" action-show="show-invoice" action-duplicate="duplicate-invoice" widget-print="widget-print" class-invoice-title="icon icon-arrow-down icon-xsmall icon-margin-right-xsmall" class-title="text-color-heading" class-edit="children-mdi-pencil edit-invoice" class-show="children-mdi-eye show-invoice" class-duplicate="children-mdi-file-replace-outline duplicate-invoice" classe-print="mdi mdi-dowload print-invoice" order-desc="invoicingDate" child-class="invoice-details padding-left-large" ></solid-display> <!-- render --> <dialog class="invoice-dialog padding-xxlarge sm-padding-medium" data-view="show-invoice"> <close-button></close-button> <div class="popup-buttons"> <button class="popup-buttons__validate download icon icon-cloud-download">${this.localize('button.download')}</button> <button class="popup-buttons__validate print icon icon-printer">${this.localize('button.print')}</button> </div> <div id="print"> <div class="invoice-render"> <img src="${logo}" width="200" /> <solid-display class="display-invoice" bind-resources fields="invoice-head(invoice-header-left(identifier, invoicingDate), invoice-header-right(customer-infos(project.customer.name, project.customer.address, project.customer.postcode, project.customer.city, project.customer.country))), batches, invoice-payment(additionalText, invoice-totals(ht-total(ht-label, htAmount), tva-total(tvaRate, tvaAmount), ttc-total(ttc-label, ttcAmount), tva-notice)), invoice-footer" value-ht-label="${this.localize('label.value_ht')}" value-ttc-label="${this.localize('label.value_ttc')}" value-tva-notice="${this.localize('value.tva_notice')}" widget-identifier="widget-render-title" label-invoicingDate="${this.localize('label.invoice_invoicing_date2')}" widget-invoicingDate="solid-display-value-date-label" widget-additionalText="widget-render-metadata" widget-batches="widget-batches" widget-htAmount="widget-money" widget-tvaAmount="widget-money" widget-ttcAmount="widget-money" widget-tvaRate="widget-tva-rate" widget-project.customer.address="solid-display-div-multiline" class-project.customer.name="segment block whitespace-normal text-color-heading text-bold margin-bottom-xsmall" class-project.customer.postcode="margin-right-xxsmall" ></solid-display> <div class="invoice-render__footer"> <div class="invoice-render__hd-address"> <p><strong>Happy Dev</strong></p> <p>75 rue du Javelot</p> <p>75013 Paris</p> <p>contact@happy-dev.fr</p> </div> <div class="invoice-render__hd-legal"> <p><strong>SIRET : </strong>81314060500011</p> <p><strong>${this.localize('text.tva_number')}</strong>FR 61 813140605</p> <p>${this.localize('text.sas_info')}</p> </div> </div> </div> </div> </dialog> <!-- /render --> </section> <!-- /customer ==================================== --> <!-- freelance ==================================== --> <section id="freelances-invoices" class="invoices-list invoices-list--freelances"> <header class="segment"> <solid-router default-route="freelance-invoice-list" ${prefix}> <solid-route name="freelance-invoice-list"></solid-route> <solid-route name="edit-freelance-invoice" use-id></solid-route> <solid-route name="add-freelance-invoice" class="segment full button text-xsmall text-bold text-uppercase text-center reversed color-third bordered icon icon-cloud-upload">${this.localize('button.invoice_import')}</solid-route> </solid-router> <div class="show-list segment half sm-full cursor-pointer text-bold text-large text-uppercase text-color-heading padding-xsmall icon icon-arrow-down">${this.localize('title.freelance_invoices')}</div> </header> <dialog data-view="add-freelance-invoice" class="invoice-form padding-xxlarge sm-padding-medium"> <close-button></close-button> <h3>${this.localize('title.invoice_import')}</h3> <solid-form data-src="${dataSrc}" class="segment full whitespace-normal new-freelance-invoice" nested-field="freelancerInvoices" fields="first-line(freelanceFullname, identifier), second-line(htAmount, nomenclatureNote), third-line(uploadUrl)" label-freelanceFullname="${this.localize('label.freelance_fullname')}" label-identifier="${this.localize('label.freelance_identifier')}" label-uploadUrl="${this.localize('label.upload_url')}" label-htAmount="${this.localize('label.freelance_ht_amount')}" widget-nomenclatureNote="widget-nomenclature" widget-uploadUrl="solid-form-file-label" widget-htAmount="solid-form-number-label" class-uploadURl="segment half" upload-url-uploadUrl="${uploadUrl}" class-first-line="segment full sm-whitespace-normal" class-second-line="segment full sm-whitespace-normal" class-third-line="segment full" class-freelanceFullname="segment half sm-full" class-identifier="segment half sm-full" class-htAmount="segment half sm-full" class-nomenclatureNote="segment half sm-full" class-invoicingDate="segment half sm-full" class-uploadUrl="segment half" required-freelanceFullname required-identifier required-htAmount required-uploadUrl next="freelance-invoice-list" submit-button="${this.localize('button.submit_form')}" class-submit-button="submit sm-full button text-xsmall text-bold text-uppercase text-center icon icon-rocket" ></solid-form> </dialog> <dialog data-view="edit-freelance-invoice" class="invoice-form padding-xxlarge sm-padding-medium"> <close-button></close-button> <h3>Modifier une facture</h3> <solid-form bind-resources class="edit-freelance-invoice segment full whitespace-normal" fields="first-line(freelanceFullname, identifier), second-line(invoicingDate, htAmount), third-line(state, uploadUrl)" label-freelanceFullname="${this.localize('label.freelance_fullname')}" label-identifier="${this.localize('label.freelance_identifier')}" label-invoicingDate="${this.localize('label.freelance_invoicing_date')}" label-state="${this.localize('label.invoice_state')}" label-uploadUrl="${this.localize('label.edit_upload_url')}" label-htAmount="${this.localize('label.freelance_ht_amount')}" widget-invoicingDate="solid-form-date-label" widget-uploadUrl="solid-form-file-label" widget-htAmount="solid-form-number-label" widget-state="solid-form-dropdown-label" upload-url-uploadUrl="${uploadUrl}" enum-state="${this.localize('option.edited')} = edited, ${this.localize('option.pending')} = pending, ${this.localize('option.sent')} = sent, ${this.localize('option.late')} = late, ${this.localize('option.paid')} = paid" class-state="custom-select segment half" class-first-line="segment full sm-whitespace-normal" class-second-line="segment full sm-whitespace-normal" class-third-line="segment full" class-freelanceFullname="segment half sm-full" class-identifier="segment half sm-full" class-htAmount="segment half sm-full" class-nomenclatureNote="segment half sm-full" class-invoicingDate="segment half sm-full" class-uploadUrl="segment half" required-freelanceFullname required-identifier required-invoicingDate required-htAmount next="freelance-invoice-list" submit-button="${this.localize('button.submit_form')}" class-submit-button="submit sm-full button text-xsmall text-bold text-uppercase text-center icon icon-check" ></solid-form> <solid-delete class="sm-full button text-xsmall text-bold text-uppercase text-center icon icon-trash" bind-resources next="freelance-invoice-list" confirmation-message="${this.localize('confirm.delete_invoice')}" confirmation-type="dialog" confirmation-submit-text="${this.localize('validate.delete_invoice')}" confirmation-cancel-text="${this.localize('cancel.delete_invoice')}" confirmation-submit-class="segment sm-full button text-xsmall text-bold reversed text-uppercase text-center color-secondary bordered" confirmation-cancel-class="cancel segment sm-full button text-xsmall text-bold text-uppercase text-center color-secondary bordered" data-label="${this.localize('button.delete_submit_form')}" ></solid-delete> </dialog> <solid-display id="freelancer-invoices" class="invoice-list-display" child-class="segment full padding-left-large" data-view="freelance-invoice-list" data-src="${dataSrc}" nested-field="freelancerInvoices" fields="invoice-title(invoice-main(invoice-header(freelanceFullname, identifier), invoice-footer(invoicingDate, htAmount)), invoice-aside(state)), invoice-hidden(actions(edit), uploadUrl)" label-edit="${this.localize('label.edit')}" class-freelanceFullname="text-color-heading" class-identifier="text-color-heading" action-edit="edit-freelance-invoice" class-edit="children-mdi-pencil edit-invoice" class-invoice-title="icon icon-arrow-down icon-xsmall icon-margin-right-xsmall" widget-htAmount="widget-money" widget-state="widget-state" widget-uploadUrl="widget-embed-drive" order-desc="invoicingDate" ></solid-display> </section> <!-- /freelance ==================================== --> </section> </div> `; } } customElements.define('solid-invoicing', SolidInvoicing);