Verbesserte EÜR-Abrechnung

This commit is contained in:
2026-10-01 13:40:51 +02:00
parent a43fd9cedf
commit bcebe2ebed
7 changed files with 252 additions and 47 deletions
@@ -5,6 +5,7 @@ namespace App\Domains\Event\Actions\CreateIncomeSurplusStatement;
use App\Enumerations\ParticipationType;
use App\Models\CostUnit;
use App\Models\Event;
use App\Models\Invoice;
use App\Providers\PdfGenerateAndDownloadProvider;
use App\Repositories\CostUnitRepository;
use App\ValueObjects\Amount;
@@ -52,8 +53,9 @@ class CreateIncomeSurplusStatementCommand
$eventData = $this->event->toResource()->toArray(new Request());
$income = $this->buildIncome($eventData, $otherIncome);
$expenses = $this->buildExpenses($costUnit);
$donations = $this->buildExpenses($costUnit, donationsOnly: true);
$income = $this->buildIncome($eventData, $otherIncome, $donations['total']);
$result = new Amount($income['total']->getAmount() - $expenses['total']->getAmount(), 'Euro');
@@ -62,6 +64,7 @@ class CreateIncomeSurplusStatementCommand
'createdAt' => new \DateTime()->format('d.m.Y'),
'income' => $income,
'expenses' => $expenses,
'donations' => $donations,
'result' => $result,
'money' => self::money(...),
])->render();
@@ -70,6 +73,7 @@ class CreateIncomeSurplusStatementCommand
$response->filename = 'EUER-' . $this->event->identifier . '.pdf';
$response->income = $income;
$response->expenses = $expenses;
$response->donations = $donations;
$response->result = $result;
$response->pdfContent = PdfGenerateAndDownloadProvider::fromHtml($html, 'portrait');
@@ -87,7 +91,7 @@ class CreateIncomeSurplusStatementCommand
*
* @return array{categories: array<int, array{name: string, total: Amount, entries: array<int, array{name: string, amount: Amount}>}>, total: Amount}
*/
private function buildIncome(array $eventData, float $otherIncome): array
private function buildIncome(array $eventData, float $otherIncome, Amount $donations): array
{
// Beiträge aller Teilnahmearten in einer Zeile: Für die Mittelverwendung zählt, was an Beiträgen
// hereingekommen ist, nicht von wem.
@@ -118,6 +122,8 @@ class CreateIncomeSurplusStatementCommand
'name' => 'Fördermittel (' . self::money($supportRate) . ' € p.P./Tag)',
'amount' => new Amount($eventData['supportPerson']['amount']->getAmount(), 'Euro'),
],
// Gespendete Auslagen: als Ausgabe ihres Typs gebucht, hier die Gegenbuchung.
['name' => 'Spenden', 'amount' => $donations],
];
$categories = [
@@ -134,14 +140,17 @@ class CreateIncomeSurplusStatementCommand
/**
* Die Ausgabenseite: eine Zeile je Ausgabentyp, dazu die Belege für die Anlage.
*
* Mit `$donationsOnly` nur die gespendeten Auslagen in derselben Gliederung -- für die Anlage „Spenden"
* und die Summe unter den Förderungen.
*
* @return array{groups: array<int, array{name: string, sum: Amount, rows: array<int, array{number: string, date: string, purpose: string, amount: Amount}>}>, total: Amount}
*/
private function buildExpenses(CostUnit $costUnit): array
private function buildExpenses(CostUnit $costUnit, bool $donationsOnly = false): array
{
$groups = [];
$total = new Amount(0, 'Euro');
foreach ($this->costUnits->groupExpensesByType($costUnit) as $group) {
foreach ($this->costUnits->groupExpensesByType($costUnit, $donationsOnly) as $group) {
$rows = [];
foreach ($group['invoices'] as $invoice) {
@@ -150,7 +159,7 @@ class CreateIncomeSurplusStatementCommand
'date' => $invoice->created_at?->format('d.m.Y') ?? '',
// Ohne die Anmerkung: dort steht, was die Kassenwart*in beim Korrigieren notiert hat,
// und das gehört auf den Beleg, nicht in den Zweck.
'purpose' => $invoice->purposeText(),
'purpose' => self::purpose($invoice),
'amount' => Amount::fromString($invoice->amount),
];
}
@@ -167,6 +176,25 @@ class CreateIncomeSurplusStatementCommand
return ['groups' => $groups, 'total' => $total];
}
/**
* Der Zweck eines Belegs für die Anlage.
*
* Erfasster oder abgeleiteter Zahlungsgrund wie in der Beleg-Übersicht. Nur wenn es keinen gibt --
* ältere Abrechnungen ohne jede Zweckangabe --, steht dort ersatzweise, wem was erstattet wurde. Bewusst
* hier und nicht in {@see Invoice::purposeText()}: Dort würde der Ersatztext als erfasster Zahlungsgrund
* erscheinen und beim Korrigieren als Änderung protokolliert.
*/
private static function purpose(Invoice $invoice): string
{
$purpose = $invoice->purposeText();
if (trim($purpose) !== '') {
return $purpose;
}
return trim('Auslagenerstattung ' . $invoice->contact_name);
}
/**
* Ein Betrag in deutscher Schreibweise: Punkt als Tausender-, Komma als Dezimaltrennzeichen.
*