134 lines
6.1 KiB
Svelte
134 lines
6.1 KiB
Svelte
<script lang="ts">
|
|
import { Aufnahme, BedarfsausweisWohnen, Enums, Objekt, Provisionen, Rechnung, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "#lib/server/prisma.js";
|
|
import moment from "moment";
|
|
import { DatePicker } from "@svelte-plugins/datepicker"
|
|
export let bestellungen: (Rechnung & {
|
|
ausweis: (VerbrauchsausweisWohnen | BedarfsausweisWohnen | VerbrauchsausweisGewerbe) & { aufnahme: Aufnahme & { objekt: Objekt }}
|
|
})[];
|
|
export let provisionen: Provisionen[];
|
|
export let email: string;
|
|
export let startdatum: Date;
|
|
export let enddatum: Date;
|
|
|
|
const bestellungenNachMonat: Record<string, (typeof bestellungen)> = {};
|
|
for (const bestellung of bestellungen) {
|
|
const monat = moment(bestellung.created_at).format("Y-MM");
|
|
if (monat in bestellungenNachMonat) {
|
|
bestellungenNachMonat[monat].push(bestellung)
|
|
} else {
|
|
bestellungenNachMonat[monat] = [bestellung]
|
|
}
|
|
}
|
|
|
|
// Wir brauchen alle Monate zwischen dem ersten Mal, dass der partner_code benutzt wurde bis zum heutigen Zeitpunkt.
|
|
const months: Record<string, string> = {
|
|
"01": "Januar", "02": "Februar", "03": "März", "04": "April",
|
|
"05": "Mai", "06": "Juni", "07": "Juli", "08": "August",
|
|
"09": "September", "10": "Oktober", "11": "November", "12": "Dezember"
|
|
};
|
|
|
|
function getMonthlyPeriods(from: Date, to: Date): moment.Moment[] {
|
|
const start = moment(from).startOf('month');
|
|
const end = moment(to).endOf('month');
|
|
|
|
const monthsArray: moment.Moment[] = [];
|
|
const current = start.clone();
|
|
|
|
while (current.isBefore(end)) {
|
|
monthsArray.push(current.clone());
|
|
current.add(1, 'month');
|
|
}
|
|
|
|
return monthsArray.reverse(); // Most recent month first
|
|
}
|
|
|
|
const periods = getMonthlyPeriods(startdatum, enddatum)
|
|
|
|
|
|
let isOpen = false;
|
|
$: formatiertesStartDatum = moment(startdatum).format("DD.MM.YYYY");
|
|
$: formatiertesEndDatum = moment(enddatum).format("DD.MM.YYYY");
|
|
function toggleDatePicker() {
|
|
isOpen = !isOpen;
|
|
}
|
|
|
|
const onChange = ({ startDate, endDate }: { startDate: number, endDate: number }) => {
|
|
window.location.href = `/dashboard/abrechnung?start=${moment(startDate).format("YYYY-MM-DD")}&end=${moment(endDate).format("YYYY-MM-DD")}`;
|
|
};
|
|
</script>
|
|
|
|
<div class="fixed top-0 left-0 right-0 bg-white p-4 shadow z-10">
|
|
<div class="flex justify-between items-center">
|
|
<DatePicker bind:isOpen bind:startDate={startdatum} bind:endDate={enddatum} isRange={true} onDateChange={onChange} isMultipane={true}>
|
|
<input type="text" class="w-min" readonly value={`${formatiertesStartDatum} - ${formatiertesEndDatum}`} on:click={toggleDatePicker} />
|
|
</DatePicker>
|
|
<p>Abrechnungsübersicht für <strong>{email}</strong></p>
|
|
</div>
|
|
</div>
|
|
|
|
<main class="my-24 flex flex-col justify-center max-w-6xl mx-auto px-4">
|
|
{#if !bestellungen || bestellungen.length === 0}
|
|
<p class="text-center text-gray-500">Keine Bestellungen gefunden.</p>
|
|
{/if}
|
|
{#each periods as dt}
|
|
{@const jahrMonat = dt.format("Y-MM")}
|
|
{#if jahrMonat in bestellungenNachMonat && bestellungenNachMonat[jahrMonat].length > 0}
|
|
<!-- Echo dropdown foreach month. -->
|
|
{@const provisionMonat = bestellungenNachMonat[jahrMonat].reduce((acc, bestellung) => {
|
|
return acc + (provisionen.find((p) => p.ausweisart === bestellung.ausweis.ausweisart)?.provision_betrag || 0);
|
|
}, 0) * 1.19}
|
|
|
|
<details class="group" open>
|
|
<summary class="flex justify-between items-center cursor-pointer p-4 bg-gray-100 hover:bg-gray-200">
|
|
<span class="font-semibold">{moment(dt).format("MMMM YYYY")}</span>
|
|
<div class="flex flex-row gap-4 items-center">
|
|
<span class="text-gray-500">{provisionMonat.toFixed(2)} €</span>
|
|
<svg class="w-4 h-4 transition-transform duration-300 group-open:rotate-180" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="6 9 12 15 18 9"></polyline>
|
|
</svg>
|
|
</div>
|
|
</summary>
|
|
<div class="p-4">
|
|
<table class="w-full mb-4 border-collapse border border-gray-300">
|
|
<thead>
|
|
<tr class="bg-primary text-white w-full">
|
|
<td class="text-center p-2 font-bold">ID</td>
|
|
<td class="text-center p-2 font-bold">Datum</td>
|
|
<td class="text-center p-2 font-bold">Ausweis</td>
|
|
<td class="text-center p-2 font-bold">Provision in %</td>
|
|
<td class="text-center p-2 font-bold">Betrag Netto</td>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="text-sm">
|
|
{#each bestellungenNachMonat[jahrMonat] as bestellung}
|
|
{@const provisionBestellung = provisionen.find((p) => p.ausweisart === bestellung.ausweis.ausweisart)}
|
|
<tr class="border-b border-gray-300 hover:bg-gray-100">
|
|
<td class="text-center py-2 px-4 w-24" style="font-family: monospace;">{bestellung.ausweis.id}</td>
|
|
<td class="text-center py-2 font-bold w-32">{moment(bestellung.created_at).format("DD.MM.YYYY HH:mm")}</td>
|
|
<td class="text-center py-2 w-32">{bestellung.ausweis.ausweisart}</td>
|
|
<td class="text-center py-2 w-32">{provisionBestellung?.provision_prozent || 0} %</td>
|
|
<td class="text-right py-2 w-24" style="font-family: monospace;">{provisionBestellung?.provision_betrag.toFixed(2)} €</td>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</details>
|
|
{:else if !bestellungenNachMonat[jahrMonat] || bestellungenNachMonat[jahrMonat].length === 0}
|
|
<details class="group">
|
|
<summary class="flex justify-between items-center cursor-pointer p-4 bg-gray-100 hover:bg-gray-200">
|
|
<span class="font-semibold">{moment(dt).format("MMMM YYYY")}</span>
|
|
<div class="flex flex-row gap-4 items-center">
|
|
<span class="text-gray-500">Keine Bestellungen gefunden</span>
|
|
<svg class="w-4 h-4 transition-transform duration-300 group-open:rotate-180" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="6 9 12 15 18 9"></polyline>
|
|
</svg>
|
|
</div>
|
|
</summary>
|
|
<div class="p-4 border-t border-gray-200">
|
|
<p class="text-gray-500">Für diesen Monat liegen uns keine Bestellungen über ihren Resellercode vor.</p>
|
|
</div>
|
|
</details>
|
|
{/if}
|
|
{/each}
|
|
</main> |