Warenwirtschaft: F-01 — Entwürfe zählen nicht mehr als Chargenverbrauch
consumedByStockEntry() schließt Entwürfe jetzt wie remainingByIngredient() aus. Regression: Chargen-/Standortbestand, zweite Produktion, Ausschussprüfung. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
0a1e796f7b
commit
9e745d1441
2 changed files with 50 additions and 0 deletions
|
|
@ -455,6 +455,9 @@ class ProductionService
|
|||
$rows = ProductionIngredient::query()
|
||||
->join('productions', 'productions.id', '=', 'production_ingredients.production_id')
|
||||
->whereNull('productions.deleted_at')
|
||||
// Prüfung 08.09.2026 F-01: Entwürfe sind nicht bestandswirksam — wie in
|
||||
// InventoryService::remainingByIngredient(), sonst blockiert ein Entwurf die Charge.
|
||||
->where('productions.status', '!=', Production::STATUS_DRAFT)
|
||||
->selectRaw('production_ingredients.stock_entry_id as stock_entry_id, SUM(production_ingredients.quantity_used) as used')
|
||||
->whereIn('production_ingredients.stock_entry_id', $stockEntryIds)
|
||||
->when($excludeProductionId !== null, fn ($q) => $q->where('production_ingredients.production_id', '!=', $excludeProductionId))
|
||||
|
|
|
|||
|
|
@ -215,3 +215,50 @@ test('AP-43: HTTP zwischenspeichern ohne ergebnis speichert entwurf; abschließe
|
|||
'quantity' => 3,
|
||||
])->assertSessionHasErrors('outputs');
|
||||
});
|
||||
|
||||
test('F-01: entwurf reduziert weder chargen- noch standortbestand und blockiert keine andere produktion', function () {
|
||||
$user = draftUser();
|
||||
$location = Location::factory()->create();
|
||||
$supplier = Supplier::factory()->create();
|
||||
$ing = draftIngredient();
|
||||
$product = draftRecipeProduct($ing);
|
||||
$stock = draftStock($ing, $location, $supplier, $user);
|
||||
|
||||
$draft = app(ProductionService::class)->store(
|
||||
['product_id' => $product->id, 'location_id' => $location->id, 'produced_at' => '2026-08-01', 'quantity' => 1, 'notes' => null],
|
||||
[['ingredient_id' => $ing->id, 'stock_entry_id' => $stock->id, 'quantity_used' => '10']],
|
||||
$user->id,
|
||||
[['product_id' => $product->id, 'quantity' => 1, 'batch_number' => null]],
|
||||
true // draft
|
||||
);
|
||||
|
||||
$productionService = app(ProductionService::class);
|
||||
$inventory = app(InventoryService::class);
|
||||
|
||||
// Chargenverbrauch ohne Ausschluss der eigenen Produktion — wie ihn andere Produktionen/Ausschuss sehen
|
||||
expect($productionService->consumedByStockEntry([$stock->id]))->toBe([])
|
||||
->and((float) $productionService->availableStockEntriesForIngredient($ing->id, $location->id)->first()->getAttribute('remaining_quantity'))->toBe(1000.0)
|
||||
->and((float) $inventory->chargesWithRemainingForIngredient($ing->id)->first()->getAttribute('remaining_quantity'))->toBe(1000.0)
|
||||
->and((float) ($inventory->remainingByLocationForIngredient($ing->id)[$location->id] ?? 0.0))->toBe(1000.0);
|
||||
|
||||
// Eine zweite Produktion kann die komplette Charge trotz Entwurf buchen
|
||||
$completed = $productionService->store(
|
||||
['product_id' => $product->id, 'location_id' => $location->id, 'produced_at' => '2026-08-02', 'quantity' => 100, 'notes' => null],
|
||||
[['ingredient_id' => $ing->id, 'stock_entry_id' => $stock->id, 'quantity_used' => '1000']],
|
||||
$user->id,
|
||||
[['product_id' => $product->id, 'quantity' => 100, 'batch_number' => null]]
|
||||
);
|
||||
|
||||
expect($completed->isDraft())->toBeFalse()
|
||||
->and($productionService->consumedByStockEntry([$stock->id])[$stock->id])->toBe(1000.0)
|
||||
->and($productionService->availableStockEntriesForIngredient($ing->id, $location->id))->toHaveCount(0);
|
||||
|
||||
// Ausschuss mit Charge: Entwurf darf die zulässige Menge nicht verkleinern (vor Abschluss der zweiten Produktion wäre 1000 g erlaubt)
|
||||
$completed->delete();
|
||||
$this->actingAs($user, 'user')
|
||||
->post(route('admin.inventory.stock-disposals.store'), [
|
||||
'disposal_type' => 'ingredient', 'ingredient_id' => $ing->id, 'stock_entry_id' => $stock->id,
|
||||
'location_id' => $location->id, 'quantity' => '1000', 'disposed_at' => '2026-08-03', 'reason' => 'Test',
|
||||
])
|
||||
->assertSessionHasNoErrors();
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue