Files

300 lines
8.4 KiB
PHP
Raw Permalink Normal View History

2026-03-15 17:08:23 +07:00
<?php
declare(strict_types=1);
namespace Modules\RestaurantDelivery\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Modules\RestaurantDelivery\Traits\HasRestaurant;
use Modules\RestaurantDelivery\Traits\HasUuid;
class RiderBonus extends Model
{
use HasFactory, HasRestaurant, HasUuid, SoftDeletes;
public const TABLE_NAME = 'restaurant_rider_bonuses';
protected $table = self::TABLE_NAME;
protected $fillable = [
'rider_id',
'restaurant_id',
'type',
'name',
'description',
'amount',
'currency',
'criteria',
'criteria_value',
'achieved_value',
'period_start',
'period_end',
'status',
'awarded_at',
'expires_at',
'earning_id',
'meta',
];
protected $casts = [
'amount' => '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',
]);
}
}