Warenwirtschaft: F-02 — getrennter Produktkatalog für Ergebniszeilen

Planung oben bleibt auf produktionsrelevante Produkte gefiltert (AP-47);
Ergebniszeilen bieten alle aktiven Einzelprodukte plus bereits gespeicherte
Output-Produkte an (Bearbeiten/Kopieren verliert keine Zuordnung mehr).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-08 10:50:24 +00:00
parent 9e745d1441
commit 73512897ee
3 changed files with 89 additions and 9 deletions

View file

@ -194,12 +194,62 @@ test('AP-47: produktions-auswahl zeigt nur produktionsrelevante produkte', funct
psProduct(['name' => 'PS47-Sichtbar', 'no_recipe_required' => true, 'production_relevant' => true], $ing);
psProduct(['name' => 'PS47-Versteckt', 'no_recipe_required' => true, 'production_relevant' => false], $ing);
$this->get(route('admin.inventory.productions.create'))
$html = $this->get(route('admin.inventory.productions.create'))
->assertSuccessful()
->assertSee('PS47-Sichtbar')
->assertDontSee('PS47-Versteckt');
->getContent();
// Planung oben: nur produktionsrelevante Produkte.
expect(psSelectHtml($html, 'id="product_id"'))->toContain('PS47-Sichtbar')->not->toContain('PS47-Versteckt')
// F-02: Ergebniszeilen: auch nicht-produktionsrelevante Produkte (5 ml aus der 50-ml-Masse).
->and(psSelectHtml($html, 'class="form-control form-control-sm output-product"'))->toContain('PS47-Sichtbar')->toContain('PS47-Versteckt');
});
test('F-02: bearbeiten behält ein nicht mehr produktionsrelevantes ergebnisprodukt und speichert unverändert', function () {
$user = psUser();
$this->actingAs($user, 'user');
$ing = psIngredient('PS-F02');
$big = psProduct(['name' => 'PS-F02-50ml', 'no_recipe_required' => true, 'production_relevant' => true], $ing);
$small = psProduct(['name' => 'PS-F02-5ml', 'no_recipe_required' => true, 'production_relevant' => false], $ing);
$location = Location::factory()->create();
$production = app(ProductionService::class)->store(
['product_id' => $big->id, 'location_id' => $location->id, 'produced_at' => '2026-09-01', 'quantity' => 50, 'notes' => null],
[],
$user->id,
[
['product_id' => $big->id, 'quantity' => 40, 'batch_number' => 'B-50'],
['product_id' => $small->id, 'quantity' => 50, 'batch_number' => 'B-5'],
]
);
$html = $this->get(route('admin.inventory.productions.edit', $production))->assertSuccessful()->getContent();
expect(psSelectHtml($html, 'id="product_id"'))->not->toContain('PS-F02-5ml')
->and(psSelectHtml($html, 'class="form-control form-control-sm output-product"'))->toContain('PS-F02-5ml');
$this->put(route('admin.inventory.productions.update', $production), [
'product_id' => $big->id, 'location_id' => $location->id, 'produced_at' => '2026-09-01', 'quantity' => 50,
'outputs' => [
['product_id' => $big->id, 'quantity' => 40, 'batch_number' => 'B-50'],
['product_id' => $small->id, 'quantity' => 50, 'batch_number' => 'B-5'],
],
])->assertSessionHasNoErrors();
expect($production->fresh()->outputs()->where('product_id', $small->id)->value('quantity'))->toBe(50);
});
/**
* Liefert das HTML eines <select>, identifiziert über ein eindeutiges Attribut im Start-Tag.
*/
function psSelectHtml(string $html, string $attribute): string
{
$start = strpos($html, $attribute);
expect($start)->not->toBeFalse();
$end = strpos($html, '</select>', $start);
return substr($html, $start, $end - $start);
}
test('AP-47: enthalten-in blendet nicht-produktionsrelevante produkte aus', function () {
$user = psUser();
$this->actingAs($user, 'user');