E3: Rechtstext-Versionierung in der DB (legal_documents)
Jede veroeffentlichte Fassung eines Rechtstexts ist eine Zeile mit type/version/effective_from — alte Fassungen sind unloeschbar, zukuenftige werden erst ab Stichtag wirksam (LegalDocument::current). Seeder legt die fuenf aktuellen Fassungen an (AGB 0.6, Datenschutz 0.3, Richtlinien 0.1, Meldeverfahren 0.1, Impressum 0.1). Die Unternehmer-Bestaetigung (T1neu) referenziert jetzt die konkrete Fassung (legal_document_id am Buchungsdatensatz, restrictOnDelete); AGB-Seite und Buchungs-Modal ziehen Version/Stand aus der DB. config/legal.php bleibt reiner Fallback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
dc2b2b0d7d
commit
7510e7633e
12 changed files with 294 additions and 11 deletions
|
|
@ -25,6 +25,7 @@ class BookingConfirmation extends Model
|
||||||
'booking_type',
|
'booking_type',
|
||||||
'booking_ref',
|
'booking_ref',
|
||||||
'interval',
|
'interval',
|
||||||
|
'legal_document_id',
|
||||||
'agb_version',
|
'agb_version',
|
||||||
'ip',
|
'ip',
|
||||||
'confirmed_at',
|
'confirmed_at',
|
||||||
|
|
@ -42,6 +43,15 @@ class BookingConfirmation extends Model
|
||||||
return $this->belongsTo(User::class);
|
return $this->belongsTo(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Die akzeptierte AGB-Fassung (E3: "agb_version_id" am
|
||||||
|
* Buchungsdatensatz); `agb_version` hält den Klartext redundant fest.
|
||||||
|
*/
|
||||||
|
public function legalDocument(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(LegalDocument::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function scopeRecentFor(Builder $query, User $user): Builder
|
public function scopeRecentFor(Builder $query, User $user): Builder
|
||||||
{
|
{
|
||||||
return $query
|
return $query
|
||||||
|
|
|
||||||
75
app/Models/LegalDocument.php
Normal file
75
app/Models/LegalDocument.php
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Database\Factories\LegalDocumentFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Veröffentlichte Fassung eines Rechtstexts (E3-Versionierung).
|
||||||
|
*
|
||||||
|
* Fassungen werden nie gelöscht oder überschrieben — eine neue Fassung ist
|
||||||
|
* eine neue Zeile mit späterem `effective_from`. `current()` liefert die
|
||||||
|
* heute gültige Fassung; config/legal.php bleibt Fallback für Umgebungen,
|
||||||
|
* in denen der Seeder (noch) nicht gelaufen ist.
|
||||||
|
*/
|
||||||
|
class LegalDocument extends Model
|
||||||
|
{
|
||||||
|
/** @use HasFactory<LegalDocumentFactory> */
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
public const TYPE_AGB = 'agb';
|
||||||
|
|
||||||
|
public const TYPE_DATENSCHUTZ = 'datenschutz';
|
||||||
|
|
||||||
|
public const TYPE_RICHTLINIEN = 'richtlinien';
|
||||||
|
|
||||||
|
public const TYPE_MELDEVERFAHREN = 'meldeverfahren';
|
||||||
|
|
||||||
|
public const TYPE_IMPRESSUM = 'impressum';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'type',
|
||||||
|
'version',
|
||||||
|
'effective_from',
|
||||||
|
'note',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'effective_from' => 'date',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Die heute gültige Fassung eines Rechtstexts (jüngstes effective_from
|
||||||
|
* in der Vergangenheit; zukünftige Fassungen zählen noch nicht).
|
||||||
|
*/
|
||||||
|
public static function current(string $type): ?self
|
||||||
|
{
|
||||||
|
return static::query()
|
||||||
|
->where('type', $type)
|
||||||
|
->where('effective_from', '<=', today())
|
||||||
|
->orderByDesc('effective_from')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Versionsstring der gültigen AGB-Fassung — mit config/legal.php als
|
||||||
|
* Fallback (Übergangslösung, bis der Seeder überall gelaufen ist).
|
||||||
|
*/
|
||||||
|
public static function currentAgbVersion(): string
|
||||||
|
{
|
||||||
|
return static::current(self::TYPE_AGB)?->version
|
||||||
|
?? (string) config('legal.agb_version');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function effectiveFromFormatted(): string
|
||||||
|
{
|
||||||
|
return Carbon::parse($this->effective_from)->locale('de')->isoFormat('D. MMMM YYYY');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,10 +7,10 @@ return [
|
||||||
| Rechtstext-Versionen (Übergangslösung bis zum legal_documents-System, E3)
|
| Rechtstext-Versionen (Übergangslösung bis zum legal_documents-System, E3)
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Zentrale Quelle für die aktuell veröffentlichte AGB-Fassung. Wird auf
|
| Fallback für die aktuell veröffentlichte AGB-Fassung. Verbindliche
|
||||||
| der AGB-Seite angezeigt und bei jeder Unternehmer-Bestätigung im
|
| Quelle ist seit 06.07. die legal_documents-Tabelle (E3-Versionierung,
|
||||||
| Checkout protokolliert (booking_confirmations.agb_version, T1neu).
|
| LegalDocument::current('agb') / LegalDocumentSeeder); diese Werte
|
||||||
| Bei einer neuen AGB-Fassung hier Version + Stand aktualisieren.
|
| greifen nur, solange der Seeder noch nicht gelaufen ist.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
||||||
37
database/factories/LegalDocumentFactory.php
Normal file
37
database/factories/LegalDocumentFactory.php
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\LegalDocument;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends Factory<LegalDocument>
|
||||||
|
*/
|
||||||
|
class LegalDocumentFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'type' => LegalDocument::TYPE_AGB,
|
||||||
|
'version' => '0.'.fake()->unique()->numberBetween(1, 999),
|
||||||
|
'effective_from' => today()->subDays(fake()->numberBetween(1, 365)),
|
||||||
|
'note' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function agb(): static
|
||||||
|
{
|
||||||
|
return $this->state(['type' => LegalDocument::TYPE_AGB]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function effectiveFrom(string $date): static
|
||||||
|
{
|
||||||
|
return $this->state(['effective_from' => $date]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rechtstext-Versionierung (E3): jede veröffentlichte Fassung eines
|
||||||
|
* Rechtstexts ist eine Zeile — alte Fassungen werden nie gelöscht, damit
|
||||||
|
* protokollierte Zustimmungen (booking_confirmations) referenzierbar bleiben.
|
||||||
|
* Der Seiteninhalt selbst wird weiterhin in den Blade-Views gepflegt; die
|
||||||
|
* Tabelle ist die verbindliche Quelle für Version + Gültigkeitsbeginn.
|
||||||
|
*/
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('legal_documents', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
// agb | datenschutz | richtlinien | meldeverfahren | impressum
|
||||||
|
$table->string('type', 40);
|
||||||
|
$table->string('version', 40);
|
||||||
|
$table->date('effective_from');
|
||||||
|
// Herkunft/Anmerkung, z. B. "docs/legal AGB v0.6, anwaltsreif"
|
||||||
|
$table->string('note')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['type', 'version']);
|
||||||
|
$table->index(['type', 'effective_from']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('legal_documents');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* E3: AGB-Zustimmung referenziert die konkrete Rechtstext-Fassung
|
||||||
|
* ("agb_version_id" am Buchungsdatensatz). Der bisherige Klartext
|
||||||
|
* `agb_version` bleibt als redundante, migrationsfeste Angabe erhalten.
|
||||||
|
*/
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('booking_confirmations', function (Blueprint $table) {
|
||||||
|
// restrictOnDelete: Fassungen, auf die Zustimmungen verweisen,
|
||||||
|
// sind unlöschbar (Rechtstext-Fassungen werden ohnehin nie gelöscht).
|
||||||
|
$table->foreignId('legal_document_id')
|
||||||
|
->nullable()
|
||||||
|
->after('interval')
|
||||||
|
->constrained('legal_documents')
|
||||||
|
->restrictOnDelete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('booking_confirmations', function (Blueprint $table) {
|
||||||
|
$table->dropConstrainedForeignId('legal_document_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -21,6 +21,7 @@ class DatabaseSeeder extends Seeder
|
||||||
AdminPresetSeeder::class,
|
AdminPresetSeeder::class,
|
||||||
PlanSeeder::class,
|
PlanSeeder::class,
|
||||||
PaymentOptionSeeder::class,
|
PaymentOptionSeeder::class,
|
||||||
|
LegalDocumentSeeder::class,
|
||||||
// CategorySeeder::class,
|
// CategorySeeder::class,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
33
database/seeders/LegalDocumentSeeder.php
Normal file
33
database/seeders/LegalDocumentSeeder.php
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\LegalDocument;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aktuelle Rechtstext-Fassungen (Stand 03.07.2026, Quelle docs/legal).
|
||||||
|
* Neue Fassungen werden als neue Zeilen ergänzt (nie überschreiben) —
|
||||||
|
* updateOrCreate greift über [type, version], damit der Seeder idempotent
|
||||||
|
* bleibt, ohne bestehende Fassungen zu verändern.
|
||||||
|
*/
|
||||||
|
class LegalDocumentSeeder extends Seeder
|
||||||
|
{
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$documents = [
|
||||||
|
[LegalDocument::TYPE_AGB, '0.6', '2026-07-03', 'B2B-Fassung (E2), anwaltsreif; externe Vorprüfung 11/11 bestätigt'],
|
||||||
|
[LegalDocument::TYPE_DATENSCHUTZ, '0.3', '2026-07-03', 'B2B-angepasst; host-aware Bausteine Hub/Spokes'],
|
||||||
|
[LegalDocument::TYPE_RICHTLINIEN, '0.1', '2026-07-03', 'Rahmentext; Kategorien aus rejection_categories'],
|
||||||
|
[LegalDocument::TYPE_MELDEVERFAHREN, '0.1', '2026-07-03', 'Drei Wege: DSA / DSGVO / Depublikation'],
|
||||||
|
[LegalDocument::TYPE_IMPRESSUM, '0.1', '2026-07-03', 'DDG-korrigiert, Verbund-Absatz, DSA-Kontaktpunkt'],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($documents as [$type, $version, $effectiveFrom, $note]) {
|
||||||
|
LegalDocument::query()->updateOrCreate(
|
||||||
|
['type' => $type, 'version' => $version],
|
||||||
|
['effective_from' => $effectiveFrom, 'note' => $note],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ use App\Enums\UserPaymentOptionStatus;
|
||||||
use App\Enums\VatTreatment;
|
use App\Enums\VatTreatment;
|
||||||
use App\Exceptions\InsufficientCreditsException;
|
use App\Exceptions\InsufficientCreditsException;
|
||||||
use App\Models\BookingConfirmation;
|
use App\Models\BookingConfirmation;
|
||||||
|
use App\Models\LegalDocument;
|
||||||
use App\Models\Plan;
|
use App\Models\Plan;
|
||||||
use App\Models\UserPaymentOption;
|
use App\Models\UserPaymentOption;
|
||||||
use App\Services\Billing\CreditPricingService;
|
use App\Services\Billing\CreditPricingService;
|
||||||
|
|
@ -58,12 +59,15 @@ new #[Layout('components.layouts.app'), Title('Buchungen & Add-ons')] class exte
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$agb = LegalDocument::current(LegalDocument::TYPE_AGB);
|
||||||
|
|
||||||
BookingConfirmation::query()->create([
|
BookingConfirmation::query()->create([
|
||||||
'user_id' => auth()->id(),
|
'user_id' => auth()->id(),
|
||||||
'booking_type' => $this->booking['type'],
|
'booking_type' => $this->booking['type'],
|
||||||
'booking_ref' => $this->booking['ref'],
|
'booking_ref' => $this->booking['ref'],
|
||||||
'interval' => $this->booking['interval'],
|
'interval' => $this->booking['interval'],
|
||||||
'agb_version' => (string) config('legal.agb_version'),
|
'legal_document_id' => $agb?->id,
|
||||||
|
'agb_version' => $agb?->version ?? (string) config('legal.agb_version'),
|
||||||
'ip' => request()->ip(),
|
'ip' => request()->ip(),
|
||||||
'confirmed_at' => now(),
|
'confirmed_at' => now(),
|
||||||
]);
|
]);
|
||||||
|
|
@ -991,7 +995,7 @@ new #[Layout('components.layouts.app'), Title('Buchungen & Add-ons')] class exte
|
||||||
<p class="text-[11.5px] text-[color:var(--color-ink-3)] m-0 ps-0">
|
<p class="text-[11.5px] text-[color:var(--color-ink-3)] m-0 ps-0">
|
||||||
{{ __('Das Angebot richtet sich ausschließlich an Unternehmen, Selbstständige und Agenturen.') }}
|
{{ __('Das Angebot richtet sich ausschließlich an Unternehmen, Selbstständige und Agenturen.') }}
|
||||||
<a href="{{ route('agb', ['edition' => 'de']) }}" target="_blank" rel="noopener" class="underline underline-offset-2 hover:text-[color:var(--color-ink)]">
|
<a href="{{ route('agb', ['edition' => 'de']) }}" target="_blank" rel="noopener" class="underline underline-offset-2 hover:text-[color:var(--color-ink)]">
|
||||||
{{ __('AGB (Version :version)', ['version' => config('legal.agb_version')]) }}
|
{{ __('AGB (Version :version)', ['version' => \App\Models\LegalDocument::currentAgbVersion()]) }}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,14 @@
|
||||||
$hubUrl = rtrim((string) config('domains.domain_portal_url', config('app.url')), '/');
|
$hubUrl = rtrim((string) config('domains.domain_portal_url', config('app.url')), '/');
|
||||||
$hubLabel = str_replace(['https://', 'http://'], '', $hubUrl);
|
$hubLabel = str_replace(['https://', 'http://'], '', $hubUrl);
|
||||||
|
|
||||||
// Zentrale Versionsangabe (config/legal.php) — dieselbe Version wird bei
|
// Verbindliche Versionsangabe aus der Rechtstext-Versionierung (E3,
|
||||||
// der Unternehmer-Bestätigung im Checkout protokolliert (T1neu).
|
// legal_documents) — dieselbe Fassung wird bei der Unternehmer-
|
||||||
$agbVersion = (string) config('legal.agb_version');
|
// Bestätigung im Checkout protokolliert (T1neu). config/legal.php
|
||||||
$agbStand = \Illuminate\Support\Carbon::parse((string) config('legal.agb_effective_from'))->locale('de')->isoFormat('D. MMMM YYYY');
|
// bleibt Fallback, falls der Seeder noch nicht gelaufen ist.
|
||||||
|
$agbDocument = \App\Models\LegalDocument::current(\App\Models\LegalDocument::TYPE_AGB);
|
||||||
|
$agbVersion = $agbDocument?->version ?? (string) config('legal.agb_version');
|
||||||
|
$agbStand = $agbDocument?->effectiveFromFormatted()
|
||||||
|
?? \Illuminate\Support\Carbon::parse((string) config('legal.agb_effective_from'))->locale('de')->isoFormat('D. MMMM YYYY');
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,14 @@
|
||||||
use App\Enums\UserPaymentOptionStatus;
|
use App\Enums\UserPaymentOptionStatus;
|
||||||
use App\Models\BillingAddress;
|
use App\Models\BillingAddress;
|
||||||
use App\Models\BookingConfirmation;
|
use App\Models\BookingConfirmation;
|
||||||
|
use App\Models\LegalDocument;
|
||||||
use App\Models\Plan;
|
use App\Models\Plan;
|
||||||
use App\Models\SinglePurchase;
|
use App\Models\SinglePurchase;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\UserPaymentOption;
|
use App\Models\UserPaymentOption;
|
||||||
use App\Services\Billing\CreditWalletService;
|
use App\Services\Billing\CreditWalletService;
|
||||||
use App\Services\Billing\StripeCheckoutService;
|
use App\Services\Billing\StripeCheckoutService;
|
||||||
|
use Database\Seeders\LegalDocumentSeeder;
|
||||||
use Database\Seeders\RolesAndPermissionsSeeder;
|
use Database\Seeders\RolesAndPermissionsSeeder;
|
||||||
use Livewire\Volt\Volt as LivewireVolt;
|
use Livewire\Volt\Volt as LivewireVolt;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
@ -296,6 +298,8 @@ test('checking the entrepreneur box records a booking confirmation with the agb
|
||||||
/** @var TestCase $this */
|
/** @var TestCase $this */
|
||||||
// T1neu (AGB v0.6 § 1 Abs. 4): Die Pflicht-Checkbox protokolliert
|
// T1neu (AGB v0.6 § 1 Abs. 4): Die Pflicht-Checkbox protokolliert
|
||||||
// Zeitpunkt + AGB-Version — das B2B-Gate stützt sich auf diesen Nachweis.
|
// Zeitpunkt + AGB-Version — das B2B-Gate stützt sich auf diesen Nachweis.
|
||||||
|
// E3: Zusätzlich wird die konkrete Rechtstext-Fassung referenziert.
|
||||||
|
$this->seed(LegalDocumentSeeder::class);
|
||||||
$user = bookingsTestCustomer();
|
$user = bookingsTestCustomer();
|
||||||
BillingAddress::factory()->create(['user_id' => $user->id]);
|
BillingAddress::factory()->create(['user_id' => $user->id]);
|
||||||
config()->set('billing.single_pm_stripe_price_id', 'price_test_single_pm');
|
config()->set('billing.single_pm_stripe_price_id', 'price_test_single_pm');
|
||||||
|
|
@ -307,10 +311,12 @@ test('checking the entrepreneur box records a booking confirmation with the agb
|
||||||
->set('entrepreneurConfirmed', true);
|
->set('entrepreneurConfirmed', true);
|
||||||
|
|
||||||
$confirmation = BookingConfirmation::query()->sole();
|
$confirmation = BookingConfirmation::query()->sole();
|
||||||
|
$agb = LegalDocument::current(LegalDocument::TYPE_AGB);
|
||||||
|
|
||||||
expect($confirmation->user_id)->toBe($user->id);
|
expect($confirmation->user_id)->toBe($user->id);
|
||||||
expect($confirmation->booking_type)->toBe('single_pm');
|
expect($confirmation->booking_type)->toBe('single_pm');
|
||||||
expect($confirmation->agb_version)->toBe((string) config('legal.agb_version'));
|
expect($confirmation->legal_document_id)->toBe($agb->id);
|
||||||
|
expect($confirmation->agb_version)->toBe($agb->version);
|
||||||
expect($confirmation->confirmed_at)->not->toBeNull();
|
expect($confirmation->confirmed_at)->not->toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
42
tests/Feature/Legal/LegalDocumentTest.php
Normal file
42
tests/Feature/Legal/LegalDocumentTest.php
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\LegalDocument;
|
||||||
|
use Database\Seeders\LegalDocumentSeeder;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
test('current returns the newest effective version and ignores future ones', function () {
|
||||||
|
LegalDocument::factory()->agb()->create(['version' => '0.5', 'effective_from' => '2026-06-01']);
|
||||||
|
$current = LegalDocument::factory()->agb()->create(['version' => '0.6', 'effective_from' => '2026-07-03']);
|
||||||
|
// Zukünftige Fassung: veröffentlicht, aber noch nicht wirksam.
|
||||||
|
LegalDocument::factory()->agb()->create(['version' => '0.7', 'effective_from' => now()->addMonth()->toDateString()]);
|
||||||
|
|
||||||
|
expect(LegalDocument::current(LegalDocument::TYPE_AGB)?->id)->toBe($current->id);
|
||||||
|
expect(LegalDocument::currentAgbVersion())->toBe('0.6');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the agb version falls back to the config while the table is empty', function () {
|
||||||
|
config()->set('legal.agb_version', '9.9');
|
||||||
|
|
||||||
|
expect(LegalDocument::current(LegalDocument::TYPE_AGB))->toBeNull();
|
||||||
|
expect(LegalDocument::currentAgbVersion())->toBe('9.9');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the seeder registers all five legal documents idempotently', function () {
|
||||||
|
/** @var TestCase $this */
|
||||||
|
$this->seed(LegalDocumentSeeder::class);
|
||||||
|
$this->seed(LegalDocumentSeeder::class); // Wiederholung darf nichts duplizieren.
|
||||||
|
|
||||||
|
expect(LegalDocument::query()->count())->toBe(5);
|
||||||
|
expect(LegalDocument::currentAgbVersion())->toBe('0.6');
|
||||||
|
expect(LegalDocument::current(LegalDocument::TYPE_DATENSCHUTZ)?->version)->toBe('0.3');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the agb page renders version and effective date from the database', function () {
|
||||||
|
/** @var TestCase $this */
|
||||||
|
LegalDocument::factory()->agb()->create(['version' => '0.6', 'effective_from' => '2026-07-03']);
|
||||||
|
|
||||||
|
$this->get('https://pressekonto.test/de/agb')
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertSeeText('Version 0.6')
|
||||||
|
->assertSeeText('Stand: 3. Juli 2026');
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue