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