diff --git a/data/customer-invoice-1.jsonld b/data/customer-invoice-1.jsonld index b6d59485653a19629616766439b0196b309f7fc6..1248d223fa3f71556eacea42e18e4147d148e19c 100644 --- a/data/customer-invoice-1.jsonld +++ b/data/customer-invoice-1.jsonld @@ -15,7 +15,7 @@ { "@id": "/data/batch-2.jsonld" } ] }, - "identifier": "F-202001", + "identifier": "202001", "title": "Création plateforme Coopedia", "state": "pending", "tvaRate": 20, diff --git a/data/customer-invoices.jsonld b/data/customer-invoices.jsonld index d95e0f1f99cc932aba3471ac078bcf9849fec221..2c2e0767f9d3b076246a8c5333e4ca8af53da8a0 100644 --- a/data/customer-invoices.jsonld +++ b/data/customer-invoices.jsonld @@ -18,7 +18,7 @@ { "@id": "/data/batch-2.jsonld" } ] }, - "identifier": "F-202001", + "identifier": "202001", "title": "Création plateforme Coopedia", "state": "pending", "tvaRate": 20, diff --git a/locales/en.json b/locales/en.json index 1e677365ec3b4911a218f857b4379b18625bd16c..f0c2dcd722f8e76dac4caceb96c2131d28e67527 100644 --- a/locales/en.json +++ b/locales/en.json @@ -62,7 +62,7 @@ "validate.delete_invoice": "Delete", "cancel.delete_invoice": "Cancel", "summary.totalTitle": "Billable amount without VAT", - "summary.totalTitleHelp":" = Customer invoices - Freelancers invoices", + "summary.totalTitleHelp":" = Customer invoices - Freelancers invoices - Operating costs - Business contribution", "summary.totalCustomerTitle":"Customer invoices", "summary.totalFreelancerTitle":"Freelancers invoices", "summary.totalFeesTitle":"Operating costs", diff --git a/locales/fr.json b/locales/fr.json index 7f8e364f35456e56960bbfd72bad6f996a34b63a..9286e32e4bcf9587d703a6bd499b21c86f68f77f 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -62,7 +62,7 @@ "validate.delete_invoice": "Supprimer", "cancel.delete_invoice": "Annuler", "summary.totalTitle":"Montant facturable en HT", - "summary.totalTitleHelp":" = factures client - factures indépendants", + "summary.totalTitleHelp":" = factures client - factures indépendants - frais de fonctionnement - apport d'affaire", "summary.totalCustomerTitle":"Factures client", "summary.totalFreelancerTitle":"Factures indépendants", "summary.totalFeesTitle":"Frais de fonctionnement", diff --git a/solid-invoicing.js b/solid-invoicing.js index 057be50bcdb71f3d5ec8677a5ec762fc48527f72..93b1401a21b8e7920c2dc67d3ad2d4d6caaabe33 100644 --- a/solid-invoicing.js +++ b/solid-invoicing.js @@ -33,9 +33,9 @@ export class SolidInvoicing extends SolidTemplateElement { } connectedCallback() { - // navigate on router + // NAVIGATE this.addEventListener('navigate', (e) => { - // remove no-render to populate the recap + // 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 customerForm = this.querySelector('#new-customer-invoice'); @@ -44,6 +44,8 @@ export class SolidInvoicing extends SolidTemplateElement { if (freelancerDisplay.hasAttribute('no-render')) freelancerDisplay.removeAttribute('no-render'); if (customerForm.hasAttribute('no-render')) customerForm.removeAttribute('no-render'); } + + // Calculate the totals when the edit form loads if (e.detail.route === "edit-invoice") { const editForm = this.querySelector('#edit-customer-invoice'); editForm.addEventListener('populate', () => { @@ -54,6 +56,7 @@ export class SolidInvoicing extends SolidTemplateElement { } }); + // CLICK this.addEventListener('click', (e) => { // Display invoice list if(e.target && e.target.closest('.show-list')){ @@ -127,8 +130,9 @@ export class SolidInvoicing extends SolidTemplateElement { } }); - // Reactivity: subscribe batches to invoices changes + // WIDGET_RENDERED this.addEventListener('widgetRendered', (e) => { + // Reactivity: subscribe batches to invoices changes if (e.target.tagName === 'WIDGET-TASKS') { const batchDisplay = e.target.closest('solid-display'); if (!batchDisplay) return; @@ -153,7 +157,7 @@ export class SolidInvoicing extends SolidTemplateElement { const form = e.target; const batches = Array.from(form.querySelectorAll('widget-form-batches')); - // BATCHES + // batches for (const batch of batches) { const tasksAmount = Array.from(batch.querySelectorAll('widget-form-tasks [name="htAmount"]')); const total = tasksAmount.reduce((acc, task) => { @@ -163,7 +167,7 @@ export class SolidInvoicing extends SolidTemplateElement { htAmount += total; } - // TOTAL + // total const totalEl = form.querySelector('widget-form-total'); totalEl.querySelector('[name="htAmount"]').setAttribute('value', htAmount.toFixed(2)); const tvaRate = form.component.value.tvaRate || 0; @@ -239,18 +243,20 @@ export class SolidInvoicing extends SolidTemplateElement { Promise.all([this.getTotalAmount(customerInvoices, true), this.getTotalAmount(freelancerInvoices, false)]) .then(([customerTotal, freelancerTotal]) => { + let recapFeesAmount = 0; if (customerTotal) { // customer amounts recapCustomerInvoices.innerText = customerTotal.amount.toFixed(2); - recapFees.innerText = (customerTotal.amount * 0.05).toFixed(2); + recapFeesAmount = customerTotal.amount * 0.05; // TODO: make it configurable + recapFees.innerText = recapFeesAmount.toFixed(2); recapBusinessProvision.innerText = (customerTotal.businessProvisionAmount).toFixed(2); - infoBusinessProvision.innerText = `${customerTotal.businessProvision}%` + infoBusinessProvision.innerText = `${customerTotal.businessProvision}%`; } if (freelancerTotal) { // freelancer amounts recapFreelancerInvoices.innerText = freelancerTotal.amount.toFixed(2); } - const recapAmount = (customerTotal.amount || 0) - (freelancerTotal.amount || 0); + const recapAmount = (customerTotal.amount || 0) - (freelancerTotal.amount || 0) - recapFeesAmount - (customerTotal.businessProvisionAmount || 0); recapTotal.innerText = recapAmount.toFixed(2); if (recapAmount > 0) { recapTotal.classList.remove('icon-dislike'); @@ -307,7 +313,7 @@ export class SolidInvoicing extends SolidTemplateElement { this.attachPopulateListeners(); } - template({dataSrc, routerPrefix, uploadUrl, logo}) { + template({ dataSrc, routerPrefix, uploadUrl }) { let prefix = routerPrefix ? `route-prefix="${routerPrefix}"`: '' return ` @@ -511,30 +517,31 @@ export class SolidInvoicing extends SolidTemplateElement { </div> </div> <div class="segment full sm-whitespace-normal"> - <div class="segment quarter sm-full padding-right-medium"> + <div class="segment quarter sm-full padding-right-medium text-top"> <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 quarter sm-full padding-right-medium text-top"> <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="segment quarter sm-full padding-right-medium text-top"> + <div class="segment full padding-medium shadow total-block bp-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="whitespace-normal text-large">Happy Dev Paris 5%</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 quarter sm-full padding-right-medium text-top"> <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="title-block text-xlarge text-color-heading text-uppercase text-bold margin-bottom-small 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> diff --git a/utils/functions.js b/utils/functions.js index 09699c850f9a94789e43f4e57e7cabe20b3ee9cd..d946abc88effd7642ecdf44b61d39066a3bb1e1c 100644 --- a/utils/functions.js +++ b/utils/functions.js @@ -23,7 +23,7 @@ export function getCSSVariables(document) { * @param {string} type - accepts 'download' or 'print' * @param {string} base_url - url of the component, used for targeting the css file * @param {string} filename - name of the file to download - * @returns {any} Window + * @returns {Window} Window */ export function openInvoiceWindow(type, base_url, filename) { const downloadWindow = window.open('', 'INVOICE', 'height=400,width=600'); @@ -78,17 +78,22 @@ export function cleanInvoiceBeforeDuplicate(invoice) { return invoice; } +/** + * Format a date for updating data + * @param {date} date - date to format + * @returns {string} date in format YYYY-MM-DD + */ function formatDateForPost(date) { return date.toISOString().split('T')[0]; } export async function getInvoiceFilename(invoice) { if (!invoice) return 'invoice'; - const projectNumber = await invoice['project.number']; + const invoiceNumber = await invoice['identifier']; const customerName = await invoice['project.customer.name']; const invoicingDate = await invoice['invoicingDate']; const splittedInvoicingDate = formatDateForPost(new Date(invoicingDate || null)).split('-'); // TODO: make this configurable - return `Facture Happy Dev - ${projectNumber || '00'} - ${customerName || 'Unknown client'} - ${splittedInvoicingDate[2]}-${splittedInvoicingDate[1]}-${splittedInvoicingDate[0]}`; + return `Facture Happy Dev - ${invoiceNumber || '00'} - ${customerName || 'Unknown client'} - ${splittedInvoicingDate[2]}-${splittedInvoicingDate[1]}-${splittedInvoicingDate[0]}`; } \ No newline at end of file