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,350 @@
<?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\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
use Modules\RestaurantDelivery\Traits\HasRestaurant;
use Modules\RestaurantDelivery\Traits\HasUuid;
class RiderPayout extends Model
{
use HasFactory, HasRestaurant, HasUuid, SoftDeletes;
public const TABLE_NAME = 'restaurant_rider_payouts';
protected $table = self::TABLE_NAME;
protected $fillable = [
'payout_number',
'rider_id',
'restaurant_id',
'period_start',
'period_end',
'period_type',
'total_deliveries_amount',
'total_tips_amount',
'total_bonuses_amount',
'total_penalties_amount',
'total_adjustments_amount',
'gross_amount',
'platform_fees',
'tax_deductions',
'other_deductions',
'net_amount',
'currency',
'total_deliveries',
'total_tips_count',
'total_bonuses_count',
'total_penalties_count',
'status',
'payment_method',
'payment_reference',
'payment_details',
'processed_at',
'paid_at',
'failed_at',
'failure_reason',
'retry_count',
'approved_by',
'approved_at',
'processed_by',
'notes',
'meta',
];
protected $casts = [
'period_start' => 'date',
'period_end' => 'date',
'total_deliveries_amount' => 'decimal:2',
'total_tips_amount' => 'decimal:2',
'total_bonuses_amount' => 'decimal:2',
'total_penalties_amount' => 'decimal:2',
'total_adjustments_amount' => 'decimal:2',
'gross_amount' => 'decimal:2',
'platform_fees' => 'decimal:2',
'tax_deductions' => 'decimal:2',
'other_deductions' => 'decimal:2',
'net_amount' => 'decimal:2',
'payment_details' => 'array',
'processed_at' => 'datetime',
'paid_at' => 'datetime',
'failed_at' => 'datetime',
'approved_at' => 'datetime',
'meta' => 'array',
];
protected static function boot()
{
parent::boot();
static::creating(function ($payout) {
if (empty($payout->payout_number)) {
$payout->payout_number = static::generatePayoutNumber();
}
});
}
/*
|--------------------------------------------------------------------------
| Relationships
|--------------------------------------------------------------------------
*/
public function rider(): BelongsTo
{
return $this->belongsTo(Rider::class, 'rider_id');
}
public function earnings(): HasMany
{
return $this->hasMany(RiderEarning::class, 'payout_id');
}
public function tips(): HasMany
{
return $this->hasMany(DeliveryTip::class, 'payout_id');
}
public function approvedBy(): BelongsTo
{
return $this->belongsTo(config('auth.providers.users.model'), 'approved_by');
}
public function processedBy(): BelongsTo
{
return $this->belongsTo(config('auth.providers.users.model'), 'processed_by');
}
/*
|--------------------------------------------------------------------------
| Scopes
|--------------------------------------------------------------------------
*/
public function scopePending($query)
{
return $query->where('status', 'pending');
}
public function scopeProcessing($query)
{
return $query->where('status', 'processing');
}
public function scopeCompleted($query)
{
return $query->where('status', 'completed');
}
public function scopeFailed($query)
{
return $query->where('status', 'failed');
}
public function scopeForPeriod($query, $startDate, $endDate)
{
return $query->whereBetween('period_start', [$startDate, $endDate]);
}
public function scopeWeekly($query)
{
return $query->where('period_type', 'weekly');
}
public function scopeMonthly($query)
{
return $query->where('period_type', 'monthly');
}
/*
|--------------------------------------------------------------------------
| Methods
|--------------------------------------------------------------------------
*/
public static function generatePayoutNumber(): string
{
do {
$number = 'PAY-'.date('Ymd').'-'.strtoupper(Str::random(6));
} while (static::where('payout_number', $number)->exists());
return $number;
}
public function approve(int $approvedBy): void
{
$this->update([
'status' => 'processing',
'approved_by' => $approvedBy,
'approved_at' => now(),
]);
}
public function process(int $processedBy): void
{
$this->update([
'processed_by' => $processedBy,
'processed_at' => now(),
]);
}
public function markAsCompleted(string $paymentReference, array $paymentDetails = []): void
{
$this->update([
'status' => 'completed',
'payment_reference' => $paymentReference,
'payment_details' => $paymentDetails,
'paid_at' => now(),
]);
// Mark all associated earnings as paid
$this->earnings()->update([
'is_paid' => true,
'paid_at' => now(),
'status' => 'paid',
]);
// Mark all associated tips as transferred
$this->tips()->update([
'is_transferred' => true,
'transferred_at' => now(),
'payment_status' => 'transferred',
]);
}
public function markAsFailed(string $reason): void
{
$this->update([
'status' => 'failed',
'failed_at' => now(),
'failure_reason' => $reason,
]);
}
public function retry(): void
{
$this->update([
'status' => 'processing',
'retry_count' => $this->retry_count + 1,
'failed_at' => null,
'failure_reason' => null,
]);
}
public function cancel(): void
{
$this->update(['status' => 'cancelled']);
// Unlink earnings from this payout
$this->earnings()->update(['payout_id' => null]);
$this->tips()->update(['payout_id' => null]);
}
public function isPending(): bool
{
return $this->status === 'pending';
}
public function isProcessing(): bool
{
return $this->status === 'processing';
}
public function isCompleted(): bool
{
return $this->status === 'completed';
}
public function isFailed(): bool
{
return $this->status === 'failed';
}
public function canRetry(): bool
{
return $this->isFailed() && $this->retry_count < 3;
}
/*
|--------------------------------------------------------------------------
| Static Factory Methods
|--------------------------------------------------------------------------
*/
public static function createForRider(
Rider $rider,
\DateTime $periodStart,
\DateTime $periodEnd,
string $periodType = 'weekly'
): ?self {
// Get all unpaid earnings for the period
$earnings = RiderEarning::where('rider_id', $rider->id)
->payable()
->forPeriod($periodStart, $periodEnd)
->get();
// Get all untransferred tips
$tips = DeliveryTip::where('rider_id', $rider->id)
->notTransferred()
->forPeriod($periodStart, $periodEnd)
->get();
if ($earnings->isEmpty() && $tips->isEmpty()) {
return null;
}
// Calculate amounts by type
$deliveriesAmount = $earnings->where('type', 'delivery')->sum('net_amount');
$bonusesAmount = $earnings->where('type', 'bonus')->sum('net_amount');
$penaltiesAmount = abs($earnings->where('type', 'penalty')->sum('net_amount'));
$adjustmentsAmount = $earnings->where('type', 'adjustment')->sum('net_amount');
$tipsAmount = $tips->sum('rider_amount');
$grossAmount = $deliveriesAmount + $bonusesAmount + $tipsAmount + $adjustmentsAmount;
$netAmount = $grossAmount - $penaltiesAmount;
// Check minimum payout amount
$minimumAmount = config('restaurant-delivery.earnings.payout.minimum_amount', 500);
if ($netAmount < $minimumAmount) {
return null;
}
$payout = static::create([
'rider_id' => $rider->id,
'restaurant_id' => getUserRestaurantId(),
'period_start' => $periodStart,
'period_end' => $periodEnd,
'period_type' => $periodType,
'total_deliveries_amount' => $deliveriesAmount,
'total_tips_amount' => $tipsAmount,
'total_bonuses_amount' => $bonusesAmount,
'total_penalties_amount' => $penaltiesAmount,
'total_adjustments_amount' => $adjustmentsAmount,
'gross_amount' => $grossAmount,
'platform_fees' => 0,
'tax_deductions' => 0,
'other_deductions' => $penaltiesAmount,
'net_amount' => $netAmount,
'currency' => config('restaurant-delivery.pricing.currency', 'BDT'),
'total_deliveries' => $earnings->where('type', 'delivery')->count(),
'total_tips_count' => $tips->count(),
'total_bonuses_count' => $earnings->where('type', 'bonus')->count(),
'total_penalties_count' => $earnings->where('type', 'penalty')->count(),
'status' => 'pending',
'payment_method' => $rider->mobile_wallet_provider ?? 'bank_transfer',
]);
// Link earnings and tips to this payout
$earnings->each(fn ($e) => $e->update(['payout_id' => $payout->id]));
$tips->each(fn ($t) => $t->update(['payout_id' => $payout->id]));
return $payout;
}
}