Files
online-energieausweis/src/lib/server/invoice.ts
2025-04-24 17:18:47 +02:00

348 lines
8.8 KiB
TypeScript

import { getAusweisartFromId } from "#components/Ausweis/types.js";
import moment from "moment";
import { createLexOfficeCustomer } from "./lexoffice.js";
import { Enums, prisma, Rechnung } from "./prisma.js";
import { LEX_OFFICE_API_KEY, PRICES } from "#lib/constants.js";
import { VerbrauchsausweisWohnenKomplett } from "./db.js";
export function createLineItem(name: string, preis: number, steuer: number) {
return {
type: "custom",
name: name,
description: "",
quantity: 1,
lineItemAmount: preis,
unitName: name,
unitPrice: {
currency: "EUR",
grossAmount: preis,
taxRatePercentage: steuer,
},
};
}
type LineItem = ReturnType<typeof createLineItem>;
export function addedServicesNeu(
data: any,
rechnung: Rechnung,
steuer: number
) {
if (rechnung.services.includes(Enums.Service.Qualitaetsdruck)) {
data["lineItems"].push(
createLineItem("Zusatzservice Postversand", 9, steuer)
);
}
if (rechnung.services.includes(Enums.Service.Aushang)) {
data["lineItems"].push(
createLineItem("Zusatzservice Aushang", 10, steuer)
);
}
if (rechnung.services.includes(Enums.Service.SameDay)) {
data["lineItems"].push(
createLineItem("Zusatzservice Same Day Service", 29, steuer)
);
}
if (rechnung.services.includes(Enums.Service.Telefonberatung)) {
data["lineItems"].push(
createLineItem("Zusatzservice Telefonische Beratung", 75, steuer)
);
}
return data;
}
/**
* Erstellt eine Rechnung in LexOffice und gibt die ID zurück.
* Die Preise basieren auf den in der Konstante `__PRICES__` definierten Preisen.
* @param Ausweis ausweis
* @param Rechnung $rechnung
* @return array
* @throws Error
*/
export async function createInvoice(
ausweis: VerbrauchsausweisWohnenKomplett,
rechnung: Rechnung
) {
if (!ausweis) {
throw new Error("Ausweis ist null");
}
if (!ausweis.benutzer_id) {
throw new Error("Benutzer ist nicht vorhanden");
}
const user = await prisma.benutzer.findUnique({
where: {
id: ausweis.benutzer_id,
},
});
if (!user) {
throw new Error("Nutzer existiert nicht!");
} else if (user.lex_office_id == null) {
// User has no valid lex_id
const lex_office_id = await createLexOfficeCustomer(user);
user.lex_office_id = lex_office_id;
await prisma.benutzer.update({
where: {
id: user.id,
},
data: {
lex_office_id: user.lex_office_id,
},
});
}
const ausweisart = getAusweisartFromId(ausweis.id);
let data = {
voucherDate: moment().format("YYYY-MM-DDTHH:mm:ss.SSSZ"),
taxConditions: {
taxType: "gross",
},
shippingConditions: {
shippingType: "none",
},
address: {
contactId: user.lex_office_id,
name: rechnung.empfaenger || "----",
street: rechnung.strasse,
city: rechnung.ort,
zip: rechnung.plz,
countryCode: "DE",
supplement: rechnung.zusatzzeile,
},
lineItems: [] as LineItem[],
totalPrice: {
currency: "EUR",
totalGrossAmount: rechnung.betrag,
},
title: "Rechnung",
introduction:
`Ihren Energieausweis (Ausweis ID ${ausweis.id}) stellen wir Ihnen hiermit in Rechnung`,
remark: "Vielen Dank für Ihren Einkauf.",
};
if (rechnung.status == Enums.Rechnungsstatus.paid) {
const types: Record<Enums.Bezahlmethoden, string> = {
[Enums.Bezahlmethoden.paypal]: "PayPal",
[Enums.Bezahlmethoden.creditcard]: "Kreditkarte",
[Enums.Bezahlmethoden.sofort]: "Sofortüberweisung",
[Enums.Bezahlmethoden.giropay]: "Giropay",
[Enums.Bezahlmethoden.rechnung]: "Rechnung",
};
const type = types[rechnung.bezahlmethode];
data[
"introduction"
] = `Vielen Dank für Ihren Einkauf (Ausweis ID ${ausweis.id}). Ihre Rechnung haben sie bereits mit ${type} bezahlt.`;
}
const tax_rate = 19;
if (rechnung.betrag == 15) {
// Korrektur alleinstehend
data["lineItems"].push(
createLineItem("Service Korrektur anfordern", rechnung.betrag, 19)
);
} else if (rechnung.betrag == 476) {
data["lineItems"].push(
createLineItem("GEG Berechnung Wohngebäude", rechnung.betrag, 19)
);
} else if (rechnung.betrag > 400 && rechnung.betrag != 476) {
data["lineItems"].push(
createLineItem("Bedarfsausweis Gewerbe", rechnung.betrag, 19)
);
} else if (ausweisart == Enums.Ausweisart.VerbrauchsausweisWohnen) {
if (ausweis.ausweistyp == Enums.AusweisTyp.Standard) {
data["lineItems"].push(
createLineItem(
`Verbrauchsausweis Online (ID ${ausweis.id})`,
PRICES.VerbrauchsausweisWohnen.Standard,
19
)
);
} else if (ausweis.ausweistyp == Enums.AusweisTyp.Beratung) {
data["lineItems"].push(
createLineItem(
`Verbrauchsausweis Online (Vorprüfung negativ) (ID ${ausweis.id})`,
PRICES.VerbrauchsausweisWohnen.Beratung,
19
)
);
} else if (ausweis.ausweistyp == Enums.AusweisTyp.Offline) {
data["lineItems"].push(
createLineItem(
`Verbrauchsausweis Offline (ID ${ausweis.id})`,
PRICES.VerbrauchsausweisWohnen.Offline,
19
)
);
}
} else if (ausweisart == Enums.Ausweisart.BedarfsausweisWohnen) {
if (ausweis.ausweistyp == Enums.AusweisTyp.Standard) {
data["lineItems"].push(
createLineItem(
`Bedarfsausweis Online (ID ${ausweis.id})`,
PRICES.BedarfsausweisWohnen.Standard,
19
)
);
} else if (ausweis.ausweistyp == Enums.AusweisTyp.Beratung) {
data["lineItems"].push(
createLineItem(
`Bedarfsausweis Online (Vorprüfung negativ) (ID ${ausweis.id})`,
PRICES.BedarfsausweisWohnen.Beratung,
19
)
);
} else if (ausweis.ausweistyp == Enums.AusweisTyp.Offline) {
data["lineItems"].push(
createLineItem(
`Bedarfsausweis Offline (ID ${ausweis.id})`,
PRICES.BedarfsausweisWohnen.Offline,
19
)
);
}
} else if (ausweisart == Enums.Ausweisart.VerbrauchsausweisGewerbe) {
if (ausweis.ausweistyp == Enums.AusweisTyp.Standard) {
data["lineItems"].push(
createLineItem(
`Verbrauchsausweis Nichtwohngebäude Online (ID ${ausweis.id})`,
PRICES.VerbrauchsausweisGewerbe.Standard,
19
)
);
} else if (ausweis.ausweistyp == Enums.AusweisTyp.Beratung) {
data["lineItems"].push(
createLineItem(
`Verbrauchsausweis Nichtwohngebäude Online (Vorprüfung negativ) (ID ${ausweis.id})`,
PRICES.VerbrauchsausweisGewerbe.Beratung,
19
)
);
} else if (ausweis.ausweistyp == Enums.AusweisTyp.Offline) {
data["lineItems"].push(
createLineItem(
`Verbrauchsausweis Nichtwohngebäude Offline (ID ${ausweis.id})`,
PRICES.VerbrauchsausweisGewerbe.Offline,
19
)
);
}
}
data = addedServicesNeu(data, rechnung, 19);
const response = await fetch(
"https://api.lexoffice.io/v1/invoices?finalize=true",
{
method: "POST",
headers: {
Accept: `application/json`,
Authorization: `Bearer ${LEX_OFFICE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(data),
}
);
if (response.status == 400) {
throw new Error("Fehler beim Absenden der Request.");
}
const invoice = await response.json();
// Wir müssen den Ausweis nun wieder abfragen, um die erstellte Rechnungsnummer zu bekommen.
const response_id = invoice["id"];
const request = await fetch(
`https://api.lexoffice.io/v1/invoices/${response_id}`,
{
method: "GET",
headers: {
Accept: `application/json`,
Authorization: `Bearer ${LEX_OFFICE_API_KEY}`,
"Content-Type": "application/json",
},
}
);
const abfrage_response = await request.json();
return {
id: invoice["id"],
voucherNumber: abfrage_response["voucherNumber"],
};
}
export async function getLexOfficeVoucherNumber(rechnung: Rechnung) {
const request = await fetch(
`https://api.lexoffice.io/v1/invoices/${rechnung.lex_office_id}`,
{
method: "GET",
headers: {
Accept: `application/json`,
Authorization: `Bearer ${LEX_OFFICE_API_KEY}`,
"Content-Type": "application/json",
},
}
);
const response = await request.json();
return response["voucherNumber"]
}
/**
* Ládt das Reechnungs PDF von LexOffice runter.
*
* @export
* @async
* @param {Rechnung} rechnung
* @returns {ArrayBuffer}
* @throws Falls eine der requests fehlschlägt.
*/
export async function getLexOfficeRechnung(rechnung: Rechnung) {
const response = await fetch(`https://api.lexoffice.io/v1/invoices/${rechnung.lex_office_id}/document`, {
method: "GET",
headers: {
"Accept": `application/json`,
"Authorization": `Bearer ${LEX_OFFICE_API_KEY}`,
"Content-Type": `application/json`
}
})
const body = await response.json()
if(!("documentFileId" in body)){
throw new Error("documentFileId nicht in request.")
}
const file_id = body["documentFileId"];
const fileRequest = await fetch(`https://api.lexoffice.io/v1/files/${file_id}`, {
method: "GET",
headers: {
"Accept": `application/pdf`,
"Authorization": `Bearer ${LEX_OFFICE_API_KEY}`,
}
})
if (fileRequest.status !== 200) {
throw new Error("File request hat nicht funktioniert.")
}
const file = await fileRequest.arrayBuffer()
return file;
}