Teilnahmerechnungen erstellen

This commit is contained in:
2026-09-03 00:08:46 +02:00
parent a61344395a
commit 9c4c28e566
79 changed files with 4303 additions and 75 deletions
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace App\Models;
use App\Scopes\CommonModel;
/**
* Ein Bild für die Dokumentvorlagen, referenziert als `{asset:name}`.
*
* Liegt in der Datenbank und nicht im Dateisystem, damit ein Release es nicht überschreibt und es mit
* jedem Dump mitwandert. `data` hält den base64-Payload, der für das PDF ohnehin als Data-URI gebraucht
* wird.
*
* Die Namen vergibt die verwaltende Person selbst -- es gibt keine fachlich vorbelegten Slots.
*
* @property string $name
* @property string|null $label
* @property string $mime
* @property string $data
*/
class DocumentAsset extends CommonModel
{
protected $table = 'document_assets';
protected $fillable = [
'name',
'label',
'mime',
'data',
];
/** Fertige Data-URI zum direkten Einsetzen in ein `src`-Attribut. */
public function toDataUri(): string
{
return sprintf('data:%s;base64,%s', $this->mime, $this->data);
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace App\Models;
use App\Scopes\CommonModel;
use Illuminate\Database\Eloquent\Collection;
/**
* Ein Block einer Dokumentvorlage. Bewusst `CommonModel` (kein `SiteScope`): die Vorlage gilt app-weit,
* tenant-spezifisch sind nur die eingesetzten Werte.
*
* @property string $document_type
* @property string $block
* @property string|null $content
* @property int $sort_order
* @property bool $editable
*/
class DocumentTemplate extends CommonModel
{
public const string TYPE_PARTICIPANT_INVOICE = 'participant_invoice';
/** Seitengerüst -- enthält die `{block:...}`-Platzhalter und bestimmt damit die Anordnung. */
public const string BLOCK_LAYOUT = 'layout';
/** CSS des Dokuments. */
public const string BLOCK_STYLE = 'style';
/** Der generierte Teil (Positionstabelle, Summen, Schlusssatz) -- nicht editierbar. */
public const string BLOCK_BODY = 'body';
protected $table = 'document_templates';
protected $fillable = [
'document_type',
'block',
'content',
'sort_order',
'editable',
];
protected $casts = [
'sort_order' => 'integer',
'editable' => 'boolean',
];
/**
* Alle Blöcke einer Dokumentart, nach Block-Slug adressierbar.
*
* @return Collection<string, self>
*/
public static function forType(string $documentType): Collection
{
return self::where('document_type', $documentType)
->orderBy('sort_order')
->get()
->keyBy('block');
}
}
+2
View File
@@ -45,6 +45,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
* @property float $support_flat
* @property int $alcoholics_age
* @property boolean $archived
* @property string|null $invoice_key
*/
class Event extends InstancedModel
{
@@ -90,6 +91,7 @@ class Event extends InstancedModel
'participation_options',
'addons',
'invoice_key',
];
protected $casts = [
+6
View File
@@ -26,12 +26,15 @@ class EventParticipant extends InstancedModel
'event_id',
'user_id',
'identifier',
'invoice_sequence',
'firstname',
'lastname',
'nickname',
'participation_type',
'fee_type',
'sibling_reduction',
'local_group',
'birthday',
@@ -91,6 +94,9 @@ class EventParticipant extends InstancedModel
'amount' => AmountCast::class,
'amount_paid' => AmountCast::class,
'payment_options' => 'array',
'invoice_sequence' => 'integer',
'sibling_reduction' => 'boolean',
];
/*
+38
View File
@@ -3,6 +3,7 @@
namespace App\Models;
use App\Scopes\CommonModel;
use App\Support\Text;
/**
* @property string $slug
@@ -12,6 +13,14 @@ use App\Scopes\CommonModel;
* @property string $account_name
* @property string $account_iban
* @property string $account_bic
* @property string|null $invoice_sender_name
* @property string|null $address_1
* @property string|null $address_2
* @property string|null $address_3
* @property string|null $phone
* @property string|null $tax_number
* @property string|null $vat_id
* @property string|null $invoice_prefix
* @property string $city
* @property string $postcode
* @property boolean $download_exports
@@ -29,16 +38,42 @@ use App\Scopes\CommonModel;
*/
class Tenant extends CommonModel
{
/**
* Ohne ausdrückliche Angabe ist das Präfix der Rechnungsnummer der großgeschriebene Slug
* (`wm` -> `WM`). Hier und nicht als DB-Default, weil er sich aus einer anderen Spalte ableitet.
*/
protected static function booted(): void
{
static::creating(static function (self $tenant): void {
$tenant->invoice_prefix ??= strtoupper((string) $tenant->slug);
});
}
public static function getTempDirectory() : string {
return app('tenant')->slug . '/temp-data/';
}
/**
* Bezeichnung des Rechnungsstellers.
*
* `name` ist ein internes Kürzel ("Wilde Möhre"); auf einer Rechnung steht die vollständige
* Bezeichnung mit Rechtsform. Ohne eigene Angabe bleibt es beim Namen des Mandanten.
*/
public function invoiceSenderName() : string {
return Text::nullIfBlank($this->invoice_sender_name) ?? (string) $this->name;
}
public const PRIMARY_TENANT_NAME = 'LV';
protected $fillable = [
'slug',
'name',
'invoice_sender_name',
'address_1',
'address_2',
'address_3',
'email',
'email_finance',
'phone',
'url',
'account_name',
'account_iban',
@@ -57,6 +92,9 @@ class Tenant extends CommonModel
'tax_exemption_reason',
'tax_exemption_note',
'vat_pricing_mode',
'tax_number',
'vat_id',
'invoice_prefix',
];
protected $casts = [