🗃 Модель данных Capital
Канонические ORM-модели живут в
capital_shared/models.py(единый источник схемы, общий для backoffice и cabinet). 38 таблиц (main5127b2b, 16.09.2026), схема ведётся Alembic. Ниже - смысловая карта: назначение таблиц, ключевые поля и инварианты. Точные типы колонок - в коде; здесь - что важно понимать про данные. В веткеfeature/xbo-integrationдополнительно:xbo_deposit_addresses,xbo_transactions(см.07/08в architecture) - в main пока нет.
Идентичность и клиенты
Operator (operators)
Оператор банка (back-office). Логин login (unique, lowercase), password_hash
(bcrypt), role (ceo/operator/compliance/viewer), status. Локаут:
failed_login_attempts + locked_until. Опциональная 2FA - totp_secret.
Последний вход - last_login_at/last_login_ip.
CabinetUser (cabinet_users)
Логин-идентичность клиента (кабинет). email (unique), password_hash,
email_verified, 2FA (totp_secret/totp_enabled/totp_last_counter). Пароль
хранится ТОЛЬКО здесь (не на Company - иначе копия дрейфует при смене пароля).
Membership (memberships)
Связь «пользователь ↔ компания» (один логин → несколько компаний). user_id,
company_id, role (default owner), is_default. Unique (user_id, company_id).
Это linchpin изоляции кабинета: активная компания в сессии обязана быть в списке
membership'ов пользователя.
Company (companies)
Клиент банка (компания). external_id (unique, capp-…), реквизиты (name,
registration_number, country, contact_email, директор, телефон, адрес,
registration_date), KYB-поля онбординга (income_volume, nace_codes).
Жизненный цикл: status (onboarding→application→kyc_pending→active),
kyc_status (none/pending/…), kyc_applicant_id (SumSub). Risk: risk_score,
risk_level, risk_note, risk_reviewed_*. Провижн в рельсе: rail_client_id.
Справочники: branch_id, referral_agent_id.
С миграцией KYC-9-блоков (b1d4f7a9c2e6, 16.09) добавлены анкетные поля
многоблочной регистрации (~37 шт. по корпоративной анкете Stefa Pay): тип
аккаунта account_kind, черновик onboarding_step, tax id / сайт компании,
данные заявителя (DOB, tax country/id), развилки applicant_is_director /
applicant_is_sole_owner, AML-профиль (обороты in/out, число транзакций, страны
операций, описание деятельности, источники дохода), preferred_tariff.
Детали - processes/01 - Онбординг клиента.md и спека регистрации.
CompanyPerson (company_persons)
Директор / акционер / UBO компании (KYB-ядро, блок 5 анкеты «Ownership and
control»). Одна Company → N персон: role (director/shareholder/ubo),
person_type (private/legal), имя или юр-название + рег. номер, DOB, страна
резидентства, tax country/id. Появилась с KYC-9-блоков (16.09).
Счета, кошельки, ledger
Account (accounts)
Счёт компании. iban (unique), currency, status (active/blocked/closed),
label, is_primary, opening_fee_due (долг комиссии за открытие). Лимит числа
счетов на компанию задаётся в коде кабинета (_MAX_ACCOUNTS_PER_COMPANY).
ClientWallet (client_wallets)
Балансовый кошелёк. client_id, опционально account_id (баланс на конкретный
IBAN), currency, kind (default deposit), available_balance. Два частичных
unique-индекса: на (account_id, kind) при заданном account_id, и на
(client_id, currency, kind) при пустом account_id (валюто-адресованный баланс).
ClientLedgerEntry (client_ledger_entries)
Проводка (append-only ledger). wallet_id, client_id, account_id, currency,
direction (credit/debit), amount, entry_type (topup/fee/fx_out/fx_in/
account_opening_fee/…), status (default posted), balance_after. Идемпотентность -
idempotency_key (unique): повторный ключ = запись не создаётся. Связь с
источником - related_entity_type/id, external_ref, JSON metadata.
ClientOtp (client_otps)
OTP-коды клиента (login и др.). code_hash, purpose, expires_at, used,
attempts.
Платежи
Beneficiary (beneficiaries)
Получатель платежа компании. name, iban (валидируется ibanutil), bank_name,
bank_bic, country.
PaymentOrder (payment_orders)
Платёжное поручение. company_id, account_id, beneficiary_id, currency,
amount, status (default submitted), reference. Связь с рельсом:
rail_transaction_id, rail_status.
PaymentApproval (payment_approvals)
4-eyes-одобрение платежа. order_id, operator_id. Unique (order_id, operator_id) -
один оператор считается один раз; 2 разных оператора выше dual_approval_threshold →
исполнение.
FX и тарифы/выручка
FxRate (fx_rates)
Курс. base_ccy, quote_ccy, rate, updated_at. Unique (base_ccy, quote_ccy).
Tariff (tariffs) / ClientTariff (client_tariffs)
Тариф пер-валюта: fixed_fee, percent_bps, min_fee, max_fee (0 = без cap),
себестоимость cost_fixed/cost_bps, account_opening_fee + opening_fee_mode
(prepay/first_topup), fx_fee_bps, dual_approval_threshold. Unique по валюте.
ClientTariff - индивидуальный override на клиента (unique (client_id, currency));
эффективный тариф = per-client override, иначе глобальный.
RevenueEntry (revenue_entries)
Проводка выручки. client_id, payment_order_id, currency, fee_amount,
cost_amount, source (payment_fee/account_opening/fx_fee/
payment_fee_reversal). Partial-unique (payment_order_id, source) при заданном
order (защита от двойной проводки на реверсе).
ClientLimit (client_limits)
Лимит платежей клиента. limit_type (per_transaction/daily_outgoing),
currency, amount. Unique (client_id, limit_type, currency).
Комплаенс: AML / Risk / Sanctions / SAR
AmlCase (aml_cases)
Кейс риска. client_id, subject_type (customer/payment), subject_id,
reason, risk_level, required_approval_level (1/2/3 = low/medium/high),
approvals_count, status (default open), raised_by (в т.ч. kyt).
AmlApproval (aml_approvals)
Одобрение AML-кейса. case_id, operator_id. Unique (case_id, operator_id) -
один оператор = один голос (4-eyes/6-eyes по уровню).
SarReport (sar_reports)
Suspicious Activity Report поверх AML-кейсов. uuid, client_id, aml_case_id,
reporter, assigned_to, priority (low/medium/high), status
(draft→submitted→filed→closed), subject_summary, narrative, filed_at.
SanctionSource (sanction_sources) / SanctionEntry (sanction_entries)
Списки санкций/watchlist (OFAC/EU/UN и синтетические демо). Source: name (unique),
kind, entry_count. Entry: full_name, normalized_name (индекс для match),
entity_type, country, program.
Учёт (Accounting)
ChartOfAccount (chart_of_accounts)
План счетов (иерархия FINREP). code (unique), name, parent_id,
account_class (asset/liability/equity), normal_side (debit/credit),
ledger_source (маппинг на источник в client-ledger), sort_order, is_system
(системные защищены от удаления). Баланс - производный из ledger, см. accounting.py.
Справочники
- Branch (
branches) - филиалы (nameunique,code). - ReferralAgent (
referral_agents) - реферальные агенты (codeunique,commission_bps; payout-движок отложен). - CustomerTag (
customer_tags) - цветные метки (nameunique,color). - CompanyTag (
company_tags) - назначение тега компании, unique(company_id, tag_id).
Отчёты, документы, сообщения, аудит
Report (reports)
Формальный отчёт (EOD/Journal). report_type, title, JSON params/summary,
row_count, generated_by. CSV регенерится из params - без блобов в БД.
ClientDocument (client_documents)
Обмен документами. client_id, doc_type, status (requested→uploaded→approved/
rejected), file_name, content_type, size_bytes, content (BLOB LargeBinary;
cap 5MB + allowlist типов + sanitize имени; изоляция по client_id; план - вынести в B2).
Message (messages)
Переписка оператор↔клиент. client_id, direction (to_client/to_bank), subject,
body, read_by_client/read_by_operator.
Invoice (invoices)
Счёт, который клиент выставляет своему плательщику. client_id, number,
payer_name/payer_email, amount, currency, issue_date/due_date, status
(draft/sent/paid/cancelled). Unique (client_id, number).
AuditLog (audit_log)
Действия операторов. operator_id/operator_login, action, entity_type/
entity_id, detail, created_at (индекс). Целевое - hash-chain + WORM-дайджесты
(см. Архитектура Capital v1.0.md).
Карты (NS Cards)
CardProgram (card_programs)
Multibrand-программа выпуска. code (unique), name, issuer (default nscards),
ns_account_ref, base_url, JSON currencies, лимиты по умолчанию
(default_per_transaction/daily/monthly), max_cards_per_company.
Card (cards)
Карта. company_id, account_id (привязка к счёту), program_id, issuer,
issuer_card_id (opaque ID эмитента), brand, form_factor (virtual/physical),
currency, holder_name, pan_last4 (только last4), expiry_*, status
(requested/active/frozen/closed). 🔒 Полный PAN НЕ персистится.
CardTransaction (card_transactions)
Транзакция по карте. card_id, company_id, issuer_tx_id, amount, currency,
merchant_name, mcc, kind (authorization/refund/…), status. Unique
(card_id, issuer_tx_id) - идемпотентность вебхука.
CardLimit (card_limits)
Спенд-лимит карты. card_id, limit_type (per_transaction/daily/monthly), amount.
Unique (card_id, limit_type).
CardRevealAudit (card_reveal_audit)
Аудит показа PAN. card_id, actor_type (operator/client), actor, ip,
created_at. Пишется только после успешного reveal (у клиента - после 3DS).
CardFundingRequest (card_funding_requests)
Заявка на фондирование выпуска/пополнения карты, матчится к входящему платежу
по уникальной сумме (base + «хвост» в центах): NS-аккаунт бренда общий на
клиентов, поэтому дискриминатор - сумма. Клиенту говорят заплатить ровно
expected_amount (например 10.37 при заявке на 10.00); матчер атрибутирует
входящий платёж этой суммы+валюты к заявке. expected_amount уникальна среди
активных (pending, неистёкших) заявок той же программы+валюты. kind
(issue/topup), card_id (null для нового выпуска), account_id (куда кредитуется
фондирование). В ledger - entry_type card_funding. Смержено 16.09 (миграция
e1d7a4c9b2f6).