Перейти до основного вмісту

Multi-currency модель

Translator note: machine-translated baseline. Needs human review by a native UK speaker. EN source is canonical.

casinocrm.io зберігає кожну грошову суму DUAL: оригінал (валюта гаманця гравця) + base-converted (reporting валюта tenant), з FX курсом зафіксованим у час події. Звіти завжди рендеряться в base tenant; profile гравця показує native гаманець.

Чому DUAL зберігання

Наївні single-currency CRMs конвертують at query time, використовуючи поточні FX курси. Це робить lifetime metrics drifting — "lifetime deposit" гравця стрибає, коли FX курси змінюються. casinocrm.io's DUAL pattern зберігає converted суму в event time, тож історичні метрики залишаються стабільними.

Конвенція зберігання

  • Subunit BIGINT — кожна сума зберігається як integer у найменшій одиниці. EUR €123.45 = 12345; BTC 0.00012345 = 12345.
  • Currency code — спарований з кожною сумою. "EUR", "USD", "BTC", "USDT", "ETH".
  • Scale — десяткові місця для валюти. EUR/USD = 2, BTC = 8, USDT = 6, JPY = 0.
  • Crypto cap — 8 десяткових max (industry standard). ETH wei (18 десяткових) переповнює BIGINT і непрактичний для CRM display.

Currency catalogue

Кожен tenant має currencies каталог. Migration seedить EUR + USD для кожного tenant; оператори додають крипту за потреби.

SELECT code, kind, scale, symbol, display_name, is_base
FROM currencies WHERE tenant_id = $1;

code | kind | scale | symbol | display_name | is_base
------+--------+-------+--------+--------------+---------
EUR | fiat | 2 || Euro | t
USD | fiat | 2 | $ | US Dollar | f
BTC | crypto | 8 || Bitcoin | f
USDT | crypto | 6 | USDT | Tether | f
ETH | crypto | 8 | Ξ | Ethereum | f

Рівно одна валюта per tenant має is_base = true; це reporting валюта.

FX курси

fx_rates — це per-tenant timeline. FX fetcher (pnpm --filter @ucrm/api fx:fetch) запускається щогодини і:

  • Тягне FIAT пари з Frankfurter (ECB-backed, без auth)
  • Тягне crypto пари з CoinGecko (no-auth tier)
  • Upsert-ить rows з (tenant_id, base, quote, ts) PK + source field

Lookup це WHERE tenant_id = ? AND base = ? AND quote = ? AND ts <= ? ORDER BY ts DESC LIMIT 1 — найновіший курс at-or-before event time.

DUAL columns на transactional таблицях

bonus_grants (видається в event time):

bonus_amount BIGINT NOT NULL, -- оригінал (наприклад, 1000 USD cents)
bonus_currency TEXT NOT NULL, -- "USD"
bonus_amount_base BIGINT NOT NULL, -- наприклад, 910 EUR cents (locked)
base_currency_code TEXT NOT NULL, -- "EUR"
fx_rate_locked NUMERIC(28, 12) NOT NULL,
fx_locked_at TIMESTAMPTZ NOT NULL

Той самий DUAL pattern на wagering_events. ClickHouse raw_events properties збагачуються amount_base + base_currency + fx_rate_locked в insert time, тож dashboard queries SUM(amount_base) працюють когерентно через гетерогенні wallet валюти.

Per-player wallet валюта

players.default_currency_code — primary wallet валюта гравця, captured на першому deposit_confirmed. До того NULL → admin UI показує tenant base. Після встановлення player profile рендерить суми нативно (₿0.0023 BTC) плюс base equivalent у tooltip.

Querying

  • Reports + segments — query amount_base_subunit columns або JSONExtractInt(properties, 'amount_base') у CH. Завжди когерентно.
  • Player profile — показує native + base (вже надано schema).
  • Bonus issuance — передайте currency_override, якщо bonus має бути denominated у чомусь, ніж wallet валюта гравця; engine locks FX в issue time.