Preisstaffel fuer Eintraege erfassen
Made-with: Cursor
This commit is contained in:
parent
a707aadd4f
commit
bfc6247322
@ -111,14 +111,16 @@ class ShoppingListController extends Controller
|
||||
|
||||
$photoPath = $request->file('photo')?->store('price-photos', 'public');
|
||||
|
||||
if (! $request->filled('price_decimal') && $photoPath === null) {
|
||||
if (! $request->filled('price_decimal') && ! $request->filled('tier_price_decimal') && $photoPath === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemPriceLog::query()->create([
|
||||
'shopping_item_id' => $shoppingItem->id,
|
||||
'store_id' => $storeId,
|
||||
'price_decimal' => $request->input('price_decimal') ?? 0,
|
||||
'price_decimal' => $request->filled('price_decimal') ? $request->input('price_decimal') : null,
|
||||
'tier_min_qty' => $request->filled('tier_min_qty') ? (int) $request->input('tier_min_qty') : null,
|
||||
'tier_price_decimal' => $request->filled('tier_price_decimal') ? $request->input('tier_price_decimal') : null,
|
||||
'currency' => 'EUR',
|
||||
'logged_at' => Carbon::now(),
|
||||
'photo_path' => $photoPath,
|
||||
@ -159,14 +161,16 @@ class ShoppingListController extends Controller
|
||||
|
||||
$photoPath = $request->file('photo')?->store('price-photos', 'public');
|
||||
|
||||
if (! $request->filled('price_decimal') && $photoPath === null) {
|
||||
if (! $request->filled('price_decimal') && ! $request->filled('tier_price_decimal') && $photoPath === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemPriceLog::query()->create([
|
||||
'shopping_item_id' => $shoppingItem->id,
|
||||
'store_id' => $storeId,
|
||||
'price_decimal' => $request->input('price_decimal') ?? 0,
|
||||
'price_decimal' => $request->filled('price_decimal') ? $request->input('price_decimal') : null,
|
||||
'tier_min_qty' => $request->filled('tier_min_qty') ? (int) $request->input('tier_min_qty') : null,
|
||||
'tier_price_decimal' => $request->filled('tier_price_decimal') ? $request->input('tier_price_decimal') : null,
|
||||
'currency' => 'EUR',
|
||||
'logged_at' => Carbon::now(),
|
||||
'photo_path' => $photoPath,
|
||||
|
||||
@ -35,6 +35,8 @@ class ToggleShoppingItemRequest extends FormRequest
|
||||
'store_id' => ['nullable', 'integer', 'exists:stores,id'],
|
||||
'new_store_name' => ['nullable', 'string', 'max:255'],
|
||||
'price_decimal' => ['nullable', 'numeric', 'min:0', 'max:999999.99'],
|
||||
'tier_min_qty' => ['nullable', 'integer', 'min:2', 'max:999999'],
|
||||
'tier_price_decimal' => ['nullable', 'numeric', 'min:0', 'max:999999.99'],
|
||||
'photo' => [
|
||||
'nullable',
|
||||
File::types(['jpg', 'jpeg', 'png', 'gif', 'webp', 'heic', 'heif'])
|
||||
@ -54,6 +56,12 @@ class ToggleShoppingItemRequest extends FormRequest
|
||||
'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.',
|
||||
'tier_min_qty.integer' => 'Die Mindestmenge (Staffel) muss eine ganze Zahl sein.',
|
||||
'tier_min_qty.min' => 'Die Mindestmenge (Staffel) muss mindestens 2 sein.',
|
||||
'tier_min_qty.max' => 'Die Mindestmenge (Staffel) ist zu gross.',
|
||||
'tier_price_decimal.numeric' => 'Der Staffelpreis muss eine Zahl sein.',
|
||||
'tier_price_decimal.min' => 'Der Staffelpreis darf nicht negativ sein.',
|
||||
'tier_price_decimal.max' => 'Der Staffelpreis ist zu gross.',
|
||||
'photo.max' => 'Das Foto darf maximal 15 MB gross sein.',
|
||||
'photo.uploaded' => 'Das Foto konnte nicht hochgeladen werden. Haeufig: Datei zu gross fuer die PHP-Grenze auf dem Server (upload_max_filesize / post_max_size). Bitte ein kleineres Bild waehlen oder die Server-Limits erhoehen (siehe scripts/apache-einkauf-debian-setup.sh, Abschnitt PHP-Uploads).',
|
||||
];
|
||||
@ -66,6 +74,8 @@ class ToggleShoppingItemRequest extends FormRequest
|
||||
'store_id' => 'Geschaeft',
|
||||
'new_store_name' => 'neues Geschaeft',
|
||||
'price_decimal' => 'Preis',
|
||||
'tier_min_qty' => 'Mindestmenge (Staffel)',
|
||||
'tier_price_decimal' => 'Staffelpreis',
|
||||
'photo' => 'Foto',
|
||||
];
|
||||
}
|
||||
|
||||
@ -31,6 +31,8 @@ class UpdateShoppingItemRequest extends FormRequest
|
||||
'new_store_name' => ['nullable', 'string', 'max:255'],
|
||||
'is_done' => ['sometimes', 'boolean'],
|
||||
'price_decimal' => ['nullable', 'numeric', 'min:0', 'max:999999.99'],
|
||||
'tier_min_qty' => ['nullable', 'integer', 'min:2', 'max:999999'],
|
||||
'tier_price_decimal' => ['nullable', 'numeric', 'min:0', 'max:999999.99'],
|
||||
'photo' => [
|
||||
'nullable',
|
||||
File::types(['jpg', 'jpeg', 'png', 'gif', 'webp', 'heic', 'heif'])
|
||||
@ -50,6 +52,12 @@ class UpdateShoppingItemRequest extends FormRequest
|
||||
'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.',
|
||||
'tier_min_qty.integer' => 'Die Mindestmenge (Staffel) muss eine ganze Zahl sein.',
|
||||
'tier_min_qty.min' => 'Die Mindestmenge (Staffel) muss mindestens 2 sein.',
|
||||
'tier_min_qty.max' => 'Die Mindestmenge (Staffel) ist zu gross.',
|
||||
'tier_price_decimal.numeric' => 'Der Staffelpreis muss eine Zahl sein.',
|
||||
'tier_price_decimal.min' => 'Der Staffelpreis darf nicht negativ sein.',
|
||||
'tier_price_decimal.max' => 'Der Staffelpreis ist zu gross.',
|
||||
'photo.max' => 'Das Foto darf maximal 15 MB gross sein.',
|
||||
'photo.uploaded' => 'Das Foto konnte nicht hochgeladen werden. Haeufig: Datei zu gross fuer die PHP-Grenze auf dem Server (upload_max_filesize / post_max_size). Bitte ein kleineres Bild waehlen oder die Server-Limits erhoehen (siehe scripts/apache-einkauf-debian-setup.sh, Abschnitt PHP-Uploads).',
|
||||
];
|
||||
@ -63,6 +71,8 @@ class UpdateShoppingItemRequest extends FormRequest
|
||||
'store_id' => 'Geschaeft',
|
||||
'new_store_name' => 'neues Geschaeft',
|
||||
'price_decimal' => 'Preis',
|
||||
'tier_min_qty' => 'Mindestmenge (Staffel)',
|
||||
'tier_price_decimal' => 'Staffelpreis',
|
||||
'photo' => 'Foto',
|
||||
];
|
||||
}
|
||||
|
||||
@ -11,6 +11,8 @@ class ItemPriceLog extends Model
|
||||
'shopping_item_id',
|
||||
'store_id',
|
||||
'price_decimal',
|
||||
'tier_min_qty',
|
||||
'tier_price_decimal',
|
||||
'currency',
|
||||
'logged_at',
|
||||
'photo_path',
|
||||
@ -21,6 +23,7 @@ class ItemPriceLog extends Model
|
||||
{
|
||||
return [
|
||||
'price_decimal' => 'decimal:2',
|
||||
'tier_price_decimal' => 'decimal:2',
|
||||
'logged_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('item_price_logs', function (Blueprint $table) {
|
||||
$table->unsignedInteger('tier_min_qty')->nullable()->after('price_decimal');
|
||||
$table->decimal('tier_price_decimal', 10, 2)->nullable()->after('tier_min_qty');
|
||||
});
|
||||
|
||||
// MySQL: Spaltenaenderung ohne doctrine/dbal.
|
||||
DB::statement('ALTER TABLE item_price_logs MODIFY price_decimal DECIMAL(10,2) NULL');
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('item_price_logs', function (Blueprint $table) {
|
||||
$table->dropColumn(['tier_min_qty', 'tier_price_decimal']);
|
||||
});
|
||||
|
||||
DB::statement('ALTER TABLE item_price_logs MODIFY price_decimal DECIMAL(10,2) NOT NULL');
|
||||
}
|
||||
};
|
||||
|
||||
@ -136,7 +136,16 @@
|
||||
Erledigt: {{ optional($item->done_at)->format('d.m.Y H:i') ?: '-' }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-600">
|
||||
Letzter Preis: {{ $item->latestPriceLog?->price_decimal !== null ? number_format((float) $item->latestPriceLog->price_decimal, 2, ',', '.') . ' EUR' : '-' }}
|
||||
Letzter Preis:
|
||||
@if($item->latestPriceLog?->price_decimal !== null)
|
||||
{{ number_format((float) $item->latestPriceLog->price_decimal, 2, ',', '.') }} EUR/Stk
|
||||
@else
|
||||
-
|
||||
@endif
|
||||
@if($item->latestPriceLog?->tier_min_qty !== null && $item->latestPriceLog?->tier_price_decimal !== null)
|
||||
<span class="mx-1">·</span>
|
||||
{{ number_format((float) $item->latestPriceLog->tier_price_decimal, 2, ',', '.') }} EUR ab {{ (int) $item->latestPriceLog->tier_min_qty }} Stk
|
||||
@endif
|
||||
</div>
|
||||
@if($item->latestPhotoLog)
|
||||
<div class="mt-2">
|
||||
@ -218,7 +227,8 @@
|
||||
<select
|
||||
id="list-switch"
|
||||
class="rounded-md border-gray-300 text-sm min-h-[44px] max-w-md flex-1"
|
||||
onchange="this.form.action = @js(url('/shopping-lists')) + '/' + this.value + '/switch'; this.form.requestSubmit();"
|
||||
data-base-url="{{ url('/shopping-lists') }}"
|
||||
onchange="this.form.action = this.dataset.baseUrl + '/' + this.value + '/switch'; this.form.requestSubmit();"
|
||||
>
|
||||
@foreach($accessibleLists as $list)
|
||||
<option value="{{ $list->id }}" @selected($list->id === $currentList->id)>
|
||||
|
||||
@ -86,15 +86,37 @@
|
||||
step="0.01"
|
||||
min="0"
|
||||
name="price_decimal"
|
||||
placeholder="Preis in EUR (optional)"
|
||||
placeholder="Preis pro 1 Stueck (optional) z. B. 3.00"
|
||||
class="rounded-md border-gray-300 text-sm min-h-[44px]"
|
||||
>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<input
|
||||
type="number"
|
||||
min="2"
|
||||
step="1"
|
||||
name="tier_min_qty"
|
||||
placeholder="ab Menge (optional) z. B. 2"
|
||||
class="rounded-md border-gray-300 text-sm min-h-[44px]"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0"
|
||||
name="tier_price_decimal"
|
||||
placeholder="Staffelpreis (optional) z. B. 2.00"
|
||||
class="rounded-md border-gray-300 text-sm min-h-[44px]"
|
||||
>
|
||||
</div>
|
||||
<label class="text-xs text-gray-600">Foto Kassenbon (optional)</label>
|
||||
<input type="file" name="photo" accept="image/*" class="text-sm min-h-[44px]">
|
||||
@if($item->latestPriceLog)
|
||||
<div class="text-xs text-gray-500">
|
||||
Letzter Preis:
|
||||
{{ number_format((float) $item->latestPriceLog->price_decimal, 2, ',', '.') }} EUR
|
||||
{{ $item->latestPriceLog->price_decimal !== null ? number_format((float) $item->latestPriceLog->price_decimal, 2, ',', '.') . ' EUR/Stk' : '-' }}
|
||||
@if($item->latestPriceLog->tier_min_qty !== null && $item->latestPriceLog->tier_price_decimal !== null)
|
||||
<span class="mx-1">·</span>
|
||||
{{ number_format((float) $item->latestPriceLog->tier_price_decimal, 2, ',', '.') }} EUR ab {{ (int) $item->latestPriceLog->tier_min_qty }} Stk
|
||||
@endif
|
||||
bei {{ $item->latestPriceLog->store?->name ?: 'ohne Geschaeft' }}
|
||||
({{ optional($item->latestPriceLog->logged_at)->format('d.m.Y H:i') }})
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user