OfficeServerJs/create_table_template.js
OfficeServer dgsoft 1bd5654f6b Initial commit: DOCX Template Server mit API und Tabellen-Support
-  Node.js/Express Server mit DOCX Template-Verarbeitung
-  Automatische Tag-Erkennung und Demo-Daten-Generierung
-  Tabellen-Unterstützung mit Schleifen-Tags
-  REST-API /api/process-template für externe Integration
-  Web-Oberfläche mit vollständiger Dokumentation
-  SSL-Unterstützung (HTTPS Port 443 öffentlich)
-  Intelligente Spaltenerkennung für Tabellen
-  Detaillierte Statusmeldungen für alle Operationen
-  Flexible Custom-Daten + Auto-Generierung
-  Template- und Dokument-Management APIs
2025-10-04 22:04:25 +02:00

355 lines
12 KiB
JavaScript

const PizZip = require('pizzip');
const fs = require('fs');
const path = require('path');
// Erstelle ein Template mit professioneller Tabelle für docxtemplater
function createTableTemplate() {
const zip = new PizZip();
// Content Types
zip.file('[Content_Types].xml', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
</Types>`);
// Main relationships
zip.file('_rels/.rels', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>`);
// Document relationships
zip.file('word/_rels/document.xml.rels', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
</Relationships>`);
// Styles
zip.file('word/styles.xml', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:docDefaults>
<w:rPrDefault>
<w:rPr>
<w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman"/>
<w:sz w:val="22"/>
<w:szCs w:val="22"/>
<w:lang w:val="de-DE"/>
</w:rPr>
</w:rPrDefault>
</w:docDefaults>
</w:styles>`);
// Document content optimiert für docxtemplater
zip.file('word/document.xml', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<!-- Erste Seite: Kopfdaten -->
<w:p>
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:rPr>
<w:b/>
<w:sz w:val="32"/>
</w:rPr>
<w:t>RECHNUNG</w:t>
</w:r>
</w:p>
<w:p/>
<w:p>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>Firma: {firma}</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>Ansprechpartner: {vorname} {nachname}</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>E-Mail: {email}</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>Adresse: {adresse}, {plz} {stadt}</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>Rechnungsdatum: {datum}</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>Rechnungsnummer: {nummer}</w:t>
</w:r>
</w:p>
<!-- Seitenumbruch -->
<w:p>
<w:r>
<w:br w:type="page"/>
</w:r>
</w:p>
<!-- Zweite Seite: Tabelle -->
<w:p>
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:rPr>
<w:b/>
<w:sz w:val="24"/>
</w:rPr>
<w:t>RECHNUNGSPOSITIONEN</w:t>
</w:r>
</w:p>
<w:p/>
<!-- Tabelle für docxtemplater optimiert -->
<w:tbl>
<w:tblPr>
<w:tblStyle w:val="TableGrid"/>
<w:tblW w:w="5000" w:type="pct"/>
<w:tblBorders>
<w:top w:val="single" w:sz="6" w:space="0" w:color="000000"/>
<w:left w:val="single" w:sz="6" w:space="0" w:color="000000"/>
<w:bottom w:val="single" w:sz="6" w:space="0" w:color="000000"/>
<w:right w:val="single" w:sz="6" w:space="0" w:color="000000"/>
<w:insideH w:val="single" w:sz="6" w:space="0" w:color="000000"/>
<w:insideV w:val="single" w:sz="6" w:space="0" w:color="000000"/>
</w:tblBorders>
</w:tblPr>
<w:tblGrid>
<w:gridCol w:w="1000"/>
<w:gridCol w:w="3000"/>
<w:gridCol w:w="1500"/>
<w:gridCol w:w="1500"/>
</w:tblGrid>
<!-- Kopfzeile -->
<w:tr>
<w:tc>
<w:tcPr>
<w:shd w:val="clear" w:color="auto" w:fill="CCCCCC"/>
<w:tcW w:w="1000" w:type="dxa"/>
</w:tcPr>
<w:p>
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>Pos.</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:shd w:val="clear" w:color="auto" w:fill="CCCCCC"/>
<w:tcW w:w="3000" w:type="dxa"/>
</w:tcPr>
<w:p>
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>Beschreibung</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:shd w:val="clear" w:color="auto" w:fill="CCCCCC"/>
<w:tcW w:w="1500" w:type="dxa"/>
</w:tcPr>
<w:p>
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>Betrag (EUR)</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:shd w:val="clear" w:color="auto" w:fill="CCCCCC"/>
<w:tcW w:w="1500" w:type="dxa"/>
</w:tcPr>
<w:p>
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>Datum</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<!-- Datenzeilen mit docxtemplater Loop -->
<w:tr>
<w:tc>
<w:p>
<w:r>
<w:t>{#items}{items_position}{/items}</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>{items_name}</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:pPr>
<w:jc w:val="right"/>
</w:pPr>
<w:r>
<w:t>{items_value}</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:t>{items_date}</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<!-- Summenzeile -->
<w:tr>
<w:tc>
<w:tcPr>
<w:shd w:val="clear" w:color="auto" w:fill="EEEEEE"/>
</w:tcPr>
<w:p>
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>∑</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:shd w:val="clear" w:color="auto" w:fill="EEEEEE"/>
</w:tcPr>
<w:p>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>GESAMTBETRAG</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:shd w:val="clear" w:color="auto" w:fill="EEEEEE"/>
</w:tcPr>
<w:p>
<w:pPr>
<w:jc w:val="right"/>
</w:pPr>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>{betrag}</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:shd w:val="clear" w:color="auto" w:fill="EEEEEE"/>
</w:tcPr>
<w:p>
<w:r>
<w:t></w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
</w:tbl>
<w:p/>
<w:p/>
<w:p>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>Bemerkungen:</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:t>{beschreibung}</w:t>
</w:r>
</w:p>
<w:p/>
<w:p/>
<w:p>
<w:r>
<w:t>Mit freundlichen Grüßen</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:rPr><w:b/></w:rPr>
<w:t>{firma}</w:t>
</w:r>
</w:p>
</w:body>
</w:document>`);
return zip.generate({ type: 'nodebuffer' });
}
// Erstelle das Template
const templateBuffer = createTableTemplate();
const templatePath = path.join(__dirname, 'templates', 'rechnung_mit_tabelle.docx');
fs.writeFileSync(templatePath, templateBuffer);
console.log('Template mit Tabelle erstellt:', templatePath);
console.log('Features:');
console.log('- Erste Seite: Rechnungskopf');
console.log('- Zweite Seite: Professionelle Tabelle mit Spalten für Position, Beschreibung, Betrag und Datum');
console.log('- Automatische Positionsnummerierung');
console.log('- Summenzeile am Ende der Tabelle');