migrate to gtea from bistbucket

This commit is contained in:
2026-03-15 17:08:23 +07:00
commit 129ca2260c
3716 changed files with 566316 additions and 0 deletions

View File

@@ -0,0 +1,386 @@
<?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 RiderEarning extends Model
{
use HasFactory, HasRestaurant, HasUuid, SoftDeletes;
public const TABLE_NAME = 'restaurant_rider_earnings';
protected $table = self::TABLE_NAME;
protected $fillable = [
'rider_id',
'delivery_id',
'restaurant_id',
'type',
'sub_type',
'gross_amount',
'platform_fee',
'tax',
'deductions',
'net_amount',
'currency',
'calculation_breakdown',
'description',
'status',
'confirmed_at',
'payout_id',
'is_paid',
'paid_at',
'earning_date',
'earning_week',
'earning_month',
'earning_year',
];
protected $casts = [
'gross_amount' => 'decimal:2',
'platform_fee' => 'decimal:2',
'tax' => 'decimal:2',
'deductions' => 'decimal:2',
'net_amount' => 'decimal:2',
'calculation_breakdown' => 'array',
'is_paid' => 'boolean',
'confirmed_at' => 'datetime',
'paid_at' => 'datetime',
'earning_date' => 'date',
];
protected static function boot()
{
parent::boot();
static::creating(function ($earning) {
// Set earning date metadata
$date = $earning->earning_date ?? now();
$earning->earning_date = $date;
$earning->earning_week = $date->weekOfYear;
$earning->earning_month = $date->month;
$earning->earning_year = $date->year;
// Calculate net amount
$earning->net_amount = $earning->gross_amount
- $earning->platform_fee
- $earning->tax
- $earning->deductions;
});
}
/*
|--------------------------------------------------------------------------
| Relationships
|--------------------------------------------------------------------------
*/
public function rider(): BelongsTo
{
return $this->belongsTo(Rider::class, 'rider_id');
}
public function delivery(): BelongsTo
{
return $this->belongsTo(Delivery::class, 'delivery_id');
}
public function payout(): BelongsTo
{
return $this->belongsTo(RiderPayout::class, 'payout_id');
}
/*
|--------------------------------------------------------------------------
| Scopes
|--------------------------------------------------------------------------
*/
public function scopePending($query)
{
return $query->where('status', 'pending');
}
public function scopeConfirmed($query)
{
return $query->where('status', 'confirmed');
}
public function scopePaid($query)
{
return $query->where('is_paid', true);
}
public function scopeUnpaid($query)
{
return $query->where('is_paid', false);
}
public function scopeDeliveryEarnings($query)
{
return $query->where('type', 'delivery');
}
public function scopeTips($query)
{
return $query->where('type', 'tip');
}
public function scopeBonuses($query)
{
return $query->where('type', 'bonus');
}
public function scopePenalties($query)
{
return $query->where('type', 'penalty');
}
public function scopeForDate($query, $date)
{
return $query->whereDate('earning_date', $date);
}
public function scopeForWeek($query, int $week, int $year)
{
return $query->where('earning_week', $week)
->where('earning_year', $year);
}
public function scopeForMonth($query, int $month, int $year)
{
return $query->where('earning_month', $month)
->where('earning_year', $year);
}
public function scopeForPeriod($query, $startDate, $endDate)
{
return $query->whereBetween('earning_date', [$startDate, $endDate]);
}
public function scopePayable($query)
{
return $query->where('status', 'confirmed')
->where('is_paid', false);
}
/*
|--------------------------------------------------------------------------
| Methods
|--------------------------------------------------------------------------
*/
public function confirm(): void
{
$this->update([
'status' => 'confirmed',
'confirmed_at' => now(),
]);
}
public function markAsPaid(int $payoutId): void
{
$this->update([
'is_paid' => true,
'paid_at' => now(),
'payout_id' => $payoutId,
'status' => 'paid',
]);
}
public function cancel(): void
{
$this->update(['status' => 'cancelled']);
}
public function isPending(): bool
{
return $this->status === 'pending';
}
public function isConfirmed(): bool
{
return $this->status === 'confirmed';
}
public function isPaid(): bool
{
return $this->is_paid;
}
public function isDebit(): bool
{
return in_array($this->type, ['penalty', 'adjustment']) && $this->net_amount < 0;
}
/*
|--------------------------------------------------------------------------
| Static Factory Methods
|--------------------------------------------------------------------------
*/
public static function createForDelivery(Delivery $delivery): ?self
{
if (! $delivery->rider) {
return null;
}
$rider = $delivery->rider;
$grossAmount = $rider->calculateEarningForDelivery($delivery);
$breakdown = [
'delivery_charge' => $delivery->total_delivery_charge,
'commission_type' => $rider->commission_type->value,
'commission_rate' => $rider->commission_rate,
'distance' => $delivery->distance,
'base_earning' => $grossAmount,
];
// Add any bonuses
$bonusAmount = static::calculateDeliveryBonuses($delivery, $rider);
$grossAmount += $bonusAmount;
$breakdown['bonuses'] = $bonusAmount;
return static::create([
'rider_id' => $rider->id,
'delivery_id' => $delivery->id,
'restaurant_id' => getUserRestaurantId(),
'type' => 'delivery',
'gross_amount' => $grossAmount,
'platform_fee' => 0,
'tax' => 0,
'deductions' => 0,
'net_amount' => $grossAmount,
'currency' => config('restaurant-delivery.pricing.currency', 'BDT'),
'calculation_breakdown' => $breakdown,
'description' => "Delivery #{$delivery->tracking_code}",
'status' => 'pending',
'earning_date' => now(),
]);
}
public static function createFromTip(DeliveryTip $tip): ?self
{
return static::create([
'rider_id' => $tip->rider_id,
'delivery_id' => $tip->delivery_id,
'restaurant_id' => getUserRestaurantId(),
'type' => 'tip',
'gross_amount' => $tip->rider_amount,
'platform_fee' => 0,
'tax' => 0,
'deductions' => 0,
'net_amount' => $tip->rider_amount,
'currency' => $tip->currency,
'calculation_breakdown' => [
'total_tip' => $tip->amount,
'rider_share' => $tip->rider_share_percentage,
'rider_amount' => $tip->rider_amount,
],
'description' => "Tip for delivery #{$tip->delivery->tracking_code}",
'status' => 'confirmed',
'confirmed_at' => now(),
'earning_date' => now(),
]);
}
public static function createBonus(
Rider $rider,
string $subType,
float $amount,
string $description,
?Delivery $delivery = null
): self {
return static::create([
'rider_id' => $rider->id,
'delivery_id' => $delivery?->id,
'restaurant_id' => getUserRestaurantId(),
'type' => 'bonus',
'sub_type' => $subType,
'gross_amount' => $amount,
'platform_fee' => 0,
'tax' => 0,
'deductions' => 0,
'net_amount' => $amount,
'currency' => config('restaurant-delivery.pricing.currency', 'BDT'),
'description' => $description,
'status' => 'confirmed',
'confirmed_at' => now(),
'earning_date' => now(),
]);
}
public static function createPenalty(
Rider $rider,
string $subType,
float $amount,
string $description,
?Delivery $delivery = null
): self {
return static::create([
'rider_id' => $rider->id,
'delivery_id' => $delivery?->id,
'restaurant_id' => getUserRestaurantId(),
'type' => 'penalty',
'sub_type' => $subType,
'gross_amount' => -$amount,
'platform_fee' => 0,
'tax' => 0,
'deductions' => 0,
'net_amount' => -$amount,
'currency' => config('restaurant-delivery.pricing.currency', 'BDT'),
'description' => $description,
'status' => 'confirmed',
'confirmed_at' => now(),
'earning_date' => now(),
]);
}
protected static function calculateDeliveryBonuses(Delivery $delivery, Rider $rider): float
{
$bonus = 0;
$config = config('restaurant-delivery.earnings.bonuses');
// Peak hour bonus
if ($config['peak_hour_bonus']['enabled'] && static::isPeakHour()) {
$bonus += $config['peak_hour_bonus']['amount'];
}
// Rating bonus
if ($config['rating_bonus']['enabled'] && $rider->rating >= $config['rating_bonus']['min_rating']) {
$bonus += $config['rating_bonus']['amount'];
}
return $bonus;
}
protected static function isPeakHour(): bool
{
$peakHours = config('restaurant-delivery.pricing.peak_hours.slots', []);
$now = now();
$currentDay = $now->dayOfWeekIso;
foreach ($peakHours as $slot) {
if (! in_array($currentDay, $slot['days'] ?? [])) {
continue;
}
$start = \Carbon\Carbon::createFromTimeString($slot['start']);
$end = \Carbon\Carbon::createFromTimeString($slot['end']);
if ($now->between($start, $end)) {
return true;
}
}
return false;
}
}