348 lines
8.8 KiB
TypeScript
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;
|
|
} |