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>
110 lines
3.6 KiB
PHP
110 lines
3.6 KiB
PHP
<?php
|
||
|
||
use App\Models\ShoppingOrder;
|
||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||
|
||
uses(Tests\TestCase::class, RefreshDatabase::class);
|
||
|
||
/**
|
||
* Phase 5 – Rechnung & Lieferschein: Positionen werden nach `is_abo_addon`
|
||
* gruppiert (Abo-Produkte oben, abgegrenzter Block „Einmalig hinzugefügte
|
||
* Artikel" darunter). Bestellungen ohne Einmal-Artikel bleiben unverändert.
|
||
*/
|
||
beforeEach(fn () => makeShopEnv());
|
||
|
||
function makeMixedOrder(bool $withAddon = true): ShoppingOrder
|
||
{
|
||
$order = ShoppingOrder::create([
|
||
'shopping_user_id' => 1,
|
||
'auth_user_id' => 1,
|
||
'country_id' => 1,
|
||
'user_shop_id' => 0,
|
||
'payment_for' => 6,
|
||
'shipping' => 0,
|
||
'shipping_net' => 0,
|
||
'subtotal_ws' => 100,
|
||
'tax' => 0,
|
||
'total_shipping' => 100,
|
||
'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,
|
||
]);
|
||
|
||
if ($withAddon) {
|
||
$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');
|
||
}
|
||
|
||
describe('Rechnung-PDF Gruppierung', function () {
|
||
it('zeigt bei gemischter Bestellung beide Block-Überschriften, Abo vor Einmal', function () {
|
||
$order = makeMixedOrder();
|
||
|
||
$html = view('pdf.invoice-detail', ['shopping_order' => $order])->render();
|
||
|
||
expect($html)->toContain(__('pdf.abo_items_heading'))
|
||
->and($html)->toContain(__('pdf.abo_addon_items_heading'));
|
||
|
||
$aboPos = strpos($html, __('pdf.abo_items_heading'));
|
||
$addonPos = strpos($html, __('pdf.abo_addon_items_heading'));
|
||
expect($aboPos)->toBeLessThan($addonPos);
|
||
});
|
||
|
||
it('zeigt KEINE Block-Überschriften bei reiner Bestellung ohne Einmal-Artikel', function () {
|
||
$order = makeMixedOrder(withAddon: false);
|
||
|
||
$html = view('pdf.invoice-detail', ['shopping_order' => $order])->render();
|
||
|
||
expect($html)->not->toContain(__('pdf.abo_addon_items_heading'))
|
||
->and($html)->not->toContain(__('pdf.abo_items_heading'));
|
||
});
|
||
});
|
||
|
||
describe('Lieferschein-PDF Gruppierung', function () {
|
||
it('zeigt bei gemischter Bestellung beide Block-Überschriften, Abo vor Einmal', function () {
|
||
$order = makeMixedOrder();
|
||
|
||
$html = view('pdf.delivery-detail', ['shopping_order' => $order])->render();
|
||
|
||
expect($html)->toContain(__('pdf.abo_items_heading'))
|
||
->and($html)->toContain(__('pdf.abo_addon_items_heading'));
|
||
|
||
$aboPos = strpos($html, __('pdf.abo_items_heading'));
|
||
$addonPos = strpos($html, __('pdf.abo_addon_items_heading'));
|
||
expect($aboPos)->toBeLessThan($addonPos);
|
||
});
|
||
|
||
it('zeigt KEINE Block-Überschriften bei reiner Bestellung ohne Einmal-Artikel', function () {
|
||
$order = makeMixedOrder(withAddon: false);
|
||
|
||
$html = view('pdf.delivery-detail', ['shopping_order' => $order])->render();
|
||
|
||
expect($html)->not->toContain(__('pdf.abo_addon_items_heading'))
|
||
->and($html)->not->toContain(__('pdf.abo_items_heading'));
|
||
});
|
||
});
|