Merge origin/dev-4.7.0: Steuer-Feature (Preismodus + DB-gestützte Befreiungsgründe)

Konflikte in den Steuer-Dateien zugunsten der weiterentwickelten lokalen Version
aufgelöst: vat_pricing_mode (Preismodus), DB-gestützte tax_exemption_reasons mit FK,
typisierte Request-DTOs, Event-Steuer-Snapshot. origin/dev-4.7.0 enthielt eine
frühere Teilmenge desselben Features.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 14:34:50 +02:00
co-authored by Claude Opus 4.8
10 changed files with 329 additions and 8 deletions
@@ -9,12 +9,12 @@ use App\Models\Tenant;
class UpdateTenantTaxRequest
{
public function __construct(
public Tenant $tenant,
public bool $taxLiable,
public int $vatRate,
public Tenant $tenant,
public bool $taxLiable,
public int $vatRate,
public ?TaxExemptionReason $exemptionReason,
public ?string $exemptionNote,
public VatPricingMode $vatPricingMode,
public VatPricingMode $vatPricingMode,
)
{
}
@@ -22,7 +22,7 @@ class ManagedTenantTaxUpdateController extends CommonController
vatRate: (int)$request->input('vat_rate', 0),
exemptionReason: TaxExemptionReason::where('slug', $request->input('tax_exemption_reason'))->first(),
exemptionNote: $request->input('tax_exemption_note'),
vatPricingMode: VatPricingMode::tryFrom((string)$request->input('vat_pricing_mode', 'inclusive')) ?? VatPricingMode::Inclusive,
vatPricingMode: VatPricingMode::tryFrom((string) $request->input('vat_pricing_mode', 'inclusive')) ?? VatPricingMode::Inclusive,
));
$response = $action->execute();
@@ -20,7 +20,7 @@ class TenantTaxUpdateController extends CommonController
vatRate: (int)$request->input('vat_rate', 0),
exemptionReason: TaxExemptionReason::where('slug', $request->input('tax_exemption_reason'))->first(),
exemptionNote: $request->input('tax_exemption_note'),
vatPricingMode: VatPricingMode::tryFrom((string)$request->input('vat_pricing_mode', 'inclusive')) ?? VatPricingMode::Inclusive,
vatPricingMode: VatPricingMode::tryFrom((string) $request->input('vat_pricing_mode', 'inclusive')) ?? VatPricingMode::Inclusive,
));
$response = $action->execute();
+2 -2
View File
@@ -44,7 +44,7 @@ class TaxExemptionReason extends CommonModel
*/
public function invoiceText(?string $customNote = null): string
{
return $this->requires_note ? trim((string)$customNote) : $this->invoice_text;
return $this->requires_note ? trim((string) $customNote) : $this->invoice_text;
}
/**
@@ -56,7 +56,7 @@ class TaxExemptionReason extends CommonModel
public static function options(): array
{
return self::all()
->map(static fn(self $reason): array => [
->map(static fn (self $reason): array => [
'value' => $reason->slug,
'label' => $reason->name,
'text' => $reason->requires_note ? '' : $reason->invoice_text,
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace App\Enumerations;
/**
* Preismodus bei bestehender Umsatzsteuerpflicht: entweder ist die USt bereits im konfigurierten Beitrag
* enthalten (Brutto/„Supermarkt") oder sie wird bei der Preisermittlung aufgeschlagen (Netto). Gespeichert
* wird immer der finale Brutto-Beitrag; der Modus beeinflusst nur die Preisermittlung im Anmeldeprozess.
*/
enum VatPricingMode: string
{
case Inclusive = 'inclusive';
case AddOn = 'add_on';
public function label(): string
{
return match ($this) {
self::Inclusive => 'Inklusive Beiträge sind Endpreise (USt enthalten)',
self::AddOn => 'Aufschlagen USt wird auf die Beiträge aufgeschlagen',
};
}
/**
* Optionen für das Frontend: [['value' => ..., 'label' => ...], ...].
*
* @return array<int, array{value: string, label: string}>
*/
public static function options(): array
{
return array_map(
static fn (self $mode): array => [
'value' => $mode->value,
'label' => $mode->label(),
],
self::cases(),
);
}
}