gruene-seele/tests/Feature/LocationDeletionTest.php
Kevin Adametz e08c8dec40 Warenwirtschaft: AP-50 — Lagerort löschbar (Buchungen übernehmen) + Köln als Default
Kundenentscheid 26.08.: „Waldbröl" gibt es nicht mehr, alles wird Köln; die
Standort-Funktion bleibt für künftige Standorte.

- LocationController::destroy überschrieben: ein belegter Lagerort lässt sich
  löschen, indem seine Buchungen (Wareneingänge, Produktionen, Ausschuss)
  transaktional auf einen Ziel-Lagerort übernommen werden. Ohne Ziel-Lagerort
  bleibt die Sperre bestehen; unbelegte Orte werden direkt gelöscht.
- Lagerort-Liste: Ziel-Auswahl („→ Köln" vorbelegt) im Lösch-Formular.
- Wareneingang: Köln als Standard-Lagerort vorbelegt (wie in der Produktion).

Außerdem AP-49-Folgeanpassung: Test-Helper hängt die Kategorie „Kosmetikrohstoffe"
an, damit Lieferanten im gefilterten INCI-Formular erscheinen; Lagerort-Sperr-Test
prüft die neue Meldung. Suite 322 grün.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-28 09:17:28 +00:00

67 lines
2.7 KiB
PHP

<?php
use App\Models\Ingredient;
use App\Models\Location;
use App\Models\StockEntry;
use App\Models\Supplier;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
uses(TestCase::class, RefreshDatabase::class);
function locDelAdmin(): User
{
$user = User::query()->create(['email' => uniqid('loc_', true).'@test.example', 'password' => bcrypt('password')]);
$user->forceFill(['admin' => 8, 'confirmed' => true, 'active' => true, 'wizard' => 100, 'blocked' => false])->save();
return $user->fresh();
}
function locDelStock(Location $location): StockEntry
{
$ing = Ingredient::query()->create(['name' => 'INCI-'.uniqid('', true), 'active' => true, 'pos' => 0]);
$supplier = Supplier::factory()->create();
return StockEntry::query()->create([
'entry_type' => 'ingredient', 'ingredient_id' => $ing->id, 'supplier_id' => $supplier->id,
'location_id' => $location->id, 'unit' => 'gram', 'ordered_by' => 1, 'ordered_at' => '2026-01-01',
'ordered_quantity' => 500, 'status' => 'received', 'received_at' => '2026-01-10',
'received_quantity' => 500, 'batch_number' => 'LOC-1', 'best_before' => '2030-12-31',
]);
}
test('AP-50: belegter lagerort wird gelöscht und buchungen auf ziel-lagerort übernommen', function () {
$this->actingAs(locDelAdmin(), 'user');
$waldbroel = Location::query()->create(['name' => 'Waldbröl', 'active' => true]);
$koeln = Location::query()->create(['name' => 'Köln', 'active' => true]);
$entry = locDelStock($waldbroel);
$this->delete(route('admin.inventory.locations.destroy', $waldbroel), ['reassign_to' => $koeln->id])
->assertRedirect(route('admin.inventory.locations.index'));
expect(Location::query()->find($waldbroel->id))->toBeNull()
->and($entry->fresh()->location_id)->toBe($koeln->id);
});
test('AP-50: belegter lagerort ohne ziel-lagerort bleibt gesperrt', function () {
$this->actingAs(locDelAdmin(), 'user');
$waldbroel = Location::query()->create(['name' => 'Waldbröl', 'active' => true]);
Location::query()->create(['name' => 'Köln', 'active' => true]);
locDelStock($waldbroel);
$this->delete(route('admin.inventory.locations.destroy', $waldbroel))
->assertRedirect(route('admin.inventory.locations.index'));
expect(Location::query()->find($waldbroel->id))->not->toBeNull();
});
test('AP-50: unbelegter lagerort wird direkt gelöscht', function () {
$this->actingAs(locDelAdmin(), 'user');
$leer = Location::query()->create(['name' => 'Leerstand', 'active' => true]);
$this->delete(route('admin.inventory.locations.destroy', $leer))
->assertRedirect(route('admin.inventory.locations.index'));
expect(Location::query()->find($leer->id))->toBeNull();
});