Newer
Older
import { SolidTemplateElement, Helpers, store } from 'https://cdn.skypack.dev/@startinblox/core@0.16';
// const base_url = import.meta.url.replace(/\/[^\/]*$/, '');
const base_url = "https://cdn.skypack.dev/@startinblox/component-invoicing@1.2";
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;
}
}
}
static get propsDefinition() {
return {
dataSrc: 'data-src',
}
}
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 } }),
);
}
// access pdf invoice
if (e.target && e.target.closest('.print')) {
let printWindow = window.open('', 'PRINT', 'height=400,width=600');
printWindow.document.write(`
<html>
<head>
<title></title>
<link rel="stylesheet" href="${base_url}/css/main.css?min" media="screen"/>
<link rel="stylesheet" href="${base_url}/css/main.css?min" media="print"/>
<style>:root{--color-primary: #6259e5;--color-secondary: #ffb700; font-family: sans-serif}</style>
</head>
<body>
${document.getElementById('print').innerHTML}
</body>
</html>
`);
printWindow.document.close();
printWindow.focus();
setTimeout(() => {
printWindow.print();
printWindow.close();
}, 1000);
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(this.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" },
}),
);
}
});
}
cleanInvoiceBeforeDuplicate(invoice) {
delete invoice["@id"];
delete invoice.batches["@id"];
invoice.batches.forEach((batch, indexb) => {
delete invoice.batches[indexb]["@id"];
batch.tasks.forEach((task, indext) => {
delete invoice.batches[indexb].tasks[indext]["@id"];
});
});
return invoice;
template({dataSrc, routerPrefix, uploadUrl}) {
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"
fields="price-line(title, htAmount)"
widget-htAmount="widget-money"
></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')}"
label-tasks=""
multiple-tasks>
</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-text"
widget-title="solid-form-placeholder-text"
label-title="${this.localize('label.title_task')}"
label-htAmount="${this.localize('label.ht_amount')}"
>
</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>${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 ==================================== -->
<!-- customer ==================================== -->
<section id="clients-invoices" class="invoices-list invoices-list--clients">
<header class="segment">
<div class="show-list segment text-bold text-large text-uppercase padding-xsmall icon icon-arrow-down">${this.localize('title.customer_invoices')}</div>
<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>
</header>
<dialog data-view="add-invoice" class="invoice-form">
<close-button></close-button>
<h3 class="text-color-heading">${this.localize('title.invoice_create')}</h3>
class="new-customer-invoice-form"
data-src="${dataSrc}"
nested-field="customerInvoices"
fields="first-line(title, identifier),
second-line(state, invoicingDate),
batches,
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"
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"
multiple-batches
required-title
required-identifier
required-invoicingDate
required-tvaRate
submit-button="${this.localize('button.submit_form')}"
<dialog data-view="edit-invoice" class="invoice-form">
<close-button></close-button>
<h3>${this.localize('title.invoice_modify')}</h3>
fields="first-line(title, identifier),
second-line(state, invoicingDate),
batches,
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"
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"
multiple-batches
next="invoice-list"
required-title
required-identifier
required-invoicingDate
required-tvaRate
submit-button="${this.localize('button.edit_submit_form')}"
<solid-delete class="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" 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"
multiple-batches
naked
<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>
<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), 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-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="mdi-pencil mdi"
class-show="mdi-eye mdi"
class-duplicate="mdi-file-replace-outline mdi"
order-desc="invoicingDate"
></solid-display>
<!-- render -->
<dialog class="invoice-dialog" data-view="show-invoice">
<close-button></close-button>
<div id="print">
<div class="invoice-render">
<img src="${base_url}/images/happy-dev-logo.png" 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))),
batches,
invoice-payment(additionalText, invoice-totals(ht-total(ht-label, htAmount), tva-total(tvaRate, tvaAmount), ttc-total(ttc-label, ttcAmount), tva-notice)),
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>
<button class="popup-buttons__validate print">${this.localize('button.print')}</button>
</dialog>
<!-- /render -->
</section>
<!-- /customer ==================================== -->
<!-- freelance ==================================== -->
<section id="freelances-invoices" class="invoices-list invoices-list--freelances">
<header class="segment">
<div class="show-list segment text-bold text-large text-uppercase padding-xsmall icon icon-arrow-down">${this.localize('title.freelance_invoices')}</div>
<solid-router default-route="freelance-invoice-list" ${prefix}>
<solid-route name="freelance-invoice-list"></solid-route>
<solid-route name="edit-freelance-invoice"></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>
</header>
<dialog data-view="add-freelance-invoice" class="invoice-form">
<close-button></close-button>
<h3>${this.localize('title.invoice_import')}</h3>
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"
required-freelanceFullname
required-identifier
required-htAmount
submit-button="${this.localize('button.submit_form')}"
></solid-form>
</dialog>
<dialog data-view="edit-freelance-invoice" class="invoice-form">
<close-button></close-button>
<h3>Modifier une facture</h3>
<solid-form
bind-resources
fields="first-line(freelanceFullname, identifier),
second-line(invoicingDate, htAmount),
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"
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"
required-freelanceFullname
required-identifier
required-invoicingDate
required-htAmount
submit-button="${this.localize('button.submit_form')}"
></solid-form>
</dialog>
<solid-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)"
action-edit="edit-freelance-invoice"
widget-htAmount="widget-money"
order-desc="invoicingDate"