fix(core): update
This commit is contained in:
commit
20ffd067b2
66
.gitea/workflows/default_nottags.yaml
Normal file
66
.gitea/workflows/default_nottags.yaml
Normal file
@ -0,0 +1,66 @@
|
||||
name: Default (not tags)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags-ignore:
|
||||
- '**'
|
||||
|
||||
env:
|
||||
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
|
||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||
|
||||
jobs:
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install pnpm and npmci
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
|
||||
- name: Run npm prepare
|
||||
run: npmci npm prepare
|
||||
|
||||
- name: Audit production dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --prod
|
||||
continue-on-error: true
|
||||
|
||||
- name: Audit development dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --dev
|
||||
continue-on-error: true
|
||||
|
||||
test:
|
||||
if: ${{ always() }}
|
||||
needs: security
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Test stable
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm test
|
||||
|
||||
- name: Test build
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm build
|
124
.gitea/workflows/default_tags.yaml
Normal file
124
.gitea/workflows/default_tags.yaml
Normal file
@ -0,0 +1,124 @@
|
||||
name: Default (tags)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
|
||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||
|
||||
jobs:
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Audit production dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --prod
|
||||
continue-on-error: true
|
||||
|
||||
- name: Audit development dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --dev
|
||||
continue-on-error: true
|
||||
|
||||
test:
|
||||
if: ${{ always() }}
|
||||
needs: security
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Test stable
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm test
|
||||
|
||||
- name: Test build
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm build
|
||||
|
||||
release:
|
||||
needs: test
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Release
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm publish
|
||||
|
||||
metadata:
|
||||
needs: test
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Code quality
|
||||
run: |
|
||||
npmci command npm install -g typescript
|
||||
npmci npm install
|
||||
|
||||
- name: Trigger
|
||||
run: npmci trigger
|
||||
|
||||
- name: Build docs and upload artifacts
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
pnpm install -g @git.zone/tsdoc
|
||||
npmci command tsdoc
|
||||
continue-on-error: true
|
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
.nogit/
|
||||
|
||||
# artifacts
|
||||
coverage/
|
||||
public/
|
||||
pages/
|
||||
|
||||
# installs
|
||||
node_modules/
|
||||
|
||||
# caches
|
||||
.yarn/
|
||||
.cache/
|
||||
.rpt2_cache
|
||||
|
||||
# builds
|
||||
dist/
|
||||
dist_*/
|
||||
|
||||
# custom
|
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "npm test",
|
||||
"name": "Run npm test",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
26
.vscode/settings.json
vendored
Normal file
26
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": ["/npmextra.json"],
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"npmci": {
|
||||
"type": "object",
|
||||
"description": "settings for npmci"
|
||||
},
|
||||
"gitzone": {
|
||||
"type": "object",
|
||||
"description": "settings for gitzone",
|
||||
"properties": {
|
||||
"projectType": {
|
||||
"type": "string",
|
||||
"enum": ["website", "element", "service", "npm", "wcc"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
28
html/index.html
Normal file
28
html/index.html
Normal file
@ -0,0 +1,28 @@
|
||||
<!--gitzone element-->
|
||||
<!-- made by Task Venture Capital GmbH -->
|
||||
<!-- checkout https://maintainedby.lossless.com for awesome OpenSource projects -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!--Lets set some basic meta tags-->
|
||||
<meta
|
||||
name="viewport"
|
||||
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height"
|
||||
/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<!--Lets load standard fonts-->
|
||||
<link rel="preconnect" href="https://assetbroker.lossless.one/" crossorigin>
|
||||
<link rel="stylesheet" href="https://assetbroker.lossless.one/fonts/fonts.css">
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0px;
|
||||
background: #222222;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="module" src="/bundle.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
10
html/index.ts
Normal file
10
html/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
// dees tools
|
||||
import * as deesWccTools from '@design.estate/dees-wcctools';
|
||||
import * as deesDomTools from '@design.estate/dees-domtools';
|
||||
|
||||
// elements and pages
|
||||
import * as elements from '../ts_web/elements/index.js';
|
||||
import * as pages from '../ts_web/pages/index.js';
|
||||
|
||||
deesWccTools.setupWccTools(elements as any, pages);
|
||||
deesDomTools.elementBasic.setup();
|
19
license
Normal file
19
license
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2022 Lossless GmbH (hello@lossless.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
20
npmextra.json
Normal file
20
npmextra.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"gitzone": {
|
||||
"projectType": "wcc",
|
||||
"module": {
|
||||
"githost": "gitlab.com",
|
||||
"gitscope": "designestate/private",
|
||||
"gitrepo": "dedocument-catalog",
|
||||
"description": "a catalog for creating documents like invoices",
|
||||
"npmPackagename": "@designestate_private/dedocument-catalog",
|
||||
"license": "MIT",
|
||||
"projectDomain": "design.estate",
|
||||
"shortDescription": "undefined variable"
|
||||
}
|
||||
},
|
||||
"npmci": {
|
||||
"npmGlobalTools": [],
|
||||
"npmAccessLevel": "private",
|
||||
"npmRegistryUrl": "verdaccio2.lossless.one"
|
||||
}
|
||||
}
|
65
package.json
Normal file
65
package.json
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "@design.estate/dees-document",
|
||||
"version": "1.0.90",
|
||||
"private": false,
|
||||
"description": "a catalog for creating documents like invoices",
|
||||
"main": "dist_ts_web/index.js",
|
||||
"typings": "dist_ts_web/index.d.ts",
|
||||
"exports": {
|
||||
"./ts": "./dist_ts/index.js",
|
||||
"./ts_web": "./dist_ts_web/index.js"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "npm run build && tstest test/",
|
||||
"build": "tsbuild --allowimplicitany && tsbuild element --allowimplicitany && tsbundle element --production",
|
||||
"watch": "tswatch element"
|
||||
},
|
||||
"author": "Lossless GmbH",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@design.estate/dees-domtools": "^2.0.34",
|
||||
"@design.estate/dees-element": "^2.0.23",
|
||||
"@design.estate/dees-wcctools": "^1.0.76",
|
||||
"@git.zone/tsrun": "^1.2.44",
|
||||
"@push.rocks/smartfile": "^10.0.26",
|
||||
"@push.rocks/smartjson": "^5.0.10",
|
||||
"@push.rocks/smartpath": "^5.0.5",
|
||||
"@push.rocks/smartpdf": "^3.0.16",
|
||||
"@push.rocks/smarttime": "^4.0.1",
|
||||
"@tsclass/tsclass": "^4.0.46",
|
||||
"@types/qrcode": "^1.5.2",
|
||||
"qrcode": "^1.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@git.zone/tsbuild": "^2.1.66",
|
||||
"@git.zone/tsbundle": "^2.0.8",
|
||||
"@git.zone/tstest": "^1.0.77",
|
||||
"@git.zone/tswatch": "^2.0.7",
|
||||
"@push.rocks/projectinfo": "^5.0.1",
|
||||
"@push.rocks/tapbundle": "^5.0.8"
|
||||
},
|
||||
"files": [
|
||||
"ts/**/*",
|
||||
"ts_web/**/*",
|
||||
"dist/**/*",
|
||||
"dist_*/**/*",
|
||||
"dist_ts/**/*",
|
||||
"dist_ts_web/**/*",
|
||||
"assets/**/*",
|
||||
"cli.js",
|
||||
"npmextra.json",
|
||||
"readme.md"
|
||||
],
|
||||
"browserslist": [
|
||||
"last 1 Chrome versions"
|
||||
],
|
||||
"type": "module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://gitlab.com/designestate/private/dedocument-catalog.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://gitlab.com/designestate/private/dedocument-catalog/issues"
|
||||
},
|
||||
"homepage": "https://gitlab.com/designestate/private/dedocument-catalog#readme"
|
||||
}
|
5518
pnpm-lock.yaml
generated
Normal file
5518
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
7
readme.md
Normal file
7
readme.md
Normal file
@ -0,0 +1,7 @@
|
||||
# @design.estate/private/dedocument-catalog
|
||||
a catalog for creating documents like invoices
|
||||
|
||||
## Usage
|
||||
|
||||
Use TypeScript for best in class intellisense
|
||||
For further information read the linked docs at the top of this readme.
|
3
test/paths.ts
Normal file
3
test/paths.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import * as plugins from './plugins.js';
|
||||
|
||||
export const nogitDir = plugins.path.join(process.cwd(), '.nogit');
|
9
test/plugins.ts
Normal file
9
test/plugins.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import * as tsclass from '@tsclass/tsclass';
|
||||
import * as smartfile from '@push.rocks/smartfile';
|
||||
import * as path from 'path';
|
||||
|
||||
export {
|
||||
tsclass,
|
||||
smartfile,
|
||||
path,
|
||||
}
|
152
test/test.ts
Normal file
152
test/test.ts
Normal file
@ -0,0 +1,152 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as paths from './paths.js';
|
||||
import { expect, tap } from '@push.rocks/tapbundle';
|
||||
import * as deesDocumentServer from '../ts/index.js';
|
||||
|
||||
let testPdfServiceInstance: deesDocumentServer.PdfService;
|
||||
const testLetterData: plugins.tsclass.business.ILetter = {
|
||||
accentColor: null,
|
||||
type: 'invoice',
|
||||
date: null,
|
||||
needsCoverSheet: true,
|
||||
objectActions: [],
|
||||
pdf: null,
|
||||
content: {
|
||||
invoiceData: {
|
||||
id: 'XX-CLIENT-48765',
|
||||
reverseCharge: true,
|
||||
dueInDays: 30,
|
||||
billedBy: {
|
||||
address: null,
|
||||
description: null,
|
||||
name: 'Some Service GmbH',
|
||||
type: null,
|
||||
customerNumber: null,
|
||||
email: null,
|
||||
facebookUrl: null,
|
||||
fax: null,
|
||||
legalEntity: null,
|
||||
sepaConnection: {
|
||||
bic: 'BPOTBEB1',
|
||||
iban: 'BE72000000001616',
|
||||
},
|
||||
},
|
||||
billedTo: null,
|
||||
status: null,
|
||||
deliveryDate: new Date().getTime(),
|
||||
periodOfPerformance: null,
|
||||
printResult: null,
|
||||
items: [
|
||||
{
|
||||
name: 'Website Creation',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 1200,
|
||||
unitType: 'item',
|
||||
vatPercentage: 0,
|
||||
currency: 'EUR',
|
||||
},
|
||||
],
|
||||
},
|
||||
contractData: {
|
||||
contractDate: Date.now(),
|
||||
id: 'LL-CONTRACT-48765',
|
||||
},
|
||||
textData: [],
|
||||
timesheetData: '',
|
||||
},
|
||||
from: {
|
||||
name: 'PdfService Test Company',
|
||||
type: 'company',
|
||||
description: 'doing pdf stuff',
|
||||
address: {
|
||||
streetName: 'Awesome Street',
|
||||
houseNumber: '5',
|
||||
city: 'Bremen',
|
||||
country: 'Germany',
|
||||
postalCode: '28359',
|
||||
},
|
||||
sepaConnection: {
|
||||
bic: 'BPOTBEB1',
|
||||
iban: 'BE72000000001616',
|
||||
},
|
||||
},
|
||||
to: {
|
||||
name: 'Awesome To Company',
|
||||
type: 'company',
|
||||
description: 'a company that does stuff',
|
||||
address: {
|
||||
streetName: 'Awesome Street',
|
||||
houseNumber: '5',
|
||||
city: 'Bremen',
|
||||
country: 'Germany',
|
||||
postalCode: '28359',
|
||||
},
|
||||
},
|
||||
incidenceId: null,
|
||||
language: null,
|
||||
legalContact: null,
|
||||
logoUrl: null,
|
||||
pdfAttachments: null,
|
||||
subject: 'Invoice XX-CLIENT-48765',
|
||||
versionInfo: {
|
||||
type: 'final',
|
||||
version: '1.0.0',
|
||||
},
|
||||
};
|
||||
|
||||
tap.test('should create a document from an invoice', async () => {
|
||||
testPdfServiceInstance = new deesDocumentServer.PdfService({});
|
||||
await testPdfServiceInstance.start();
|
||||
expect(testPdfServiceInstance).toBeInstanceOf(deesDocumentServer.PdfService);
|
||||
});
|
||||
|
||||
tap.test('should create an invoice', async () => {
|
||||
let counter = 0;
|
||||
const saveResult = async (letterDataArg: plugins.tsclass.business.ILetter) => {
|
||||
const pdfResult = await testPdfServiceInstance.createPdfFromLetterObject(letterDataArg);
|
||||
await plugins.smartfile.memory.toFs(Buffer.from(pdfResult.buffer), plugins.path.join(paths.nogitDir, `test-${counter++}.pdf`));
|
||||
}
|
||||
await saveResult(testLetterData);
|
||||
await saveResult({
|
||||
...testLetterData,
|
||||
versionInfo: {
|
||||
type: 'draft',
|
||||
version: '1.0.0',
|
||||
}
|
||||
});
|
||||
testLetterData.content.invoiceData.items = [
|
||||
{
|
||||
name: 'Website Creation',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 1200,
|
||||
unitType: 'item',
|
||||
vatPercentage: 0,
|
||||
currency: 'EUR',
|
||||
},
|
||||
{
|
||||
name: 'Hosting',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 1200,
|
||||
unitType: 'item',
|
||||
vatPercentage: 19,
|
||||
currency: 'EUR',
|
||||
},
|
||||
{
|
||||
name: 'Overnight Shipping',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 1200,
|
||||
unitType: 'item',
|
||||
vatPercentage: 24,
|
||||
currency: 'EUR',
|
||||
},
|
||||
],
|
||||
await saveResult({
|
||||
...testLetterData,
|
||||
})
|
||||
});
|
||||
|
||||
tap.test('should stop the service', async () => {
|
||||
await testPdfServiceInstance.stop();
|
||||
});
|
||||
|
||||
tap.start();
|
8
ts/00_commitinfo_data.ts
Normal file
8
ts/00_commitinfo_data.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* autocreated commitinfo by @pushrocks/commitinfo
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-document',
|
||||
version: '1.0.91',
|
||||
description: 'a catalog for creating documents like invoices'
|
||||
}
|
51
ts/classes.pdfservice.ts
Normal file
51
ts/classes.pdfservice.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as paths from './paths.js';
|
||||
import * as helpers from './helpers.js';
|
||||
|
||||
export interface IPdfServiceConstructorOptions {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* a pdf service for generating pdfs
|
||||
*/
|
||||
export class PdfService {
|
||||
// INSTANCE
|
||||
options: IPdfServiceConstructorOptions;
|
||||
|
||||
public smartpdfInstance: plugins.smartpdf.SmartPdf;
|
||||
|
||||
constructor(optionsArg: IPdfServiceConstructorOptions) {
|
||||
this.options = optionsArg;
|
||||
}
|
||||
|
||||
/**
|
||||
* starts the PdfService
|
||||
*/
|
||||
public async start() {
|
||||
this.smartpdfInstance = new plugins.smartpdf.SmartPdf();
|
||||
await this.smartpdfInstance.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* stops the PdfService
|
||||
*/
|
||||
public async stop() {
|
||||
await this.smartpdfInstance.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* creates an letter
|
||||
*/
|
||||
public async createPdfFromLetterObject(letterDataArg: plugins.tsclass.business.ILetter) {
|
||||
const html = `
|
||||
<script type="module">
|
||||
${await helpers.getBundleAsString()}
|
||||
</script>
|
||||
<dedocument-dedocument printMode letterData="${plugins.smartjson.stringifyBase64(letterDataArg)}"></dedocument-dedocument>
|
||||
`;
|
||||
// console.log(html);
|
||||
const pdfResult = await this.smartpdfInstance.getA4PdfResultForHtmlString(html);
|
||||
return pdfResult;
|
||||
}
|
||||
}
|
6
ts/helpers.ts
Normal file
6
ts/helpers.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as paths from './paths.js';
|
||||
|
||||
export const getBundleAsString = async () => {
|
||||
return plugins.smartfile.fs.toStringSync(paths.bundleFile);
|
||||
}
|
2
ts/index.ts
Normal file
2
ts/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './helpers.js';
|
||||
export * from './classes.pdfservice.js';
|
9
ts/paths.ts
Normal file
9
ts/paths.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import * as plugins from './plugins.js';
|
||||
|
||||
export const packageDir = plugins.path.join(plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), '../');
|
||||
|
||||
export const nogitDir = plugins.path.join(packageDir, '.nogit/');
|
||||
plugins.smartfile.fs.ensureDirSync(nogitDir);
|
||||
|
||||
export const bundleDir = plugins.path.join(packageDir, './dist_bundle');
|
||||
export const bundleFile = plugins.path.join(bundleDir, './bundle.js');
|
26
ts/plugins.ts
Normal file
26
ts/plugins.ts
Normal file
@ -0,0 +1,26 @@
|
||||
// node native
|
||||
import * as path from 'path';
|
||||
|
||||
export {
|
||||
path
|
||||
}
|
||||
|
||||
// @push.rocks/scope
|
||||
import * as smartfile from '@push.rocks/smartfile';
|
||||
import * as smartjson from '@push.rocks/smartjson';
|
||||
import * as smartpath from '@push.rocks/smartpath';
|
||||
import * as smartpdf from '@push.rocks/smartpdf';
|
||||
|
||||
export {
|
||||
smartfile,
|
||||
smartpath,
|
||||
smartjson,
|
||||
smartpdf,
|
||||
}
|
||||
|
||||
// @tsclass scope
|
||||
import * as tsclass from '@tsclass/tsclass';
|
||||
|
||||
export {
|
||||
tsclass,
|
||||
}
|
8
ts_web/00_commitinfo_data.ts
Normal file
8
ts_web/00_commitinfo_data.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* autocreated commitinfo by @pushrocks/commitinfo
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-document',
|
||||
version: '1.0.91',
|
||||
description: 'a catalog for creating documents like invoices'
|
||||
}
|
385
ts_web/elements/contentinvoice.ts
Normal file
385
ts_web/elements/contentinvoice.ts
Normal file
@ -0,0 +1,385 @@
|
||||
/**
|
||||
* 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`
|
||||
<style>
|
||||
.demoContainer {
|
||||
background: white;
|
||||
padding: 50px;
|
||||
}
|
||||
</style>
|
||||
<div class="demoContainer">
|
||||
<dedocument-contentinvoice></dedocument-contentinvoice>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@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`
|
||||
<div class="trimmedContent"></div>
|
||||
<div class="repeatedContent"></div>
|
||||
<div class="currentContent"></div>
|
||||
`;
|
||||
}
|
||||
|
||||
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<string | number | symbol, unknown>) {
|
||||
super.firstUpdated(_changedProperties);
|
||||
this.attachInvoiceDom();
|
||||
}
|
||||
|
||||
public async attachInvoiceDom() {
|
||||
const contentNodes = await this.getContentNodes();
|
||||
render(
|
||||
html`
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 40px auto 80px 80px 90px 90px;
|
||||
}
|
||||
.topLine {
|
||||
margin-top: 5px;
|
||||
background: #e7e7e7;
|
||||
font-weight: bold;
|
||||
}
|
||||
.lineItem {
|
||||
font-size: 12px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.invoiceLine {
|
||||
border-bottom: 1px dotted #ccc;
|
||||
}
|
||||
|
||||
.sums {
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
padding-left: 50%;
|
||||
}
|
||||
|
||||
.sums .sumline {
|
||||
margin-top: 3px;
|
||||
display: grid;
|
||||
grid-template-columns: auto 90px;
|
||||
}
|
||||
|
||||
.sums .sumline .label {
|
||||
padding: 2px 5px;
|
||||
border-right: 1px solid #ccc;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sums .sumline .value {
|
||||
padding: 2px 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin-top: 8px;
|
||||
border-top: 1px dotted #ccc;
|
||||
}
|
||||
|
||||
.taxNote {
|
||||
font-size: 12px;
|
||||
padding: 4px;
|
||||
background: #eeeeeb;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.infoBox {
|
||||
border-radius: 7px;
|
||||
border: 1px solid #ddd;
|
||||
border-left: 3px solid #c5e1a5;
|
||||
padding: 8px;
|
||||
margin-top: 16px;
|
||||
line-height: 1.4em;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.infoBox .label {
|
||||
padding-bottom: 2px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.paymentCode {
|
||||
text-align: center;
|
||||
border-left: 2px solid #666;
|
||||
}
|
||||
</style>
|
||||
<div>We hereby invoice products and services provided to you by Lossless GmbH:</div>
|
||||
<div class="grid topLine dataHeader">
|
||||
<div class="lineItem">Item Pos.</div>
|
||||
<div class="lineItem">Description</div>
|
||||
<div class="lineItem">Quantity</div>
|
||||
<div class="lineItem">Unit Type</div>
|
||||
<div class="lineItem">Unit Net Price</div>
|
||||
<div class="lineItem">Total Net Price</div>
|
||||
</div>
|
||||
${(() => {
|
||||
let counter = 1;
|
||||
return this.letterData?.content.invoiceData?.items?.map(
|
||||
(invoiceItem) => html`
|
||||
<div class="grid invoiceLine needsDataHeader">
|
||||
<div class="lineItem">${counter++}</div>
|
||||
<div class="lineItem">${invoiceItem.name}</div>
|
||||
<div class="lineItem">${invoiceItem.unitQuantity}</div>
|
||||
<div class="lineItem">${invoiceItem.unitType}</div>
|
||||
<div class="lineItem">${invoiceItem.unitNetPrice} ${invoiceItem.currency}</div>
|
||||
<div class="lineItem">
|
||||
${invoiceItem.unitQuantity * invoiceItem.unitNetPrice} ${invoiceItem.currency}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
);
|
||||
})()}
|
||||
<div class="sums">
|
||||
<div class="sumline">
|
||||
<div class="label">Total net</div>
|
||||
<div class="value">${this.getTotalNet()} EUR</div>
|
||||
</div>
|
||||
${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`
|
||||
<div class="sumline">
|
||||
<div class="label">
|
||||
Vat ${vatGroupArg.vatPercentage}%<br />
|
||||
<span style="font-weight: normal">(on item positions: ${itemNumbers})</span>
|
||||
</div>
|
||||
<div class="value">${vatGroupArg.vatAmountSum} EUR</div>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
<div class="sumline">
|
||||
<div class="label">Total gross</div>
|
||||
<div class="value">${this.getTotalGross()} EUR</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
${this.letterData?.content.invoiceData.reverseCharge
|
||||
? html`
|
||||
<div class="taxNote">
|
||||
VAT arises on a reverse charge basis and is payable by the customer.
|
||||
</div>
|
||||
`
|
||||
: ``}
|
||||
<div class="infoBox">
|
||||
<div class="label">Payment Terms:</div>
|
||||
Payment is due within 30 days starting from the reception of this invoice. Please use the
|
||||
following SEPA details:
|
||||
<br /><br />
|
||||
Beneficiary: ${this.letterData?.from.name}<br />
|
||||
IBAN: ${this.letterData?.from?.sepaConnection?.iban}<br />
|
||||
BIC: ${this.letterData?.from?.sepaConnection?.bic}<br />
|
||||
Description: ${this.letterData?.content.invoiceData?.id}<br />
|
||||
Amount: ${this.getTotalGross()} ${this.letterData?.content.invoiceData.items[0].currency}
|
||||
</div>
|
||||
${this.letterData?.content?.contractData?.contractDate
|
||||
? html`
|
||||
<div class="infoBox">
|
||||
<div class="label">Referenced contract:</div>
|
||||
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')}.
|
||||
</div>
|
||||
`
|
||||
: html``}
|
||||
<div class="infoBox paymentCode">
|
||||
<div class="label">Sepa Payment Code:</div>
|
||||
</div>
|
||||
`,
|
||||
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);
|
||||
}
|
||||
}
|
168
ts_web/elements/document.ts
Normal file
168
ts_web/elements/document.ts
Normal file
@ -0,0 +1,168 @@
|
||||
import {
|
||||
DeesElement,
|
||||
property,
|
||||
html,
|
||||
customElement,
|
||||
type TemplateResult,
|
||||
css,
|
||||
state,
|
||||
cssManager,
|
||||
unsafeCSS,
|
||||
domtools,
|
||||
} from '@design.estate/dees-element';
|
||||
|
||||
import * as plugins from '../plugins.js';
|
||||
|
||||
|
||||
import { DePage } from './page.js';
|
||||
import { DeContentInvoice } from './contentinvoice.js';
|
||||
|
||||
import * as shared from './shared/index.js';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'dedocument-dedocument': DeDocument;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('dedocument-dedocument')
|
||||
export class DeDocument extends DeesElement {
|
||||
public static demo = () => html`
|
||||
<dedocument-dedocument .format="${'a4'}" .letterData=${shared.demoLetter}></dedocument-dedocument>
|
||||
`;
|
||||
|
||||
@property({
|
||||
type: String,
|
||||
})
|
||||
public format: 'a4' = 'a4';
|
||||
|
||||
@property({
|
||||
type: Number,
|
||||
})
|
||||
public viewWidth: number = shared.a4Width;
|
||||
|
||||
@property({
|
||||
type: Boolean,
|
||||
})
|
||||
printMode = false;
|
||||
|
||||
@property({
|
||||
type: Object,
|
||||
reflect: true,
|
||||
converter: (valueArg) => {
|
||||
if (typeof valueArg === 'string') {
|
||||
return plugins.smartjson.parseBase64(valueArg)
|
||||
} else {
|
||||
return valueArg;
|
||||
}
|
||||
},
|
||||
|
||||
})
|
||||
public letterData: plugins.tsclass.business.ILetter;
|
||||
|
||||
@property({
|
||||
type: String,
|
||||
})
|
||||
public letterDataUrl: string;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
domtools.DomTools.setupDomTools();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
domtools.elementBasic.staticStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
color: #333;
|
||||
padding: 0px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.betweenPagesSpacer {
|
||||
height: 20px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
:host {
|
||||
transform-origin: top left;
|
||||
transform: ${this.viewWidth
|
||||
? unsafeCSS(
|
||||
`scale(${this.viewWidth / shared.a4Width}, ${this.viewWidth / shared.a4Width})`
|
||||
)
|
||||
: unsafeCSS('')};
|
||||
}
|
||||
</style>
|
||||
<div class="scaleport"></div>
|
||||
`;
|
||||
}
|
||||
|
||||
public async firstUpdated(_changedProperties: Map<string | number | symbol, unknown>) {
|
||||
if (this.letterDataUrl) {
|
||||
const response = await fetch(this.letterDataUrl);
|
||||
this.letterData = await response.json();
|
||||
}
|
||||
this.renderDocument();
|
||||
}
|
||||
|
||||
public async renderDocument() {
|
||||
const domtools = await this.domtoolsPromise;
|
||||
const scaleport = this.shadowRoot.querySelector('.scaleport');
|
||||
let pages: DePage[] = [];
|
||||
let pageCounter = 0;
|
||||
let complete = false;
|
||||
const content: DeContentInvoice = document.createElement(
|
||||
'dedocument-contentinvoice'
|
||||
) as DeContentInvoice;
|
||||
content.letterData = this.letterData;
|
||||
document.body.appendChild(content);
|
||||
await domtools.convenience.smartdelay.delayFor(0);
|
||||
let overallContentOffset: number = 0;
|
||||
let currentContentOffset: number;
|
||||
let trimmed: number;
|
||||
while (!complete) {
|
||||
pageCounter++;
|
||||
const currentContent = content.cloneNode(true) as DeContentInvoice;
|
||||
const newPage = new DePage();
|
||||
newPage.printMode = this.printMode;
|
||||
newPage.letterData = this.letterData;
|
||||
pages.push(newPage);
|
||||
newPage.pageNumber = pageCounter;
|
||||
newPage.append(currentContent);
|
||||
newPage.pageTotalNumber = pageCounter;
|
||||
scaleport.append(newPage);
|
||||
// betweenPagesSpacer
|
||||
if (!this.printMode) {
|
||||
const betweenPagesSpacerDiv = document.createElement('div');
|
||||
betweenPagesSpacerDiv.classList.add('betweenPagesSpacer');
|
||||
scaleport.append(betweenPagesSpacerDiv);
|
||||
}
|
||||
await currentContent.elementDomReady;
|
||||
await currentContent.trimStartToOffset(overallContentOffset);
|
||||
let newPageOverflows = await newPage.checkOverflow();
|
||||
trimmed = 0;
|
||||
while (newPageOverflows) {
|
||||
await currentContent.trimEndByOne();
|
||||
trimmed++;
|
||||
newPageOverflows = await newPage.checkOverflow();
|
||||
}
|
||||
currentContentOffset = await currentContent.getContentLength();
|
||||
overallContentOffset = overallContentOffset + currentContentOffset;
|
||||
if (trimmed === 0) {
|
||||
complete = true;
|
||||
}
|
||||
// complete = true;
|
||||
console.log(currentContentOffset);
|
||||
}
|
||||
document.body.removeChild(content);
|
||||
|
||||
for (const page of pages) {
|
||||
page.pageTotalNumber = pageCounter;
|
||||
}
|
||||
}
|
||||
}
|
8
ts_web/elements/index.ts
Normal file
8
ts_web/elements/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export * from './contentinvoice.js';
|
||||
export * from './document.js';
|
||||
export * from './letterheader.js';
|
||||
export * from './page.js';
|
||||
export * from './pagecontainer.js';
|
||||
export * from './pagecontent.js';
|
||||
export * from './pagefooter.js';
|
||||
export * from './pageheader.js';
|
125
ts_web/elements/letterheader.ts
Normal file
125
ts_web/elements/letterheader.ts
Normal file
@ -0,0 +1,125 @@
|
||||
import {
|
||||
DeesElement,
|
||||
property,
|
||||
html,
|
||||
customElement,
|
||||
type TemplateResult,
|
||||
css,
|
||||
cssManager,
|
||||
unsafeCSS,
|
||||
} from '@design.estate/dees-element';
|
||||
import * as domtools from '@design.estate/dees-domtools';
|
||||
|
||||
import * as shared from './shared/index.js';
|
||||
import * as tsclass from '@tsclass/tsclass';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'dedocument-letterheader': DeLetterHeader;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('dedocument-letterheader')
|
||||
export class DeLetterHeader extends DeesElement {
|
||||
public static demo = () => html`
|
||||
<dedocument-letterheader .format="${'a4'}" .letterData=${shared.demoLetter}></dedocument-letterheader>
|
||||
`;
|
||||
|
||||
@property({
|
||||
type: Object,
|
||||
reflect: true
|
||||
})
|
||||
public letterData: tsclass.business.ILetter;
|
||||
|
||||
@property({
|
||||
type: Number,
|
||||
reflect: true,
|
||||
})
|
||||
public pageNumber: number = 1;
|
||||
|
||||
@property({
|
||||
type: Number,
|
||||
reflect: true,
|
||||
})
|
||||
public pageTotalNumber: number = 1;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
domtools.DomTools.setupDomTools();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
domtools.elementBasic.staticStyles,
|
||||
css`
|
||||
:host {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.recepientInfo {
|
||||
position: absolute;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
top: 200px;
|
||||
right: ${unsafeCSS(shared.rightMargin + 'px')};
|
||||
width: 200px;
|
||||
text-align: right;
|
||||
}
|
||||
.recepientInfo .label {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.date {
|
||||
position: absolute;
|
||||
top: 180px;
|
||||
right: ${unsafeCSS(shared.rightMargin + 'px')};
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.address {
|
||||
position: absolute;
|
||||
top: 180px;
|
||||
left: ${unsafeCSS(shared.leftMargin + 'px')};
|
||||
}
|
||||
|
||||
.address .from {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.address .to {
|
||||
margin-top: 10px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
<div class="date">
|
||||
${new Date(this.letterData.date).getDate()}. ${new Date(this.letterData.date).toLocaleString('default', { month: 'long' })}
|
||||
${new Date(this.letterData.date).getFullYear()}
|
||||
</div>
|
||||
<div class="address">
|
||||
<div class="from">
|
||||
${this.letterData.from.name}, ${this.letterData.from.address.streetName}
|
||||
${this.letterData.from.address.houseNumber}, ${this.letterData.from.address.postalCode}
|
||||
${this.letterData.from.address.city}, ${this.letterData.from.address.country}
|
||||
</div>
|
||||
<div class="to">
|
||||
${this.letterData.to.name}<br />
|
||||
${this.letterData.to.address.streetName} ${this.letterData.to.address.houseNumber}<br />
|
||||
${this.letterData.to.address.postalCode} ${this.letterData.to.address.city}<br />
|
||||
${this.letterData.from.address.country}
|
||||
</div>
|
||||
</div>
|
||||
<div class="recepientInfo">
|
||||
<div class="label">your customer id:</div>
|
||||
${this.letterData.to.customerNumber || 'not registered'}
|
||||
|
||||
<div class="label">your vat id on file:</div>
|
||||
${this.letterData.to.vatId || 'not provided'}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
142
ts_web/elements/page.ts
Normal file
142
ts_web/elements/page.ts
Normal file
@ -0,0 +1,142 @@
|
||||
import * as tsclass from '@tsclass/tsclass';
|
||||
import {
|
||||
DeesElement,
|
||||
property,
|
||||
html,
|
||||
customElement,
|
||||
type TemplateResult,
|
||||
css,
|
||||
cssManager,
|
||||
unsafeCSS,
|
||||
} from '@design.estate/dees-element';
|
||||
import * as domtools from '@design.estate/dees-domtools';
|
||||
|
||||
import * as shared from './shared/index.js';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'dedocument-page': DePage;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('dedocument-page')
|
||||
export class DePage extends DeesElement {
|
||||
public static demo = () => html` <dedocument-page .format="${'a4'}"></dedocument-page> `;
|
||||
|
||||
@property({
|
||||
type: String,
|
||||
})
|
||||
public format: 'a4' = 'a4';
|
||||
|
||||
@property({
|
||||
type: Number,
|
||||
})
|
||||
public pageNumber: number = 1;
|
||||
|
||||
@property({
|
||||
type: Number,
|
||||
})
|
||||
public pageTotalNumber: number = 1;
|
||||
|
||||
@property({
|
||||
type: Object,
|
||||
})
|
||||
public letterData: tsclass.business.ILetter = shared.demoLetter;
|
||||
|
||||
@property({
|
||||
type: Boolean,
|
||||
reflect: true,
|
||||
})
|
||||
printMode = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
domtools.DomTools.setupDomTools();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
domtools.elementBasic.staticStyles,
|
||||
css`
|
||||
:host {
|
||||
}
|
||||
.versionOverlay {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
font-family: monospace;
|
||||
font-size: 16px;
|
||||
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.topInfo {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
left: 40px;
|
||||
color: red;
|
||||
transform: rotate(-5deg);
|
||||
}
|
||||
|
||||
.bigDraftText {
|
||||
transform: rotate(-45deg);
|
||||
font-size: 200px;
|
||||
opacity: 0.05;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
<dedocument-pagecontainer .printMode=${this.printMode}>
|
||||
<dedocument-pageheader
|
||||
.letterData=${this.letterData}
|
||||
.pageNumber="${this.pageNumber}"
|
||||
.pageTotalNumber="${this.pageTotalNumber}"
|
||||
></dedocument-pageheader>
|
||||
${this.pageNumber === 1
|
||||
? html`
|
||||
<dedocument-letterheader
|
||||
.pageNumber="${this.pageNumber}"
|
||||
.letterData=${this.letterData}
|
||||
.pageTotalNumber="${this.pageTotalNumber}"
|
||||
></dedocument-letterheader>
|
||||
`
|
||||
: html``}
|
||||
<dedocument-pagecontent
|
||||
.pageNumber="${this.pageNumber}"
|
||||
.pageTotalNumber="${this.pageTotalNumber}"
|
||||
.letterData=${this.letterData}
|
||||
><slot></slot
|
||||
></dedocument-pagecontent>
|
||||
<dedocument-pagefooter
|
||||
.letterData=${this.letterData}
|
||||
.pageNumber="${this.pageNumber}"
|
||||
.pageTotalNumber="${this.pageTotalNumber}"
|
||||
></dedocument-pagefooter>
|
||||
<div class="versionOverlay">
|
||||
${this.letterData.versionInfo.type === 'draft'
|
||||
? html`
|
||||
<div class="topInfo">
|
||||
Please note: THIS IS A DRAFT ONLY. NO RIGHTS CAN BE DERIVED FROM THIS.<br>
|
||||
-> Revision/Document version: ${this.letterData.versionInfo.version}
|
||||
</div>
|
||||
<div class="bigDraftText">DRAFT</div>
|
||||
`
|
||||
: html``}
|
||||
</div>
|
||||
</dedocument-pagecontainer>
|
||||
`;
|
||||
}
|
||||
|
||||
public async checkOverflow() {
|
||||
await this.elementDomReady;
|
||||
const pageContent = this.shadowRoot.querySelector('dedocument-pagecontent');
|
||||
return pageContent.checkOverflow();
|
||||
}
|
||||
}
|
69
ts_web/elements/pagecontainer.ts
Normal file
69
ts_web/elements/pagecontainer.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import {
|
||||
DeesElement,
|
||||
property,
|
||||
html,
|
||||
customElement,
|
||||
type TemplateResult,
|
||||
css,
|
||||
cssManager,
|
||||
unsafeCSS,
|
||||
} from '@design.estate/dees-element';
|
||||
import * as domtools from '@design.estate/dees-domtools';
|
||||
|
||||
import * as shared from './shared/index.js';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'dedocument-pagecontainer': DePageContainer;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('dedocument-pagecontainer')
|
||||
export class DePageContainer extends DeesElement {
|
||||
public static demo = () => html`
|
||||
<dedocument-pagecontainer .format="${'a4'}"></dedocument-pagecontainer>
|
||||
`;
|
||||
|
||||
@property({
|
||||
type: String,
|
||||
})
|
||||
public format: 'a4' = 'a4';
|
||||
|
||||
@property({
|
||||
type: Boolean,
|
||||
})
|
||||
public printMode = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
domtools.DomTools.setupDomTools();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
domtools.elementBasic.staticStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
background: white;
|
||||
color: #333;
|
||||
padding: 0px;
|
||||
width: ${unsafeCSS(shared.a4Width + 'px')};
|
||||
height: ${unsafeCSS(shared.a4Height + 'px')};
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
:host {
|
||||
box-shadow: ${this.printMode ? `none` : `0px 0px 10px rgba(0,0,0,0.3)`};
|
||||
}
|
||||
</style>
|
||||
<slot></slot>
|
||||
`;
|
||||
}
|
||||
}
|
147
ts_web/elements/pagecontent.ts
Normal file
147
ts_web/elements/pagecontent.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import {
|
||||
DeesElement,
|
||||
property,
|
||||
html,
|
||||
customElement,
|
||||
type TemplateResult,
|
||||
css,
|
||||
cssManager,
|
||||
unsafeCSS,
|
||||
} from '@design.estate/dees-element';
|
||||
import * as domtools from '@design.estate/dees-domtools';
|
||||
|
||||
import * as shared from './shared/index.js';
|
||||
import * as tsclass from '@tsclass/tsclass';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'dedocument-pagecontent': DePageContent;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('dedocument-pagecontent')
|
||||
export class DePageContent extends DeesElement {
|
||||
public static demo = () => html`
|
||||
<dedocument-pagecontent .format="${'a4'}"></dedocument-pagecontent>
|
||||
`;
|
||||
|
||||
@property({
|
||||
type: Number,
|
||||
})
|
||||
public letterData: tsclass.business.ILetter;
|
||||
|
||||
@property({
|
||||
type: Number,
|
||||
})
|
||||
public pageNumber: number = 1;
|
||||
|
||||
@property({
|
||||
type: Number,
|
||||
})
|
||||
public pageTotalNumber: number = 1;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
domtools.DomTools.setupDomTools();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
domtools.elementBasic.staticStyles,
|
||||
css`
|
||||
:host {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: absolute;
|
||||
|
||||
left: ${unsafeCSS(shared.leftMargin + 'px')};
|
||||
right: ${unsafeCSS(shared.rightMargin + 'px')};
|
||||
bottom: 170px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.content .subject {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content .text {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.subjectRepeated {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
background: #eeeeee;
|
||||
color: #999;
|
||||
border-radius: 50px;
|
||||
padding: 5px 10px;
|
||||
margin: auto;
|
||||
margin-bottom: 10px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.continuesOnNextPage {
|
||||
display: inline-block;
|
||||
background: #eeeeee;
|
||||
color: #999;
|
||||
border-radius: 50px;
|
||||
padding: 5px 10px;
|
||||
margin-top: 8px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.finalPage {
|
||||
display: inline-block;
|
||||
background: #29b000;
|
||||
color: #fff;
|
||||
border-radius: 50px;
|
||||
padding: 5px 10px;
|
||||
margin-top: 8px;
|
||||
font-size: 10px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
.content {
|
||||
top: ${this.pageNumber === 1 ? unsafeCSS('450px') : unsafeCSS('200px')};
|
||||
}
|
||||
</style>
|
||||
<div class="content">
|
||||
${this.pageNumber === 1
|
||||
? html`<div class="subject">${this.letterData.subject}</div>`
|
||||
: html`
|
||||
<div class="subjectRepeated">
|
||||
${this.letterData.subject} (Page ${this.pageNumber})
|
||||
</div>
|
||||
`}
|
||||
<slot></slot>
|
||||
${this.pageTotalNumber !== this.pageNumber
|
||||
? html`<div class="continuesOnNextPage">Continues on page ${this.pageNumber + 1}</div>`
|
||||
: html`<div class="finalPage">This is the final page of this document.</div>`}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
public firstUpdated(_changedProperties: Map<string | number | symbol, unknown>): void {
|
||||
super.firstUpdated(_changedProperties);
|
||||
this.checkOverflow();
|
||||
}
|
||||
|
||||
public async checkOverflow() {
|
||||
await this.elementDomReady;
|
||||
const contentContainer = this.shadowRoot.querySelector('.content');
|
||||
if (contentContainer.scrollHeight > contentContainer.clientHeight) {
|
||||
console.log('overflows');
|
||||
return true;
|
||||
} else {
|
||||
console.log('does not overflow!');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
132
ts_web/elements/pagefooter.ts
Normal file
132
ts_web/elements/pagefooter.ts
Normal file
@ -0,0 +1,132 @@
|
||||
import {
|
||||
DeesElement,
|
||||
property,
|
||||
html,
|
||||
customElement,
|
||||
type TemplateResult,
|
||||
css,
|
||||
cssManager,
|
||||
unsafeCSS,
|
||||
} from '@design.estate/dees-element';
|
||||
import * as domtools from '@design.estate/dees-domtools';
|
||||
|
||||
import * as shared from './shared/index.js';
|
||||
import * as tsclass from '@tsclass/tsclass';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'dedocument-pagefooter': DePageFooter;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('dedocument-pagefooter')
|
||||
export class DePageFooter extends DeesElement {
|
||||
public static demo = () => html`
|
||||
<dedocument-pagefooter .format="${'a4'}"></dedocument-pagefooter>
|
||||
`;
|
||||
|
||||
@property({
|
||||
type: Object,
|
||||
})
|
||||
letterData: tsclass.business.ILetter;
|
||||
|
||||
@property({
|
||||
type: Number
|
||||
})
|
||||
public pageNumber: number = 1;
|
||||
|
||||
@property({
|
||||
type: Number
|
||||
})
|
||||
public pageTotalNumber: number = 1;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
domtools.DomTools.setupDomTools();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
domtools.elementBasic.staticStyles,
|
||||
css`
|
||||
:host {
|
||||
color: #333;
|
||||
}
|
||||
.bottomstripe {
|
||||
position: absolute;
|
||||
display: grid;
|
||||
font-size: 11px;
|
||||
overflow: visible;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
height: 130px;
|
||||
content: '';
|
||||
padding: 30px ${unsafeCSS(shared.rightMargin + 'px')} 10px ${unsafeCSS(shared.leftMargin + 'px')};
|
||||
grid-template-columns: calc(100% / 4) calc(100% / 4) calc(100% / 4) calc(100% / 4);
|
||||
grid-gap: 5px;
|
||||
border-top: 2px solid #e4002b;
|
||||
}
|
||||
|
||||
.bottomstripe .pageNumber {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: ${unsafeCSS(shared.rightMargin + 'px')};
|
||||
background: #e4002b;
|
||||
padding: 3px;
|
||||
font-size: 9px;
|
||||
color: #fff;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
.bottomstripe .documentTitle {
|
||||
position: absolute;
|
||||
top: -18px;
|
||||
right: ${unsafeCSS(shared.rightMargin + 'px')};
|
||||
background: #dddddd;
|
||||
padding: 3px;
|
||||
font-size: 9px;
|
||||
color: #333;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
<div class="bottomstripe">
|
||||
<div>
|
||||
<strong>Address:</strong><br />
|
||||
${this.letterData.from.name}<br />
|
||||
${this.letterData.from.address.streetName} ${this.letterData.from.address.houseNumber}<br />
|
||||
${this.letterData.from.address.postalCode} ${this.letterData.from.address.city}<br />
|
||||
${this.letterData.from.address.country}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Registration Info:</strong><br />
|
||||
Amtsgericht Bremen<br />
|
||||
<i>reg-#:</i> HRB 35230 HB<br />
|
||||
<i>vat-id:</i> ${this.letterData.from.vatId}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Contact Info:</strong><br />
|
||||
<i>email:</i> ${this.letterData.from.email}<br />
|
||||
<i>phone:</i> ${this.letterData.from.phone}<br />
|
||||
<i>fax:</i> ${this.letterData.from.fax}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Bank Connection:</strong><br />
|
||||
<i>beneficiary:</i> ${this.letterData?.from?.name}<br />
|
||||
<i>institution:</i> ${this.letterData?.from?.sepaConnection?.institution}<br />
|
||||
<i>iban:</i> ${this.letterData?.from?.sepaConnection?.iban}<br />
|
||||
<i>bic:</i> ${this.letterData?.from?.sepaConnection?.bic}<br />
|
||||
</div>
|
||||
<div class="documentTitle">Subject: <b>${this.letterData?.subject}</b>${(() => {
|
||||
const uidString = html`/ Document-UID: <b>${html`<a href="https://uid.signature.digital/">https://uid.signature.digital/</a>`}</b>`;
|
||||
return ``;
|
||||
})()}</div>
|
||||
<div class="pageNumber">page ${this.pageNumber} of ${this.pageTotalNumber}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
96
ts_web/elements/pageheader.ts
Normal file
96
ts_web/elements/pageheader.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import {
|
||||
DeesElement,
|
||||
property,
|
||||
html,
|
||||
customElement,
|
||||
type TemplateResult,
|
||||
css,
|
||||
cssManager,
|
||||
unsafeCSS,
|
||||
} from '@design.estate/dees-element';
|
||||
import * as domtools from '@design.estate/dees-domtools';
|
||||
|
||||
import * as shared from './shared/index.js';
|
||||
import * as tsclass from '@tsclass/tsclass';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'dedocument-pageheader': DePageHeader;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('dedocument-pageheader')
|
||||
export class DePageHeader extends DeesElement {
|
||||
public static demo = () => html`
|
||||
<dedocument-pageheader .format="${'a4'}"></dedocument-pageheader>
|
||||
`;
|
||||
|
||||
@property({
|
||||
type: Object,
|
||||
})
|
||||
public letterData: tsclass.business.ILetter = null;
|
||||
|
||||
@property({
|
||||
type: Number,
|
||||
})
|
||||
public pageNumber: number = 1;
|
||||
|
||||
@property({
|
||||
type: Number,
|
||||
})
|
||||
public pageTotalNumber: number = 1;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
domtools.DomTools.setupDomTools();
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
domtools.elementBasic.staticStyles,
|
||||
css`
|
||||
:host {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.topstripe {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
height: 130px;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
border-bottom: 2px solid #00000020;
|
||||
}
|
||||
.topstripe2 {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
top: 130px;
|
||||
left: auto;
|
||||
right: ${unsafeCSS(shared.rightMargin + 'px')};
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
color: #333;
|
||||
font-size: 10px;
|
||||
}
|
||||
.topstripe img {
|
||||
filter: invert(1);
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
height: 25px;
|
||||
left: auto;
|
||||
right: ${unsafeCSS(shared.rightMargin + 'px')};
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
<div class="topstripe">
|
||||
<img src="https://assetbroker.lossless.one/brandfiles/lossless/svg-minimal-bright.svg" />
|
||||
</div>
|
||||
<div class="topstripe2">${this.letterData?.from?.description || '[no letterData.from.description set]'}</div>
|
||||
`;
|
||||
}
|
||||
}
|
121
ts_web/elements/shared/demoletter.ts
Normal file
121
ts_web/elements/shared/demoletter.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import * as tsclass from '@tsclass/tsclass';
|
||||
const fromContact: tsclass.business.IContact = {
|
||||
name: 'Awesome From Company',
|
||||
type: 'company',
|
||||
description: 'a company that does stuff',
|
||||
address: {
|
||||
streetName: 'Awesome Street',
|
||||
houseNumber: '5',
|
||||
city: 'Bremen',
|
||||
country: 'Germany',
|
||||
postalCode: '28359',
|
||||
},
|
||||
vatId: 'DE12345678',
|
||||
sepaConnection: {
|
||||
bic: 'BPOTBEB1',
|
||||
iban: 'BE01234567891616'
|
||||
},
|
||||
email: 'hello@awesome.company',
|
||||
phone: '+49 421 1234567',
|
||||
fax: '+49 421 1234568',
|
||||
|
||||
};
|
||||
|
||||
const toContact: tsclass.business.IContact = {
|
||||
name: 'Awesome To GmbH',
|
||||
type: 'company',
|
||||
customerNumber: 'LL-CLIENT-123',
|
||||
description: 'a company that does stuff',
|
||||
address: {
|
||||
streetName: 'Awesome Street',
|
||||
houseNumber: '5',
|
||||
city: 'Bremen',
|
||||
country: 'Germany',
|
||||
postalCode: '28359'
|
||||
},
|
||||
vatId: 'BE12345678',
|
||||
}
|
||||
|
||||
export const demoLetter: tsclass.business.ILetter = {
|
||||
versionInfo: {
|
||||
type: 'draft',
|
||||
version: '1.0.0',
|
||||
},
|
||||
accentColor: null,
|
||||
content: {
|
||||
textData: null,
|
||||
timesheetData: null,
|
||||
contractData: {
|
||||
contractDate: Date.now(),
|
||||
id: 'someid'
|
||||
},
|
||||
invoiceData: {
|
||||
id: 'LL-INV-48765',
|
||||
reverseCharge: true,
|
||||
dueInDays: 30,
|
||||
billedBy: fromContact,
|
||||
billedTo: toContact,
|
||||
status: null,
|
||||
deliveryDate: new Date().getTime(),
|
||||
periodOfPerformance: null,
|
||||
printResult: null,
|
||||
|
||||
items: [
|
||||
{
|
||||
name: 'Item with 19% VAT',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 100,
|
||||
unitType: 'hours',
|
||||
vatPercentage: 19,
|
||||
currency: 'EUR',
|
||||
},
|
||||
{
|
||||
name: 'Item with 7% VAT',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 100,
|
||||
unitType: 'hours',
|
||||
vatPercentage: 7,
|
||||
currency: 'EUR',
|
||||
},
|
||||
{
|
||||
name: 'Item with 7% VAT',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 230,
|
||||
unitType: 'hours',
|
||||
vatPercentage: 7,
|
||||
currency: 'EUR',
|
||||
},
|
||||
{
|
||||
name: 'Item with 21% VAT',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 230,
|
||||
unitType: 'hours',
|
||||
vatPercentage: 21,
|
||||
currency: 'EUR',
|
||||
},
|
||||
{
|
||||
name: 'Item with 0% VAT',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 230,
|
||||
unitType: 'hours',
|
||||
vatPercentage: 0,
|
||||
currency: 'EUR',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
|
||||
date: Date.now(),
|
||||
type: 'invoice',
|
||||
needsCoverSheet: false,
|
||||
objectActions: [],
|
||||
pdf: null,
|
||||
from: fromContact,
|
||||
to: toContact,
|
||||
incidenceId: null,
|
||||
language: null,
|
||||
legalContact: null,
|
||||
logoUrl: null,
|
||||
pdfAttachments: null,
|
||||
subject: 'Invoice: LL-INV-48765',
|
||||
}
|
6
ts_web/elements/shared/index.ts
Normal file
6
ts_web/elements/shared/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export const a4Height = 1122;
|
||||
export const a4Width = 794;
|
||||
export const rightMargin = 70;
|
||||
export const leftMargin = 90;
|
||||
|
||||
export * from './demoletter.js';
|
1
ts_web/index.ts
Normal file
1
ts_web/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './elements/index.js';
|
15
ts_web/plugins.ts
Normal file
15
ts_web/plugins.ts
Normal file
@ -0,0 +1,15 @@
|
||||
// tsclass scope
|
||||
import * as tsclass from '@tsclass/tsclass';
|
||||
|
||||
export { tsclass };
|
||||
|
||||
// @pushrocks scope
|
||||
import * as smartjson from '@push.rocks/smartjson';
|
||||
import * as smarttime from '@push.rocks/smarttime';
|
||||
|
||||
export { smartjson, smarttime };
|
||||
|
||||
// third party
|
||||
import * as qrcode from 'qrcode';
|
||||
|
||||
export { qrcode };
|
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"useDefineForClassFields": false,
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"esModuleInterop": true,
|
||||
"verbatimModuleSyntax": true
|
||||
},
|
||||
"exclude": [
|
||||
"dist_*/**/*.d.ts"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user