Warenwirtschaft: AP-45 + AP-47 — Verpackung aus Produktionsergebnis, Produktionsrelevant-Flag
AP-45: Verpackungsverbrauch (Etikett/Tiegel) wird jetzt aus dem tatsächlichen Produktionsergebnis gebucht — je Ergebnisprodukt dessen eigene Stückliste × Ergebnismenge, über alle Outputs summiert — statt aus der geplanten Stückzahl. syncPackagingSnapshot liest die production_outputs und läuft daher nach persistOutputs (store + update umgestellt). AP-47: Neues Produkt-Kennzeichen „produktionsrelevant" (Boolean, Default true). Nur so markierte Produkte erscheinen in der Produktions-Auswahl (ProductionController create/edit/copy) und in der „Enthalten in"-Box des Rohstoffbestands (blendet z. B. Aloe-Handelsware und Zweitgrößen aus). Checkbox im Produktformular (mit Hidden-Fallback 0). Migration Default true, damit Bestandsdaten unverändert bleiben. Migrationen add_production_relevant auf Dev-/Live-DB ausgeführt. 4 Tests, Suite 314 grün. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e6007efc41
commit
d51860af39
7 changed files with 132 additions and 17 deletions
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
use App\Models\Ingredient;
|
||||
use App\Models\Location;
|
||||
use App\Models\PackagingItem;
|
||||
use App\Models\PackagingMaterial;
|
||||
use App\Models\Product;
|
||||
use App\Models\ProductIngredient;
|
||||
use App\Models\Production;
|
||||
|
|
@ -156,3 +158,57 @@ test('AP-40: recipeJson-payload enthält aktuellen bestand je INCI (für differe
|
|||
expect($line['current_stock'])->toBe(800.0)
|
||||
->and($line['required_grams_total'])->toBe(1000.0);
|
||||
});
|
||||
|
||||
test('AP-45: verpackung wird je ergebnisprodukt aus dem produktionsergebnis gebucht', function () {
|
||||
$user = psUser();
|
||||
$supplier = Supplier::factory()->create();
|
||||
$location = Location::factory()->create();
|
||||
$material = PackagingMaterial::factory()->create();
|
||||
|
||||
$pkA = PackagingItem::factory()->create(['packaging_material_id' => $material->id, 'supplier_id' => $supplier->id, 'name' => 'PS-Tiegel-A', 'category' => 'packaging']);
|
||||
$pkB = PackagingItem::factory()->create(['packaging_material_id' => $material->id, 'supplier_id' => $supplier->id, 'name' => 'PS-Tiegel-B', 'category' => 'packaging']);
|
||||
|
||||
// Beide Ergebnisprodukte ohne Rezepturpflicht (kein Rohstoff-Abwiegen nötig).
|
||||
$planned = Product::query()->create(['name' => uniqid('PS-Plan-', true), 'title' => 'x', 'active' => true, 'no_recipe_required' => true]);
|
||||
$planned->packagings()->sync([$pkA->id => ['quantity' => 1, 'pos' => 0]]);
|
||||
$other = Product::query()->create(['name' => uniqid('PS-Other-', true), 'title' => 'y', 'active' => true, 'no_recipe_required' => true]);
|
||||
$other->packagings()->sync([$pkB->id => ['quantity' => 2, 'pos' => 0]]);
|
||||
|
||||
// Eine Charge, Ergebnis: 40× Plan (Tiegel A) + 50× Other (Tiegel B).
|
||||
$production = app(ProductionService::class)->store([
|
||||
'product_id' => $planned->id, 'location_id' => $location->id, 'produced_at' => '2026-08-01', 'quantity' => 40,
|
||||
], [], $user->id, [
|
||||
['product_id' => $planned->id, 'quantity' => 40, 'batch_number' => 'PS-A1'],
|
||||
['product_id' => $other->id, 'quantity' => 50, 'batch_number' => 'PS-B1'],
|
||||
]);
|
||||
|
||||
$byItem = $production->productionPackagings->keyBy('packaging_item_id');
|
||||
expect($byItem[$pkA->id]->quantity_used)->toBe(40) // 1 × 40 (Ergebnis Plan)
|
||||
->and($byItem[$pkB->id]->quantity_used)->toBe(100); // 2 × 50 (Ergebnis Other)
|
||||
});
|
||||
|
||||
test('AP-47: produktions-auswahl zeigt nur produktionsrelevante produkte', function () {
|
||||
$user = psUser();
|
||||
$this->actingAs($user, 'user');
|
||||
$ing = psIngredient('PS-Rel');
|
||||
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'))
|
||||
->assertSuccessful()
|
||||
->assertSee('PS47-Sichtbar')
|
||||
->assertDontSee('PS47-Versteckt');
|
||||
});
|
||||
|
||||
test('AP-47: enthalten-in blendet nicht-produktionsrelevante produkte aus', function () {
|
||||
$user = psUser();
|
||||
$this->actingAs($user, 'user');
|
||||
$ing = psIngredient('PS-EnthaltenRel');
|
||||
psProduct(['name' => 'PS47-EnthaltenSichtbar', 'production_relevant' => true], $ing);
|
||||
psProduct(['name' => 'PS47-EnthaltenVersteckt', 'production_relevant' => false], $ing);
|
||||
|
||||
$this->get(route('admin.inventory.raw-material-stock.show', $ing))
|
||||
->assertSuccessful()
|
||||
->assertSee('PS47-EnthaltenSichtbar')
|
||||
->assertDontSee('PS47-EnthaltenVersteckt');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue