Fixed DATEV Export #39
This commit is contained in:
@@ -11,58 +11,64 @@ import {secrets} from "../utils/secrets";
|
|||||||
import {createSEPAExport} from "../utils/export/sepa";
|
import {createSEPAExport} from "../utils/export/sepa";
|
||||||
|
|
||||||
const createDatevExport = async (server:FastifyInstance,req:any,startDate,endDate,beraternr,mandantennr) => {
|
const createDatevExport = async (server:FastifyInstance,req:any,startDate,endDate,beraternr,mandantennr) => {
|
||||||
console.log(startDate,endDate,beraternr,mandantennr)
|
try {
|
||||||
|
console.log(startDate,endDate,beraternr,mandantennr)
|
||||||
|
|
||||||
// 1) ZIP erzeugen
|
// 1) ZIP erzeugen
|
||||||
const buffer = await buildExportZip(server,req.user.tenant_id, startDate, endDate, beraternr, mandantennr)
|
const buffer = await buildExportZip(server,req.user.tenant_id, startDate, endDate, beraternr, mandantennr)
|
||||||
console.log("ZIP created")
|
console.log("ZIP created")
|
||||||
console.log(buffer)
|
console.log(buffer)
|
||||||
|
|
||||||
// 2) Dateiname & Key festlegen
|
// 2) Dateiname & Key festlegen
|
||||||
const fileKey = `${req.user.tenant_id}/exports/Export_${dayjs(startDate).format("YYYY-MM-DD")}_${dayjs(endDate).format("YYYY-MM-DD")}_${randomUUID()}.zip`
|
const fileKey = `${req.user.tenant_id}/exports/Export_${dayjs(startDate).format("YYYY-MM-DD")}_${dayjs(endDate).format("YYYY-MM-DD")}_${randomUUID()}.zip`
|
||||||
console.log(fileKey)
|
console.log(fileKey)
|
||||||
|
|
||||||
// 3) In S3 hochladen
|
// 3) In S3 hochladen
|
||||||
await s3.send(
|
await s3.send(
|
||||||
new PutObjectCommand({
|
new PutObjectCommand({
|
||||||
Bucket: secrets.S3_BUCKET,
|
Bucket: secrets.S3_BUCKET,
|
||||||
Key: fileKey,
|
Key: fileKey,
|
||||||
Body: buffer,
|
Body: buffer,
|
||||||
ContentType: "application/zip",
|
ContentType: "application/zip",
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
// 4) Presigned URL erzeugen (24h gültig)
|
// 4) Presigned URL erzeugen (24h gültig)
|
||||||
const url = await getSignedUrl(
|
const url = await getSignedUrl(
|
||||||
s3,
|
s3,
|
||||||
new GetObjectCommand({
|
new GetObjectCommand({
|
||||||
Bucket: secrets.S3_BUCKET,
|
Bucket: secrets.S3_BUCKET,
|
||||||
Key: fileKey,
|
Key: fileKey,
|
||||||
}),
|
}),
|
||||||
{ expiresIn: 60 * 60 * 24 }
|
{ expiresIn: 60 * 60 * 24 }
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log(url)
|
console.log(url)
|
||||||
|
|
||||||
|
// 5) In Supabase-DB speichern
|
||||||
|
const { data, error } = await server.supabase
|
||||||
|
.from("exports")
|
||||||
|
.insert([
|
||||||
|
{
|
||||||
|
tenant_id: req.user.tenant_id,
|
||||||
|
start_date: startDate,
|
||||||
|
end_date: endDate,
|
||||||
|
valid_until: dayjs().add(24,"hours").toISOString(),
|
||||||
|
file_path: fileKey,
|
||||||
|
url: url,
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.select()
|
||||||
|
.single()
|
||||||
|
|
||||||
|
console.log(data)
|
||||||
|
console.log(error)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
|
||||||
// 5) In Supabase-DB speichern
|
|
||||||
const { data, error } = await server.supabase
|
|
||||||
.from("exports")
|
|
||||||
.insert([
|
|
||||||
{
|
|
||||||
tenant_id: req.user.tenant_id,
|
|
||||||
start_date: startDate,
|
|
||||||
end_date: endDate,
|
|
||||||
valid_until: dayjs().add(24,"hours").toISOString(),
|
|
||||||
file_path: fileKey,
|
|
||||||
url: url,
|
|
||||||
created_at: new Date().toISOString(),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.select()
|
|
||||||
.single()
|
|
||||||
|
|
||||||
console.log(data)
|
|
||||||
console.log(error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,327 +1,389 @@
|
|||||||
import xmlbuilder from "xmlbuilder";
|
import xmlbuilder from "xmlbuilder";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import isBetween from "dayjs/plugin/isBetween.js"
|
import isBetween from "dayjs/plugin/isBetween.js";
|
||||||
import {BlobWriter, Data64URIReader, TextReader, TextWriter, ZipWriter} from "@zip.js/zip.js";
|
import { BlobWriter, Data64URIReader, TextReader, ZipWriter } from "@zip.js/zip.js";
|
||||||
import {FastifyInstance} from "fastify";
|
import { FastifyInstance } from "fastify";
|
||||||
import {GetObjectCommand} from "@aws-sdk/client-s3";
|
import { GetObjectCommand } from "@aws-sdk/client-s3";
|
||||||
import {s3} from "../s3";
|
import { s3 } from "../s3";
|
||||||
import {secrets} from "../secrets";
|
import { secrets } from "../secrets";
|
||||||
dayjs.extend(isBetween)
|
|
||||||
|
|
||||||
const getCreatedDocumentTotal = (item) => {
|
// Drizzle Core Imports
|
||||||
let totalNet = 0
|
import { eq, and, inArray, gte, lte, asc, aliasedTable } from "drizzle-orm";
|
||||||
let total19 = 0
|
|
||||||
let total7 = 0
|
|
||||||
|
|
||||||
item.rows.forEach(row => {
|
// Tabellen Imports (keine Relations nötig!)
|
||||||
if(!['pagebreak','title','text'].includes(row.mode)){
|
import {
|
||||||
let rowPrice = Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) /100) ).toFixed(3)
|
statementallocations,
|
||||||
totalNet = totalNet + Number(rowPrice)
|
createddocuments,
|
||||||
|
incominginvoices,
|
||||||
|
accounts,
|
||||||
|
files,
|
||||||
|
customers,
|
||||||
|
vendors,
|
||||||
|
bankaccounts,
|
||||||
|
bankstatements,
|
||||||
|
ownaccounts
|
||||||
|
} from "../../../db/schema";
|
||||||
|
|
||||||
if(row.taxPercent === 19) {
|
dayjs.extend(isBetween);
|
||||||
// @ts-ignore
|
|
||||||
total19 = total19 + Number(rowPrice * 0.19)
|
// ---------------------------------------------------------
|
||||||
} else if(row.taxPercent === 7) {
|
// HELPER FUNCTIONS (Unverändert)
|
||||||
// @ts-ignore
|
// ---------------------------------------------------------
|
||||||
total7 = total7 + Number(rowPrice * 0.07)
|
|
||||||
}
|
const getCreatedDocumentTotal = (item: any) => {
|
||||||
|
let totalNet = 0;
|
||||||
|
let total19 = 0;
|
||||||
|
let total7 = 0;
|
||||||
|
const rows = Array.isArray(item.rows) ? item.rows : [];
|
||||||
|
rows.forEach((row: any) => {
|
||||||
|
if (!['pagebreak', 'title', 'text'].includes(row.mode)) {
|
||||||
|
let rowPrice = Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) / 100)).toFixed(3);
|
||||||
|
totalNet = totalNet + Number(rowPrice);
|
||||||
|
if (row.taxPercent === 19) total19 += Number(rowPrice * 0.19);
|
||||||
|
else if (row.taxPercent === 7) total7 += Number(rowPrice * 0.07);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
let totalGross = Number(totalNet.toFixed(2)) + Number(total19.toFixed(2)) + Number(total7.toFixed(2))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
totalNet: totalNet,
|
totalNet, total19, total7,
|
||||||
total19: total19,
|
totalGross: Number(totalNet.toFixed(2)) + Number(total19.toFixed(2)) + Number(total7.toFixed(2))
|
||||||
total7: total7,
|
};
|
||||||
totalGross: totalGross,
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const escapeString = (str) => {
|
const escapeString = (str: string | null | undefined) => {
|
||||||
|
return (str || "").replaceAll("\n", "").replaceAll(";", "").replaceAll(/\r/g, "").replaceAll(/"/g, "").replaceAll(/ü/g, "ue").replaceAll(/ä/g, "ae").replaceAll(/ö/g, "oe");
|
||||||
|
};
|
||||||
|
|
||||||
str = (str ||"")
|
const displayCurrency = (input: number, onlyAbs = false) => {
|
||||||
.replaceAll("\n","")
|
return (onlyAbs ? Math.abs(input) : input).toFixed(2).replace(".", ",");
|
||||||
.replaceAll(";","")
|
};
|
||||||
.replaceAll(/\r/g,"")
|
|
||||||
.replaceAll(/"/g,"")
|
|
||||||
.replaceAll(/ü/g,"ue")
|
|
||||||
.replaceAll(/ä/g,"ae")
|
|
||||||
.replaceAll(/ö/g,"oe")
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
const displayCurrency = (input, onlyAbs = false) => {
|
// ---------------------------------------------------------
|
||||||
|
// MAIN EXPORT FUNCTION
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
if(onlyAbs) {
|
export async function buildExportZip(
|
||||||
return Math.abs(input).toFixed(2).replace(".",",")
|
server: FastifyInstance,
|
||||||
} else {
|
tenantId: number,
|
||||||
return input.toFixed(2).replace(".",",")
|
startDate: string,
|
||||||
}
|
endDate: string,
|
||||||
}
|
beraternr: string,
|
||||||
|
mandantennr: string
|
||||||
export async function buildExportZip(server: FastifyInstance, tenant: number, startDate: string, endDate: string, beraternr: string, mandantennr: string): Promise<Buffer> {
|
): Promise<Buffer> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const zipFileWriter = new BlobWriter()
|
const zipFileWriter = new BlobWriter();
|
||||||
const zipWriter = new ZipWriter(zipFileWriter)
|
const zipWriter = new ZipWriter(zipFileWriter);
|
||||||
|
|
||||||
|
// Header Infos
|
||||||
|
const dateNowStr = dayjs().format("YYYYMMDDHHmmssSSS");
|
||||||
|
const startDateFmt = dayjs(startDate).format("YYYYMMDD");
|
||||||
|
const endDateFmt = dayjs(endDate).format("YYYYMMDD");
|
||||||
|
|
||||||
|
let header = `"EXTF";700;21;"Buchungsstapel";13;${dateNowStr};;"FE";"Florian Federspiel";;${beraternr};${mandantennr};20250101;4;${startDateFmt};${endDateFmt};"Buchungsstapel";"FF";1;0;1;"EUR";;"";;;"03";;;"";""`;
|
||||||
|
let colHeaders = `Umsatz;Soll-/Haben-Kennzeichen;WKZ Umsatz;Kurs;Basisumsatz;WKZ Basisumsatz;Konto;Gegenkonto;BU-Schluessel;Belegdatum;Belegfeld 1;Belegfeld 2;Skonto;Buchungstext;Postensperre;Diverse Adressnummer;Geschaeftspartnerbank;Sachverhalt;Zinssperre;Beleglink;Beleginfo - Art 1;Beleginfo - Inhalt 1;Beleginfo - Art 2;Beleginfo - Inhalt 2;Beleginfo - Art 3;Beleginfo - Inhalt 3;Beleginfo - Art 4;Beleginfo - Inhalt 4;Beleginfo - Art 5;Beleginfo - Inhalt 5;Beleginfo - Art 6;Beleginfo - Inhalt 6;Beleginfo - Art 7;Beleginfo - Inhalt 7;Beleginfo - Art 8;Beleginfo - Inhalt 8;KOST1 - Kostenstelle;KOST2 - Kostenstelle;Kost Menge;EU-Land u. USt-IdNr. (Bestimmung);EU-Steuersatz (Bestimmung);Abw. Versteuerungsart;Sachverhalt L+L;Funktionsergaenzung L+L;BU 49 Hauptfunktionstyp;BU 49 Hauptfunktionsnummer;BU 49 Funktionsergaenzung;Zusatzinformation - Art 1;Zusatzinformation - Inhalt 1;Zusatzinformation - Art 2;Zusatzinformation - Inhalt 2;Zusatzinformation - Art 3;Zusatzinformation - Inhalt 3;Zusatzinformation - Art 4;Zusatzinformation - Inhalt 4;Zusatzinformation - Art 5;Zusatzinformation - Inhalt 5;Zusatzinformation - Art 6;Zusatzinformation - Inhalt 6;Zusatzinformation - Art 7;Zusatzinformation - Inhalt 7;Zusatzinformation - Art 8;Zusatzinformation - Inhalt 8;Zusatzinformation - Art 9;Zusatzinformation - Inhalt 9;Zusatzinformation - Art 10;Zusatzinformation - Inhalt 10;Zusatzinformation - Art 11;Zusatzinformation - Inhalt 11;Zusatzinformation - Art 12;Zusatzinformation - Inhalt 12;Zusatzinformation - Art 13;Zusatzinformation - Inhalt 13;Zusatzinformation - Art 14;Zusatzinformation - Inhalt 14;Zusatzinformation - Art 15;Zusatzinformation - Inhalt 15;Zusatzinformation - Art 16;Zusatzinformation - Inhalt 16;Zusatzinformation - Art 17;Zusatzinformation - Inhalt 17;Zusatzinformation - Art 18;Zusatzinformation - Inhalt 18;Zusatzinformation - Art 19;Zusatzinformation - Inhalt 19;Zusatzinformation - Art 20;Zusatzinformation - Inhalt 20;Stueck;Gewicht;Zahlweise;Zahlweise;Veranlagungsjahr;Zugeordnete Faelligkeit;Skontotyp;Auftragsnummer;Buchungstyp;USt-Schluessel (Anzahlungen);EU-Mitgliedstaat (Anzahlungen);Sachverhalt L+L (Anzahlungen);EU-Steuersatz (Anzahlungen);Erloeskonto (Anzahlungen);Herkunft-Kz;Leerfeld;KOST-Datum;SEPA-Mandatsreferenz;Skontosperre;Gesellschaftername;Beteiligtennummer;Identifikationsnummer;Zeichnernummer;Postensperre bis;Bezeichnung SoBil-Sachverhalt;Kennzeichen SoBil-Buchung;Festschreibung;Leistungsdatum;Datum Zuord. Steuerperiode;Faelligkeit;Generalumkehr;Steuersatz;Land;Abrechnungsreferenz;BVV-Position;EU-Mitgliedstaat u. UStID(Ursprung);EU-Steuersatz(Ursprung);Abw. Skontokonto`;
|
||||||
|
|
||||||
//Basic Information
|
// ---------------------------------------------------------
|
||||||
|
// 1. DATEN LADEN (CORE API SELECT & JOIN)
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
let header = `"EXTF";700;21;"Buchungsstapel";13;${dayjs().format("YYYYMMDDHHmmssSSS")};;"FE";"Florian Federspiel";;${beraternr};${mandantennr};20250101;4;${dayjs(startDate).format("YYYYMMDD")};${dayjs(endDate).format("YYYYMMDD")};"Buchungsstapel";"FF";1;0;1;"EUR";;"";;;"03";;;"";""`
|
// --- A) Created Documents ---
|
||||||
|
// Wir brauchen das Dokument und den Kunden dazu
|
||||||
|
const cdRaw = await server.db.select({
|
||||||
|
doc: createddocuments,
|
||||||
|
customer: customers
|
||||||
|
})
|
||||||
|
.from(createddocuments)
|
||||||
|
.leftJoin(customers, eq(createddocuments.customer, customers.id))
|
||||||
|
.where(and(
|
||||||
|
eq(createddocuments.tenant, tenantId),
|
||||||
|
inArray(createddocuments.type, ["invoices", "advanceInvoices", "cancellationInvoices"]),
|
||||||
|
eq(createddocuments.state, "Gebucht"),
|
||||||
|
eq(createddocuments.archived, false),
|
||||||
|
gte(createddocuments.documentDate, startDate),
|
||||||
|
lte(createddocuments.documentDate, endDate)
|
||||||
|
));
|
||||||
|
|
||||||
let colHeaders = `Umsatz;Soll-/Haben-Kennzeichen;WKZ Umsatz;Kurs;Basisumsatz;WKZ Basisumsatz;Konto;Gegenkonto;BU-Schluessel;Belegdatum;Belegfeld 1;Belegfeld 2;Skonto;Buchungstext;Postensperre;Diverse Adressnummer;Geschaeftspartnerbank;Sachverhalt;Zinssperre;Beleglink;Beleginfo - Art 1;Beleginfo - Inhalt 1;Beleginfo - Art 2;Beleginfo - Inhalt 2;Beleginfo - Art 3;Beleginfo - Inhalt 3;Beleginfo - Art 4;Beleginfo - Inhalt 4;Beleginfo - Art 5;Beleginfo - Inhalt 5;Beleginfo - Art 6;Beleginfo - Inhalt 6;Beleginfo - Art 7;Beleginfo - Inhalt 7;Beleginfo - Art 8;Beleginfo - Inhalt 8;KOST1 - Kostenstelle;KOST2 - Kostenstelle;Kost Menge;EU-Land u. USt-IdNr. (Bestimmung);EU-Steuersatz (Bestimmung);Abw. Versteuerungsart;Sachverhalt L+L;Funktionsergaenzung L+L;BU 49 Hauptfunktionstyp;BU 49 Hauptfunktionsnummer;BU 49 Funktionsergaenzung;Zusatzinformation - Art 1;Zusatzinformation - Inhalt 1;Zusatzinformation - Art 2;Zusatzinformation - Inhalt 2;Zusatzinformation - Art 3;Zusatzinformation - Inhalt 3;Zusatzinformation - Art 4;Zusatzinformation - Inhalt 4;Zusatzinformation - Art 5;Zusatzinformation - Inhalt 5;Zusatzinformation - Art 6;Zusatzinformation - Inhalt 6;Zusatzinformation - Art 7;Zusatzinformation - Inhalt 7;Zusatzinformation - Art 8;Zusatzinformation - Inhalt 8;Zusatzinformation - Art 9;Zusatzinformation - Inhalt 9;Zusatzinformation - Art 10;Zusatzinformation - Inhalt 10;Zusatzinformation - Art 11;Zusatzinformation - Inhalt 11;Zusatzinformation - Art 12;Zusatzinformation - Inhalt 12;Zusatzinformation - Art 13;Zusatzinformation - Inhalt 13;Zusatzinformation - Art 14;Zusatzinformation - Inhalt 14;Zusatzinformation - Art 15;Zusatzinformation - Inhalt 15;Zusatzinformation - Art 16;Zusatzinformation - Inhalt 16;Zusatzinformation - Art 17;Zusatzinformation - Inhalt 17;Zusatzinformation - Art 18;Zusatzinformation - Inhalt 18;Zusatzinformation - Art 19;Zusatzinformation - Inhalt 19;Zusatzinformation - Art 20;Zusatzinformation - Inhalt 20;Stueck;Gewicht;Zahlweise;Zahlweise;Veranlagungsjahr;Zugeordnete Faelligkeit;Skontotyp;Auftragsnummer;Buchungstyp;USt-Schluessel (Anzahlungen);EU-Mitgliedstaat (Anzahlungen);Sachverhalt L+L (Anzahlungen);EU-Steuersatz (Anzahlungen);Erloeskonto (Anzahlungen);Herkunft-Kz;Leerfeld;KOST-Datum;SEPA-Mandatsreferenz;Skontosperre;Gesellschaftername;Beteiligtennummer;Identifikationsnummer;Zeichnernummer;Postensperre bis;Bezeichnung SoBil-Sachverhalt;Kennzeichen SoBil-Buchung;Festschreibung;Leistungsdatum;Datum Zuord. Steuerperiode;Faelligkeit;Generalumkehr;Steuersatz;Land;Abrechnungsreferenz;BVV-Position;EU-Mitgliedstaat u. UStID(Ursprung);EU-Steuersatz(Ursprung);Abw. Skontokonto`
|
// Mapping: Flat Result -> Nested Object (damit der Rest des Codes gleich bleiben kann)
|
||||||
|
const createddocumentsList = cdRaw.map(r => ({
|
||||||
|
...r.doc,
|
||||||
|
customer: r.customer
|
||||||
|
}));
|
||||||
|
|
||||||
//Get Bookings
|
// --- B) Incoming Invoices ---
|
||||||
const {data:statementallocationsRaw,error: statementallocationsError} = await server.supabase.from("statementallocations").select('*, account(*), bs_id(*, account(*)), cd_id(*,customer(*)), ii_id(*, vendor(*)), vendor(*), customer(*), ownaccount(*)').eq("tenant", tenant);
|
// Wir brauchen die Rechnung und den Lieferanten
|
||||||
let {data:createddocumentsRaw,error: createddocumentsError} = await server.supabase.from("createddocuments").select('*,customer(*)').eq("tenant", tenant).in("type",["invoices","advanceInvoices","cancellationInvoices"]).eq("state","Gebucht").eq("archived",false)
|
const iiRaw = await server.db.select({
|
||||||
let {data:incominginvoicesRaw,error: incominginvoicesError} = await server.supabase.from("incominginvoices").select('*, vendor(*)').eq("tenant", tenant).eq("state","Gebucht").eq("archived",false)
|
inv: incominginvoices,
|
||||||
const {data:accounts} = await server.supabase.from("accounts").select()
|
vendor: vendors
|
||||||
const {data:tenantData} = await server.supabase.from("tenants").select().eq("id",tenant).single()
|
})
|
||||||
|
.from(incominginvoices)
|
||||||
|
.leftJoin(vendors, eq(incominginvoices.vendor, vendors.id))
|
||||||
|
.where(and(
|
||||||
|
eq(incominginvoices.tenant, tenantId),
|
||||||
|
eq(incominginvoices.state, "Gebucht"),
|
||||||
|
eq(incominginvoices.archived, false),
|
||||||
|
gte(incominginvoices.date, startDate),
|
||||||
|
lte(incominginvoices.date, endDate)
|
||||||
|
));
|
||||||
|
|
||||||
let createddocuments = createddocumentsRaw.filter(i => dayjs(i.documentDate).isBetween(startDate,endDate,"day","[]"))
|
const incominginvoicesList = iiRaw.map(r => ({
|
||||||
let incominginvoices = incominginvoicesRaw.filter(i => dayjs(i.date).isBetween(startDate,endDate,"day","[]"))
|
...r.inv,
|
||||||
let statementallocations = statementallocationsRaw.filter(i => dayjs(i.bs_id.date).isBetween(startDate,endDate,"day","[]"))
|
vendor: r.vendor
|
||||||
|
}));
|
||||||
|
|
||||||
|
// --- C) Statement Allocations ---
|
||||||
|
// Das ist der komplexeste Teil. Wir müssen Tabellen aliasen, da wir z.B. Customers doppelt joinen könnten
|
||||||
|
// (Einmal via CreatedDocument, einmal direkt an der Allocation).
|
||||||
|
|
||||||
const {data:filesCreateddocuments, error: filesErrorCD} = await server.supabase.from("files").select().eq("tenant",tenant).or(`createddocument.in.(${createddocuments.map(i => i.id).join(",")})`)
|
const CdCustomer = aliasedTable(customers, "cd_customer");
|
||||||
const {data:filesIncomingInvoices, error: filesErrorII} = await server.supabase.from("files").select().eq("tenant",tenant).or(`incominginvoice.in.(${incominginvoices.map(i => i.id).join(",")})`)
|
const IiVendor = aliasedTable(vendors, "ii_vendor");
|
||||||
|
|
||||||
const downloadFile = async (bucketName, filePath, downloadFilePath,fileId) => {
|
const allocRaw = await server.db.select({
|
||||||
|
allocation: statementallocations,
|
||||||
|
bs: bankstatements,
|
||||||
|
ba: bankaccounts,
|
||||||
|
cd: createddocuments,
|
||||||
|
cd_cust: CdCustomer,
|
||||||
|
ii: incominginvoices,
|
||||||
|
ii_vend: IiVendor,
|
||||||
|
acc: accounts,
|
||||||
|
direct_vend: vendors, // Direkte Zuordnung an Kreditor
|
||||||
|
direct_cust: customers, // Direkte Zuordnung an Debitor
|
||||||
|
own: ownaccounts
|
||||||
|
})
|
||||||
|
.from(statementallocations)
|
||||||
|
// JOIN 1: Bankstatement (Pflicht, für Datum Filter)
|
||||||
|
.innerJoin(bankstatements, eq(statementallocations.bankstatement, bankstatements.id))
|
||||||
|
// JOIN 2: Bankaccount (für DATEV Nummer)
|
||||||
|
.leftJoin(bankaccounts, eq(bankstatements.account, bankaccounts.id))
|
||||||
|
|
||||||
console.log(filePath)
|
// JOIN 3: Ausgangsrechnung & deren Kunde
|
||||||
|
.leftJoin(createddocuments, eq(statementallocations.createddocument, createddocuments.id))
|
||||||
|
.leftJoin(CdCustomer, eq(createddocuments.customer, CdCustomer.id))
|
||||||
|
|
||||||
const command = new GetObjectCommand({
|
// JOIN 4: Eingangsrechnung & deren Lieferant
|
||||||
Bucket: secrets.S3_BUCKET,
|
.leftJoin(incominginvoices, eq(statementallocations.incominginvoice, incominginvoices.id))
|
||||||
Key: filePath,
|
.leftJoin(IiVendor, eq(incominginvoices.vendor, IiVendor.id))
|
||||||
})
|
|
||||||
|
|
||||||
const { Body, ContentType } = await s3.send(command)
|
// JOIN 5: Direkte Zuordnungen
|
||||||
|
.leftJoin(accounts, eq(statementallocations.account, accounts.id))
|
||||||
|
.leftJoin(vendors, eq(statementallocations.vendor, vendors.id))
|
||||||
|
.leftJoin(customers, eq(statementallocations.customer, customers.id))
|
||||||
|
.leftJoin(ownaccounts, eq(statementallocations.ownaccount, ownaccounts.id))
|
||||||
|
|
||||||
const chunks: any[] = []
|
.where(and(
|
||||||
// @ts-ignore
|
eq(statementallocations.tenant, tenantId),
|
||||||
for await (const chunk of Body) {
|
eq(statementallocations.archived, false),
|
||||||
chunks.push(chunk)
|
// Datum Filter direkt auf dem Bankstatement
|
||||||
|
gte(bankstatements.date, startDate),
|
||||||
|
lte(bankstatements.date, endDate)
|
||||||
|
));
|
||||||
|
|
||||||
|
// Mapping: Wir bauen das komplexe Objekt nach, das die CSV Logik erwartet
|
||||||
|
const statementallocationsList = allocRaw.map(r => ({
|
||||||
|
...r.allocation,
|
||||||
|
bankstatement: {
|
||||||
|
...r.bs,
|
||||||
|
account: r.ba // Nesting für bs.account.datevNumber
|
||||||
|
},
|
||||||
|
createddocument: r.cd ? {
|
||||||
|
...r.cd,
|
||||||
|
customer: r.cd_cust
|
||||||
|
} : null,
|
||||||
|
incominginvoice: r.ii ? {
|
||||||
|
...r.ii,
|
||||||
|
vendor: r.ii_vend
|
||||||
|
} : null,
|
||||||
|
account: r.acc,
|
||||||
|
vendor: r.direct_vend,
|
||||||
|
customer: r.direct_cust,
|
||||||
|
ownaccount: r.own
|
||||||
|
}));
|
||||||
|
|
||||||
|
// --- D) Stammdaten Accounts ---
|
||||||
|
const accountsList = await server.db.select().from(accounts);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// 2. FILES LADEN
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
// IDs sammeln für IN (...) Abfragen
|
||||||
|
const cdIds = createddocumentsList.map(i => i.id);
|
||||||
|
const iiIds = incominginvoicesList.map(i => i.id);
|
||||||
|
|
||||||
|
let filesCreateddocuments: any[] = [];
|
||||||
|
if (cdIds.length > 0) {
|
||||||
|
filesCreateddocuments = await server.db.select().from(files).where(and(
|
||||||
|
eq(files.tenant, tenantId),
|
||||||
|
inArray(files.createddocument, cdIds),
|
||||||
|
eq(files.archived, false)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let filesIncomingInvoices: any[] = [];
|
||||||
|
if (iiIds.length > 0) {
|
||||||
|
filesIncomingInvoices = await server.db.select().from(files).where(and(
|
||||||
|
eq(files.tenant, tenantId),
|
||||||
|
inArray(files.incominginvoice, iiIds),
|
||||||
|
eq(files.archived, false)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// 3. DOWNLOAD & ZIP
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
const downloadFile = async (filePath: string, fileId: string) => {
|
||||||
|
try {
|
||||||
|
const command = new GetObjectCommand({
|
||||||
|
Bucket: secrets.S3_BUCKET,
|
||||||
|
Key: filePath,
|
||||||
|
});
|
||||||
|
const { Body } = await s3.send(command);
|
||||||
|
if (!Body) return;
|
||||||
|
const chunks: any[] = [];
|
||||||
|
// @ts-ignore
|
||||||
|
for await (const chunk of Body) chunks.push(chunk);
|
||||||
|
const buffer = Buffer.concat(chunks);
|
||||||
|
const dataURL = `data:application/pdf;base64,${buffer.toString('base64')}`;
|
||||||
|
const dataURLReader = new Data64URIReader(dataURL);
|
||||||
|
const ext = filePath.includes('.') ? filePath.split(".").pop() : "pdf";
|
||||||
|
await zipWriter.add(`${fileId}.${ext}`, dataURLReader);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error downloading file ${fileId}`, e);
|
||||||
}
|
}
|
||||||
const buffer = Buffer.concat(chunks)
|
|
||||||
|
|
||||||
const dataURL = `data:application/pdf;base64,${buffer.toString('base64')}`
|
|
||||||
|
|
||||||
const dataURLReader = new Data64URIReader(dataURL)
|
|
||||||
await zipWriter.add(`${fileId}.${downloadFilePath.split(".").pop()}`, dataURLReader)
|
|
||||||
|
|
||||||
//await fs.writeFile(`./output/${fileId}.${downloadFilePath.split(".").pop()}`, buffer, () => {});
|
|
||||||
console.log(`File added to Zip`);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const file of filesCreateddocuments) {
|
for (const file of filesCreateddocuments) if(file.path) await downloadFile(file.path, file.id);
|
||||||
await downloadFile("filesdev",file.path,`./output/files/${file.path.split("/")[file.path.split("/").length - 1]}`,file.id);
|
for (const file of filesIncomingInvoices) if(file.path) await downloadFile(file.path, file.id);
|
||||||
}
|
|
||||||
for (const file of filesIncomingInvoices) {
|
|
||||||
await downloadFile("filesdev",file.path,`./output/files/${file.path.split("/")[file.path.split("/").length - 1]}`,file.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
let bookingLines = []
|
// ---------------------------------------------------------
|
||||||
|
// 4. CSV GENERIERUNG (Logic ist gleich geblieben)
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
createddocuments.forEach(createddocument => {
|
let bookingLines: string[] = [];
|
||||||
|
|
||||||
let file = filesCreateddocuments.find(i => i.createddocument === createddocument.id);
|
// AR
|
||||||
|
createddocumentsList.forEach(cd => {
|
||||||
|
let file = filesCreateddocuments.find(i => i.createddocument === cd.id);
|
||||||
|
let total = 0;
|
||||||
|
let typeString = "";
|
||||||
|
|
||||||
let total = 0
|
if(cd.type === "invoices") {
|
||||||
let typeString = ""
|
total = getCreatedDocumentTotal(cd).totalGross;
|
||||||
|
typeString = "AR";
|
||||||
if(createddocument.type === "invoices") {
|
} else if(cd.type === "advanceInvoices") {
|
||||||
total = getCreatedDocumentTotal(createddocument).totalGross
|
total = getCreatedDocumentTotal(cd).totalGross;
|
||||||
|
typeString = "ARAbschlag";
|
||||||
console.log()
|
} else if(cd.type === "cancellationInvoices") {
|
||||||
if(createddocument.usedAdvanceInvoices.length > 0){
|
total = getCreatedDocumentTotal(cd).totalGross;
|
||||||
createddocument.usedAdvanceInvoices.forEach(usedAdvanceInvoice => {
|
typeString = "ARStorno";
|
||||||
total -= getCreatedDocumentTotal(createddocumentsRaw.find(i => i.id === usedAdvanceInvoice)).totalGross
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(total)
|
|
||||||
|
|
||||||
typeString = "AR"
|
|
||||||
} else if(createddocument.type === "advanceInvoices") {
|
|
||||||
total = getCreatedDocumentTotal(createddocument).totalGross
|
|
||||||
typeString = "ARAbschlag"
|
|
||||||
} else if(createddocument.type === "cancellationInvoices") {
|
|
||||||
total = getCreatedDocumentTotal(createddocument).totalGross
|
|
||||||
typeString = "ARStorno"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let shSelector = "S"
|
let shSelector = Math.sign(total) === -1 ? "H" : "S";
|
||||||
if(Math.sign(total) === 1) {
|
const cust = cd.customer; // durch Mapping verfügbar
|
||||||
shSelector = "S"
|
|
||||||
} else if (Math.sign(total) === -1) {
|
bookingLines.push(`${displayCurrency(total,true)};"${shSelector}";;;;;${cust?.customerNumber || ""};8400;"";${dayjs(cd.documentDate).format("DDMM")};"${cd.documentNumber}";;;"${`${typeString} ${cd.documentNumber} - ${cust?.name || ""}`.substring(0,59)}";;;;;;${file ? `"BEDI ""${file.id}"""` : ""};"Geschäftspartner";"${cust?.name || ""}";"Kundennummer";"${cust?.customerNumber || ""}";"Belegnummer";"${cd.documentNumber}";"Leistungsdatum";"${dayjs(cd.deliveryDate).format("DD.MM.YYYY")}";"Belegdatum";"${dayjs(cd.documentDate).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`);
|
||||||
shSelector = "H"
|
});
|
||||||
|
|
||||||
|
// ER
|
||||||
|
incominginvoicesList.forEach(ii => {
|
||||||
|
const accs = ii.accounts as any[] || [];
|
||||||
|
accs.forEach(account => {
|
||||||
|
let file = filesIncomingInvoices.find(i => i.incominginvoice === ii.id);
|
||||||
|
let accountData = accountsList.find(i => i.id === account.account);
|
||||||
|
if (!accountData) return;
|
||||||
|
|
||||||
|
let buschluessel = "9";
|
||||||
|
if(account.taxType === '19') buschluessel = "9";
|
||||||
|
else if(account.taxType === 'null') buschluessel = "";
|
||||||
|
else if(account.taxType === '7') buschluessel = "8";
|
||||||
|
else if(account.taxType === '19I') buschluessel = "19";
|
||||||
|
else if(account.taxType === '7I') buschluessel = "18";
|
||||||
|
else buschluessel = "-";
|
||||||
|
|
||||||
|
let amountGross = account.amountGross ? account.amountGross : (account.amountNet || 0) + (account.amountTax || 0);
|
||||||
|
let shSelector = Math.sign(amountGross) === -1 ? "H" : "S";
|
||||||
|
let text = `ER ${ii.reference}: ${escapeString(ii.description)}`.substring(0,59);
|
||||||
|
const vend = ii.vendor; // durch Mapping verfügbar
|
||||||
|
|
||||||
|
bookingLines.push(`${Math.abs(amountGross).toFixed(2).replace(".",",")};"${shSelector}";;;;;${accountData.number};${vend?.vendorNumber || ""};"${buschluessel}";${dayjs(ii.date).format("DDMM")};"${ii.reference}";;;"${text}";;;;;;${file ? `"BEDI ""${file.id}"""` : ""};"Geschäftspartner";"${vend?.name || ""}";"Kundennummer";"${vend?.vendorNumber || ""}";"Belegnummer";"${ii.reference}";"Leistungsdatum";"${dayjs(ii.date).format("DD.MM.YYYY")}";"Belegdatum";"${dayjs(ii.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bank
|
||||||
|
statementallocationsList.forEach(alloc => {
|
||||||
|
const bs = alloc.bankstatement; // durch Mapping verfügbar
|
||||||
|
if(!bs) return;
|
||||||
|
|
||||||
|
let shSelector = Math.sign(alloc.amount) === -1 ? "H" : "S";
|
||||||
|
// @ts-ignore
|
||||||
|
let datevKonto = bs.account?.datevNumber || "";
|
||||||
|
let dateVal = dayjs(bs.date).format("DDMM");
|
||||||
|
let dateFull = dayjs(bs.date).format("DD.MM.YYYY");
|
||||||
|
let bsText = escapeString(bs.text);
|
||||||
|
|
||||||
|
if(alloc.createddocument && alloc.createddocument.customer) {
|
||||||
|
const cd = alloc.createddocument;
|
||||||
|
const cust = cd.customer;
|
||||||
|
bookingLines.push(`${displayCurrency(alloc.amount,true)};"H";;;;;${cust?.customerNumber};${datevKonto};"3";${dayjs(cd.documentDate).format("DDMM")};"${cd.documentNumber}";;;"${`ZE${alloc.description}${bsText}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${cust?.name}";"Kundennummer";"${cust?.customerNumber}";"Belegnummer";"${cd.documentNumber}";"Leistungsdatum";"${dayjs(cd.deliveryDate).format("DD.MM.YYYY")}";"Belegdatum";"${dayjs(cd.documentDate).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`);
|
||||||
|
} else if(alloc.incominginvoice && alloc.incominginvoice.vendor) {
|
||||||
|
const ii = alloc.incominginvoice;
|
||||||
|
const vend = ii.vendor;
|
||||||
|
bookingLines.push(`${displayCurrency(alloc.amount,true)};"${shSelector}";;;;;${datevKonto};${vend?.vendorNumber};"";${dayjs(ii.date).format("DDMM")};"${ii.reference}";;;"${`ZA${alloc.description} ${bsText} `.substring(0,59)}";;;;;;;"Geschäftspartner";"${vend?.name}";"Kundennummer";"${vend?.vendorNumber}";"Belegnummer";"${ii.reference}";"Leistungsdatum";"${dayjs(ii.date).format("DD.MM.YYYY")}";"Belegdatum";"${dayjs(ii.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`);
|
||||||
|
} else if(alloc.account) {
|
||||||
|
const acc = alloc.account;
|
||||||
|
let vorzeichen = Math.sign(alloc.amount) > 0 ? "ZE" : "ZA";
|
||||||
|
bookingLines.push(`${displayCurrency(alloc.amount,true)};"${shSelector}";;;;;${datevKonto};${acc.number};"";${dateVal};"";;;"${`${vorzeichen} ${acc.number} - ${escapeString(acc.label)}${escapeString(alloc.description)}${bsText}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${bs.credName || ''}";"Kundennummer";"";"Belegnummer";"";"Leistungsdatum";"";"Belegdatum";"${dateFull}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`);
|
||||||
|
} else if(alloc.vendor) {
|
||||||
|
const vend = alloc.vendor;
|
||||||
|
let vorzeichen = Math.sign(alloc.amount) > 0 ? "ZE" : "ZA";
|
||||||
|
bookingLines.push(`${displayCurrency(alloc.amount,true)};"${shSelector}";;;;;${datevKonto};${vend.vendorNumber};"";${dateVal};"";;;"${`${vorzeichen} ${vend.vendorNumber} - ${escapeString(vend.name)}${escapeString(alloc.description)}${bsText}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${vend.name}";"Kundennummer";"";"Belegnummer";"";"Leistungsdatum";"";"Belegdatum";"${dateFull}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`);
|
||||||
|
} else if(alloc.customer) {
|
||||||
|
const cust = alloc.customer;
|
||||||
|
let vorzeichen = Math.sign(alloc.amount) > 0 ? "ZE" : "ZA";
|
||||||
|
bookingLines.push(`${displayCurrency(alloc.amount,true)};"${shSelector}";;;;;${datevKonto};${cust.customerNumber};"";${dateVal};"";;;"${`${vorzeichen} ${cust.customerNumber} - ${escapeString(cust.name)}${escapeString(alloc.description)}${bsText}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${cust.name}";"Kundennummer";"";"Belegnummer";"";"Leistungsdatum";"";"Belegdatum";"${dateFull}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`);
|
||||||
|
} else if(alloc.ownaccount) {
|
||||||
|
const own = alloc.ownaccount;
|
||||||
|
let vorzeichen = Math.sign(alloc.amount) > 0 ? "ZE" : "ZA";
|
||||||
|
bookingLines.push(`${displayCurrency(alloc.amount,true)};"${shSelector}";;;;;${datevKonto};${own.number};"";${dateVal};"";;;"${`${vorzeichen} ${own.number} - ${escapeString(own.name)}${escapeString(alloc.description)}${bsText}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${own.name}";"Kundennummer";"";"Belegnummer";"";"Leistungsdatum";"";"Belegdatum";"${dateFull}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
bookingLines.push(`${displayCurrency(total,true)};"${shSelector}";;;;;${createddocument.customer.customerNumber};8400;"";${dayjs(createddocument.documentDate).format("DDMM")};"${createddocument.documentNumber}";;;"${`${typeString} ${createddocument.documentNumber} - ${createddocument.customer.name}`.substring(0,59)}";;;;;;${file ? `"BEDI ""${file.id}"""` : ""};"Geschäftspartner";"${createddocument.customer.name}";"Kundennummer";"${createddocument.customer.customerNumber}";"Belegnummer";"${createddocument.documentNumber}";"Leistungsdatum";"${dayjs(createddocument.deliveryDate).format("DD.MM.YYYY")}";"Belegdatum";"${dayjs(createddocument.documentDate).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`)
|
// ---------------------------------------------------------
|
||||||
|
// 5. STAMMDATEN CSV
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
const csvString = `${header}\n${colHeaders}\n` + bookingLines.join("\n") + "\n";
|
||||||
|
await zipWriter.add(
|
||||||
|
`EXTF_Buchungsstapel_von_${startDateFmt}_bis_${endDateFmt}.csv`,
|
||||||
|
new TextReader(csvString)
|
||||||
|
);
|
||||||
|
|
||||||
})
|
const headerStammdaten = `"EXTF";700;16;"Debitoren/Kreditoren";5;${dateNowStr};;"FE";"Florian Federspiel";;${beraternr};${mandantennr};20250101;4;${startDateFmt};${endDateFmt};"Debitoren & Kreditoren";"FF";1;0;1;"EUR";;"";;;"03";;;"";""`;
|
||||||
|
const colHeadersStammdaten = `Konto;Name (Adressattyp Unternehmen);Unternehmensgegenstand;Name (Adressattyp natuerl. Person);Vorname (Adressattyp natuerl. Person);Name (Adressattyp keine Angabe);Adressatentyp;Kurzbezeichnung;EU-Land;EU-UStID;Anrede;Titel/Akad. Grad;Adelstitel;Namensvorsatz;Adressart;Strasse;Postfach;Postleitzahl;Ort;Land;Versandzusatz;Adresszusatz;Abweichende Anrede;Abw. Zustellbezeichnung 1;Abw. Zustellbezeichnung 2;Kennz. Korrespondenzadresse;Adresse Gueltig von;Adresse Gueltig bis;Telefon;Bemerkung (Telefon);Telefon GL;Bemerkung (Telefon GL);E-Mail;Bemerkung (E-Mail);Internet;Bemerkung (Internet);Fax;Bemerkung (Fax);Sonstige;Bemerkung (Sonstige);Bankleitzahl 1;Bankbezeichnung 1;Bankkonto-Nummer 1;Laenderkennzeichen 1;IBAN-Nr. 1;Leerfeld;SWIFT-Code 1;Abw. Kontoinhaber 1;Kennz. Haupt-Bankverb. 1;Bankverb. 1 Gueltig von;Bankverb. 1 Gueltig bis;Bankleitzahl 2;Bankbezeichnung 2;Bankkonto-Nummer 2;Laenderkennzeichen 2;IBAN-Nr. 2;Leerfeld;SWIFT-Code 2;Abw. Kontoinhaber 2;Kennz. Haupt-Bankverb. 2;Bankverb. 2 gueltig von;Bankverb. 2 gueltig bis;Bankleitzahl 3;Bankbezeichnung 3;Bankkonto-Nummer 3;Laenderkennzeichen 3;IBAN-Nr. 3;Leerfeld;SWIFT-Code 3;Abw. Kontoinhaber 3;Kennz. Haupt-Bankverb. 3;Bankverb. 3 gueltig von;Bankverb. 3 gueltig bis;Bankleitzahl 4;Bankbezeichnung 4;Bankkonto-Nummer 4;Laenderkennzeichen 4;IBAN-Nr. 4;Leerfeld;SWIFT-Code 4;Abw. Kontoinhaber 4;Kennz. Haupt-Bankverb. 4;Bankverb. 4 gueltig von;Bankverb. 4 gueltig bis;Bankleitzahl 5;Bankbezeichnung 5;Bankkonto-Nummer 5;Laenderkennzeichen 5;IBAN-Nr. 5;Leerfeld;SWIFT-Code 5;Abw. Kontoinhaber 5;Kennz. Haupt-Bankverb. 5;Bankverb. 5 gueltig von;Bankverb. 5 gueltig bis;Leerfeld;Briefanrede;Grussformel;Kunden-/Lief.-Nr.;Steuernummer;Sprache;Ansprechpartner;Vertreter;Sachbearbeiter;Diverse-Konto;Ausgabeziel;Waehrungssteuerung;Kreditlimit (Debitor);Zahlungsbedingung;Faelligkeit in Tagen (Debitor);Skonto in Prozent (Debitor);Kreditoren-Ziel 1 Tg.;Kreditoren-Skonto 1 %;Kreditoren-Ziel 2 Tg.;Kreditoren-Skonto 2 %;Kreditoren-Ziel 3 Brutto Tg.;Kreditoren-Ziel 4 Tg.;Kreditoren-Skonto 4 %;Kreditoren-Ziel 5 Tg.;Kreditoren-Skonto 5 %;Mahnung;Kontoauszug;Mahntext 1;Mahntext 2;Mahntext 3;Kontoauszugstext;Mahnlimit Betrag;Mahnlimit %;Zinsberechnung;Mahnzinssatz 1;Mahnzinssatz 2;Mahnzinssatz 3;Lastschrift;Verfahren;Mandantenbank;Zahlungstraeger;Indiv. Feld 1;Indiv. Feld 2;Indiv. Feld 3;Indiv. Feld 4;Indiv. Feld 5;Indiv. Feld 6;Indiv. Feld 7;Indiv. Feld 8;Indiv. Feld 9;Indiv. Feld 10;Indiv. Feld 11;Indiv. Feld 12;Indiv. Feld 13;Indiv. Feld 14;Indiv. Feld 15;Abweichende Anrede (Rechnungsadresse);Adressart (Rechnungsadresse);Strasse (Rechnungsadresse);Postfach (Rechnungsadresse);Postleitzahl (Rechnungsadresse);Ort (Rechnungsadresse);Land (Rechnungsadresse);Versandzusatz (Rechnungsadresse);Adresszusatz (Rechnungsadresse);Abw. Zustellbezeichnung 1 (Rechnungsadresse);Abw. Zustellbezeichnung 2 (Rechnungsadresse);Adresse Gueltig von (Rechnungsadresse);Adresse Gueltig bis (Rechnungsadresse);Bankleitzahl 6;Bankbezeichnung 6;Bankkonto-Nummer 6;Laenderkennzeichen 6;IBAN-Nr. 6;Leerfeld;SWIFT-Code 6;Abw. Kontoinhaber 6;Kennz. Haupt-Bankverb. 6;Bankverb. 6 gueltig von;Bankverb. 6 gueltig bis;Bankleitzahl 7;Bankbezeichnung 7;Bankkonto-Nummer 7;Laenderkennzeichen 7;IBAN-Nr. 7;Leerfeld;SWIFT-Code 7;Abw. Kontoinhaber 7;Kennz. Haupt-Bankverb. 7;Bankverb. 7 gueltig von;Bankverb. 7 gueltig bis;Bankleitzahl 8;Bankbezeichnung 8;Bankkonto-Nummer 8;Laenderkennzeichen 8;IBAN-Nr. 8;Leerfeld;SWIFT-Code 8;Abw. Kontoinhaber 8;Kennz. Haupt-Bankverb. 8;Bankverb. 8 gueltig von;Bankverb. 8 gueltig bis;Bankleitzahl 9;Bankbezeichnung 9;Bankkonto-Nummer 9;Laenderkennzeichen 9;IBAN-Nr. 9;Leerfeld;SWIFT-Code 9;Abw. Kontoinhaber 9;Kennz. Haupt-Bankverb. 9;Bankverb. 9 gueltig von;Bankverb. 9 gueltig bis;Bankleitzahl 10;Bankbezeichnung 10;Bankkonto-Nummer 10;Laenderkennzeichen 10;IBAN-Nr. 10;Leerfeld;SWIFT-Code 10;Abw. Kontoinhaber 10;Kennz. Haupt-Bankverb. 10;Bankverb 10 Gueltig von;Bankverb 10 Gueltig bis;Nummer Fremdsystem;Insolvent;SEPA-Mandatsreferenz 1;SEPA-Mandatsreferenz 2;SEPA-Mandatsreferenz 3;SEPA-Mandatsreferenz 4;SEPA-Mandatsreferenz 5;SEPA-Mandatsreferenz 6;SEPA-Mandatsreferenz 7;SEPA-Mandatsreferenz 8;SEPA-Mandatsreferenz 9;SEPA-Mandatsreferenz 10;Verknuepftes OPOS-Konto;Mahnsperre bis;Lastschriftsperre bis;Zahlungssperre bis;Gebuehrenberechnung;Mahngebuehr 1;Mahngebuehr 2;Mahngebuehr 3;Pauschalberechnung;Verzugspauschale 1;Verzugspauschale 2;Verzugspauschale 3;Alternativer Suchname;Status;Anschrift manuell geaendert (Korrespondenzadresse);Anschrift individuell (Korrespondenzadresse);Anschrift manuell geaendert (Rechnungsadresse);Anschrift individuell (Rechnungsadresse);Fristberechnung bei Debitor;Mahnfrist 1;Mahnfrist 2;Mahnfrist 3;Letzte Frist`;
|
||||||
|
|
||||||
incominginvoices.forEach(incominginvoice => {
|
const customersList = await server.db.select().from(customers).where(and(eq(customers.tenant, tenantId), eq(customers.active, true))).orderBy(asc(customers.customerNumber));
|
||||||
console.log(incominginvoice.id);
|
const vendorsList = await server.db.select().from(vendors).where(and(eq(vendors.tenant, tenantId), eq(vendors.archived, false))).orderBy(asc(vendors.vendorNumber));
|
||||||
incominginvoice.accounts.forEach(account => {
|
|
||||||
|
|
||||||
let file = filesIncomingInvoices.find(i => i.incominginvoice === incominginvoice.id);
|
let bookinglinesStammdaten: string[] = [];
|
||||||
|
|
||||||
|
customersList.forEach(c => {
|
||||||
|
const info = c.infoData as any || {};
|
||||||
|
bookinglinesStammdaten.push(`${c.customerNumber};"${c.isCompany ? (c.name || "").substring(0,48): ''}";;"${!c.isCompany ? (c.lastname ? c.lastname : c.name) : ''}";"${!c.isCompany ? (c.firstname ? c.firstname : '') : ''}";;${c.isCompany ? 2 : 1};;;;;;;;"STR";"${info.street || ''}";;"${info.zip || ''}";"${info.city || ''}";;;"${info.special || ''}";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;`);
|
||||||
|
});
|
||||||
|
|
||||||
let accountData = accounts.find(i => i.id === account.account)
|
vendorsList.forEach(v => {
|
||||||
let buschluessel: string = "9"
|
const info = v.infoData as any || {};
|
||||||
|
bookinglinesStammdaten.push(`${v.vendorNumber};"${(v.name || "").substring(0,48)}";;;;;2;;;;;;;;"STR";"${info.street || ''}";;"${info.zip || ''}";"${info.city || ''}";;;"${info.special || ''}";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;`);
|
||||||
if(account.taxType === '19'){
|
});
|
||||||
buschluessel = "9"
|
|
||||||
} else if(account.taxType === 'null') {
|
|
||||||
buschluessel = ""
|
|
||||||
} else if(account.taxType === '7') {
|
|
||||||
buschluessel = "8"
|
|
||||||
} else if(account.taxType === '19I') {
|
|
||||||
buschluessel = "19"
|
|
||||||
} else if(account.taxType === '7I') {
|
|
||||||
buschluessel = "18"
|
|
||||||
} else {
|
|
||||||
buschluessel = "-"
|
|
||||||
}
|
|
||||||
|
|
||||||
let shSelector = "S"
|
|
||||||
let amountGross = account.amountGross ? account.amountGross : account.amountNet + account.amountTax
|
|
||||||
|
|
||||||
|
|
||||||
if(Math.sign(amountGross) === 1) {
|
|
||||||
shSelector = "S"
|
|
||||||
} else if(Math.sign(amountGross) === -1) {
|
|
||||||
shSelector = "H"
|
|
||||||
}
|
|
||||||
|
|
||||||
let text = `ER ${incominginvoice.reference}: ${escapeString(incominginvoice.description)}`.substring(0,59)
|
|
||||||
console.log(incominginvoice)
|
|
||||||
bookingLines.push(`${Math.abs(amountGross).toFixed(2).replace(".",",")};"${shSelector}";;;;;${accountData.number};${incominginvoice.vendor.vendorNumber};"${buschluessel}";${dayjs(incominginvoice.date).format("DDMM")};"${incominginvoice.reference}";;;"${text}";;;;;;${file ? `"BEDI ""${file.id}"""` : ""};"Geschäftspartner";"${incominginvoice.vendor.name}";"Kundennummer";"${incominginvoice.vendor.vendorNumber}";"Belegnummer";"${incominginvoice.reference}";"Leistungsdatum";"${dayjs(incominginvoice.date).format("DD.MM.YYYY")}";"Belegdatum";"${dayjs(incominginvoice.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
statementallocations.forEach(statementallocation => {
|
|
||||||
|
|
||||||
let shSelector = "S"
|
|
||||||
|
|
||||||
if(Math.sign(statementallocation.amount) === 1) {
|
|
||||||
shSelector = "S"
|
|
||||||
} else if(Math.sign(statementallocation.amount) === -1) {
|
|
||||||
shSelector = "H"
|
|
||||||
}
|
|
||||||
|
|
||||||
if(statementallocation.cd_id) {
|
|
||||||
bookingLines.push(`${displayCurrency(statementallocation.amount,true)};"H";;;;;${statementallocation.cd_id.customer.customerNumber};${statementallocation.bs_id.account.datevNumber};"3";${dayjs(statementallocation.cd_id.documentDate).format("DDMM")};"${statementallocation.cd_id.documentNumber}";;;"${`ZE${statementallocation.description}${escapeString(statementallocation.bs_id.text)}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${statementallocation.cd_id.customer.name}";"Kundennummer";"${statementallocation.cd_id.customer.customerNumber}";"Belegnummer";"${statementallocation.cd_id.documentNumber}";"Leistungsdatum";"${dayjs(statementallocation.cd_id.deliveryDate).format("DD.MM.YYYY")}";"Belegdatum";"${dayjs(statementallocation.cd_id.documentDate).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`)
|
|
||||||
} else if(statementallocation.ii_id) {
|
|
||||||
bookingLines.push(`${displayCurrency(statementallocation.amount,true)};"${shSelector}";;;;;${statementallocation.bs_id.account.datevNumber};${statementallocation.ii_id.vendor.vendorNumber};"";${dayjs(statementallocation.ii_id.date).format("DDMM")};"${statementallocation.ii_id.reference}";;;"${`ZA${statementallocation.description} ${escapeString(statementallocation.bs_id.text)} `.substring(0,59)}";;;;;;;"Geschäftspartner";"${statementallocation.ii_id.vendor.name}";"Kundennummer";"${statementallocation.ii_id.vendor.vendorNumber}";"Belegnummer";"${statementallocation.ii_id.reference}";"Leistungsdatum";"${dayjs(statementallocation.ii_id.date).format("DD.MM.YYYY")}";"Belegdatum";"${dayjs(statementallocation.ii_id.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`)
|
|
||||||
} else if(statementallocation.account) {
|
|
||||||
bookingLines.push(`${displayCurrency(statementallocation.amount,true)};"${shSelector}";;;;;${statementallocation.bs_id.account.datevNumber};${statementallocation.account.number};"";${dayjs(statementallocation.bs_id.date).format("DDMM")};"";;;"${`${Math.sign(statementallocation.amount) > 0 ? "ZE" : "ZA"} ${statementallocation.account.number} - ${escapeString(statementallocation.account.label)}${escapeString(statementallocation.description)}${statementallocation.bs_id.text}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${statementallocation.bs_id.credName}";"Kundennummer";"";"Belegnummer";"";"Leistungsdatum";"";"Belegdatum";"${dayjs(statementallocation.bs_id.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`)
|
|
||||||
} else if(statementallocation.vendor) {
|
|
||||||
bookingLines.push(`${displayCurrency(statementallocation.amount,true)};"${shSelector}";;;;;${statementallocation.bs_id.account.datevNumber};${statementallocation.vendor.vendorNumber};"";${dayjs(statementallocation.bs_id.date).format("DDMM")};"";;;"${`${Math.sign(statementallocation.amount) > 0 ? "ZE" : "ZA"} ${statementallocation.vendor.vendorNumber} - ${escapeString(statementallocation.vendor.name)}${escapeString(statementallocation.description)}${statementallocation.bs_id.text}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${statementallocation.vendor.name}";"Kundennummer";"";"Belegnummer";"";"Leistungsdatum";"";"Belegdatum";"${dayjs(statementallocation.bs_id.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`)
|
|
||||||
} else if(statementallocation.customer) {
|
|
||||||
bookingLines.push(`${displayCurrency(statementallocation.amount,true)};"${shSelector}";;;;;${statementallocation.bs_id.account.datevNumber};${statementallocation.customer.customerNumber};"";${dayjs(statementallocation.bs_id.date).format("DDMM")};"";;;"${`${Math.sign(statementallocation.amount) > 0 ? "ZE" : "ZA"} ${statementallocation.customer.customerNumber} - ${escapeString(statementallocation.customer.name)}${escapeString(statementallocation.description)}${statementallocation.bs_id.text}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${statementallocation.customer.name}";"Kundennummer";"";"Belegnummer";"";"Leistungsdatum";"";"Belegdatum";"${dayjs(statementallocation.bs_id.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`)
|
|
||||||
} else if(statementallocation.ownaccount) {
|
|
||||||
bookingLines.push(`${displayCurrency(statementallocation.amount,true)};"${shSelector}";;;;;${statementallocation.bs_id.account.datevNumber};${statementallocation.ownaccount.number};"";${dayjs(statementallocation.bs_id.date).format("DDMM")};"";;;"${`${Math.sign(statementallocation.amount) > 0 ? "ZE" : "ZA"} ${statementallocation.ownaccount.number} - ${escapeString(statementallocation.ownaccount.name)}${escapeString(statementallocation.description)}${statementallocation.bs_id.text}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${statementallocation.ownaccount.name}";"Kundennummer";"";"Belegnummer";"";"Leistungsdatum";"";"Belegdatum";"${dayjs(statementallocation.bs_id.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
let csvString = `${header}\n${colHeaders}\n`;
|
|
||||||
bookingLines.forEach(line => {
|
|
||||||
csvString += `${line}\n`;
|
|
||||||
})
|
|
||||||
|
|
||||||
const buchungsstapelReader = new TextReader(csvString)
|
|
||||||
await zipWriter.add(`EXTF_Buchungsstapel_von_${dayjs(startDate).format("DDMMYYYY")}_bis_${dayjs(endDate).format("DDMMYYYY")}.csv`, buchungsstapelReader)
|
|
||||||
|
|
||||||
/*fs.writeFile(`output/EXTF_Buchungsstapel_von_${dayjs(startDate).format("DDMMYYYY")}_bis_${dayjs(endDate).format("DDMMYYYY")}.csv`, csvString, 'utf8', function (err) {
|
|
||||||
if (err) {
|
|
||||||
console.log('Some error occured - file either not saved or corrupted file saved.');
|
|
||||||
console.log(err);
|
|
||||||
} else{
|
|
||||||
console.log('It\'s saved!');
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
|
|
||||||
// Kreditoren/Debitoren
|
|
||||||
let headerStammdaten = `"EXTF";700;16;"Debitoren/Kreditoren";5;${dayjs().format("YYYYMMDDHHmmssSSS")};;"FE";"Florian Federspiel";;${beraternr};${mandantennr};20250101;4;${dayjs(startDate).format("YYYYMMDD")};${dayjs(endDate).format("YYYYMMDD")};"Debitoren & Kreditoren";"FF";1;0;1;"EUR";;"";;;"03";;;"";""`
|
|
||||||
|
|
||||||
let colHeadersStammdaten = `Konto;Name (Adressattyp Unternehmen);Unternehmensgegenstand;Name (Adressattyp natuerl. Person);Vorname (Adressattyp natuerl. Person);Name (Adressattyp keine Angabe);Adressatentyp;Kurzbezeichnung;EU-Land;EU-UStID;Anrede;Titel/Akad. Grad;Adelstitel;Namensvorsatz;Adressart;Strasse;Postfach;Postleitzahl;Ort;Land;Versandzusatz;Adresszusatz;Abweichende Anrede;Abw. Zustellbezeichnung 1;Abw. Zustellbezeichnung 2;Kennz. Korrespondenzadresse;Adresse Gueltig von;Adresse Gueltig bis;Telefon;Bemerkung (Telefon);Telefon GL;Bemerkung (Telefon GL);E-Mail;Bemerkung (E-Mail);Internet;Bemerkung (Internet);Fax;Bemerkung (Fax);Sonstige;Bemerkung (Sonstige);Bankleitzahl 1;Bankbezeichnung 1;Bankkonto-Nummer 1;Laenderkennzeichen 1;IBAN-Nr. 1;Leerfeld;SWIFT-Code 1;Abw. Kontoinhaber 1;Kennz. Haupt-Bankverb. 1;Bankverb. 1 Gueltig von;Bankverb. 1 Gueltig bis;Bankleitzahl 2;Bankbezeichnung 2;Bankkonto-Nummer 2;Laenderkennzeichen 2;IBAN-Nr. 2;Leerfeld;SWIFT-Code 2;Abw. Kontoinhaber 2;Kennz. Haupt-Bankverb. 2;Bankverb. 2 gueltig von;Bankverb. 2 gueltig bis;Bankleitzahl 3;Bankbezeichnung 3;Bankkonto-Nummer 3;Laenderkennzeichen 3;IBAN-Nr. 3;Leerfeld;SWIFT-Code 3;Abw. Kontoinhaber 3;Kennz. Haupt-Bankverb. 3;Bankverb. 3 gueltig von;Bankverb. 3 gueltig bis;Bankleitzahl 4;Bankbezeichnung 4;Bankkonto-Nummer 4;Laenderkennzeichen 4;IBAN-Nr. 4;Leerfeld;SWIFT-Code 4;Abw. Kontoinhaber 4;Kennz. Haupt-Bankverb. 4;Bankverb. 4 gueltig von;Bankverb. 4 gueltig bis;Bankleitzahl 5;Bankbezeichnung 5;Bankkonto-Nummer 5;Laenderkennzeichen 5;IBAN-Nr. 5;Leerfeld;SWIFT-Code 5;Abw. Kontoinhaber 5;Kennz. Haupt-Bankverb. 5;Bankverb. 5 gueltig von;Bankverb. 5 gueltig bis;Leerfeld;Briefanrede;Grussformel;Kunden-/Lief.-Nr.;Steuernummer;Sprache;Ansprechpartner;Vertreter;Sachbearbeiter;Diverse-Konto;Ausgabeziel;Waehrungssteuerung;Kreditlimit (Debitor);Zahlungsbedingung;Faelligkeit in Tagen (Debitor);Skonto in Prozent (Debitor);Kreditoren-Ziel 1 Tg.;Kreditoren-Skonto 1 %;Kreditoren-Ziel 2 Tg.;Kreditoren-Skonto 2 %;Kreditoren-Ziel 3 Brutto Tg.;Kreditoren-Ziel 4 Tg.;Kreditoren-Skonto 4 %;Kreditoren-Ziel 5 Tg.;Kreditoren-Skonto 5 %;Mahnung;Kontoauszug;Mahntext 1;Mahntext 2;Mahntext 3;Kontoauszugstext;Mahnlimit Betrag;Mahnlimit %;Zinsberechnung;Mahnzinssatz 1;Mahnzinssatz 2;Mahnzinssatz 3;Lastschrift;Verfahren;Mandantenbank;Zahlungstraeger;Indiv. Feld 1;Indiv. Feld 2;Indiv. Feld 3;Indiv. Feld 4;Indiv. Feld 5;Indiv. Feld 6;Indiv. Feld 7;Indiv. Feld 8;Indiv. Feld 9;Indiv. Feld 10;Indiv. Feld 11;Indiv. Feld 12;Indiv. Feld 13;Indiv. Feld 14;Indiv. Feld 15;Abweichende Anrede (Rechnungsadresse);Adressart (Rechnungsadresse);Strasse (Rechnungsadresse);Postfach (Rechnungsadresse);Postleitzahl (Rechnungsadresse);Ort (Rechnungsadresse);Land (Rechnungsadresse);Versandzusatz (Rechnungsadresse);Adresszusatz (Rechnungsadresse);Abw. Zustellbezeichnung 1 (Rechnungsadresse);Abw. Zustellbezeichnung 2 (Rechnungsadresse);Adresse Gueltig von (Rechnungsadresse);Adresse Gueltig bis (Rechnungsadresse);Bankleitzahl 6;Bankbezeichnung 6;Bankkonto-Nummer 6;Laenderkennzeichen 6;IBAN-Nr. 6;Leerfeld;SWIFT-Code 6;Abw. Kontoinhaber 6;Kennz. Haupt-Bankverb. 6;Bankverb. 6 gueltig von;Bankverb. 6 gueltig bis;Bankleitzahl 7;Bankbezeichnung 7;Bankkonto-Nummer 7;Laenderkennzeichen 7;IBAN-Nr. 7;Leerfeld;SWIFT-Code 7;Abw. Kontoinhaber 7;Kennz. Haupt-Bankverb. 7;Bankverb. 7 gueltig von;Bankverb. 7 gueltig bis;Bankleitzahl 8;Bankbezeichnung 8;Bankkonto-Nummer 8;Laenderkennzeichen 8;IBAN-Nr. 8;Leerfeld;SWIFT-Code 8;Abw. Kontoinhaber 8;Kennz. Haupt-Bankverb. 8;Bankverb. 8 gueltig von;Bankverb. 8 gueltig bis;Bankleitzahl 9;Bankbezeichnung 9;Bankkonto-Nummer 9;Laenderkennzeichen 9;IBAN-Nr. 9;Leerfeld;SWIFT-Code 9;Abw. Kontoinhaber 9;Kennz. Haupt-Bankverb. 9;Bankverb. 9 gueltig von;Bankverb. 9 gueltig bis;Bankleitzahl 10;Bankbezeichnung 10;Bankkonto-Nummer 10;Laenderkennzeichen 10;IBAN-Nr. 10;Leerfeld;SWIFT-Code 10;Abw. Kontoinhaber 10;Kennz. Haupt-Bankverb. 10;Bankverb 10 Gueltig von;Bankverb 10 Gueltig bis;Nummer Fremdsystem;Insolvent;SEPA-Mandatsreferenz 1;SEPA-Mandatsreferenz 2;SEPA-Mandatsreferenz 3;SEPA-Mandatsreferenz 4;SEPA-Mandatsreferenz 5;SEPA-Mandatsreferenz 6;SEPA-Mandatsreferenz 7;SEPA-Mandatsreferenz 8;SEPA-Mandatsreferenz 9;SEPA-Mandatsreferenz 10;Verknuepftes OPOS-Konto;Mahnsperre bis;Lastschriftsperre bis;Zahlungssperre bis;Gebuehrenberechnung;Mahngebuehr 1;Mahngebuehr 2;Mahngebuehr 3;Pauschalberechnung;Verzugspauschale 1;Verzugspauschale 2;Verzugspauschale 3;Alternativer Suchname;Status;Anschrift manuell geaendert (Korrespondenzadresse);Anschrift individuell (Korrespondenzadresse);Anschrift manuell geaendert (Rechnungsadresse);Anschrift individuell (Rechnungsadresse);Fristberechnung bei Debitor;Mahnfrist 1;Mahnfrist 2;Mahnfrist 3;Letzte Frist`
|
|
||||||
const {data:customers} = await server.supabase.from("customers").select().eq("tenant",tenant).order("customerNumber")
|
|
||||||
const {data:vendors} = await server.supabase.from("vendors").select().eq("tenant",tenant).order("vendorNumber")
|
|
||||||
|
|
||||||
let bookinglinesStammdaten = []
|
|
||||||
|
|
||||||
customers.forEach(customer => {
|
|
||||||
bookinglinesStammdaten.push(`${customer.customerNumber};"${customer.isCompany ? customer.name.substring(0,48): ''}";;"${!customer.isCompany ? (customer.lastname ? customer.lastname : customer.name) : ''}";"${!customer.isCompany ? (customer.firstname ? customer.firstname : '') : ''}";;${customer.isCompany ? 2 : 1};;;;;;;;"STR";"${customer.infoData.street ? customer.infoData.street : ''}";;"${customer.infoData.zip ? customer.infoData.zip : ''}";"${customer.infoData.city ? customer.infoData.city : ''}";;;"${customer.infoData.special ? customer.infoData.special : ''}";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;`)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
vendors.forEach(vendor => {
|
|
||||||
bookinglinesStammdaten.push(`${vendor.vendorNumber};"${vendor.name.substring(0,48)}";;;;;2;;;;;;;;"STR";"${vendor.infoData.street ? vendor.infoData.street : ''}";;"${vendor.infoData.zip ? vendor.infoData.zip : ''}";"${vendor.infoData.city ? vendor.infoData.city : ''}";;;"${vendor.infoData.special ? vendor.infoData.special : ''}";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;`)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
let csvStringStammdaten = `${headerStammdaten}\n${colHeadersStammdaten}\n`;
|
|
||||||
bookinglinesStammdaten.forEach(line => {
|
|
||||||
csvStringStammdaten += `${line}\n`;
|
|
||||||
})
|
|
||||||
|
|
||||||
const stammdatenReader = new TextReader(csvStringStammdaten)
|
|
||||||
await zipWriter.add(`EXTF_Stammdaten_von_${dayjs(startDate).format("DDMMYYYY")}_bis_${dayjs(endDate).format("DDMMYYYY")}.csv`, stammdatenReader)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*fs.writeFile(`output/EXTF_Stammdaten_von_${dayjs(startDate).format("DDMMYYYY")}_bis_${dayjs(endDate).format("DDMMYYYY")}.csv`, csvStringStammdaten, 'utf8', function (err) {
|
|
||||||
if (err) {
|
|
||||||
console.log('Some error occured - file either not saved or corrupted file saved.');
|
|
||||||
console.log(err);
|
|
||||||
} else{
|
|
||||||
console.log('It\'s saved!');
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
|
|
||||||
//Sachkonten
|
|
||||||
let headerSachkonten = `"EXTF";700;20;"Kontenbeschriftungen";3;${dayjs().format("YYYYMMDDHHmmssSSS")};;"FE";"Florian Federspiel";;${beraternr};${mandantennr};20250101;4;${dayjs(startDate).format("YYYYMMDD")};${dayjs(endDate).format("YYYYMMDD")};"Sachkonten";"FF";1;0;1;"EUR";;"";;;"03";;;"";""`
|
|
||||||
|
|
||||||
let colHeadersSachkonten = `Konto;Kontenbeschriftung;Sprach-ID;Kontenbeschriftung lang`
|
|
||||||
const {data:bankaccounts} = await server.supabase.from("bankaccounts").select().eq("tenant",tenant).order("datevNumber")
|
|
||||||
|
|
||||||
let bookinglinesSachkonten = []
|
|
||||||
|
|
||||||
bankaccounts.forEach(bankaccount => {
|
|
||||||
bookinglinesSachkonten.push(`${bankaccount.datevNumber};"${bankaccount.name}";"de-DE";`)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
let csvStringSachkonten = `${headerSachkonten}\n${colHeadersSachkonten}\n`;
|
|
||||||
bookinglinesSachkonten.forEach(line => {
|
|
||||||
csvStringSachkonten += `${line}\n`;
|
|
||||||
})
|
|
||||||
|
|
||||||
const sachkontenReader = new TextReader(csvStringSachkonten)
|
|
||||||
await zipWriter.add(`EXTF_Sachkonten_von_${dayjs(startDate).format("DDMMYYYY")}_bis_${dayjs(endDate).format("DDMMYYYY")}.csv`, sachkontenReader)
|
|
||||||
|
|
||||||
/*fs.writeFile(`output/EXTF_Sachkonten_von_${dayjs(startDate).format("DDMMYYYY")}_bis_${dayjs(endDate).format("DDMMYYYY")}.csv`, csvStringSachkonten, 'utf8', function (err) {
|
|
||||||
if (err) {
|
|
||||||
console.log('Some error occured - file either not saved or corrupted file saved.');
|
|
||||||
console.log(err);
|
|
||||||
} else{
|
|
||||||
console.log('It\'s saved!');
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
|
|
||||||
|
await zipWriter.add(
|
||||||
|
`EXTF_Stammdaten_von_${startDateFmt}_bis_${endDateFmt}.csv`,
|
||||||
|
new TextReader(`${headerStammdaten}\n${colHeadersStammdaten}\n` + bookinglinesStammdaten.join("\n") + "\n")
|
||||||
|
);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// 6. XML METADATA
|
||||||
|
// ---------------------------------------------------------
|
||||||
let obj = {
|
let obj = {
|
||||||
archive: {
|
archive: {
|
||||||
'@version':"5.0",
|
'@version':"5.0",
|
||||||
@@ -333,56 +395,34 @@ export async function buildExportZip(server: FastifyInstance, tenant: number, st
|
|||||||
date: dayjs().format("YYYY-MM-DDTHH:mm:ss")
|
date: dayjs().format("YYYY-MM-DDTHH:mm:ss")
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
document: []
|
document: [] as any[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
filesCreateddocuments.forEach(file => {
|
const addXmlDoc = (file: any) => {
|
||||||
|
if(!file.path) return;
|
||||||
|
const ext = file.path.includes('.') ? file.path.split(".").pop() : "pdf";
|
||||||
obj.archive.content.document.push({
|
obj.archive.content.document.push({
|
||||||
"@guid": file.id,
|
"@guid": file.id,
|
||||||
extension: {
|
extension: {
|
||||||
"@xsi:type":"File",
|
"@xsi:type":"File",
|
||||||
"@name":`${file.id}.pdf`
|
"@name":`${file.id}.${ext}`
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
})
|
};
|
||||||
|
|
||||||
filesIncomingInvoices.forEach(file => {
|
filesCreateddocuments.forEach(addXmlDoc);
|
||||||
obj.archive.content.document.push({
|
filesIncomingInvoices.forEach(addXmlDoc);
|
||||||
"@guid": file.id,
|
|
||||||
extension: {
|
|
||||||
"@xsi:type":"File",
|
|
||||||
"@name":`${file.id}.pdf`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
let doc = xmlbuilder.create(obj, {encoding: 'UTF-8', standalone: true})
|
const doc = xmlbuilder.create(obj, {encoding: 'UTF-8', standalone: true});
|
||||||
|
await zipWriter.add(`document.xml`, new TextReader(doc.end({pretty: true})));
|
||||||
|
|
||||||
//console.log(doc.end({pretty: true}));
|
const arrayBuffer = await (await zipWriter.close()).arrayBuffer();
|
||||||
|
return Buffer.from(arrayBuffer);
|
||||||
|
|
||||||
const documentsReader = new TextReader(doc.end({pretty: true}))
|
|
||||||
await zipWriter.add(`document.xml`, documentsReader)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*function toBuffer(arrayBuffer) {
|
|
||||||
const buffer = Buffer.alloc(arrayBuffer.byteLength);
|
|
||||||
const view = new Uint8Array(arrayBuffer);
|
|
||||||
for (let i = 0; i < buffer.length; ++i) {
|
|
||||||
buffer[i] = view[i];
|
|
||||||
}
|
|
||||||
return buffer;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
const arrayBuffer = await (await zipWriter.close()).arrayBuffer()
|
|
||||||
return Buffer.from(arrayBuffer)
|
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.log(error)
|
console.error("DATEV Export Error:", error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user