Warenwirtschaft: F-15 — ml-Einkauf bleibt beim Kopieren ml-Bestellung
Einheit, ml-Menge und Dichte-Snapshot werden übernommen; fehlt die Rohstoff-Dichte inzwischen, greift der Snapshot der Vorlage. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
8808ff538f
commit
c2a17835ef
3 changed files with 59 additions and 1 deletions
|
|
@ -160,6 +160,15 @@ class StockEntryController extends Controller
|
|||
$data['ordered_by'] = (int) auth()->id();
|
||||
$data['status'] = 'pending';
|
||||
|
||||
// Prüfung 08.09.2026 F-15: In ml erfasste Bestellungen als ml kopieren — Menge
|
||||
// zurück in ml (über den Dichte-Snapshot), Einheit und Dichte mitgeben. Sonst wird
|
||||
// die Kopie zur Gramm-Bestellung und ein Wareneingang „100“ bucht 100 g statt 79 g.
|
||||
if ($stockEntry->purchase_unit === 'ml' && $stockEntry->density_used !== null && (float) $stockEntry->density_used > 0) {
|
||||
$data['ordered_unit'] = 'ml';
|
||||
$data['ordered_quantity'] = round((float) $stockEntry->ordered_quantity / (float) $stockEntry->density_used, 2);
|
||||
$data['density_used'] = (float) $stockEntry->density_used;
|
||||
}
|
||||
|
||||
$copy = $this->stockEntryRepository->create($data);
|
||||
|
||||
\Session::flash('alert-warning', __('Kopie angelegt – bitte Menge/Charge prüfen und speichern.'));
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ class StockEntryRepository
|
|||
protected function applyPurchaseUnit(array $data): array
|
||||
{
|
||||
$orderedUnit = $data['ordered_unit'] ?? 'gram';
|
||||
// F-15: Dichte-Snapshot der Vorlage (Kopie) als Rückfall, falls die Rohstoff-Dichte
|
||||
// inzwischen entfernt wurde — sonst würde die ml-Menge still als Gramm gebucht.
|
||||
$fallbackDensity = isset($data['density_used']) && (float) $data['density_used'] > 0 ? (float) $data['density_used'] : null;
|
||||
unset($data['ordered_unit']);
|
||||
|
||||
if (($data['entry_type'] ?? '') !== 'ingredient') {
|
||||
|
|
@ -63,7 +66,7 @@ class StockEntryRepository
|
|||
$data['density_used'] = null;
|
||||
|
||||
if ($orderedUnit === 'ml') {
|
||||
$density = Ingredient::query()->whereKey($data['ingredient_id'] ?? null)->value('density');
|
||||
$density = Ingredient::query()->whereKey($data['ingredient_id'] ?? null)->value('density') ?? $fallbackDensity;
|
||||
if ($density !== null && (float) $density > 0) {
|
||||
$data['purchase_unit'] = 'ml';
|
||||
$data['density_used'] = (float) $density;
|
||||
|
|
|
|||
|
|
@ -126,3 +126,49 @@ test('AP-42: wareneingang in ml wird über die dichte in gramm gebucht', functio
|
|||
|
||||
expect((float) $entry->fresh()->received_quantity)->toBe(200.0);
|
||||
});
|
||||
|
||||
test('F-15: kopie einer ml-bestellung bleibt ml-bestellung und bucht den wareneingang über die dichte', function () {
|
||||
[$admin, $ingredient, $location, $supplier, $quality] = densSetup(0.79);
|
||||
$this->actingAs($admin, 'user');
|
||||
|
||||
$this->post(route('admin.inventory.stock-entries.store'), [
|
||||
'entry_type' => 'ingredient', 'ingredient_id' => (string) $ingredient->id, 'supplier_id' => (string) $supplier->id,
|
||||
'location_id' => (string) $location->id, 'quality_id' => (string) $quality->id, 'ordered_at' => '2026-08-01',
|
||||
'ordered_quantity' => '100', 'ordered_unit' => 'ml', 'price_per_kg' => '10,00',
|
||||
])->assertRedirect();
|
||||
$original = StockEntry::query()->latest('id')->firstOrFail();
|
||||
|
||||
$this->post(route('admin.inventory.stock-entries.copy', $original))->assertRedirect();
|
||||
$copy = StockEntry::query()->latest('id')->firstOrFail();
|
||||
|
||||
expect($copy->id)->not->toBe($original->id)
|
||||
->and($copy->purchase_unit)->toBe('ml')
|
||||
->and((float) $copy->density_used)->toBe(0.79)
|
||||
->and((float) $copy->ordered_quantity)->toBe(79.0); // kanonisch Gramm = 100 ml
|
||||
|
||||
// Wareneingang „100“ (ml) → 79 g, nicht 100 g.
|
||||
$this->put(route('admin.inventory.stock-entries.receive', $copy), [
|
||||
'received_at' => '2026-08-05', 'received_quantity' => '100', 'batch_number' => 'F15-1', 'best_before' => '2030-12-31',
|
||||
])->assertRedirect();
|
||||
expect((float) $copy->fresh()->received_quantity)->toBe(79.0);
|
||||
});
|
||||
|
||||
test('F-15: kopie nutzt den dichte-snapshot, wenn die rohstoff-dichte inzwischen fehlt', function () {
|
||||
[$admin, $ingredient, $location, $supplier, $quality] = densSetup(0.80);
|
||||
$this->actingAs($admin, 'user');
|
||||
|
||||
$this->post(route('admin.inventory.stock-entries.store'), [
|
||||
'entry_type' => 'ingredient', 'ingredient_id' => (string) $ingredient->id, 'supplier_id' => (string) $supplier->id,
|
||||
'location_id' => (string) $location->id, 'quality_id' => (string) $quality->id, 'ordered_at' => '2026-08-01',
|
||||
'ordered_quantity' => '250', 'ordered_unit' => 'ml', 'price_per_kg' => '10,00',
|
||||
])->assertRedirect();
|
||||
$original = StockEntry::query()->latest('id')->firstOrFail();
|
||||
$ingredient->forceFill(['density' => null])->save();
|
||||
|
||||
$this->post(route('admin.inventory.stock-entries.copy', $original))->assertRedirect();
|
||||
$copy = StockEntry::query()->latest('id')->firstOrFail();
|
||||
|
||||
expect($copy->purchase_unit)->toBe('ml')
|
||||
->and((float) $copy->density_used)->toBe(0.80)
|
||||
->and((float) $copy->ordered_quantity)->toBe(200.0);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue