Warenwirtschaft: F-16 — fachliche Vorbedingungen innerhalb der Sperre prüfen

- Statusservice: Vorbedingung (Ausgangsstatus, Zahlungsfreigabe) wird nach
  lockForUpdate erneut geprüft (Board übergibt sie als Closure).
- Wareneingang: Transaktion + Sperre, „offen“ wird innerhalb der Sperre geprüft.
- Ausschuss: betroffene Wareneingänge sperren und Restbestand in der
  Transaktion erneut prüfen.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-08 11:09:53 +00:00
parent c2a17835ef
commit 731b2461e2
7 changed files with 170 additions and 36 deletions

View file

@ -6,6 +6,8 @@ use App\Models\Ingredient;
use App\Models\StockEntry;
use App\Models\TaxRate;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
class StockEntryRepository
{
@ -133,18 +135,28 @@ class StockEntryRepository
*/
public function receive(StockEntry $stockEntry, array $data): StockEntry
{
$data['status'] = 'received';
$data['received_by'] = auth()->id();
return DB::transaction(function () use ($stockEntry, $data) {
// Prüfung 08.09.2026 F-16: Zeile sperren und „offen“ innerhalb der Sperre erneut
// prüfen — zwei parallele Wareneingänge schreiben sonst nacheinander verschiedene
// Mengen auf denselben Eintrag.
$locked = StockEntry::query()->lockForUpdate()->findOrFail($stockEntry->id);
if (! $locked->isPending()) {
throw ValidationException::withMessages(['status' => __('Eintrag nicht mehr offen.')]);
}
// AP-42: Wurde in ml eingekauft, wird auch die gelieferte Menge in ml erfasst
// und über die (beim Bestellen gespeicherte) Dichte in Gramm umgerechnet.
if ($stockEntry->purchase_unit === 'ml' && $stockEntry->density_used !== null && isset($data['received_quantity'])) {
$data['received_quantity'] = round((float) $data['received_quantity'] * (float) $stockEntry->density_used, 2);
}
$data['status'] = 'received';
$data['received_by'] = auth()->id();
$stockEntry->update($data);
// AP-42: Wurde in ml eingekauft, wird auch die gelieferte Menge in ml erfasst
// und über die (beim Bestellen gespeicherte) Dichte in Gramm umgerechnet.
if ($locked->purchase_unit === 'ml' && $locked->density_used !== null && isset($data['received_quantity'])) {
$data['received_quantity'] = round((float) $data['received_quantity'] * (float) $locked->density_used, 2);
}
return $stockEntry->fresh();
$locked->update($data);
return $locked->fresh();
});
}
/**