Implement a comprehensive notification system using Kavenegar SMS
gateway and enhance system traceability through audit logging and
detailed shipment status history.
- SMS Integration:
- Add Kavenegar SMS service with configurable API keys and sender
numbers via system settings.
- Implement automated SMS notifications for shipment approval,
rejection, successful payments, and tracking updates.
- Add administrative UI in Filament to manage SMS gateway settings
and toggle specific notification types.
- Audit & Tracking:
- Apply `Auditable` trait to core models (User, Shipment, Wallet,
etc.) to track changes.
- Refactor `ShipmentStatusHistory` to include status transitions
(`from_status` to `to_status`) and specific reasons for changes.
- Implement `ShipmentObserver` to automate notification triggers
on status changes.
- Database & Config:
- Add migrations for enhanced shipment status history tracking.
- Update `.env.example` and `config/ifnex.php` with Kavenegar
configuration parameters.
132 lines
3.2 KiB
PHP
132 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
|
use App\Enums\TransactionType;
|
|
use App\Enums\TransactionStatus;
|
|
use App\Enums\PaymentGateway;
|
|
use App\Traits\Auditable;
|
|
|
|
class WalletTransaction extends Model
|
|
{
|
|
use SoftDeletes, Auditable;
|
|
|
|
protected $fillable = [
|
|
'wallet_id',
|
|
'amount',
|
|
'balance_before',
|
|
'balance_after',
|
|
'type',
|
|
'status',
|
|
'gateway',
|
|
'gateway_reference_id',
|
|
'description',
|
|
'admin_notes',
|
|
'created_by',
|
|
'approved_by',
|
|
'approved_at',
|
|
'transactionable_type',
|
|
'transactionable_id',
|
|
'ip_address',
|
|
'user_agent',
|
|
'metadata',
|
|
];
|
|
|
|
protected $casts = [
|
|
'amount' => 'decimal:2',
|
|
'balance_before' => 'decimal:2',
|
|
'balance_after' => 'decimal:2',
|
|
'type' => TransactionType::class,
|
|
'status' => TransactionStatus::class,
|
|
'gateway' => PaymentGateway::class,
|
|
'approved_at' => 'datetime',
|
|
'metadata' => 'array',
|
|
];
|
|
|
|
public function wallet(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Wallet::class);
|
|
}
|
|
|
|
public function creator(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'created_by');
|
|
}
|
|
|
|
public function approver(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'approved_by');
|
|
}
|
|
|
|
public function transactionable(): MorphTo
|
|
{
|
|
return $this->morphTo();
|
|
}
|
|
|
|
// Scopes
|
|
public function scopeCompleted($query)
|
|
{
|
|
return $query->where('status', TransactionStatus::COMPLETED);
|
|
}
|
|
|
|
public function scopePending($query)
|
|
{
|
|
return $query->where('status', TransactionStatus::PENDING);
|
|
}
|
|
|
|
public function scopeDeposits($query)
|
|
{
|
|
return $query->where('type', TransactionType::DEPOSIT);
|
|
}
|
|
|
|
public function scopeWithdrawals($query)
|
|
{
|
|
return $query->where('type', TransactionType::WITHDRAWAL);
|
|
}
|
|
|
|
// Helper methods
|
|
public function isCompleted(): bool
|
|
{
|
|
return $this->status === TransactionStatus::COMPLETED;
|
|
}
|
|
|
|
public function isPending(): bool
|
|
{
|
|
return $this->status === TransactionStatus::PENDING;
|
|
}
|
|
|
|
public function approve(?User $admin = null): bool
|
|
{
|
|
return $this->update([
|
|
'status' => TransactionStatus::COMPLETED,
|
|
'approved_by' => $admin?->id,
|
|
'approved_at' => now(),
|
|
]);
|
|
}
|
|
|
|
public function fail(string $reason = null): bool
|
|
{
|
|
$metadata = $this->metadata ?? [];
|
|
$metadata['fail_reason'] = $reason;
|
|
|
|
return $this->update([
|
|
'status' => TransactionStatus::FAILED,
|
|
'metadata' => $metadata,
|
|
]);
|
|
}
|
|
|
|
public function getFormattedAmountAttribute(): string
|
|
{
|
|
$prefix = $this->amount >= 0 ? '+' : '';
|
|
return $prefix . number_format($this->amount, 0) . ' ریال';
|
|
}
|
|
|
|
public function getGatewayLabelAttribute(): string
|
|
{
|
|
return $this->gateway?->label() ?? 'نامشخص';
|
|
}
|
|
} |