Newer
Older
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() {
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',
uploadUrl: 'upload-dir',
logo: 'logo-dir'
// navigate on router
this.addEventListener('navigate', (e) => {
// remove no-render to populate the recap
if (e.target === this.querySelector('#invoice-router') && e.detail.route === "invoices-summary") {
const customerDisplay = this.querySelector('#customer-invoices');
const freelancerDisplay = this.querySelector('#freelancer-invoices');
if (customerDisplay.hasAttribute('no-render')) customerDisplay.removeAttribute('no-render');
if (freelancerDisplay.hasAttribute('no-render')) freelancerDisplay.removeAttribute('no-render');
}
});
if(e.target && e.target.closest('.show-list')){
let invoice = e.target.closest('.invoices-list');
invoice.classList.toggle('invoices-list-visible')
}
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')
}
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 } }),
);
}
if (e.target && e.target.closest('.print')) {
const printWindow = openInvoiceWindow('print', base_url);
setTimeout(() => {
printWindow.print();
printWindow.close();
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.subscribeVirtualContainerTo(batchDisplay.dataset.src, invoiceSrc); // batch
store.subscribeVirtualContainerTo(e.target.dataset.src, invoiceSrc); // tasks of batch
}
if (e.target.classList.contains('js-task-display')) { // task
const taskSrc = e.target.getAttribute('value')
if (!taskSrc) return;
const invoiceSrc = e.target.closest('.invoice-details')?.dataset.src;
if (!invoiceSrc) return;
store.subscribeVirtualContainerTo(taskSrc, 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
*/
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>
</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>N°\${value}</div>
</template>
</solid-widget>
<solid-widget name="widget-batches">
<template>
<solid-display
class="batches-list"
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"
widget-title="solid-display-div-multiline"
></solid-display>
</template>
</solid-widget>
<solid-widget name="widget-form-batches">
<template>
<solid-form
data-holder
naked
fields="batch-header(title), batch-body(tasks)"
widget-tasks="widget-form-tasks"
widget-title="solid-form-placeholder-text"
label-title="${this.localize('label.title_batch')}"
multiple-tasks
required-title
> </solid-form>
</template>
</solid-widget>
<solid-widget name="widget-form-tasks">
<template>
<solid-form
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
</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>${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="sm-full button text-xsmall text-bold text-center reversed color-secondary bordered">${this.localize('option.edited')}</div>' : '' }
\${value == 'pending' ? '<div class="sm-full button text-xsmall text-bold text-center color-secondary bordered">${this.localize('option.pending')}</div>' : '' }
\${value == 'sent' ? '<div class="sm-full button text-xsmall text-bold text-center color-third bordered">${this.localize('option.sent')}</div>' : '' }
\${value == 'paid' ? '<div class="sm-full button text-xsmall text-bold text-center reversed color-third bordered">${this.localize('option.paid')}</div>' : '' }
\${value == 'late' ? '<div class="sm-full button text-xsmall text-bold text-center reversed color-primary bordered">${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 id="invoice-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 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 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 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>
</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 sm-full button text-xsmall text-bold text-uppercase text-center reversed color-secondary 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 padding-xsmall icon icon-arrow-down">${this.localize('title.customer_invoices')}</div>
<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-invoicingDate
required-tvaRate
submit-button="${this.localize('button.submit_form')}"
class-submit-button="submit sm-full button text-xsmall text-bold text-uppercase text-center color-secondary bordered reversed"
></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
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-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 color-secondary bordered reversed"
<solid-delete class="segment sm-full button text-xsmall text-bold text-uppercase text-center color-secondary bordered 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">
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
<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
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"
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"
<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 print">${this.localize('button.print')}</button>
<button class="popup-buttons__validate download">${this.localize('button.download')}</button>
</div>
<div id="print">
<div class="invoice-render">
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
<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))),
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"
class-project.customer.name="segment block whitespace-normal text-color-heading text-bold margin-bottom-xsmall"
></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>
</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 sm-full button text-xsmall text-bold text-uppercase text-center reversed color-secondary 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 padding-xsmall icon icon-arrow-down">${this.localize('title.freelance_invoices')}</div>
<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}"
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"
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"
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 color-secondary bordered reversed"
></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-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"
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 color-secondary bordered reversed"
<solid-delete class="sm-full button text-xsmall text-bold text-uppercase text-center color-secondary bordered 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>
class="invoice-list-display"
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')}"
action-edit="edit-freelance-invoice"
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);