300 lines
8.4 KiB
PHP
300 lines
8.4 KiB
PHP
<?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',
|
|
]);
|
|
}
|
|
}
|