@cenk1cenk2/md-printer 2.11.2 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/dev.js +1 -1
- package/bin/run.js +1 -0
- package/dist/commands/index.js +45 -18
- package/dist/constants/file.constants.js +17 -1
- package/dist/constants/index.js +2 -2
- package/oclif.manifest.json +48 -3
- package/package.json +12 -10
- package/templates/invoice/template.html.j2 +3 -0
- package/templates/mail/settings.json +9 -0
- package/templates/mail/tailwind.css +132 -0
- package/templates/mail/template.html.j2 +14 -0
- package/templates/privat-rechnung/template.html.j2 +90 -102
- package/templates/invoice/tailwind.config.cjs +0 -4
package/bin/dev.js
CHANGED
package/bin/run.js
CHANGED
package/dist/commands/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequiredTemplateFiles, TEMPLATE_DIRECTORY, TemplateFiles } from "../constants/template.constants.js";
|
|
2
|
-
import {
|
|
2
|
+
import { InputFileType, OutputFileType } from "../constants/file.constants.js";
|
|
3
3
|
import "../constants/index.js";
|
|
4
4
|
import tailwind from "@tailwindcss/postcss";
|
|
5
5
|
import { watch } from "chokidar";
|
|
@@ -8,7 +8,7 @@ import { defaultConfig } from "md-to-pdf/dist/lib/config.js";
|
|
|
8
8
|
import { convertMdToPdf } from "md-to-pdf/dist/lib/md-to-pdf.js";
|
|
9
9
|
import { serveDirectory } from "md-to-pdf/dist/lib/serve-dir.js";
|
|
10
10
|
import Nunjucks from "nunjucks";
|
|
11
|
-
import { basename, dirname, extname, join } from "path";
|
|
11
|
+
import { basename, dirname, extname, isAbsolute, join } from "path";
|
|
12
12
|
import postcss from "postcss";
|
|
13
13
|
import puppeteer from "puppeteer";
|
|
14
14
|
import showdown from "showdown";
|
|
@@ -18,6 +18,29 @@ import { Args, Command, ConfigService, FileSystemService, Flags, JsonParser, Mer
|
|
|
18
18
|
var MDPrinter = class extends Command {
|
|
19
19
|
static description = "Generates a PDF from the given markdown file with the selected HTML template.";
|
|
20
20
|
static flags = {
|
|
21
|
+
stdin: Flags.string({
|
|
22
|
+
char: "I",
|
|
23
|
+
description: "Read the input from stdin.",
|
|
24
|
+
required: false,
|
|
25
|
+
exclusive: ["file"],
|
|
26
|
+
allowStdin: "only"
|
|
27
|
+
}),
|
|
28
|
+
["input-filetype"]: Flags.string({
|
|
29
|
+
char: "f",
|
|
30
|
+
options: Object.values(InputFileType),
|
|
31
|
+
description: "File type to be processed. By default it is detected by the file extension.",
|
|
32
|
+
default: InputFileType.MARKDOWN
|
|
33
|
+
}),
|
|
34
|
+
["output-filetype"]: Flags.string({
|
|
35
|
+
char: "F",
|
|
36
|
+
options: Object.values(OutputFileType),
|
|
37
|
+
description: "File type to be processed. By default it is detected by the file extension.",
|
|
38
|
+
default: OutputFileType.PDF
|
|
39
|
+
}),
|
|
40
|
+
stdout: Flags.boolean({
|
|
41
|
+
char: "O",
|
|
42
|
+
description: "Write to stdout instead of a file."
|
|
43
|
+
}),
|
|
21
44
|
template: Flags.string({
|
|
22
45
|
char: "t",
|
|
23
46
|
default: "default",
|
|
@@ -42,10 +65,7 @@ var MDPrinter = class extends Command {
|
|
|
42
65
|
})
|
|
43
66
|
};
|
|
44
67
|
static args = {
|
|
45
|
-
file: Args.string({
|
|
46
|
-
description: "File to be processed.",
|
|
47
|
-
required: true
|
|
48
|
-
}),
|
|
68
|
+
file: Args.string({ description: "File to be processed." }),
|
|
49
69
|
output: Args.string({
|
|
50
70
|
description: "Output file that will be generated. Overwrites the one define in front-matter.",
|
|
51
71
|
required: false
|
|
@@ -71,20 +91,21 @@ var MDPrinter = class extends Command {
|
|
|
71
91
|
}
|
|
72
92
|
async run() {
|
|
73
93
|
this.tasks.add([{ task: async (ctx) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
94
|
+
if (!this.flags.stdout && this.args.file) {
|
|
95
|
+
ctx.file = isAbsolute(this.args.file) ? this.args.file : join(process.cwd(), this.args.file);
|
|
96
|
+
if (!this.fs.exists(ctx.file)) throw new Error(`File does not exists: ${ctx.file}`);
|
|
97
|
+
this.logger.info("Loading file: %s", ctx.file);
|
|
98
|
+
ctx.content = await this.fs.read(ctx.file);
|
|
99
|
+
} else ctx.content = this.flags.stdin;
|
|
100
|
+
switch (this.flags["input-filetype"] ? this.flags["input-filetype"] : extname(ctx.file).replace(/^\./, "")) {
|
|
101
|
+
case InputFileType.MARKDOWN: {
|
|
102
|
+
const data = graymatter(ctx.content);
|
|
82
103
|
ctx.content = data.content;
|
|
83
104
|
ctx.metadata = data.data;
|
|
84
105
|
break;
|
|
85
106
|
}
|
|
86
|
-
case
|
|
87
|
-
|
|
107
|
+
case InputFileType.YAML:
|
|
108
|
+
case InputFileType.YAML_SHORT:
|
|
88
109
|
ctx.metadata = await this.app.get(ParserService).parse(ctx.file, ctx.content);
|
|
89
110
|
break;
|
|
90
111
|
default: throw new Error("File type is not accepted.");
|
|
@@ -106,10 +127,13 @@ var MDPrinter = class extends Command {
|
|
|
106
127
|
[TemplateFiles.TEMPLATE]: join(ctx.templates, TemplateFiles.TEMPLATE)
|
|
107
128
|
};
|
|
108
129
|
ctx.options = await this.cs.extend([paths[TemplateFiles.SETTINGS], {
|
|
109
|
-
dest: this.args?.output ?? ctx.metadata?.dest ?? `${basename(this.args.file, extname(this.args.file))}.
|
|
130
|
+
dest: this.args?.output ?? ctx.metadata?.dest ?? `${basename(this.args.file, extname(this.args.file))}.${this.flags["output-filetype"]}`,
|
|
110
131
|
document_title: ctx.metadata?.document_title ?? this.flags.title ?? this.args.file,
|
|
111
132
|
launch_options: { executablePath: this.flags.browser }
|
|
112
133
|
}]);
|
|
134
|
+
this.flags.stdout ??= ctx.metadata.stdout;
|
|
135
|
+
if (this.flags.stdout) ctx.options.dest = "stdout";
|
|
136
|
+
if (this.flags["output-filetype"] === OutputFileType.HTML || ctx.metadata["output-filetype"] === OutputFileType.HTML) ctx.options.as_html = true;
|
|
113
137
|
this.logger.debug("Options: %o", ctx.options);
|
|
114
138
|
if (this.fs.exists(paths[TemplateFiles.HEADER])) {
|
|
115
139
|
this.logger.debug("Header exists for template.");
|
|
@@ -178,8 +202,11 @@ var MDPrinter = class extends Command {
|
|
|
178
202
|
resolve(null);
|
|
179
203
|
}));
|
|
180
204
|
if (output) {
|
|
205
|
+
if (this.flags.stdout) {
|
|
206
|
+
process.stdout.write(output.content);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
181
209
|
if (!output.filename) throw new Error("Output should either be defined with the variable or front-matter.");
|
|
182
|
-
else if (!OUTPUT_FILE_ACCEPTED_TYPES.includes(extname(output.filename))) throw new Error(`Output file should be ending with the extension: ${OUTPUT_FILE_ACCEPTED_TYPES.join(", ")} -> current: ${extname(output.filename)}`);
|
|
183
210
|
this.logger.info("Output file will be: %s", output.filename);
|
|
184
211
|
await this.fs.mkdir(dirname(output.filename));
|
|
185
212
|
this.logger.info("Writing file to output: %s", output.filename);
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
//#region src/constants/file.constants.ts
|
|
2
2
|
const OUTPUT_FILE_ACCEPTED_TYPES = [".pdf"];
|
|
3
|
+
const INPUT_FILE_ACCEPTED_TYPES = [
|
|
4
|
+
".md",
|
|
5
|
+
".yml",
|
|
6
|
+
".yaml"
|
|
7
|
+
];
|
|
8
|
+
let OutputFileType = /* @__PURE__ */ function(OutputFileType$1) {
|
|
9
|
+
OutputFileType$1["PDF"] = "pdf";
|
|
10
|
+
OutputFileType$1["HTML"] = "html";
|
|
11
|
+
return OutputFileType$1;
|
|
12
|
+
}({});
|
|
13
|
+
let InputFileType = /* @__PURE__ */ function(InputFileType$1) {
|
|
14
|
+
InputFileType$1["MARKDOWN"] = "md";
|
|
15
|
+
InputFileType$1["YAML"] = "yaml";
|
|
16
|
+
InputFileType$1["YAML_SHORT"] = "yml";
|
|
17
|
+
return InputFileType$1;
|
|
18
|
+
}({});
|
|
3
19
|
|
|
4
20
|
//#endregion
|
|
5
|
-
export { OUTPUT_FILE_ACCEPTED_TYPES };
|
|
21
|
+
export { INPUT_FILE_ACCEPTED_TYPES, InputFileType, OUTPUT_FILE_ACCEPTED_TYPES, OutputFileType };
|
package/dist/constants/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { RequiredTemplateFiles, TEMPLATE_DIRECTORY, TemplateFiles } from "./template.constants.js";
|
|
2
|
-
import { OUTPUT_FILE_ACCEPTED_TYPES } from "./file.constants.js";
|
|
2
|
+
import { INPUT_FILE_ACCEPTED_TYPES, InputFileType, OUTPUT_FILE_ACCEPTED_TYPES, OutputFileType } from "./file.constants.js";
|
|
3
3
|
|
|
4
|
-
export { OUTPUT_FILE_ACCEPTED_TYPES, RequiredTemplateFiles, TEMPLATE_DIRECTORY, TemplateFiles };
|
|
4
|
+
export { INPUT_FILE_ACCEPTED_TYPES, InputFileType, OUTPUT_FILE_ACCEPTED_TYPES, OutputFileType, RequiredTemplateFiles, TEMPLATE_DIRECTORY, TemplateFiles };
|
package/oclif.manifest.json
CHANGED
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
"args": {
|
|
6
6
|
"file": {
|
|
7
7
|
"description": "File to be processed.",
|
|
8
|
-
"name": "file"
|
|
9
|
-
"required": true
|
|
8
|
+
"name": "file"
|
|
10
9
|
},
|
|
11
10
|
"output": {
|
|
12
11
|
"description": "Output file that will be generated. Overwrites the one define in front-matter.",
|
|
@@ -55,6 +54,52 @@
|
|
|
55
54
|
"allowNo": false,
|
|
56
55
|
"type": "boolean"
|
|
57
56
|
},
|
|
57
|
+
"stdin": {
|
|
58
|
+
"char": "I",
|
|
59
|
+
"description": "Read the input from stdin.",
|
|
60
|
+
"exclusive": [
|
|
61
|
+
"file"
|
|
62
|
+
],
|
|
63
|
+
"name": "stdin",
|
|
64
|
+
"required": false,
|
|
65
|
+
"hasDynamicHelp": false,
|
|
66
|
+
"multiple": false,
|
|
67
|
+
"type": "option"
|
|
68
|
+
},
|
|
69
|
+
"input-filetype": {
|
|
70
|
+
"char": "f",
|
|
71
|
+
"description": "File type to be processed. By default it is detected by the file extension.",
|
|
72
|
+
"name": "input-filetype",
|
|
73
|
+
"default": "md",
|
|
74
|
+
"hasDynamicHelp": false,
|
|
75
|
+
"multiple": false,
|
|
76
|
+
"options": [
|
|
77
|
+
"md",
|
|
78
|
+
"yaml",
|
|
79
|
+
"yml"
|
|
80
|
+
],
|
|
81
|
+
"type": "option"
|
|
82
|
+
},
|
|
83
|
+
"output-filetype": {
|
|
84
|
+
"char": "F",
|
|
85
|
+
"description": "File type to be processed. By default it is detected by the file extension.",
|
|
86
|
+
"name": "output-filetype",
|
|
87
|
+
"default": "pdf",
|
|
88
|
+
"hasDynamicHelp": false,
|
|
89
|
+
"multiple": false,
|
|
90
|
+
"options": [
|
|
91
|
+
"pdf",
|
|
92
|
+
"html"
|
|
93
|
+
],
|
|
94
|
+
"type": "option"
|
|
95
|
+
},
|
|
96
|
+
"stdout": {
|
|
97
|
+
"char": "O",
|
|
98
|
+
"description": "Write to stdout instead of a file.",
|
|
99
|
+
"name": "stdout",
|
|
100
|
+
"allowNo": false,
|
|
101
|
+
"type": "boolean"
|
|
102
|
+
},
|
|
58
103
|
"template": {
|
|
59
104
|
"char": "t",
|
|
60
105
|
"description": "HTML template for the generated PDF file.",
|
|
@@ -106,5 +151,5 @@
|
|
|
106
151
|
"enableJsonFlag": false
|
|
107
152
|
}
|
|
108
153
|
},
|
|
109
|
-
"version": "2.11.
|
|
154
|
+
"version": "2.11.3"
|
|
110
155
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cenk1cenk2/md-printer",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"description": "A markdown printer.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -70,12 +70,12 @@
|
|
|
70
70
|
"cenk1cenk2"
|
|
71
71
|
],
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@cenk1cenk2/oclif-common": "^6.4.
|
|
73
|
+
"@cenk1cenk2/oclif-common": "^6.4.12",
|
|
74
74
|
"@listr2/manager": "^3.0.3",
|
|
75
75
|
"@oclif/core": "^4.5.2",
|
|
76
76
|
"@oclif/plugin-help": "^6.2.32",
|
|
77
77
|
"@tailwindcss/forms": "^0.5.10",
|
|
78
|
-
"@tailwindcss/postcss": "^4.1.
|
|
78
|
+
"@tailwindcss/postcss": "^4.1.13",
|
|
79
79
|
"@tailwindcss/typography": "^0.5.16",
|
|
80
80
|
"@tsconfig/node20": "^20.1.6",
|
|
81
81
|
"chokidar": "^4.0.3",
|
|
@@ -86,28 +86,30 @@
|
|
|
86
86
|
"md-to-pdf": "^5.2.4",
|
|
87
87
|
"nunjucks": "^3.2.4",
|
|
88
88
|
"postcss": "^8.5.6",
|
|
89
|
-
"puppeteer": "^24.
|
|
89
|
+
"puppeteer": "^24.19.0",
|
|
90
90
|
"showdown": "^2.1.0",
|
|
91
91
|
"source-map-support": "^0.5.21",
|
|
92
|
-
"tailwindcss": "^4.1.
|
|
92
|
+
"tailwindcss": "^4.1.13",
|
|
93
93
|
"yaml": "^2.8.1"
|
|
94
94
|
},
|
|
95
95
|
"devDependencies": {
|
|
96
|
-
"@cenk1cenk2/cz-cc": "^2.1.
|
|
97
|
-
"@cenk1cenk2/eslint-config": "^3.1.
|
|
96
|
+
"@cenk1cenk2/cz-cc": "^2.1.5",
|
|
97
|
+
"@cenk1cenk2/eslint-config": "^3.1.67",
|
|
98
98
|
"@types/config": "^3.3.5",
|
|
99
99
|
"@types/fs-extra": "^11.0.4",
|
|
100
|
-
"@types/node": "^24.3.
|
|
100
|
+
"@types/node": "^24.3.1",
|
|
101
101
|
"@types/nunjucks": "^3.2.6",
|
|
102
102
|
"@types/showdown": "^2.0.6",
|
|
103
|
-
"eslint": "^9.
|
|
103
|
+
"eslint": "^9.35.0",
|
|
104
104
|
"execa": "^9.6.0",
|
|
105
105
|
"globby": "^14.1.0",
|
|
106
106
|
"lint-staged": "^16.1.6",
|
|
107
|
-
"oclif": "^4.22.
|
|
107
|
+
"oclif": "^4.22.18",
|
|
108
108
|
"prettier": "^3.6.2",
|
|
109
|
+
"prettier-plugin-jinja-template": "^2.1.0",
|
|
109
110
|
"simple-git-hooks": "^2.13.1",
|
|
110
111
|
"theme-colors": "^0.1.0",
|
|
112
|
+
"tpm": "^1.4.14",
|
|
111
113
|
"ts-node": "^10.9.2",
|
|
112
114
|
"tsconfig-paths": "^4.2.0",
|
|
113
115
|
"tsdown": "^0.14.2",
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
@import 'tailwindcss';
|
|
2
|
+
@plugin '@tailwindcss/typography';
|
|
3
|
+
|
|
4
|
+
@theme {
|
|
5
|
+
--color-primary-500: #cd0043;
|
|
6
|
+
--color-gray-50: oklch(98.5% 0.002 247.839);
|
|
7
|
+
--color-gray-100: #abb2bf;
|
|
8
|
+
--color-gray-200: #979eab;
|
|
9
|
+
--color-gray-300: #7c8a9d;
|
|
10
|
+
--color-gray-400: #5c6370;
|
|
11
|
+
--color-gray-500: #4b5263;
|
|
12
|
+
--color-gray-600: #2c333d;
|
|
13
|
+
--color-gray-700: #22282f;
|
|
14
|
+
--color-gray-800: #1e2127;
|
|
15
|
+
--color-gray-900: #17191e;
|
|
16
|
+
--color-gray-950: oklch(13% 0.028 261.692);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
*,
|
|
20
|
+
::after,
|
|
21
|
+
::before,
|
|
22
|
+
::backdrop,
|
|
23
|
+
::file-selector-button {
|
|
24
|
+
border-color: var(--color-gray-200, currentcolor);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
address {
|
|
28
|
+
@apply text-sm;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Typography styles converted from config */
|
|
32
|
+
.prose a {
|
|
33
|
+
text-decoration: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.prose h1 {
|
|
37
|
+
font-weight: 700;
|
|
38
|
+
padding-top: 0.1875rem;
|
|
39
|
+
/* 0.75 * 0.25rem */
|
|
40
|
+
padding-bottom: 0.1875rem;
|
|
41
|
+
margin-bottom: 0;
|
|
42
|
+
margin-top: 0;
|
|
43
|
+
border-width: 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.prose h2 {
|
|
47
|
+
padding-top: 0.125rem;
|
|
48
|
+
/* 0.5 * 0.25rem */
|
|
49
|
+
padding-bottom: 0.125rem;
|
|
50
|
+
margin-bottom: 0;
|
|
51
|
+
margin-top: 0;
|
|
52
|
+
border-width: 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.prose h3 {
|
|
56
|
+
padding-top: 0.0625rem;
|
|
57
|
+
/* 0.25 * 0.25rem */
|
|
58
|
+
padding-bottom: 0.0625rem;
|
|
59
|
+
margin-bottom: 0;
|
|
60
|
+
margin-top: 0;
|
|
61
|
+
border-width: 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
p {
|
|
65
|
+
margin: 0.1em 0em;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.prose blockquote {
|
|
69
|
+
font-weight: 400;
|
|
70
|
+
color: var(--color-gray-600);
|
|
71
|
+
font-style: normal;
|
|
72
|
+
quotes: '\201C' '\201D' '\2018' '\2019';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.prose blockquote p:first-of-type::before {
|
|
76
|
+
content: '';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.prose blockquote p:last-of-type::after {
|
|
80
|
+
content: '';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.prose ul > li {
|
|
84
|
+
padding-left: 1em;
|
|
85
|
+
text-align: left;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.prose ol > li {
|
|
89
|
+
padding-left: 1em;
|
|
90
|
+
text-align: left;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.prose ol > li::before {
|
|
94
|
+
top: calc(0.875em - 0.1em);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.prose ul > li::before {
|
|
98
|
+
top: calc(0.875em - 0.1em);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
html {
|
|
102
|
+
font-size: 12px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.page-break {
|
|
106
|
+
page-break-after: always;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.text-vertical {
|
|
110
|
+
text-orientation: upright;
|
|
111
|
+
writing-mode: vertical-lr;
|
|
112
|
+
display: flex;
|
|
113
|
+
flex-direction: column;
|
|
114
|
+
justify-content: center;
|
|
115
|
+
@apply p-4;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
table th {
|
|
119
|
+
@apply bg-gray-700 text-white;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
table tr {
|
|
123
|
+
border-top: 0;
|
|
124
|
+
@apply even:bg-gray-50;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
table th,
|
|
128
|
+
table td,
|
|
129
|
+
table tr {
|
|
130
|
+
border: 0;
|
|
131
|
+
padding: 0.5em 0.5em;
|
|
132
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
vim: ft=htmldjango
|
|
3
|
+
-->
|
|
4
|
+
<!DOCTYPE html>
|
|
5
|
+
<html>
|
|
6
|
+
<head>
|
|
7
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
8
|
+
<meta charset="UTF-8" />
|
|
9
|
+
</head>
|
|
10
|
+
|
|
11
|
+
<body>
|
|
12
|
+
<div class="leading-tight prose">{{ content | safe }}</div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -1,30 +1,34 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
vim: ft=htmldjango
|
|
3
|
+
-->
|
|
1
4
|
<!DOCTYPE html>
|
|
2
5
|
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
</head>
|
|
9
|
-
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
<div class="grid-cols-
|
|
6
|
+
<head>
|
|
7
|
+
<meta charset="UTF-8" />
|
|
8
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
10
|
+
<title>Document</title>
|
|
11
|
+
</head>
|
|
12
|
+
|
|
13
|
+
<body>
|
|
14
|
+
<div class="max-w-full leading-tight prose">
|
|
15
|
+
<h1 class="mb-2 text-center border-b-2">Privatrechnung</h1>
|
|
16
|
+
<div class="text-center">
|
|
17
|
+
<p class="m-0 font-bold">Rechnungsnummer</p>
|
|
18
|
+
<p class="m-0 font-bold">{{ id }}</p>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="grid grid-cols-2" style="margin-bottom: 1em !important;">
|
|
21
|
+
<div class="grid-cols-1">
|
|
18
22
|
<div>
|
|
19
23
|
<h4 class="">Absender</h4>
|
|
20
24
|
</div>
|
|
21
25
|
<div>
|
|
22
26
|
<p class="my-2 font-semibold">{{ sender.name }}</p>
|
|
23
27
|
{% if sender.email %}
|
|
24
|
-
|
|
28
|
+
<p class="m-0 my-2">{{ sender.email }}</p>
|
|
25
29
|
{% endif %}
|
|
26
30
|
{% if sender.phone %}
|
|
27
|
-
|
|
31
|
+
<p class="m-0 my-2">{{ sender.phone }}</p>
|
|
28
32
|
{% endif %}
|
|
29
33
|
<div class="mt-4">
|
|
30
34
|
<p class="m-0">{{ sender.address }}</p>
|
|
@@ -32,18 +36,18 @@
|
|
|
32
36
|
<p class="m-0">{{ sender.location }}</p>
|
|
33
37
|
</div>
|
|
34
38
|
</div>
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
</div>
|
|
40
|
+
<div class="grid-cols-1 text-right">
|
|
37
41
|
<div>
|
|
38
42
|
<h4 class="">Empfänger</h4>
|
|
39
43
|
</div>
|
|
40
44
|
<div class="leading-tight">
|
|
41
45
|
<p class="my-2 font-semibold">{{ receiver.name }}</p>
|
|
42
46
|
{% if receiver.email %}
|
|
43
|
-
|
|
47
|
+
<p class="m-0 my-2">{{ receiver.email }}</p>
|
|
44
48
|
{% endif %}
|
|
45
49
|
{% if receiver.phone %}
|
|
46
|
-
|
|
50
|
+
<p class="m-0 my-2">{{ receiver.phone }}</p>
|
|
47
51
|
{% endif %}
|
|
48
52
|
<div class="mt-4">
|
|
49
53
|
<p class="m-0">{{ receiver.address }}</p>
|
|
@@ -51,90 +55,74 @@
|
|
|
51
55
|
<p class="m-0">{{ receiver.location }}</p>
|
|
52
56
|
</div>
|
|
53
57
|
</div>
|
|
54
|
-
|
|
58
|
+
</div>
|
|
55
59
|
</div>
|
|
56
|
-
|
|
60
|
+
<div class="border-t-2">
|
|
57
61
|
<p class="font-bold">Dies ist eine Privatrechnung über eine nicht gewerbliche Tätigkeit. Umsatzsteuer wird daher nicht in Rechnung gestellt.</p>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<td
|
|
82
|
-
<td
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<tr class="border-0 bg-gray-100! font-semibold text-center">
|
|
120
|
-
<td>{{ payment.name }}</td>
|
|
121
|
-
<td>{{ payment.bank }}</td>
|
|
122
|
-
<td>{{ payment.bic }}</td>
|
|
123
|
-
<td>{{ payment.iban }}</td>
|
|
124
|
-
</tr>
|
|
125
|
-
</tfoot>
|
|
126
|
-
</table>
|
|
127
|
-
</div>
|
|
128
|
-
<hr class="border-t-2 mt-0! mb-2!" style="margin-top: 0 !important; margin-bottom: 1.5em !important;" />
|
|
129
|
-
<div class="grid grid-cols-2 font-semibold">
|
|
130
|
-
<div class="flex flex-row items-center text-center">
|
|
131
|
-
<div class="w-full">
|
|
132
|
-
<p class="my-4">{{ location }}</p>
|
|
133
|
-
<p class="my-4">{{ date }}</p>
|
|
62
|
+
<table class="w-full rounded-lg border-gray-200 table-fixed">
|
|
63
|
+
<thead class="border-b-2 border-primary-500">
|
|
64
|
+
<tr class="bg-gray-200">
|
|
65
|
+
<th class="px-2 w-full">Leistung</th>
|
|
66
|
+
<th class="px-8">Menge</th>
|
|
67
|
+
<th class="px-4">Einzelpreis</th>
|
|
68
|
+
<th class="px-4">Gesamtpreis</th>
|
|
69
|
+
</tr>
|
|
70
|
+
</thead>
|
|
71
|
+
<tbody>
|
|
72
|
+
{% set sum = 0 %}
|
|
73
|
+
{% for item in items %}
|
|
74
|
+
<tr class="even:bg-gray-50">
|
|
75
|
+
<td class="px-2">{{ item.description }}</td>
|
|
76
|
+
<td class="text-center">{{ item.quantity }} {{ item.type }}</td>
|
|
77
|
+
<td class="text-center">{{ item.price }}{{ currency }}</td>
|
|
78
|
+
<td class="font-semibold text-center">{{ item.quantity * item.price }}{{ currency }}</td>
|
|
79
|
+
</tr>
|
|
80
|
+
{% set sum = sum + item.quantity * item.price %}
|
|
81
|
+
{% endfor %}
|
|
82
|
+
</tbody>
|
|
83
|
+
<tfoot class="text-black border-t-2 border-primary-500">
|
|
84
|
+
<tr class="border-0 bg-gray-200!">
|
|
85
|
+
<td></td>
|
|
86
|
+
<td></td>
|
|
87
|
+
<th class="text-center">Gesamt</th>
|
|
88
|
+
<td class="font-bold text-center text-primary-500">{{ sum }}{{ currency }}</td>
|
|
89
|
+
</tr>
|
|
90
|
+
</tfoot>
|
|
91
|
+
</table>
|
|
92
|
+
</div>
|
|
93
|
+
<h3>Anmerkung</h3>
|
|
94
|
+
<div class="py-2 px-4 mt-0 w-full rounded-lg border-2 border-gray-200">{{ content }}</div>
|
|
95
|
+
<h3>Zahlungsdaten</h3>
|
|
96
|
+
<div>
|
|
97
|
+
<table class="mt-0 w-full rounded-lg border-gray-200 table-fixed">
|
|
98
|
+
<thead class="border-b-2 border-gray-500">
|
|
99
|
+
<tr class="text-center bg-gray-200">
|
|
100
|
+
<th class="px-24">Name</th>
|
|
101
|
+
<th class="px-24">Bank</th>
|
|
102
|
+
<th class="px-12">BIC</th>
|
|
103
|
+
<th class="w-full">IBAN</th>
|
|
104
|
+
</tr>
|
|
105
|
+
</thead>
|
|
106
|
+
<tfoot class="text-black border-t-2 border-primary-500">
|
|
107
|
+
<tr class="border-0 bg-gray-100! font-semibold text-center">
|
|
108
|
+
<td>{{ payment.name }}</td>
|
|
109
|
+
<td>{{ payment.bank }}</td>
|
|
110
|
+
<td>{{ payment.bic }}</td>
|
|
111
|
+
<td>{{ payment.iban }}</td>
|
|
112
|
+
</tr>
|
|
113
|
+
</tfoot>
|
|
114
|
+
</table>
|
|
115
|
+
</div>
|
|
116
|
+
<hr class="border-t-2 mt-0! mb-2!" style="margin-top: 0 !important; margin-bottom: 1.5em !important;" />
|
|
117
|
+
<div class="grid grid-cols-2 font-semibold">
|
|
118
|
+
<div class="flex flex-row items-center text-center">
|
|
119
|
+
<div class="w-full">
|
|
120
|
+
<p class="my-4">{{ location }}</p>
|
|
121
|
+
<p class="my-4">{{ date }}</p>
|
|
122
|
+
</div>
|
|
134
123
|
</div>
|
|
124
|
+
<div class="p-20 text-center rounded-lg border-2 border-gray-200">Unterschrift</div>
|
|
135
125
|
</div>
|
|
136
|
-
<div class="p-20 text-center rounded-lg border-2 border-gray-200">Unterschrift</div>
|
|
137
126
|
</div>
|
|
138
|
-
</
|
|
139
|
-
</body>
|
|
127
|
+
</body>
|
|
140
128
|
</html>
|