Skip to content
Snippets Groups Projects
solid-invoicing.js 18.1 KiB
Newer Older
import { SolidTemplateElement, Helpers, store } from 'https://cdn.skypack.dev/@startinblox/core@beta';
// const base_url = import.meta.url.replace(/\/[^\/]*$/, '');
Matthieu Fesselier's avatar
Matthieu Fesselier committed
const base_url = "https://cdn.skypack.dev/@startinblox/component-invoicing@1.1";
Helpers.importCSS(`${base_url}/css/main.css?min`);

export class SolidInvoicing extends SolidTemplateElement {
  constructor() {
    super()
  }

  static get propsDefinition() {
    return {
      dataSrc: 'data-src',
      routerPrefix: 'router-prefix',
      uploadUrl: 'upload-url'
    }
  }

  connectedCallback() {
    this.addEventListener('click', (e) => {
      // 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{--solid-invoicing-primary: #6259e5;--solid-invoicing-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">
    <!-- widgets ==================================== -->
    <solid-widget name="widget-money">
      <template>
        <div>\${value?value:0} €</div>
      </template>
    </solid-widget>

    <solid-widget name="widget-tva-rate">
      <template>
        <div>TVA \${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"
          data-src="\${value}"
          fields="batch-header(title, amount),
          batch-body(tasks),
          batch-footer(ht-total(ht-label, htAmount))"
          value-amount="Montants"
          value-ht-label="Total HT"
          widget-tasks="widget-tasks"
          widget-htAmount="widget-money"
        ></solid-display>
      </template>
    </solid-widget>

    <solid-widget name="widget-tasks">
      <template>
        <solid-display
          class="tasks-list"
          data-src="\${value}"
          fields="price-line(title, htAmount)"
          widget-htAmount="widget-money"
        ></solid-display>
      </template>
    </solid-widget>

    <solid-widget name="widget-form-batches">
      <template>
        <solid-form
          data-src="\${value}"
          data-holder
          naked
          fields="batch-header(title), batch-body(tasks)"
          widget-tasks="widget-form-tasks"
          widget-title="solid-form-placeholder-text"
          label-title="Nom du lot"
          label-tasks=""
          multiple-tasks>
        </solid-form>
      </template>
    </solid-widget>

    <solid-widget name="widget-form-tasks">
      <template>
        <solid-form
          data-src="\${value}"
          class="tasks-list"
          data-holder
          naked
          fields="price-line(title, htAmount)"
          widget-htAmount="solid-form-placeholder-text"
          widget-title="solid-form-placeholder-text"
          label-title="Tâche"
          label-htAmount="Montant"
        >
        </solid-form>
      </template>
    </solid-widget>


    <solid-widget name="widget-render-title">
      <template>
        <div><strong>Facture N°\${value}</strong></div>
      </template>
    </solid-widget>

    <solid-widget name="widget-render-metadata">
      <template>
        <div>
          <div class="invoice-render__fees">
            <p>\${value}</p>
            <p>Intérêts de retard : 6% / an</p>
            <p>Une indemnité forfaitaire de 40€ sera exigée en sus de la pénalité de retard dès le premier jour de retard.</p>
          </div>
          <div class="invoice-render__rib">
            <div><strong>Règlement par virement :</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="close-button">
      <template>
        <button class="close-button">
          <img src="${base_url}/images/close-icon.svg" width="14" height="14" />
        </button>
      </template>
    </solid-widget>

    <!-- /widgets ==================================== -->


    <!-- customer ==================================== -->
    <section id="clients-invoices" class="invoices-list invoices-list--clients">
      <header>
        <h2>Factures pour le client</h2>
        <solid-router default-route="invoice-list" ${prefix}>
          <solid-route name="invoice-list"></solid-route>
          <solid-route name="add-invoice" class="button">Créer une nouvelle facture</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>Créer une nouvelle facture</h3>

        <solid-form
          class="new-customer-invoice-form"
          data-src="${dataSrc}"
          nested-field="customerInvoices"
          fields="first-line(title, identifier),
          second-line(state, invoicingDate),
          batches,
          last-line(tvaRate, additionalText)"
          label-title="Nom de la facture"
          label-identifier="Identifiant"
          label-state="Statut"
          label-invoicingDate="Date"
          label-tvaRate="TVA"
          label-additionalText="Mention Libre"
          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"
          enum-state="En attente = pending, Envoyé = sent, Payé = paid"
          class-state="custom-select"
          multiple-batches
          required-title
          required-identifier
          required-invoicingDate
          required-tvaRate

          next="invoice-list"
          ></solid-form>
      <dialog data-view="edit-invoice" class="invoice-form">
        <close-button></close-button>
        <h3>Modifier la facture</h3>
          bind-resources
          fields="first-line(title, identifier),
          second-line(state, invoicingDate),
          batches,
          last-line(tvaRate, additionalText)"
          label-title="Nom de la facture"
          label-identifier="Identifiant"
          label-state="Statut"
          label-invoicingDate="Date"
          label-tvaRate="TVA"
          label-additionalText="Mention Libre"
          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"
          enum-state="En attente = pending, Envoyé = sent, Payé = paid"
          class-state="custom-select"
          multiple-batches
          next="invoice-list"
          required-title
          required-identifier
          required-invoicingDate
          required-tvaRate
        ></solid-form>
      </dialog>

      <dialog data-view="duplicate-invoice" class="invoice-form" id="duplicate">
        <close-button></close-button>
        <h3>Dupliquer la facture</h3>
        <h4>Voulez-vous dupliquer cette facture ?</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="En attente = pending, Envoyé = sent, Payé = paid"
          class-state="custom-select"
          multiple-batches
          naked
        ></solid-form>
        <div class="popup-buttons">
          <button class="popup-buttons__cancel close-duplication-dialog">Annuler</button>
          <button class="popup-buttons__validate duplication-button">Confirmer</button>
        </div>
      <solid-display
        data-view="invoice-list"
        class="invoices-list__item"
        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="TOTAL DE LA FACTURE"
        value-ht-label="Total HT"
        value-ttc-label="Total 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="Éditer"
        label-show="Aperçu client"
        label-duplicate="Dupliquer"
        action-edit="edit-invoice"
        action-show="show-invoice"
        action-duplicate="duplicate-invoice"
        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(customer.name, 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="Total HT"
              value-ttc-label="Total TTC"
              value-tva-notice="TVA récupérable à l'encaissement"

              widget-identifier="widget-render-title"
              label-invoicingDate="Date: "
              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"
            ></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>N° de TVA intracommunautaire : </strong>FR 61 813140605</p>
                <p>SAS à capital variable enregistrée au RCS de Paris</p>
              </div>
            </div>
          </div>
        </div>
        <div class="popup-buttons">
          <button class="popup-buttons__validate print">Imprimer</button>
        </div>
      </dialog>
      <!-- /render -->

    </section>
    <!-- /customer ==================================== -->

    <!-- freelance ==================================== -->
    <section id="freelances-invoices" class="invoices-list invoices-list--freelances">
      <header>
        <h2>Factures des indépendants</h2>
        <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="button">Importer une facture</solid-route>
        </solid-router>
      </header>

      <dialog data-view="add-freelance-invoice" class="invoice-form">
        <close-button></close-button>
        <h3>Importer une facture</h3>
        <solid-form
          data-src="${dataSrc}"
          nested-field="freelancerInvoices"
          fields="first-line(freelanceFullname, identifier),
          second-line(htAmount, invoicingDate),
          third-line(state, uploadUrl)"
          label-freelanceFullname="Nom du prestataire"
          label-identifier="Numéro de la prestation"
          label-invoicingDate="Date de facturation"
          label-state="Statut"
          label-uploadUrl="Lien de la facture"
          label-htAmount="Montant HT"
          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="En attente = pending, Envoyé = sent, Payé = paid"
          class-state="custom-select"
          required-freelanceFullname
          required-identifier
          required-invoicingDate
          required-htAmount
          next="freelance-invoice-list"
        ></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),
          third-line(state, uploadUrl)"
          label-freelanceFullname="Nom du prestataire"
          label-identifier="Numéro de la prestation"
          label-invoicingDate="Date de facturation"
          label-state="Statut"
          label-uploadUrl="Lien de la facture"
          label-htAmount="Montant HT"
          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="En attente = pending, Envoyé = sent, Payé = paid"
          class-state="custom-select"
          required-freelanceFullname
          required-identifier
          required-invoicingDate
          required-htAmount
          next="freelance-invoice-list"
        ></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)"
        label-edit="Éditer"
        action-edit="edit-freelance-invoice"
        widget-htAmount="widget-money"
        widget-uploadUrl="widget-embed-drive"
        order-desc="invoicingDate"
      ></solid-display>
    </section>
    <!-- /freelance ==================================== -->
  </div>
 `;
  }
}

customElements.define('solid-invoicing', SolidInvoicing);