Warenwirtschaft: F-03 — Entwurf sichert Chargenzeilen verlustfrei

Entwürfe speichern batch_key roh (stock_entry_id nullable), keine Auflösung
und keine Filterung unfertiger Zeilen mehr beim Zwischenspeichern. Eine nicht
mehr verfügbare Charge wird im Formular markiert statt alle Zeilen zu verwerfen.
Ergebniszeilen dürfen im Entwurf ohne Stückzahl bleiben. Auflösung, Prüfung und
Buchung erst beim Abschluss.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-08 10:53:36 +00:00
parent 73512897ee
commit 859b1f083d
10 changed files with 200 additions and 41 deletions

View file

@ -50,6 +50,7 @@
function buildStockSelect(idx, batches, preselectedBatchKey) {
var select = $('<select class="form-control form-control-sm" name="ingredient_lines[' + idx + '][batch_key]" required></select>');
select.append('<option value="">{{ __('Charge wählen') }}</option>');
var found = false;
(batches || []).forEach(function (batch) {
var label = batch.label;
if (batch.remaining != null) {
@ -61,9 +62,18 @@
var opt = $('<option></option>').attr('value', batch.key).text(label);
if (preselectedBatchKey && String(batch.key) === String(preselectedBatchKey)) {
opt.prop('selected', true);
found = true;
}
select.append(opt);
});
// F-03: Eine im Entwurf gesicherte, inzwischen nicht mehr verfügbare Charge
// sichtbar markieren statt still zu verwerfen — der Abschluss lehnt sie ab.
if (preselectedBatchKey && !found) {
var parts = String(preselectedBatchKey).split(':');
var label = '{{ __('Charge') }} ' + (parts.length > 2 ? parts.slice(2).join(':') : preselectedBatchKey) + ' — {{ __('nicht (mehr) verfügbar') }}';
select.append($('<option></option>').attr('value', preselectedBatchKey).text(label).prop('selected', true));
select.addClass('is-invalid');
}
return select;
}

View file

@ -24,7 +24,7 @@
$existingLines = $model
? $model->productionIngredients->groupBy('ingredient_id')->map(function ($lines) {
return $lines
->groupBy(fn ($l) => $l->stockEntry?->batchGroupKey() ?? 'e:'.$l->stock_entry_id)
->groupBy(fn ($l) => $l->formBatchKey())
->map(fn ($group, $key) => ['batch_key' => $key, 'quantity_used' => (float) $group->sum('quantity_used')])
->values();
})

View file

@ -31,7 +31,7 @@
// AP-27: Zeilen je Charge (batch_key) zusammenfassen — mehrere Einkäufe derselben Charge = eine Formularzeile.
$existingLines = $model->productionIngredients->groupBy('ingredient_id')->map(function ($lines) {
return $lines
->groupBy(fn ($l) => $l->stockEntry?->batchGroupKey() ?? 'e:'.$l->stock_entry_id)
->groupBy(fn ($l) => $l->formBatchKey())
->map(fn ($group, $key) => ['batch_key' => $key, 'quantity_used' => (float) $group->sum('quantity_used')])
->values();
});

View file

@ -111,7 +111,12 @@
<tr>
<td>{{ $line->ingredient?->name ?? '—' }}</td>
<td>
#{{ $line->stock_entry_id }}
@if($line->stock_entry_id === null)
{{ __('Charge') }} {{ explode(':', (string) $line->batch_key, 3)[2] ?? $line->batch_key }}
<span class="text-muted">({{ __('Entwurf — noch nicht aufgelöst') }})</span>
@else
#{{ $line->stock_entry_id }}
@endif
@if($line->stockEntry?->batch_number)
— {{ $line->stockEntry->batch_number }}
@endif