mivita/tests/Feature/PreviewInvoiceCommandTest.php
Kevin dcd0384878 Abo Einmalprodukte: Phasen 5-7, konfigurierbare Sperrfristen, Lokalisierung & Dashboard
Rechnung/Lieferschein:
- PDF-Gruppierung Abo- vs. Einmal-Artikel (is_abo_addon), rueckwaertskompatibel
- Vorschau-Command sales:preview-invoice (umgeht Test-System-Gate, ohne Rechnungsnummer)

Reminder-Mail (vorbereitet, Cron inaktiv):
- MailAboExecutionReminder + Template, abo:send-execution-reminder, Spalte onetime_reminder_sent_for
- Berater -> Sales Center, Kunde -> Portal; Vorschau abo:preview-reminder
- Datum-Fix: formatDateDB() statt formatDate() (ICU-Format)

Sperrfristen konfigurierbar (Admin-Settings):
- abo-lock-days-change (Liefertag + Abo-Produkte), abo-lock-days-pause-cancel
- Meldungen mit :lock-Platzhalter; Pausieren/Kuendigen vom Change-Lock entkoppelt
- Abo-Produkt-Aenderungen im Lock-Fenster server- + clientseitig eingefroren
- Selbstbedienung: Add-only/Lock nach $view (kein Admin-Bypass), Stale-Cache-Fix beim Mengen-Update

Dashboard:
- Einmalprodukte-Card + Team-Abo-KPI-Card, News-Card responsiv (col-xl-6)

Lokalisierung:
- Sprachdateien en/es/fr vollstaendig mit de synchronisiert (inkl. marketingplan/portal)
- LocalizationSyncTest sichert Key-Gleichheit ab

Tests: AboOneTimeInvoiceGrouping, AboExecutionReminder, AboLockDaysSetting,
AboOnetimeDashboardAndMail, PreviewInvoiceCommand, TeamAboOverview + erweiterte View-Tests

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 13:38:11 +00:00

75 lines
2.5 KiB
PHP

<?php
use App\Models\ShoppingOrder;
use App\Models\ShoppingUser;
use App\Models\UserInvoice;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
uses(Tests\TestCase::class, RefreshDatabase::class);
beforeEach(fn () => makeShopEnv());
function makePreviewOrder(): ShoppingOrder
{
$shoppingUser = ShoppingUser::forceCreate([
'auth_user_id' => 0,
'billing_firstname' => 'Max',
'billing_lastname' => 'Mustermann',
'billing_email' => 'preview@example.com',
'billing_country_id' => 1,
'shipping_country_id' => 1,
'wp_order_number' => 0,
'language' => 'de',
]);
$order = ShoppingOrder::create([
'shopping_user_id' => $shoppingUser->id,
'auth_user_id' => 0,
'country_id' => 1,
'user_shop_id' => 0,
'payment_for' => 6,
'shipping' => 0,
'shipping_net' => 0,
'subtotal_ws' => 80,
'subtotal' => 80,
'tax' => 0,
'total_shipping' => 80,
'points' => 0,
'is_abo' => true,
]);
$aboProduct = makeProduct(['12'], 50, 0);
$order->shopping_order_items()->create([
'product_id' => $aboProduct->id, 'is_abo_addon' => false, 'qty' => 1,
'price' => 50, 'price_net' => 50, 'tax_rate' => 0, 'tax' => 0, 'price_vk_net' => 50, 'discount' => 0, 'points' => 0,
]);
$addonProduct = makeProduct(['2'], 30, 0);
$order->shopping_order_items()->create([
'product_id' => $addonProduct->id, 'is_abo_addon' => true, 'qty' => 1,
'price' => 30, 'price_net' => 30, 'tax_rate' => 0, 'tax' => 0, 'price_vk_net' => 30, 'discount' => 0, 'points' => 0,
]);
return $order->fresh('shopping_order_items.product');
}
it('lehnt eine unbekannte Bestellung ab', function () {
$this->artisan('sales:preview-invoice', ['order' => 999999])
->expectsOutputToContain('nicht gefunden')
->assertExitCode(1);
});
it('erzeugt Vorschau-PDFs ohne offizielle Rechnung oder Nummernverbrauch', function () {
Storage::fake('public');
$order = makePreviewOrder();
$this->artisan('sales:preview-invoice', ['order' => $order->id])
->assertExitCode(0);
// Keine offizielle Rechnung angelegt, keine Rechnungsnummer verbraucht.
expect(UserInvoice::count())->toBe(0);
// Vorschau-PDFs liegen im Preview-Ordner.
Storage::disk('public')->assertExists('invoice/preview/VORSCHAU-'.$order->id.'-MIVITA-Rechnung.pdf');
Storage::disk('public')->assertExists('delivery/preview/VORSCHAU-'.$order->id.'-MIVITA-Lieferschein.pdf');
});