feat(pricing): centralize exchange rate management via ExchangeRateService
Migrate exchange rate handling from static system settings to a centralized `ExchangeRateService`. This change ensures that currency rates are managed through a single source of truth that supports history tracking, approval workflows, and API updates. Laravel: - Remove manual exchange rate fields (`aed_to_irr`, `usd_to_irr`, etc.) from `IfnexSettingsPage` and the `SystemSetting` persistence logic. - Update `PricingPageController` and `PriceCalculatorService` to retrieve live rates from `ExchangeRateService` instead of `SystemSetting`. - Refactor `IfnexSettingsPage` UI to display current rates as read-only information with a link to the dedicated exchange rate management resource. Documentation: - Update `CLIENT_DELIVERY.md` to reflect the change in exchange rate management workflow.
This commit is contained in:
parent
d170007a8f
commit
499e0731b1
@ -3,6 +3,7 @@
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use App\Models\SystemSetting;
|
||||
use App\Services\ExchangeRateService;
|
||||
use Filament\Pages\Page;
|
||||
|
||||
class IfnexSettingsPage extends Page
|
||||
@ -17,10 +18,6 @@ class IfnexSettingsPage extends Page
|
||||
public float $vat_rate = 0.09;
|
||||
public float $packing_cost_default = 100000;
|
||||
public float $profit_margin = 1.25;
|
||||
public float $aed_to_irr = 455000;
|
||||
public float $usd_to_irr = 0;
|
||||
public float $cny_to_irr = 0;
|
||||
public float $eur_to_irr = 0;
|
||||
|
||||
public string $kavenegar_api_key = '';
|
||||
public string $kavenegar_sender = '10008566';
|
||||
@ -36,10 +33,6 @@ class IfnexSettingsPage extends Page
|
||||
if (isset($settings['vat_rate'])) $this->vat_rate = (float) $settings['vat_rate'];
|
||||
if (isset($settings['packing_cost_default'])) $this->packing_cost_default = (float) $settings['packing_cost_default'];
|
||||
if (isset($settings['profit_margin'])) $this->profit_margin = (float) $settings['profit_margin'];
|
||||
if (isset($settings['aed_to_irr'])) $this->aed_to_irr = (float) $settings['aed_to_irr'];
|
||||
if (isset($settings['usd_to_irr'])) $this->usd_to_irr = (float) $settings['usd_to_irr'];
|
||||
if (isset($settings['cny_to_irr'])) $this->cny_to_irr = (float) $settings['cny_to_irr'];
|
||||
if (isset($settings['eur_to_irr'])) $this->eur_to_irr = (float) $settings['eur_to_irr'];
|
||||
|
||||
if (isset($settings['kavenegar_api_key'])) $this->kavenegar_api_key = (string) $settings['kavenegar_api_key'];
|
||||
if (isset($settings['kavenegar_sender'])) $this->kavenegar_sender = (string) $settings['kavenegar_sender'];
|
||||
@ -49,6 +42,20 @@ class IfnexSettingsPage extends Page
|
||||
if (isset($settings['kavenegar_send_tracking_update'])) $this->kavenegar_send_tracking_update = (bool) $settings['kavenegar_send_tracking_update'];
|
||||
}
|
||||
|
||||
/**
|
||||
* نرخهای ارز زنده — فقط برای نمایش.
|
||||
* تغییر نرخ فقط از منوی «نرخ ارز» (با تاریخچه و گردش تأیید) انجام میشود.
|
||||
*/
|
||||
public function getCurrentRates(): array
|
||||
{
|
||||
$service = app(ExchangeRateService::class);
|
||||
|
||||
return [
|
||||
'AED' => $service->getCurrentRate('AED', 'IRR'),
|
||||
'USD' => $service->getCurrentRate('USD', 'IRR'),
|
||||
];
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$userId = auth()->id();
|
||||
@ -65,22 +72,6 @@ class IfnexSettingsPage extends Page
|
||||
['key' => 'profit_margin'],
|
||||
['value' => $this->profit_margin, 'updated_by' => $userId]
|
||||
);
|
||||
SystemSetting::updateOrCreate(
|
||||
['key' => 'aed_to_irr'],
|
||||
['value' => $this->aed_to_irr, 'updated_by' => $userId]
|
||||
);
|
||||
SystemSetting::updateOrCreate(
|
||||
['key' => 'usd_to_irr'],
|
||||
['value' => $this->usd_to_irr, 'updated_by' => $userId]
|
||||
);
|
||||
SystemSetting::updateOrCreate(
|
||||
['key' => 'cny_to_irr'],
|
||||
['value' => $this->cny_to_irr, 'updated_by' => $userId]
|
||||
);
|
||||
SystemSetting::updateOrCreate(
|
||||
['key' => 'eur_to_irr'],
|
||||
['value' => $this->eur_to_irr, 'updated_by' => $userId]
|
||||
);
|
||||
|
||||
SystemSetting::updateOrCreate(
|
||||
['key' => 'kavenegar_api_key'],
|
||||
|
||||
@ -12,7 +12,7 @@ class PricingPageController extends Controller
|
||||
{
|
||||
$countries = Country::orderBy('name')->get(['id', 'name', 'iso_code']);
|
||||
$settings = [
|
||||
'aed_to_irr' => (float) SystemSetting::get('aed_to_irr', 455000),
|
||||
'aed_to_irr' => app(\App\Services\ExchangeRateService::class)->getCurrentRate('AED', 'IRR'),
|
||||
'profit_margin' => (float) SystemSetting::get('profit_margin', 1.25),
|
||||
'vat_rate' => (float) SystemSetting::get('vat_rate', 0.09),
|
||||
];
|
||||
|
||||
@ -123,7 +123,8 @@ class PriceCalculatorService
|
||||
private function calculateFinancials(float $baseRate): array
|
||||
{
|
||||
$profitMargin = (float) SystemSetting::get('profit_margin', 1.25);
|
||||
$aedToIrr = (float) SystemSetting::get('aed_to_irr', 455000);
|
||||
// نرخ ارز فقط از ExchangeRateService خوانده میشود (منبع واحد — منوی نرخ ارز)
|
||||
$aedToIrr = app(ExchangeRateService::class)->getCurrentRate('AED', 'IRR');
|
||||
|
||||
$netDirham = $baseRate * $profitMargin;
|
||||
$netRial = $netDirham * $aedToIrr;
|
||||
|
||||
@ -19,25 +19,29 @@
|
||||
<input type="number" step="0.01" wire:model="profit_margin" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
|
||||
<p class="mt-1 text-sm text-gray-500">Example: 1.25 for 25% margin</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">AED to IRR</label>
|
||||
<input type="number" wire:model="aed_to_irr" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
|
||||
{{-- نرخهای ارز: فقط نمایش — تغییر فقط از منوی «نرخ ارز» (تاریخچه + گردش تأیید) --}}
|
||||
<div class="mt-6 rounded-lg border border-amber-200 bg-amber-50 dark:border-gray-700 dark:bg-gray-800 p-4">
|
||||
<div class="flex items-center justify-between flex-wrap gap-3">
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold text-gray-900 dark:text-white">💱 نرخهای ارز (فقط نمایش)</h3>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
تغییر نرخ فقط از منوی «نرخ ارز» انجام میشود — همیشه با تاریخچه، امکان آپدیت از API و گردش تأیید.
|
||||
</p>
|
||||
</div>
|
||||
<a href="{{ \App\Filament\Resources\ExchangeRateHistoryResource::getUrl() }}"
|
||||
class="text-xs font-semibold text-amber-600 hover:text-amber-700 dark:text-amber-400">
|
||||
مدیریت نرخها در منوی نرخ ارز ←
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">USD to IRR</label>
|
||||
<input type="number" wire:model="usd_to_irr" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">CNY to IRR</label>
|
||||
<input type="number" wire:model="cny_to_irr" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">EUR to IRR</label>
|
||||
<input type="number" wire:model="eur_to_irr" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 mt-3">
|
||||
@foreach ($this->getCurrentRates() as $currency => $rate)
|
||||
<div class="flex items-center justify-between rounded-md bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 px-3 py-2">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-300">{{ $currency }} → IRR</span>
|
||||
<span class="text-sm font-bold text-gray-900 dark:text-white">{{ number_format($rate) }} ریال</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</x-filament::card>
|
||||
|
||||
@ -396,6 +396,8 @@ CSV / approved import format
|
||||
* [ ] Money / currency precision
|
||||
* [ ] Multi-currency debt integrity
|
||||
|
||||
> ✅ **انجام شد (2026-10-03):** تغییر نرخ ارز فقط از منوی «نرخ ارز» (تاریخچه + گردش تأیید + آپدیت از API) — فیلدهای مستقیم نرخ در «تنظیمات سیستم» حذف و به نمایش فقطخواندنی تبدیل شدند.
|
||||
|
||||
## Application
|
||||
|
||||
* [ ] Legacy Order Flow review
|
||||
|
||||
Loading…
Reference in New Issue
Block a user