- Lagerort-Stammdaten Raum/Regal/Fach (storage_rooms/shelves/slots) mit CRUD auf Einstellungen -> Allgemein (SettingCrudController-Kinder, ein gemeinsamer StoreStorageSegmentRequest, generische Partials setting-list-card/-segment-form) - Ingredient: alt_name (Handelsname), Lagerort-FKs + storageLabel(), scopeUsedInActiveRecipes() - INCI-Formular: Lieferanten-Repeater mit Shop-URL je Lieferant (leer = Mail), Alternativname, drei Lagerort-Dropdowns - INCI-Liste: Filter "nur aktive (in Rezeptur aktiver Produkte)" + Spalte Alternativer Name - Rohstoffbestand-Detail: Bestellweg pro INCI-Lieferant (Pivot-URL statt order_method-Fallback), Kopf zeigt Alternativname + Lagerort - Hinweise-Doku fortgeschrieben inkl. Chargen-Format-Entscheid ttmmjj (AP-24) Tests: StorageSettingsTest, IngredientActiveRecipeFilterTest, IngredientOrderFieldsTest (Repeater-Format), RawMaterialStockTest erweitert (volle Suite 170 gruen, 613 Assertions) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
104 lines
3.3 KiB
PHP
Executable file
104 lines
3.3 KiB
PHP
Executable file
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Http\Requests\StoreIngredientRequest;
|
|
use App\Models\DeliveryTime;
|
|
use App\Models\Ingredient;
|
|
use App\Models\ProductIngredient;
|
|
use App\Models\StorageRoom;
|
|
use App\Models\StorageShelf;
|
|
use App\Models\StorageSlot;
|
|
use App\Models\Supplier;
|
|
use App\Models\TaxRate;
|
|
use Illuminate\Http\Request;
|
|
|
|
class IngredientController extends Controller
|
|
{
|
|
public function __construct()
|
|
{
|
|
$this->middleware('copyreader');
|
|
}
|
|
|
|
public function index(Request $request)
|
|
{
|
|
$onlyActive = $request->boolean('only_active');
|
|
|
|
$data = [
|
|
'values' => Ingredient::query()
|
|
->when($onlyActive, fn ($q) => $q->usedInActiveRecipes())
|
|
->get(),
|
|
'onlyActive' => $onlyActive,
|
|
];
|
|
|
|
return view('admin.ingredient.index', $data);
|
|
}
|
|
|
|
public function edit($id)
|
|
{
|
|
if ($id === 'new') {
|
|
$model = new Ingredient;
|
|
$model->active = true;
|
|
} else {
|
|
$model = Ingredient::with('suppliers')->findOrFail($id);
|
|
}
|
|
$data = [
|
|
'model' => $model,
|
|
'taxRates' => TaxRate::query()->active()->orderBy('pos')->orderBy('name')->get(),
|
|
'deliveryTimes' => DeliveryTime::query()->active()->orderBy('pos')->orderBy('label')->get(),
|
|
'suppliers' => Supplier::query()->where('active', true)->orderBy('name')->get(),
|
|
'storageRooms' => StorageRoom::query()->active()->orderBy('pos')->orderBy('label')->get(),
|
|
'storageShelves' => StorageShelf::query()->active()->orderBy('pos')->orderBy('label')->get(),
|
|
'storageSlots' => StorageSlot::query()->active()->orderBy('pos')->orderBy('label')->get(),
|
|
];
|
|
|
|
return view('admin.ingredient.edit', $data);
|
|
}
|
|
|
|
public function store(StoreIngredientRequest $request)
|
|
{
|
|
$data = $request->validated();
|
|
$data['default_factor'] = $data['default_factor'] ?? 1.10;
|
|
|
|
$supplierRows = $data['suppliers'] ?? [];
|
|
unset($data['suppliers']);
|
|
|
|
if ($request->input('id') === 'new') {
|
|
$model = Ingredient::create($data);
|
|
} else {
|
|
$model = Ingredient::findOrFail($request->input('id'));
|
|
$model->fill($data)->save();
|
|
}
|
|
|
|
// Repeater-Zeilen → Pivot-Sync; URL leer = Bestellung per Mail. Doppelte Lieferanten: letzte Zeile gewinnt.
|
|
$sync = [];
|
|
foreach ($supplierRows as $row) {
|
|
$supplierId = (int) ($row['id'] ?? 0);
|
|
if ($supplierId > 0) {
|
|
$url = trim((string) ($row['url'] ?? ''));
|
|
$sync[$supplierId] = ['url' => $url === '' ? null : $url];
|
|
}
|
|
}
|
|
$model->suppliers()->sync($sync);
|
|
|
|
\Session()->flash('alert-save', '1');
|
|
|
|
return redirect(route('admin_product_ingredients'));
|
|
|
|
}
|
|
|
|
public function delete($id)
|
|
{
|
|
|
|
if (ProductIngredient::where('ingredient_id', $id)->count()) {
|
|
\Session()->flash('alert-error', 'Eintrag wird als Produkt-Inhaltsstoff verwendet');
|
|
|
|
return redirect(route('admin_product_ingredients'));
|
|
}
|
|
$model = Ingredient::findOrFail($id);
|
|
$model->delete();
|
|
\Session()->flash('alert-success', 'Eintrag gelöscht');
|
|
|
|
return redirect(route('admin_product_ingredients'));
|
|
}
|
|
}
|