import { QUANTITY_SCALE } from "./quantity"; import { addMonths } from "./recurrence"; /** * Varlık türleri (Bölüm 6.5–6.6): altın, ziynet, gümüş ve döviz. Miktarlar binde bir tamsayı * (5 gr → 5000, 2 adet çeyrek → 2000). Fiyatlar birim başına kuruş. * * Fiyat terimleri kuyumcu/döviz bürosu tarafından: "alış" = kurumun sizden aldığı (düşük), * "satış" = kurumun size sattığı (yüksek). Faz 0 kararı: varlıklar ve aynî alacaklar ALIŞ, * aynî borçlar SATIŞ fiyatıyla değerlenir. */ export const ASSET_KEYS = [ "gram_gold", "quarter_gold", "half_gold", "full_gold", "republic_gold", "bracelet_22k", "silver_gram", "usd", "eur", "gbp", ] as const; export type AssetKey = (typeof ASSET_KEYS)[number]; export type AssetUnit = "gr" | "adet" | "birim"; export const ASSETS: Record< AssetKey, { label: string; unit: AssetUnit; group: "gold" | "silver" | "fx" } > = { gram_gold: { label: "Gram altın (24 ayar)", unit: "gr", group: "gold" }, quarter_gold: { label: "Çeyrek altın", unit: "adet", group: "gold" }, half_gold: { label: "Yarım altın", unit: "adet", group: "gold" }, full_gold: { label: "Tam altın", unit: "adet", group: "gold" }, republic_gold: { label: "Cumhuriyet (Ata) altını", unit: "adet", group: "gold" }, bracelet_22k: { label: "22 ayar bilezik", unit: "gr", group: "gold" }, silver_gram: { label: "Gümüş (gram)", unit: "gr", group: "silver" }, usd: { label: "ABD doları", unit: "birim", group: "fx" }, eur: { label: "Euro", unit: "birim", group: "fx" }, gbp: { label: "İngiliz sterlini", unit: "birim", group: "fx" }, }; const FX_SYMBOL: Partial> = { usd: "USD", eur: "EUR", gbp: "GBP" }; /** "5 gr Gram altın (24 ayar)", "2 adet Çeyrek altın", "1.500 USD". */ export function formatAssetQuantity(key: AssetKey, quantity: number): string { const n = (quantity / QUANTITY_SCALE).toLocaleString("tr-TR", { maximumFractionDigits: 3 }); const fx = FX_SYMBOL[key]; if (fx) return `${n} ${fx}`; const a = ASSETS[key]; return `${n} ${a.unit} ${a.label}`; } /** Miktar × birim fiyat (kuruş, yuvarlanır). */ export function valueOf(quantity: number, unitPriceMinor: number): number { return Math.round((quantity * unitPriceMinor) / QUANTITY_SCALE); } /** TL tutarın karşılığı olan miktar (binde bir, yuvarlanır). */ export function quantityForAmount(amountMinor: number, unitPriceMinor: number): number { if (unitPriceMinor <= 0) return 0; return Math.round((amountMinor * QUANTITY_SCALE) / unitPriceMinor); } export type InKindDirection = "debt" | "receivable"; /** Değerlemede kullanılacak fiyat: borç → satış, alacak → alış. */ export function valuationPrice( direction: InKindDirection, price: { buyMinor: number; sellMinor: number } | null, ): number | null { if (!price) return null; return direction === "debt" ? price.sellMinor : price.buyMinor; } export type InKindProgress = { remainingQuantity: number; /** Kalan miktarın güncel TL karşılığı (fiyat yoksa null). */ currentValueMinor: number | null; /** Birikim planı: biriken − ödemelerde kullanılan. */ accumulatedMinor: number; remainingNeedMinor: number | null; /** Aylık birikimle kalan ihtiyaç kaç ayda kapanır; "YYYY-MM" (plan yoksa null). */ estimatedCloseMonth: string | null; }; export function inKindProgress(input: { quantity: number; paidQuantity: number; unitPriceMinor: number | null; contributedMinor: number; spentMinor: number; monthlyPlanMinor: number | null; today: string; }): InKindProgress { const remainingQuantity = Math.max(0, input.quantity - input.paidQuantity); const currentValueMinor = input.unitPriceMinor === null ? null : valueOf(remainingQuantity, input.unitPriceMinor); const accumulatedMinor = Math.max(0, input.contributedMinor - input.spentMinor); const remainingNeedMinor = currentValueMinor === null ? null : Math.max(0, currentValueMinor - accumulatedMinor); let estimatedCloseMonth: string | null = null; if (remainingQuantity === 0) estimatedCloseMonth = null; else if (remainingNeedMinor === 0) estimatedCloseMonth = input.today.slice(0, 7); else if (remainingNeedMinor !== null && input.monthlyPlanMinor && input.monthlyPlanMinor > 0) { const months = Math.ceil(remainingNeedMinor / input.monthlyPlanMinor); estimatedCloseMonth = addMonths(`${input.today.slice(0, 7)}-01`, months).slice(0, 7); } return { remainingQuantity, currentValueMinor, accumulatedMinor, remainingNeedMinor, estimatedCloseMonth, }; }