backup: before auth-required booking and phone number feature

This commit is contained in:
ghassan committed 2026-04-16 09:43:55 +03:00
1 parent 83d9a67188
commit 900eb24b37
10 files changed
+618 -253

No files matched your search

@@ -52,6 +52,10 @@ class ParkingLotController extends Controller
{
$data = $request->validated();
// Never let validated() overwrite the stored image path with null.
// Only update the image column when a new file is actually uploaded.
unset($data['image']);
if ($request->hasFile('image')) {
if ($parkingLot->image) {
Storage::disk('public')->delete($parkingLot->image);
@@ -91,6 +95,38 @@ class ParkingLotController extends Controller
]);
}
public function updatePricing(Request $request, ParkingLot $parkingLot): JsonResponse
{
$data = $request->validate([
'price_per_hour' => 'required|numeric|min:0|max:100000',
'pricing_rules' => 'nullable|array',
'pricing_rules.*'=> 'nullable|numeric|min:0|max:100000',
]);
// Build clean rules array: only include days that differ from base price.
// Keys 1-7 (Mon-Sun). Null or empty string means "use base price".
$rules = [];
foreach ($data['pricing_rules'] ?? [] as $day => $rate) {
if ($rate !== null && $rate !== '' && (float) $rate !== (float) $data['price_per_hour']) {
$rules[(int) $day] = (float) $rate;
}
}
$parkingLot->update([
'price_per_hour' => $data['price_per_hour'],
'pricing_rules' => empty($rules) ? null : $rules,
]);
return response()->json([
'success' => true,
'message' => 'تم حفظ أسعار الموقف بنجاح',
'data' => [
'price_per_hour' => (float) $parkingLot->price_per_hour,
'pricing_rules' => $parkingLot->pricing_rules,
],
]);
}
public function toggleStatus(ParkingLot $parkingLot): JsonResponse
{
$parkingLot->update(['is_active' => !$parkingLot->is_active]);
@@ -18,6 +18,9 @@ class BookingController extends Controller
{
$validated = $request->validated();
$lot = \App\Models\ParkingLot::find($validated['parking_lot_id']);
$validated['pricing_snapshot'] = $lot?->pricingSnapshot();
$booking = \App\Models\Booking::create($validated);
// Note: In production, create CarRegistry here for the booking car
@@ -80,14 +80,15 @@ class OperatorController extends Controller
}
$booking = Booking::create([
'parking_lot_id' => $lot->id,
'vehicle_plate' => $data['vehicle_plate'],
'customer_name' => $data['customer_name'] ?? null,
'phone' => $data['phone'] ?? null,
'source' => 'walk_in',
'start_time' => now(),
'end_time' => now()->addHours($data['duration_hours']),
'status' => 'active',
'parking_lot_id' => $lot->id,
'vehicle_plate' => $data['vehicle_plate'],
'customer_name' => $data['customer_name'] ?? null,
'phone' => $data['phone'] ?? null,
'source' => 'walk_in',
'start_time' => now(),
'end_time' => now()->addHours($data['duration_hours']),
'status' => 'active',
'pricing_snapshot' => $lot->pricingSnapshot(),
]);
return response()->json([
@@ -128,7 +129,7 @@ class OperatorController extends Controller
$start = $booking->start_time;
$end = now();
$duration = $start->diffInMinutes($end);
$calc = $lot->calculateFee($start, $end);
$calc = $lot->calculateFee($start, $end, $booking->pricing_snapshot);
return response()->json([
'success' => true,
@@ -163,7 +164,7 @@ class OperatorController extends Controller
$lot = $booking->parkingLot;
$start = $booking->start_time;
$end = now();
$calc = $lot->calculateFee($start, $end);
$calc = $lot->calculateFee($start, $end, $booking->pricing_snapshot);
$proofPath = null;
if ($request->hasFile('payment_proof')) {
+3 -1
View File
@@ -25,13 +25,15 @@ class Booking extends Model
'payment_method',
'payment_proof',
'paid_at',
'pricing_snapshot',
];
protected $casts = [
'start_time' => 'datetime',
'end_time' => 'datetime',
'paid_at' => 'datetime',
'total_fee' => 'decimal:2',
'total_fee' => 'decimal:2',
'pricing_snapshot' => 'array',
];
public function parkingLot(): BelongsTo
+22 -4
View File
@@ -81,15 +81,33 @@ class ParkingLot extends Model
return $activeBookings + $activeRegistries;
}
/**
* Build the pricing snapshot array to store on a booking at creation time.
* Format: ['base' => float, 'rules' => [1 => float, ..., 7 => float]]
*/
public function pricingSnapshot(): array
{
return [
'base' => (float) $this->price_per_hour,
'rules' => $this->pricing_rules ?? [],
];
}
/**
* Calculate fee and per-day breakdown between two timestamps.
* Uses pricing_rules (ISO weekday keys 1–7) if set, otherwise price_per_hour.
*
* Pass a $snapshot (from booking->pricing_snapshot) to use the rates that were
* in effect when the booking was created, guaranteeing price changes never
* retroactively affect historical or in-progress bookings.
*
* Returns ['total' => float, 'details' => [['day','date','hours','rate','subtotal'], ...]]
*/
public function calculateFee(Carbon $start, Carbon $end): array
public function calculateFee(Carbon $start, Carbon $end, ?array $snapshot = null): array
{
$rules = $this->pricing_rules ?? [];
$base = $snapshot ? (float) ($snapshot['base'] ?? $this->price_per_hour)
: (float) $this->price_per_hour;
$rules = $snapshot ? ($snapshot['rules'] ?? [])
: ($this->pricing_rules ?? []);
$details = [];
$total = 0.0;
$cursor = $start->copy()->seconds(0);
@@ -99,7 +117,7 @@ class ParkingLot extends Model
$segEnd = ($dayEnd < $end) ? $dayEnd : $end;
$dow = (int) $cursor->format('N'); // 1=Mon … 7=Sun
$rate = isset($rules[$dow]) ? (float) $rules[$dow] : (float) $this->price_per_hour;
$rate = isset($rules[$dow]) ? (float) $rules[$dow] : $base;
$hours = round($cursor->diffInMinutes($segEnd) / 60, 4);
$subtotal = $hours * $rate;