feat: client-side currency conversion with admin-configurable rates
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
- CurrencyRatesService: RUB-based rates, persisted via localStorage - CurrencyConvertPipe: impure pipe converting item price to selected currency - Admin settings page: editable currency rates form - Applied conversion to product-card, product-information, quick-view-dialog, delivery-information, compare-table, cart totals + payment payload Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
53
src/app/services/currency-rates.service.ts
Normal file
53
src/app/services/currency-rates.service.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Injectable, Signal, inject, signal } from '@angular/core';
|
||||
import { LocalStorageService } from '../core/storage/local-storage.service';
|
||||
|
||||
const RATES_KEY = 'currencyRates.v1';
|
||||
|
||||
/** Fallback rates relative to RUB (1 RUB = rate[code] units of code), used until admin overrides them. */
|
||||
const DEFAULT_RATES: Record<string, number> = {
|
||||
RUB: 1,
|
||||
USD: 0.011,
|
||||
EUR: 0.01,
|
||||
AMD: 4.3,
|
||||
};
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CurrencyRatesService {
|
||||
private readonly localStorage = inject(LocalStorageService);
|
||||
|
||||
private readonly ratesSignal = signal<Record<string, number>>(this.readStoredRates());
|
||||
|
||||
readonly rates: Signal<Record<string, number>> = this.ratesSignal.asReadonly();
|
||||
|
||||
/** Base currency all rates are relative to. */
|
||||
readonly baseCurrency = 'RUB';
|
||||
|
||||
getRate(code: string): number {
|
||||
return this.ratesSignal()[code] ?? DEFAULT_RATES[code] ?? 1;
|
||||
}
|
||||
|
||||
setRate(code: string, rate: number): void {
|
||||
if (!Number.isFinite(rate) || rate <= 0) {
|
||||
return;
|
||||
}
|
||||
const next = { ...this.ratesSignal(), [code]: rate };
|
||||
this.ratesSignal.set(next);
|
||||
this.localStorage.setJSON(RATES_KEY, next);
|
||||
}
|
||||
|
||||
/** Converts an amount expressed in `fromCurrency` into `toCurrency` via the RUB base rate. */
|
||||
convert(amount: number, fromCurrency: string, toCurrency: string): number {
|
||||
if (fromCurrency === toCurrency) {
|
||||
return amount;
|
||||
}
|
||||
const fromRate = this.getRate(fromCurrency);
|
||||
const toRate = this.getRate(toCurrency);
|
||||
const amountInBase = amount / fromRate;
|
||||
return amountInBase * toRate;
|
||||
}
|
||||
|
||||
private readStoredRates(): Record<string, number> {
|
||||
const stored = this.localStorage.getJSON<Record<string, number>>(RATES_KEY);
|
||||
return { ...DEFAULT_RATES, ...stored };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user