'decimal:2', 'criteria_value' => 'decimal:2', 'achieved_value' => 'decimal:2', 'period_start' => 'datetime', 'period_end' => 'datetime', 'awarded_at' => 'datetime', 'expires_at' => 'datetime', 'meta' => 'array', ]; /* |-------------------------------------------------------------------------- | Relationships |-------------------------------------------------------------------------- */ public function rider(): BelongsTo { return $this->belongsTo(Rider::class, 'rider_id'); } public function earning(): BelongsTo { return $this->belongsTo(RiderEarning::class, 'earning_id'); } /* |-------------------------------------------------------------------------- | Scopes |-------------------------------------------------------------------------- */ public function scopePending($query) { return $query->where('status', 'pending'); } public function scopeAwarded($query) { return $query->where('status', 'awarded'); } public function scopeExpired($query) { return $query->where('status', 'expired'); } public function scopeForPeriod($query, $start, $end) { return $query->whereBetween('period_start', [$start, $end]); } public function scopeOfType($query, string $type) { return $query->where('type', $type); } public function scopeActive($query) { return $query->where('status', 'awarded') ->where(function ($q) { $q->whereNull('expires_at') ->orWhere('expires_at', '>', now()); }); } /* |-------------------------------------------------------------------------- | Bonus Types |-------------------------------------------------------------------------- */ public static function bonusTypes(): array { return [ 'peak_hour' => 'Peak Hour Bonus', 'rain' => 'Rain/Weather Bonus', 'consecutive_delivery' => 'Consecutive Delivery Bonus', 'rating' => 'High Rating Bonus', 'weekly_target' => 'Weekly Target Bonus', 'referral' => 'Referral Bonus', 'signup' => 'Signup Bonus', 'special' => 'Special Bonus', ]; } public function getTypeLabel(): string { return self::bonusTypes()[$this->type] ?? ucfirst($this->type); } /* |-------------------------------------------------------------------------- | Status Methods |-------------------------------------------------------------------------- */ public function isPending(): bool { return $this->status === 'pending'; } public function isAwarded(): bool { return $this->status === 'awarded'; } public function isExpired(): bool { return $this->status === 'expired'; } public function isActive(): bool { return $this->isAwarded() && (! $this->expires_at || $this->expires_at > now()); } /** * Award the bonus to rider. */ public function award(): bool { if (! $this->isPending()) { return false; } $this->update([ 'status' => 'awarded', 'awarded_at' => now(), ]); return true; } /** * Mark bonus as expired. */ public function markExpired(): bool { if (! $this->isPending()) { return false; } $this->update([ 'status' => 'expired', ]); return true; } /* |-------------------------------------------------------------------------- | Factory Methods |-------------------------------------------------------------------------- */ /** * Create a peak hour bonus. */ public static function createPeakHourBonus(Rider $rider, float $amount): self { return static::create([ 'rider_id' => $rider->id, 'restaurant_id' => getUserRestaurantId(), 'type' => 'peak_hour', 'name' => 'Peak Hour Bonus', 'description' => 'Bonus for delivering during peak hours', 'amount' => $amount, 'currency' => config('restaurant-delivery.pricing.currency', 'BDT'), 'status' => 'pending', ]); } /** * Create a weather bonus. */ public static function createWeatherBonus(Rider $rider, float $amount, string $condition = 'rain'): self { return static::create([ 'rider_id' => $rider->id, 'restaurant_id' => getUserRestaurantId(), 'type' => 'rain', 'name' => ucfirst($condition).' Bonus', 'description' => "Bonus for delivering during {$condition}", 'amount' => $amount, 'currency' => config('restaurant-delivery.pricing.currency', 'BDT'), 'status' => 'pending', 'meta' => ['weather_condition' => $condition], ]); } /** * Create a consecutive delivery bonus. */ public static function createConsecutiveBonus(Rider $rider, float $amount, int $deliveryCount): self { return static::create([ 'rider_id' => $rider->id, 'restaurant_id' => getUserRestaurantId(), 'type' => 'consecutive_delivery', 'name' => 'Consecutive Delivery Bonus', 'description' => "Bonus for {$deliveryCount} consecutive deliveries", 'amount' => $amount, 'currency' => config('restaurant-delivery.pricing.currency', 'BDT'), 'criteria' => 'consecutive_deliveries', 'criteria_value' => $deliveryCount, 'achieved_value' => $deliveryCount, 'status' => 'pending', ]); } /** * Create a weekly target bonus. */ public static function createWeeklyTargetBonus( Rider $rider, float $amount, int $targetDeliveries, int $achievedDeliveries ): self { return static::create([ 'rider_id' => $rider->id, 'restaurant_id' => getUserRestaurantId(), 'type' => 'weekly_target', 'name' => 'Weekly Target Bonus', 'description' => "Bonus for completing {$targetDeliveries} deliveries in a week", 'amount' => $amount, 'currency' => config('restaurant-delivery.pricing.currency', 'BDT'), 'criteria' => 'weekly_deliveries', 'criteria_value' => $targetDeliveries, 'achieved_value' => $achievedDeliveries, 'period_start' => now()->startOfWeek(), 'period_end' => now()->endOfWeek(), 'status' => 'pending', ]); } /** * Create a rating bonus. */ public static function createRatingBonus(Rider $rider, float $amount, float $rating): self { return static::create([ 'rider_id' => $rider->id, 'restaurant_id' => getUserRestaurantId(), 'type' => 'rating', 'name' => 'High Rating Bonus', 'description' => "Bonus for maintaining {$rating}+ rating", 'amount' => $amount, 'currency' => config('restaurant-delivery.pricing.currency', 'BDT'), 'criteria' => 'min_rating', 'criteria_value' => $rating, 'achieved_value' => $rider->rating, 'status' => 'pending', ]); } }