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; 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.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; }