first(); if (!$currency) { return config('ifnex.financial_defaults.aed_to_irr', 455000); } return (float) $currency->rate_to_rial; } /** * تبدیل مبلغ یک ارز به ریال ایران با نرخ روز. * ریال ارز مبناست و نرخ تمام ارزها فقط نسبت به ریال ذخیره می‌شود. * * @param float $amount مبلغ به ارز داده‌شده * @param string $currencyCode کد ارز مبدأ (مثل AED, USD, EUR) * @return float مبلغ معادل به ریال */ public function convertToIrr(float $amount, string $currencyCode = 'AED'): float { if ($amount == 0) { return 0.0; } // ریال → ریال: تبدیل لازم نیست if (strtoupper($currencyCode) === 'IRR') { return $amount; } return $amount * $this->getCurrentRate($currencyCode); } /** * تنظیم نرخ جدید برای یک ارز (نسبت به ریال). * * @param string $currencyCode کد ارز * @param float $newRate نرخ جدید نسبت به ریال * @param int|null $userId آیدی کاربر تغییردهنده */ public function setRate( string $currencyCode, float $newRate, ?int $userId = null, ?string $notes = null ): ExchangeRateHistory { $currencyCode = strtoupper($currencyCode); $oldRate = $this->getCurrentRate($currencyCode); Currency::where('code', $currencyCode)->update([ 'rate_to_rial' => $newRate, ]); return $this->recordChange( currencyCode: $currencyCode, oldRate: $oldRate, newRate: $newRate, source: 'manual', userId: $userId, notes: $notes ); } /** * دریافت آخرین تغییرات نرخ ارز */ public function getLatestChanges(int $limit = 10) { return ExchangeRateHistory::with('changedBy') ->orderBy('created_at', 'desc') ->limit($limit) ->get(); } /** * دریافت اطلاعات آخرین نرخ برای Widget */ public function getLatestRateInfo(string $currencyCode = 'AED'): array { $latest = ExchangeRateHistory::where('currency_from', strtoupper($currencyCode)) ->where('status', 'approved') ->orderBy('created_at', 'desc') ->with('changedBy') ->first(); $currentRate = $this->getCurrentRate($currencyCode); return [ 'current_rate' => $currentRate, 'last_change' => $latest, 'last_updated' => $latest?->created_at, 'changed_by' => $latest?->changedBy?->name, 'source' => $latest?->change_source, 'change_percent' => $latest?->change_percent, ]; } /** * دریافت آخرین N نرخ برای Chart */ public function getRateChart(string $currencyCode = 'AED', int $limit = 7): array { return ExchangeRateHistory::where('currency_from', strtoupper($currencyCode)) ->where('status', 'approved') ->orderBy('created_at', 'asc') ->limit($limit) ->pluck('new_rate') ->toArray(); } /** * ثبت تغییر نرخ (داخلی). */ private function recordChange( string $currencyCode, float $oldRate, float $newRate, string $source = 'manual', ?int $userId = null, ?string $apiSource = null, ?string $notes = null ): ExchangeRateHistory { $changePercent = null; if ($oldRate > 0) { $changePercent = round((($newRate - $oldRate) / $oldRate) * 100, 4); } return ExchangeRateHistory::create([ 'currency_from' => $currencyCode, 'currency_to' => 'IRR', // همیشه ریال 'old_rate' => $oldRate, 'new_rate' => $newRate, 'change_percent' => $changePercent, 'change_source' => $source, 'changed_by' => $userId, 'approved_by' => $userId, 'approved_at' => now(), 'api_source' => $apiSource, 'notes' => $notes, 'status' => 'approved', ]); } /** * تأیید یک نرخ در انتظار توسط مدیر. */ public function approveRate(ExchangeRateHistory $history, int $userId): ExchangeRateHistory { if (! $history->isPending()) { throw new RuntimeException('این رکورد در وضعیت قابل تأیید نیست.'); } return DB::transaction(function () use ($history, $userId) { Currency::where('code', $history->currency_from)->update([ 'rate_to_rial' => $history->new_rate, ]); $history->update([ 'status' => 'approved', 'approved_by' => $userId, 'approved_at' => now(), ]); return $history->fresh(); }); } /** * رد یک نرخ در انتظار. */ public function rejectRate(ExchangeRateHistory $history, int $userId, ?string $reason = null): ExchangeRateHistory { if (! $history->isPending()) { throw new RuntimeException('این رکورد در وضعیت قابل رد نیست.'); } $history->update([ 'status' => 'rejected', 'approved_by' => $userId, 'approved_at' => now(), 'notes' => $reason ?: $history->notes, ]); return $history->fresh(); } /** * لیست نرخ‌های در انتظار تأیید. */ public function getPendingRates(int $limit = 20) { return ExchangeRateHistory::where('status', 'pending') ->orderBy('created_at', 'desc') ->limit($limit) ->get(); } /** * تعداد نرخ‌های در انتظار تأیید (برای badge). */ public function getPendingCount(): int { return ExchangeRateHistory::where('status', 'pending')->count(); } }