/** * content for invoices */ import { DeesElement, property, html, customElement, type TemplateResult, css, cssManager, unsafeCSS, render, domtools, } from '@design.estate/dees-element'; import * as plugins from '../plugins.js'; import * as shared from '../../ts/shared/index.js'; declare global { interface HTMLElementTagNameMap { 'dedocument-contentinvoice': DeContentInvoice; } } @customElement('dedocument-contentinvoice') export class DeContentInvoice extends DeesElement { public static demo = () => html`
`; @property({ type: Object, reflect: true, }) public letterData: plugins.tsclass.business.ILetter; constructor() { super(); domtools.DomTools.setupDomTools(); } public static styles = [ domtools.elementBasic.staticStyles, css` :host { color: #333; } .trimmedContent { display: none; } .repeatedContent { } `, ]; public render(): TemplateResult { return html`
`; } public getTotalNet = (): number => { let totalNet = 0; if (!this.letterData) { return totalNet; } for (const item of this.letterData.content.invoiceData.items) { totalNet += item.unitNetPrice * item.unitQuantity; } return totalNet; }; public getTotalGross = (): number => { let totalVat = 0; if (!this.letterData) { return totalVat; } for (const taxgroup of this.getVatGroups()) { totalVat += taxgroup.vatAmountSum; } return this.getTotalNet() + totalVat; }; public getVatGroups = () => { const vatGroups: { vatPercentage: number; items: plugins.tsclass.finance.IInvoice['items']; vatAmountSum: number; }[] = []; if (!this.letterData) { return vatGroups; } const taxAmounts: number[] = []; for (const item of this.letterData.content.invoiceData.items) { taxAmounts.includes(item.vatPercentage) ? null : taxAmounts.push(item.vatPercentage); } for (const taxAmount of taxAmounts) { const matchingItems = this.letterData.content.invoiceData.items.filter( (itemArg) => itemArg.vatPercentage === taxAmount ); let sum = 0; for (const matchingItem of matchingItems) { sum += matchingItem.unitNetPrice * matchingItem.unitQuantity * (taxAmount / 100); } vatGroups.push({ items: matchingItems, vatPercentage: taxAmount, vatAmountSum: Math.round(sum * 100) / 100, }); } return vatGroups; }; public async getContentNodes() { await this.elementDomReady; return { currentContent: this.shadowRoot.querySelector('.currentContent') as HTMLElement, trimmedContent: this.shadowRoot.querySelector('.trimmedContent') as HTMLElement, repeatedContent: this.shadowRoot.querySelector('.repeatedContent') as HTMLElement, }; } public async getContentLength() { await this.elementDomReady; return (await this.getContentNodes()).currentContent.children.length; } public async trimEndByOne() { await this.elementDomReady; this.shadowRoot .querySelector('.trimmedContent') .append( (await this.getContentNodes()).currentContent.children.item( (await this.getContentNodes()).currentContent.children.length - 1 ) ); } public async trimStartToOffset(contentOffsetArg: number) { await this.elementDomReady; const beginningLength = (await this.getContentNodes()).currentContent.children.length; while ( (await this.getContentNodes()).currentContent.children.length !== beginningLength - contentOffsetArg ) { (await this.getContentNodes()).trimmedContent.append( (await this.getContentNodes()).currentContent.children.item(0) ); console.log('hey' + this.shadowRoot.children.length); } if ( (await this.getContentNodes()).currentContent.children .item(0) .classList.contains('needsDataHeader') ) { const trimmedContent = (await this.getContentNodes()).trimmedContent; let startPoint = trimmedContent.children.length; while (startPoint > 0) { const element = trimmedContent.children.item(startPoint - 1); if (element.classList.contains('dataHeader')) { (await this.getContentNodes()).repeatedContent.append(element); break; } startPoint--; } } } public async firstUpdated(_changedProperties: Map) { super.firstUpdated(_changedProperties); this.attachInvoiceDom(); } public async attachInvoiceDom() { const contentNodes = await this.getContentNodes(); render( html`
We hereby invoice products and services provided to you by Lossless GmbH:
Item Pos.
${shared.translation.translate('DE', 'description', 'Description')}
${shared.translation.translate('DE', 'quantity', 'Quantity')}
Unit Type
Unit Net Price
Total Net Price
${(() => { let counter = 1; return this.letterData?.content.invoiceData?.items?.map( (invoiceItem) => html`
${counter++}
${invoiceItem.name}
${invoiceItem.unitQuantity}
${invoiceItem.unitType}
${invoiceItem.unitNetPrice} ${this.letterData?.content.invoiceData.currency}
${invoiceItem.unitQuantity * invoiceItem.unitNetPrice} ${this.letterData?.content.invoiceData.currency}
` ); })()}
Total net
${this.getTotalNet()} EUR
${this.getVatGroups().map((vatGroupArg) => { let itemNumbers = ''; for (const item of vatGroupArg.items) { const itemIndex = this.letterData.content.invoiceData.items.indexOf(item); itemNumbers += ` ${itemIndex + 1},`; } return html`
Vat ${vatGroupArg.vatPercentage}%
(on item positions: ${itemNumbers})
${vatGroupArg.vatAmountSum} EUR
`; })}
Total gross
${this.getTotalGross()} EUR
${this.letterData?.content.invoiceData.reverseCharge ? html`
VAT arises on a reverse charge basis and is payable by the customer.
` : ``}
Payment Terms:
Payment is due within 30 days starting from the reception of this invoice. Please use the following SEPA details:

Beneficiary: ${this.letterData?.from.name}
IBAN: ${this.letterData?.from?.sepaConnection?.iban}
BIC: ${this.letterData?.from?.sepaConnection?.bic}
Description: ${this.letterData?.content.invoiceData?.id}
Amount: ${this.getTotalGross()} ${this.letterData?.content.invoiceData.currency}
${this.letterData?.content?.contractData?.contractDate ? html`
Referenced contract:
This invoice is adhering to agreements made by contract between the parties on ${plugins.smarttime.ExtendedDate.fromMillis(this.letterData?.content.contractData.contractDate).format('MMMM D, YYYY')}.
` : html``}
Sepa Payment Code:
`, contentNodes.currentContent ); const canvas = document.createElement('canvas'); plugins.qrcode.toCanvas( canvas, `BCD 001 1 SCT ${this.letterData.content.invoiceData.billedBy.sepaConnection.bic} ${this.letterData.content.invoiceData.billedBy.name} ${this.letterData.content.invoiceData.billedBy.sepaConnection.iban} EUR${this.getTotalGross()} CHAR ${this.letterData.content.invoiceData.id} ${this.letterData.content.invoiceData.id} EPC QR Code`, (error) => { if (error) console.error(error); console.log('success!'); } ); contentNodes.currentContent.querySelector('.paymentCode').append(canvas); } }