ifnex/04_Laravel/app/Models/WalletTransaction.php
Kazem Alghasi d6e04d53fa feat(core): integrate Kavenegar SMS notifications and audit logging
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.
2026-09-10 00:52:18 +03:30

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() ?? 'نامشخص';
}
}