/**
* content for invoices
*/
import {
DeesElement,
property,
html,
customElement,
type TemplateResult,
css,
cssManager,
unsafeCSS,
render,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import * as plugins from '../plugins.js';
import * as shared from './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:
${(() => {
let counter = 1;
return this.letterData?.content.invoiceData?.items?.map(
(invoiceItem) => html`
`
);
})()}
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``}
`,
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);
}
}