*/ use HasFactory, LogsActivity, SoftDeletes; /** * Audit trail on status transitions and driver/vehicle assignment only — * not every column (domain.md §6, §5a; T6.2). */ public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() ->logOnly(['status', 'driver_name', 'driver_phone', 'car_plate_number', 'car_model']) ->logOnlyDirty() ->dontLogEmptyChanges() ->useLogName('booking'); } /** * @var list */ protected $fillable = [ 'booking_ref', 'user_id', 'openid', 'ev_route_id', 'linked_booking_id', 'is_return_leg', 'departure_time_slot_id', 'travel_date', 'passenger_name', 'passenger_phone', 'notes', 'remark', 'pickup_address', 'pickup_lat', 'pickup_lng', 'dropoff_address', 'dropoff_lat', 'dropoff_lng', 'price', 'status', 'created_by_channel', 'driver_name', 'driver_phone', 'car_plate_number', 'car_model', ]; /** * @return array */ protected function casts(): array { return [ 'travel_date' => 'date', 'pickup_lat' => 'decimal:7', 'pickup_lng' => 'decimal:7', 'dropoff_lat' => 'decimal:7', 'dropoff_lng' => 'decimal:7', 'price' => 'decimal:2', 'status' => BookingStatus::class, 'is_return_leg' => 'boolean', 'created_by_channel' => BookingChannel::class, ]; } public function user(): BelongsTo { return $this->belongsTo(User::class); } public function route(): BelongsTo { return $this->belongsTo(EvRoute::class, 'ev_route_id'); } /** * The other leg of a round trip (outbound <-> return), linked * bidirectionally by CreateBookingAction. Null for a plain one-way * booking — see the `isRoundTrip()` accessor (domain.md §2b). */ public function linkedBooking(): BelongsTo { return $this->belongsTo(Booking::class, 'linked_booking_id'); } public function timeSlot(): BelongsTo { return $this->belongsTo(DepartureTimeSlot::class, 'departure_time_slot_id'); } public function vehicleOptions(): HasMany { return $this->hasMany(BookingVehicleOption::class); } public function payments(): HasMany { return $this->hasMany(Payment::class); } /** * True when this booking has a linked leg — i.e. it's one half of a * round trip. Computed, not stored: presence of `linked_booking_id` is * the single source of truth, so it can't drift out of sync the way a * separate flag column could (domain.md §2b). */ public function isRoundTrip(): Attribute { return Attribute::make( get: fn (): bool => $this->linked_booking_id !== null, ); } }