'boolean', 'surge_enabled' => 'boolean', 'base_fare' => 'decimal:2', 'minimum_fare' => 'decimal:2', 'per_km_charge' => 'decimal:2', 'free_distance' => 'decimal:2', 'max_distance' => 'decimal:2', 'surge_multiplier' => 'decimal:2', 'conditions' => 'array', 'valid_from' => 'datetime:H:i', 'valid_until' => 'datetime:H:i', ]; /* |-------------------------------------------------------------------------- | Relationships |-------------------------------------------------------------------------- */ public function zone(): BelongsTo { return $this->belongsTo(DeliveryZone::class, 'zone_id'); } /* |-------------------------------------------------------------------------- | Scopes |-------------------------------------------------------------------------- */ public function scopeActive($query) { return $query->where('is_active', true); } public function scopeByPriority($query) { return $query->orderBy('priority'); } public function scopeValidNow($query) { $now = now(); $currentTime = $now->format('H:i:s'); $currentDay = pow(2, $now->dayOfWeek); // Bitmask for current day return $query->where(function ($q) use ($currentTime) { $q->whereNull('valid_from') ->orWhere(function ($q2) use ($currentTime) { $q2->where('valid_from', '<=', $currentTime) ->where('valid_until', '>=', $currentTime); }); })->where(function ($q) use ($currentDay) { $q->whereNull('valid_days') ->orWhereRaw('(valid_days & ?) > 0', [$currentDay]); }); } /* |-------------------------------------------------------------------------- | Methods |-------------------------------------------------------------------------- */ public function isValidNow(): bool { $now = now(); // Check time validity if ($this->valid_from && $this->valid_until) { $currentTime = $now->format('H:i:s'); if ($currentTime < $this->valid_from || $currentTime > $this->valid_until) { return false; } } // Check day validity (bitmask) if ($this->valid_days !== null) { $currentDayBit = pow(2, $now->dayOfWeek); if (($this->valid_days & $currentDayBit) === 0) { return false; } } return true; } public function calculateCharge(float $distance): float { // Check max distance if ($this->max_distance && $distance > $this->max_distance) { return -1; // Indicates delivery not possible } // Calculate distance charge $chargeableDistance = max(0, $distance - $this->free_distance); $distanceCharge = $chargeableDistance * $this->per_km_charge; // Calculate total $total = $this->base_fare + $distanceCharge; // Apply surge if enabled if ($this->surge_enabled && $this->surge_multiplier > 1) { $total *= $this->surge_multiplier; } // Apply minimum fare $total = max($total, $this->minimum_fare); return round($total, 2); } public function getChargeBreakdown(float $distance): array { $chargeableDistance = max(0, $distance - $this->free_distance); $distanceCharge = $chargeableDistance * $this->per_km_charge; $baseTotal = $this->base_fare + $distanceCharge; $surgeAmount = 0; if ($this->surge_enabled && $this->surge_multiplier > 1) { $surgeAmount = $baseTotal * ($this->surge_multiplier - 1); } $total = $baseTotal + $surgeAmount; $minimumApplied = false; if ($total < $this->minimum_fare) { $total = $this->minimum_fare; $minimumApplied = true; } return [ 'base_fare' => $this->base_fare, 'distance' => $distance, 'free_distance' => $this->free_distance, 'chargeable_distance' => $chargeableDistance, 'per_km_charge' => $this->per_km_charge, 'distance_charge' => $distanceCharge, 'surge_enabled' => $this->surge_enabled, 'surge_multiplier' => $this->surge_multiplier, 'surge_amount' => $surgeAmount, 'minimum_fare' => $this->minimum_fare, 'minimum_applied' => $minimumApplied, 'total' => round($total, 2), ]; } public function meetsConditions(array $deliveryData): bool { if (empty($this->conditions)) { return true; } foreach ($this->conditions as $condition) { if (! $this->evaluateCondition($condition, $deliveryData)) { return false; } } return true; } protected function evaluateCondition(array $condition, array $data): bool { $field = $condition['field'] ?? null; $operator = $condition['operator'] ?? null; $value = $condition['value'] ?? null; if (! $field || ! $operator) { return true; } $fieldValue = $data[$field] ?? null; return match ($operator) { 'equals' => $fieldValue == $value, 'not_equals' => $fieldValue != $value, 'greater_than' => $fieldValue > $value, 'less_than' => $fieldValue < $value, 'greater_or_equal' => $fieldValue >= $value, 'less_or_equal' => $fieldValue <= $value, 'in' => in_array($fieldValue, (array) $value), 'not_in' => ! in_array($fieldValue, (array) $value), 'is_true' => (bool) $fieldValue === true, 'is_false' => (bool) $fieldValue === false, default => true, }; } }