first(); if (!$setting) { return config('ifnex.financial_defaults.aed_to_irr', 455000); } return (float) $setting->value; } /** * تنظیم نرخ جدید به‌صورت دستی (توسط مدیر) → مستقیم اعمال می‌شه (status=approved) */ public function setRate( string $from, string $to, float $newRate, ?int $userId = null, ?string $notes = null ): ExchangeRateHistory { $key = strtolower("{$from}_to_{$to}"); $oldRate = $this->getCurrentRate($from, $to); SystemSetting::updateOrCreate( ['key' => $key], [ 'value' => (string) $newRate, 'type' => 'number', 'description' => "{$from} to {$to} exchange rate", ] ); return $this->recordChange( from: $from, to: $to, 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 $from = 'AED', string $to = 'IRR'): array { $latest = ExchangeRateHistory::where('currency_from', $from) ->where('currency_to', $to) ->where('status', 'approved') // فقط تغییرات تأییدشده ->orderBy('created_at', 'desc') ->with('changedBy') ->first(); $currentRate = $this->getCurrentRate($from, $to); 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 $from = 'AED', string $to = 'IRR', int $limit = 7): array { return ExchangeRateHistory::where('currency_from', $from) ->where('currency_to', $to) ->where('status', 'approved') // فقط تغییرات تأییدشده ->orderBy('created_at', 'asc') ->limit($limit) ->pluck('new_rate') ->toArray(); } /** * ثبت تغییر نرخ (داخلی). ورود دستی → status=approved (مستقیم اعمال). */ private function recordChange( string $from, string $to, 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' => $from, 'currency_to' => $to, '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', // ورود دستی مستقیم اعمال می‌شه ]); } /** * تأیید یک نرخ در انتظار توسط مدیر. * نرخ روی SystemSetting (زنده) اعمال می‌شه و رکورد history تأیید می‌شه. */ public function approveRate(ExchangeRateHistory $history, int $userId): ExchangeRateHistory { if (! $history->isPending()) { throw new RuntimeException('این رکورد در وضعیت قابل تأیید نیست.'); } return DB::transaction(function () use ($history, $userId) { $key = strtolower("{$history->currency_from}_to_{$history->currency_to}"); SystemSetting::updateOrCreate( ['key' => $key], [ 'value' => (string) $history->new_rate, 'description' => "{$history->currency_from} to {$history->currency_to} exchange rate", 'updated_by' => $userId, ] ); $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(); } }