This commit is contained in:
Philipp Kunz 2024-12-05 01:33:16 +01:00
parent 9517687902
commit f2d585ed36
12 changed files with 97 additions and 68 deletions

2
.npmrc
View File

@ -1 +1 @@
registry=https://verdaccio.nevermind.cloud/ registry=https://registry.npmjs.org/

View File

@ -39,7 +39,7 @@
"@git.zone/tsbuild": "^2.2.0", "@git.zone/tsbuild": "^2.2.0",
"@git.zone/tsbundle": "^2.1.0", "@git.zone/tsbundle": "^2.1.0",
"@git.zone/tstest": "^1.0.90", "@git.zone/tstest": "^1.0.90",
"@git.zone/tswatch": "^2.0.25", "@git.zone/tswatch": "^2.0.34",
"@push.rocks/projectinfo": "^5.0.2", "@push.rocks/projectinfo": "^5.0.2",
"@push.rocks/tapbundle": "^5.5.3" "@push.rocks/tapbundle": "^5.5.3"
}, },

13
pnpm-lock.yaml generated
View File

@ -58,8 +58,8 @@ importers:
specifier: ^1.0.90 specifier: ^1.0.90
version: 1.0.90(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0))(@aws-sdk/credential-providers@3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0)))(socks@2.8.3) version: 1.0.90(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0))(@aws-sdk/credential-providers@3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0)))(socks@2.8.3)
'@git.zone/tswatch': '@git.zone/tswatch':
specifier: ^2.0.25 specifier: ^2.0.34
version: 2.0.25 version: 2.0.34
'@push.rocks/projectinfo': '@push.rocks/projectinfo':
specifier: ^5.0.2 specifier: ^5.0.2
version: 5.0.2 version: 5.0.2
@ -607,8 +607,8 @@ packages:
resolution: {integrity: sha512-McytXK46GiReEps7wHWW6zOHYCFF4sywjj6auHjhGqzOogA2Wju1YtZRL+o+OAUb61kQxNFRras6Xg/4Zth0Bw==} resolution: {integrity: sha512-McytXK46GiReEps7wHWW6zOHYCFF4sywjj6auHjhGqzOogA2Wju1YtZRL+o+OAUb61kQxNFRras6Xg/4Zth0Bw==}
hasBin: true hasBin: true
'@git.zone/tswatch@2.0.25': '@git.zone/tswatch@2.0.34':
resolution: {integrity: sha512-2A2TzJhw5AHI2BUOEjtZJzNgmRDMKZc4+p+yCdgj3mYcD7l1XeM6HTTcyRTxGDrDXggVIWN1O+UxU6ftHg94ag==} resolution: {integrity: sha512-nwrJffX3aAf8DOfbWPXErskn4RTdQVaE4WLnV8QEU2WdNehI3KQCdVPYHcskl7eqatjbZdQEck39DItmNacGxw==}
hasBin: true hasBin: true
'@hapi/bourne@3.0.0': '@hapi/bourne@3.0.0':
@ -5070,7 +5070,7 @@ snapshots:
- supports-color - supports-color
- utf-8-validate - utf-8-validate
'@git.zone/tswatch@2.0.25': '@git.zone/tswatch@2.0.34':
dependencies: dependencies:
'@api.global/typedserver': 3.0.51 '@api.global/typedserver': 3.0.51
'@git.zone/tsbundle': 2.1.0 '@git.zone/tsbundle': 2.1.0
@ -5080,12 +5080,15 @@ snapshots:
'@push.rocks/smartchok': 1.0.34 '@push.rocks/smartchok': 1.0.34
'@push.rocks/smartcli': 4.0.11 '@push.rocks/smartcli': 4.0.11
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartfile': 11.0.21
'@push.rocks/smartlog': 3.0.7 '@push.rocks/smartlog': 3.0.7
'@push.rocks/smartlog-destination-local': 9.0.2 '@push.rocks/smartlog-destination-local': 9.0.2
'@push.rocks/smartshell': 3.0.6 '@push.rocks/smartshell': 3.0.6
'@push.rocks/taskbuffer': 3.1.7 '@push.rocks/taskbuffer': 3.1.7
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil
- supports-color - supports-color
- utf-8-validate
'@hapi/bourne@3.0.0': {} '@hapi/bourne@3.0.0': {}

View File

@ -213,7 +213,7 @@ export const demoLetter: plugins.tsclass.business.ILetter = {
export const demoDocumentSettings: interfaces.IDocumentSettings = { export const demoDocumentSettings: interfaces.IDocumentSettings = {
enableTopDraftText: true, enableTopDraftText: true,
enableDefaultHeader: false, enableDefaultHeader: true,
enableDefaultFooter: false, enableDefaultFooter: true,
languageCode: 'DE', languageCode: 'DE',
}; };

View File

@ -5,4 +5,5 @@ export interface IDocumentSettings {
enableDefaultHeader?: boolean; enableDefaultHeader?: boolean;
enableDefaultFooter?: boolean; enableDefaultFooter?: boolean;
languageCode?: translation.TLanguageCode; languageCode?: translation.TLanguageCode;
vatGroupPositions?: boolean;
} }

View File

@ -1,2 +1 @@
export * from './document.js'; export * from './document.js';
export * from './translation.js';

View File

@ -1,19 +0,0 @@
export interface IDeDocumentTranslations {
address: string;
bankConnection: string;
contactInfo: string;
description: string;
invoice: string;
itemPos: string;
quantity: string;
registrationInfo: string;
reverseVatNote: string;
totalNetPrice: string;
unitNetPrice: string;
unitType: string;
yourCustomerId: string;
yourVatId: string;
continuesOnPage: string;
finalPageStatement: string;
page: string;
}

View File

@ -1,10 +1,7 @@
import * as interfaces from './interfaces/index.js'; import * as interfaces from './interfaces/index.js';
type TTranslationImplementation = { // Define English translations without enforcing TTranslationImplementation yet
[key in keyof interfaces.IDeDocumentTranslations]: string; export const EN_translations = {
}
export const EN_translations: TTranslationImplementation = {
address: 'Address', address: 'Address',
bankConnection: 'Bank Connection', bankConnection: 'Bank Connection',
contactInfo: 'Contact Info', contactInfo: 'Contact Info',
@ -22,8 +19,17 @@ export const EN_translations: TTranslationImplementation = {
continuesOnPage: 'Continues on page', continuesOnPage: 'Continues on page',
finalPageStatement: 'This is the final page of this document.', finalPageStatement: 'This is the final page of this document.',
page: 'Page', page: 'Page',
} as const;
// Infer keys of EN_translations
export type TTranslationKey = keyof typeof EN_translations;
// Define the type for all translations based on EN_translations keys
export type TTranslationImplementation = {
[key in TTranslationKey]: string;
}; };
// Define German translations
export const DE_translations: TTranslationImplementation = { export const DE_translations: TTranslationImplementation = {
address: 'Adresse', address: 'Adresse',
bankConnection: 'Bankverbindung', bankConnection: 'Bankverbindung',
@ -33,9 +39,10 @@ export const DE_translations: TTranslationImplementation = {
itemPos: 'Pos.', itemPos: 'Pos.',
quantity: 'Anzahl', quantity: 'Anzahl',
registrationInfo: 'HRA/HRB Info', registrationInfo: 'HRA/HRB Info',
reverseVatNote: 'Umkehr der Umsatzsteuerpflicht: Der Rechnungsempfänger ist für die korrekte Abrechnung der Umsatzsteuer zuständig.', reverseVatNote:
totalNetPrice: 'Nettopreis Summe', 'Umkehr der Umsatzsteuerpflicht: Der Rechnungsempfänger ist für die korrekte Abrechnung der Umsatzsteuer zuständig.',
unitNetPrice: 'Nettopreis', totalNetPrice: 'Summe netto',
unitNetPrice: 'Einheit netto',
unitType: 'Einheit', unitType: 'Einheit',
yourCustomerId: 'Ihre Kundennummer:', yourCustomerId: 'Ihre Kundennummer:',
yourVatId: 'Ihre Umsatzsteuer-ID:', yourVatId: 'Ihre Umsatzsteuer-ID:',
@ -44,6 +51,7 @@ export const DE_translations: TTranslationImplementation = {
page: 'Seite', page: 'Seite',
}; };
// Define Spanish translations
export const ES_translations: TTranslationImplementation = { export const ES_translations: TTranslationImplementation = {
address: 'Dirección', address: 'Dirección',
bankConnection: 'Conexión bancaria', bankConnection: 'Conexión bancaria',
@ -64,6 +72,7 @@ export const ES_translations: TTranslationImplementation = {
page: 'Página', page: 'Página',
}; };
// Define French translations
export const FR_translations: TTranslationImplementation = { export const FR_translations: TTranslationImplementation = {
address: 'Adresse', address: 'Adresse',
bankConnection: 'Coordonnées bancaires', bankConnection: 'Coordonnées bancaires',
@ -72,11 +81,12 @@ export const FR_translations: TTranslationImplementation = {
invoice: 'Facture', invoice: 'Facture',
itemPos: 'Position', itemPos: 'Position',
quantity: 'Quantité', quantity: 'Quantité',
registrationInfo: 'Informations d\'enregistrement', registrationInfo: "Informations d'enregistrement",
reverseVatNote: 'La TVA s\'applique selon le mécanisme d\'autoliquidation et est à payer par le client.', reverseVatNote:
"La TVA s'applique selon le mécanisme d'autoliquidation et est à payer par le client.",
totalNetPrice: 'Prix net total', totalNetPrice: 'Prix net total',
unitNetPrice: 'Prix unitaire net', unitNetPrice: 'Prix unitaire net',
unitType: 'Type d\'unité', unitType: "Type d'unité",
yourCustomerId: 'Votre numéro de client :', yourCustomerId: 'Votre numéro de client :',
yourVatId: 'Votre numéro de TVA :', yourVatId: 'Votre numéro de TVA :',
continuesOnPage: 'Continue sur la page', continuesOnPage: 'Continue sur la page',
@ -84,6 +94,7 @@ export const FR_translations: TTranslationImplementation = {
page: 'Page', page: 'Page',
}; };
// Define Italian translations
export const IT_translations: TTranslationImplementation = { export const IT_translations: TTranslationImplementation = {
address: 'Indirizzo', address: 'Indirizzo',
bankConnection: 'Coordinate bancarie', bankConnection: 'Coordinate bancarie',
@ -93,7 +104,7 @@ export const IT_translations: TTranslationImplementation = {
itemPos: 'Pos.', itemPos: 'Pos.',
quantity: 'Quantità', quantity: 'Quantità',
registrationInfo: 'Informazioni di registrazione', registrationInfo: 'Informazioni di registrazione',
reverseVatNote: 'L\'IVA è applicata con inversione contabile ed è a carico del cliente.', reverseVatNote: "L'IVA è applicata con inversione contabile ed è a carico del cliente.",
totalNetPrice: 'Prezzo netto totale', totalNetPrice: 'Prezzo netto totale',
unitNetPrice: 'Prezzo netto unitario', unitNetPrice: 'Prezzo netto unitario',
unitType: 'Tipo di unità', unitType: 'Tipo di unità',
@ -104,21 +115,24 @@ export const IT_translations: TTranslationImplementation = {
page: 'Pagina', page: 'Pagina',
}; };
export const languageCodeMap = { // Language Code Map
'DE': DE_translations, export const languageCodeMap: Record<string, TTranslationImplementation> = {
'EN': EN_translations, EN: EN_translations,
'ES': ES_translations, DE: DE_translations,
'FR': FR_translations, ES: ES_translations,
'IT': IT_translations, FR: FR_translations,
IT: IT_translations,
}; };
// Language Code Type
export type TLanguageCode = keyof typeof languageCodeMap; export type TLanguageCode = keyof typeof languageCodeMap;
export const translate = (languageCode: TLanguageCode, key: string, defaultValue: string) => { // Translate Function
export const translate = (
languageCode: TLanguageCode,
key: TTranslationKey,
defaultValue: string
): string => {
const translations = languageCodeMap[languageCode] || EN_translations; const translations = languageCodeMap[languageCode] || EN_translations;
if (translations && translations[key]) { return translations[key] || defaultValue;
return translations[key];
} else {
return defaultValue;
}
}; };

View File

@ -59,6 +59,7 @@ export class DeContentInvoice extends DeesElement {
css` css`
:host { :host {
color: #333; color: #333;
font-family: inherit;
} }
.trimmedContent { .trimmedContent {
@ -204,7 +205,7 @@ export class DeContentInvoice extends DeesElement {
<style> <style>
.grid { .grid {
display: grid; display: grid;
grid-template-columns: 40px auto 80px 80px 90px 90px; grid-template-columns: 40px auto 60px 60px 84px 84px 46px;
} }
.topLine { .topLine {
margin-top: 5px; margin-top: 5px;
@ -214,6 +215,12 @@ export class DeContentInvoice extends DeesElement {
.lineItem { .lineItem {
font-size: 12px; font-size: 12px;
padding: 5px; padding: 5px;
font-family: 'Dees Code', monospace;
border-right: 1px dashed #ccc;
}
.lineItem.rightAlign {
text-align: right;
} }
.invoiceLine { .invoiceLine {
@ -224,6 +231,7 @@ export class DeContentInvoice extends DeesElement {
margin-top: 5px; margin-top: 5px;
font-size: 12px; font-size: 12px;
padding-left: 50%; padding-left: 50%;
font-family: 'Dees Code', monospace;
} }
.sums .sumline { .sums .sumline {
@ -279,26 +287,30 @@ export class DeContentInvoice extends DeesElement {
</style> </style>
<div>We hereby invoice products and services provided to you by Lossless GmbH:</div> <div>We hereby invoice products and services provided to you by Lossless GmbH:</div>
<div class="grid topLine dataHeader"> <div class="grid topLine dataHeader">
<div class="lineItem">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'itemPos', 'Item Pos.')}</div> <div class="lineItem rightAlign">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'itemPos', 'Item Pos.')}</div>
<div class="lineItem">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'description', 'Description')}</div> <div class="lineItem">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'description', 'Description')}</div>
<div class="lineItem">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'quantity', 'Quantity')}</div> <div class="lineItem rightAlign">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'quantity', 'Quantity')}</div>
<div class="lineItem">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'unitType', 'Unit Type')}</div> <div class="lineItem">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'unitType', 'Unit Type')}</div>
<div class="lineItem">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'unitNetPrice', 'Unit Net Price')}</div> <div class="lineItem rightAlign">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'unitNetPrice', 'Unit Net Price')}</div>
<div class="lineItem">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'totalNetPrice', 'Total Net Price')}</div> <div class="lineItem rightAlign">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'totalNetPrice', 'Total Net Price')}</div>
<div class="lineItem rightAlign">${plugins.shared.translation.translate(this.documentSettings.languageCode, 'vatShort', 'VAT')}</div>
</div> </div>
${(() => { ${(() => {
let counter = 1; let counter = 1;
return this.letterData?.content.invoiceData?.items?.map( return this.letterData?.content.invoiceData?.items?.map(
(invoiceItem) => html` (invoiceItem) => html`
<div class="grid invoiceLine needsDataHeader"> <div class="grid invoiceLine needsDataHeader">
<div class="lineItem">${counter++}</div> <div class="lineItem rightAlign">${counter++}</div>
<div class="lineItem">${invoiceItem.name}</div> <div class="lineItem">${invoiceItem.name}</div>
<div class="lineItem">${invoiceItem.unitQuantity}</div> <div class="lineItem rightAlign">${invoiceItem.unitQuantity}</div>
<div class="lineItem">${invoiceItem.unitType}</div> <div class="lineItem">${invoiceItem.unitType}</div>
<div class="lineItem">${invoiceItem.unitNetPrice} ${this.letterData?.content.invoiceData.currency}</div> <div class="lineItem rightAlign">${invoiceItem.unitNetPrice} ${this.letterData?.content.invoiceData.currency}</div>
<div class="lineItem"> <div class="lineItem rightAlign">
${invoiceItem.unitQuantity * invoiceItem.unitNetPrice} ${this.letterData?.content.invoiceData.currency} ${invoiceItem.unitQuantity * invoiceItem.unitNetPrice} ${this.letterData?.content.invoiceData.currency}
</div> </div>
<div class="lineItem rightAlign">
${invoiceItem.vatPercentage}%
</div>
</div> </div>
` `
); );
@ -317,8 +329,10 @@ export class DeContentInvoice extends DeesElement {
return html` return html`
<div class="sumline"> <div class="sumline">
<div class="label"> <div class="label">
Vat ${vatGroupArg.vatPercentage}%<br /> Vat ${vatGroupArg.vatPercentage}%
<span style="font-weight: normal">(on item positions: ${itemNumbers})</span> ${this.documentSettings.vatGroupPositions ? html`
<br /><span style="font-weight: normal">(on item positions: ${itemNumbers})</span>
` : html``}
</div> </div>
<div class="value">${vatGroupArg.vatAmountSum} EUR</div> <div class="value">${vatGroupArg.vatAmountSum} EUR</div>
</div> </div>

View File

@ -16,6 +16,8 @@ export const defaultDocumentSettings: plugins.shared.interfaces.IDocumentSetting
enableTopDraftText: true, enableTopDraftText: true,
enableDefaultHeader: true, enableDefaultHeader: true,
enableDefaultFooter: true, enableDefaultFooter: true,
languageCode: 'EN',
vatGroupPositions: true,
}; };
@ -97,6 +99,7 @@ export class DeDocument extends DeesElement {
color: #333; color: #333;
padding: 0px; padding: 0px;
position: relative; position: relative;
font-family: 'Dees Sans', sans-serif;
} }
.betweenPagesSpacer { .betweenPagesSpacer {
@ -112,6 +115,12 @@ export class DeDocument extends DeesElement {
} }
public async firstUpdated(_changedProperties: Map<string | number | symbol, unknown>) { public async firstUpdated(_changedProperties: Map<string | number | symbol, unknown>) {
domtools.plugins.smartdelay.delayFor(0).then(() => {
this.documentSettings = {
...defaultDocumentSettings,
...this.documentSettings,
}
});
const resizeObserver = new ResizeObserver((entries) => { const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) { for (const entry of entries) {
const width = entry.contentRect.width; const width = entry.contentRect.width;
@ -126,13 +135,19 @@ export class DeDocument extends DeesElement {
}) })
} }
public latestDocumentSettings: plugins.shared.interfaces.IDocumentSettings = null;
public latestRenderedLetterData: plugins.tsclass.business.ILetter = null; public latestRenderedLetterData: plugins.tsclass.business.ILetter = null;
public async renderDocument() { public async renderDocument() {
const domtools = await this.domtoolsPromise;
this.latestDocumentSettings = this.documentSettings;
this.latestRenderedLetterData = this.letterData;
console.log(`rendering with settings:`);
console.log(this.latestDocumentSettings);
const domtools = await this.domtoolsPromise;
const documentBuildContainer = document.createElement('div'); const documentBuildContainer = document.createElement('div');
document.body.appendChild(documentBuildContainer); document.body.appendChild(documentBuildContainer);
@ -199,13 +214,13 @@ export class DeDocument extends DeesElement {
} }
} }
this.adjustDePageScaling(); this.adjustDePageScaling();
this.latestRenderedLetterData = this.letterData;
} }
async updated(changedProperties: Map<string | number | symbol, unknown>): Promise<void> { async updated(changedProperties: Map<string | number | symbol, unknown>): Promise<void> {
super.updated(changedProperties); super.updated(changedProperties);
const domtools = await this.domtoolsPromise; const domtools = await this.domtoolsPromise;
const renderedDocIsUpToDate = domtools.convenience.smartjson.deepEqualObjects(this.letterData, this.latestRenderedLetterData); let renderedDocIsUpToDate = domtools.convenience.smartjson.deepEqualObjects(this.letterData, this.latestRenderedLetterData)
&& domtools.convenience.smartjson.deepEqualObjects(this.documentSettings, this.latestDocumentSettings);
if (!renderedDocIsUpToDate) { if (!renderedDocIsUpToDate) {
this.renderDocument(); this.renderDocument();
} }

View File

@ -77,6 +77,7 @@ export class DePage extends DeesElement {
css` css`
:host { :host {
display: block; display: block;
font-family: inherit;
} }
#scaleWrapper { #scaleWrapper {

View File

@ -49,6 +49,7 @@ export class DePageContent extends DeesElement {
css` css`
:host { :host {
color: #333; color: #333;
font-family: inherit;
} }
.content { .content {