diff --git a/app/Http/Controllers/ShoppingListController.php b/app/Http/Controllers/ShoppingListController.php index afdfdf5..a7641c9 100644 --- a/app/Http/Controllers/ShoppingListController.php +++ b/app/Http/Controllers/ShoppingListController.php @@ -98,11 +98,37 @@ class ShoppingListController extends Controller $request->user()->id ); - $shoppingItem->update([ - 'product_name' => $request->string('product_name')->toString(), - 'quantity' => $request->filled('quantity') ? $request->string('quantity')->toString() : null, - 'store_id' => $storeId, - ]); + $isDone = $request->boolean('is_done'); + + DB::transaction(function () use ($request, $shoppingItem, $storeId, $isDone): void { + $shoppingItem->update([ + 'product_name' => $request->string('product_name')->toString(), + 'quantity' => $request->filled('quantity') ? $request->string('quantity')->toString() : null, + 'store_id' => $storeId, + 'is_done' => $isDone, + 'done_at' => $isDone ? Carbon::now() : null, + ]); + + if (! $isDone) { + return; + } + + $photoPath = $request->file('photo')?->store('price-photos', 'public'); + + if (! $request->filled('price_decimal') && $photoPath === null) { + return; + } + + ItemPriceLog::query()->create([ + 'shopping_item_id' => $shoppingItem->id, + 'store_id' => $storeId, + 'price_decimal' => $request->input('price_decimal') ?? 0, + 'currency' => 'EUR', + 'logged_at' => Carbon::now(), + 'photo_path' => $photoPath, + 'source' => 'manual', + ]); + }); return back()->with('status', 'Eintrag wurde gespeichert.'); } diff --git a/app/Http/Requests/UpdateShoppingItemRequest.php b/app/Http/Requests/UpdateShoppingItemRequest.php index f42bcba..b2abc17 100644 --- a/app/Http/Requests/UpdateShoppingItemRequest.php +++ b/app/Http/Requests/UpdateShoppingItemRequest.php @@ -3,6 +3,7 @@ namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Rules\File; class UpdateShoppingItemRequest extends FormRequest { @@ -28,6 +29,13 @@ class UpdateShoppingItemRequest extends FormRequest 'quantity' => ['nullable', 'string', 'max:255'], 'store_id' => ['nullable', 'integer', 'exists:stores,id'], 'new_store_name' => ['nullable', 'string', 'max:255'], + 'is_done' => ['sometimes', 'boolean'], + 'price_decimal' => ['nullable', 'numeric', 'min:0', 'max:999999.99'], + 'photo' => [ + 'nullable', + File::types(['jpg', 'jpeg', 'png', 'gif', 'webp', 'heic', 'heif']) + ->max(15 * 1024), + ], ]; } @@ -39,6 +47,10 @@ class UpdateShoppingItemRequest extends FormRequest 'quantity.max' => 'Die Menge darf maximal 255 Zeichen lang sein.', 'store_id.exists' => 'Das ausgewaehlte Geschaeft existiert nicht.', 'new_store_name.max' => 'Der neue Geschaeftsname darf maximal 255 Zeichen lang sein.', + 'price_decimal.numeric' => 'Der Preis muss eine Zahl sein.', + 'price_decimal.min' => 'Der Preis darf nicht negativ sein.', + 'price_decimal.max' => 'Der Preis ist zu gross.', + 'photo.max' => 'Das Foto darf maximal 15 MB gross sein.', ]; } @@ -49,6 +61,8 @@ class UpdateShoppingItemRequest extends FormRequest 'quantity' => 'Menge', 'store_id' => 'Geschaeft', 'new_store_name' => 'neues Geschaeft', + 'price_decimal' => 'Preis', + 'photo' => 'Foto', ]; } } diff --git a/resources/views/shopping-list/index.blade.php b/resources/views/shopping-list/index.blade.php index 997afb0..20084b2 100644 --- a/resources/views/shopping-list/index.blade.php +++ b/resources/views/shopping-list/index.blade.php @@ -101,7 +101,6 @@ @include('shopping-list.partials.item-pencil-panel', [ 'item' => $item, 'stores' => $stores, - 'showToggleExtras' => true, ]) @@ -158,7 +157,6 @@ @include('shopping-list.partials.item-pencil-panel', [ 'item' => $item, 'stores' => $stores, - 'showToggleExtras' => true, ]) diff --git a/resources/views/shopping-list/partials/item-pencil-panel.blade.php b/resources/views/shopping-list/partials/item-pencil-panel.blade.php index f8336d3..9afda8e 100644 --- a/resources/views/shopping-list/partials/item-pencil-panel.blade.php +++ b/resources/views/shopping-list/partials/item-pencil-panel.blade.php @@ -1,8 +1,4 @@ -{{-- Ein Bleistift oeffnet alle Aenderungsoptionen: Stammdaten (update) + optional Preis/Foto beim Abhaken (nur wenn $showToggleExtras) --}} -@php - $showToggleExtras = $showToggleExtras ?? false; -@endphp - +{{-- Ein Bleistift oeffnet ein Formular: Stammdaten, Erledigt-Status, optional Preis/Foto --}}

Eintrag bearbeiten

-

Zwei getrennte Aktionen – bitte den passenden Button nutzen.

+

Produkt, Geschaeft, Erledigt-Status sowie optional Preis und Foto in einem Schritt speichern.

@if($item->latestPhotoLog)
@@ -31,11 +27,12 @@
@endif -
-

1. Produkt & Geschaeft

-

Nur Name, Menge und Geschaeft. Der Status (offen/erledigt) bleibt dabei gleich.

-
-
+ @csrf @method('PATCH') @@ -72,85 +69,41 @@ list="store-options" class="rounded-md border-gray-300 text-sm min-h-[44px]" > + +

Preis und Foto werden nur gespeichert, wenn der Eintrag als erledigt markiert ist.

+ + + + @if($item->latestPriceLog) +
+ Letzter Preis: + {{ number_format((float) $item->latestPriceLog->price_decimal, 2, ',', '.') }} EUR + bei {{ $item->latestPriceLog->store?->name ?: 'ohne Geschaeft' }} + ({{ optional($item->latestPriceLog->logged_at)->format('d.m.Y H:i') }}) +
+ @endif
- - @if($showToggleExtras) -
-
- @if($item->is_done) -

2. Neuer Preis oder Kassenbon

-

Fuer die Statistik: weiterer Einkauf desselben Artikels. Alles optional.

- @else -

2. Erledigen & Kasse erfassen

-

Hakt die Zeile ab und speichert optional Preis und Foto. Oder nutze nur die Checkbox in der Liste zum Abhaken ohne Kasse.

- @endif -
-
- @csrf - @method('PATCH') - - - - - - - - @if($item->latestPriceLog) -
- Letzter Preis: - {{ number_format((float) $item->latestPriceLog->price_decimal, 2, ',', '.') }} EUR - bei {{ $item->latestPriceLog->store?->name ?: 'ohne Geschaeft' }} - ({{ optional($item->latestPriceLog->logged_at)->format('d.m.Y H:i') }}) -
- @endif - -
- @endif