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.4";
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 class="text-color-heading">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">
\${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>' : '' }
<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 full button text-xsmall text-bold text-uppercase text-center reversed color-third bordered icon icon-plus icon-margin-right-xsmall">${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>
<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.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
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 icon icon-check"
<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">
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
618
<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-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"
<dialog class="invoice-dialog padding-xxlarge sm-padding-medium" data-view="show-invoice">
<close-button></close-button>
<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 id="print">
<div class="invoice-render">
<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>
</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 icon-margin-right-xsmall">${this.localize('button.invoice_import')}</solid-route>
<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>
<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"
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 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-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 icon icon-check"
<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>
class="invoice-list-display"
child-class="segment full block 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-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);