Warenwirtschaft: OPT-3 Index-Limits + OPT-4 show()-Refactoring
- Wareneingang-/Produktions-Uebersicht: Eingegangene auf 500 gedeckelt (offene Bestellungen immer vollstaendig), ?all=1 + Hinweis-Link; ungenutzte User-Eager-Loads entfernt - RawMaterialStockController::show() entschlackt: Chargen-Restmengen (FEFO) und offene Bestellungen im InventoryService, letzter Netto-Preis je Lieferant als Window-Function-Query im StockEntryRepository Tests: RawMaterialStockTest +2, volle Suite 186 gruen, 663 Assertions Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
209ea467eb
commit
e47d848d3d
10 changed files with 165 additions and 75 deletions
|
|
@ -8,9 +8,14 @@ use App\Models\ProductionPackaging;
|
|||
use App\Models\StockDisposal;
|
||||
use App\Models\StockEntry;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class InventoryService
|
||||
{
|
||||
public function __construct(
|
||||
protected ProductionService $productionService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Lookback-Fenster (Tage) für die Berechnung des durchschnittlichen Tagesverbrauchs
|
||||
* aus der Produktionshistorie.
|
||||
|
|
@ -277,6 +282,51 @@ class InventoryService
|
|||
return 'ok';
|
||||
}
|
||||
|
||||
/**
|
||||
* Eingegangene Chargen eines Rohstoffs mit Restmenge (> 0), FEFO-sortiert
|
||||
* (frühestes MHD zuerst). Restmenge = Eingang − Produktionsverbrauch je Charge.
|
||||
*
|
||||
* @return Collection<int, StockEntry>
|
||||
*/
|
||||
public function chargesWithRemainingForIngredient(int $ingredientId)
|
||||
{
|
||||
$entries = StockEntry::query()
|
||||
->with(['supplier', 'location'])
|
||||
->where('status', 'received')
|
||||
->where('entry_type', 'ingredient')
|
||||
->where('ingredient_id', $ingredientId)
|
||||
->orderByRaw('best_before is null, best_before asc')
|
||||
->orderBy('id')
|
||||
->get();
|
||||
|
||||
$consumed = $this->productionService->consumedByStockEntry($entries->pluck('id')->all());
|
||||
|
||||
return $entries->map(function (StockEntry $entry) use ($consumed) {
|
||||
$received = $entry->received_quantity !== null ? (float) $entry->received_quantity : 0.0;
|
||||
$entry->setAttribute('remaining_quantity', round($received - ($consumed[(int) $entry->id] ?? 0.0), 2));
|
||||
|
||||
return $entry;
|
||||
})->filter(fn (StockEntry $entry) => (float) $entry->getAttribute('remaining_quantity') > 0.0)
|
||||
->values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Offene (bestellte, noch nicht eingegangene) Einkäufe eines Rohstoffs.
|
||||
*
|
||||
* @return Collection<int, StockEntry>
|
||||
*/
|
||||
public function openOrdersForIngredient(int $ingredientId)
|
||||
{
|
||||
return StockEntry::query()
|
||||
->with(['supplier', 'location'])
|
||||
->where('status', 'pending')
|
||||
->where('entry_type', 'ingredient')
|
||||
->where('ingredient_id', $ingredientId)
|
||||
->orderBy('ordered_at')
|
||||
->orderBy('id')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Anzahl der kritischen (Meldebestand unterschritten) aktiven Rohstoffe – für das Sidenav-Badge.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue