/* Fyro Finance — conversione EUR/USD. Nel database: monthly_exchange_rates, from_currency EUR, to_currency USD, average_rate = USD per 1 EUR. Regole: - Total EUR = EUR + USD ÷ tasso del mese - Total USD = USD + EUR × tasso del mese - ogni riga si converte dal proprio totale e si arrotonda ai centesimi - trimestre e anno: somma delle conversioni mese per mese, ognuna col suo tasso - mese senza tasso: si usa l'ultimo tasso noto precedente (o il primo successivo) e lo si segnala */ import type { MoneyPair } from "@/lib/cashflow"; export type TotalIn = "EUR" | "USD"; export interface RateRow { year: number | string; month: number | string; // 1–12 average_rate: number | string; } export interface ResolvedRate { rate: number; /** month = tasso del mese; previous/next = stimato da un altro mese */ source: "month" | "previous" | "next"; year: number; month: number; // 1–12, il mese da cui viene il tasso } const k = (year: number, month: number) => year * 12 + (month - 1); const cents = (v: number) => { const r = Math.round(v * 100) / 100; return Object.is(r, -0) ? 0 : r; }; export function buildRateResolver(rows: RateRow[]) { const known = rows .map((r) => ({ year: Number(r.year), month: Number(r.month), rate: Number(r.average_rate) })) .filter((r) => Number.isFinite(r.rate) && r.rate > 0 && r.month >= 1 && r.month <= 12) .map((r) => ({ ...r, key: k(r.year, r.month) })) .sort((a, b) => a.key - b.key); return (year: number, month: number): ResolvedRate | null => { if (known.length === 0) return null; const target = k(year, month); let prev: (typeof known)[number] | null = null; for (const r of known) { if (r.key === target) return { rate: r.rate, source: "month", year: r.year, month: r.month }; if (r.key > target) { return prev ? { rate: prev.rate, source: "previous", year: prev.year, month: prev.month } : { rate: r.rate, source: "next", year: r.year, month: r.month }; } prev = r; } return { rate: prev!.rate, source: "previous", year: prev!.year, month: prev!.month }; }; } /** La parte convertita: USD → EUR se il totale è in EUR, EUR → USD se è in USD. */ export function convertedPart(p: MoneyPair, rate: number, totalIn: TotalIn): number { return cents(totalIn === "EUR" ? p.usd / rate : p.eur * rate); } export function toTotal(p: MoneyPair, rate: number, totalIn: TotalIn): number { const own = totalIn === "EUR" ? p.eur : p.usd; return cents(own + convertedPart(p, rate, totalIn)); } /** Somma su più mesi, ognuno col suo tasso. pairs[i] e rates[i] sono lo stesso mese. */ export function totalOverMonths(pairs: MoneyPair[], rates: (number | null)[], totalIn: TotalIn): number | null { let sum = 0; for (let i = 0; i < pairs.length; i++) { const p = pairs[i]; if (p.eur === 0 && p.usd === 0) continue; const r = rates[i]; if (!r) return null; sum += toTotal(p, r, totalIn); } return cents(sum); } /** Etichetta della formula per intestazioni e popover: "USD ÷ 1.086420" · "EUR × 1.086420" */ export function rateFormula(rate: string, totalIn: TotalIn): string { return totalIn === "EUR" ? `USD ÷ ${rate}` : `EUR × ${rate}`; } export const isEstimated = (r: ResolvedRate | null) => !!r && r.source !== "month";