Эпик 1 (Благорост): контракт capital — займы, расходы программы, L2-роли #3

Merged
ant merged 20 commits from feat/blagorost-E1-contract into blagorost 2026-06-02 06:17:25 +00:00
Owner

Зонтичный PR Эпика 1 — контрактный слой Благорост

Закрывает 11 stories из спринт-плана Благорост (blago issues 51F-13…51F-23). Целиком замыкает контрактный слой одной волной — дальше Эпики 2 (cooptypes) и 4 (controller) могут идти без возвратов в C++.

Зеркало github PR coopenomics/mono#402 (миграция с github на gitea).

Stories и commits

# Story blago
1.1 debtpaydcln + статус PAY_PENDING — откат outpay без удаления долга 51F-13
1.2 settledebt — прямое погашение деньгами, REPAY 80/58, статус SETTLED 51F-14
1.3 markdebtoverd + due_at = now + 1 год в debtpaycnfrm, статус OVERDUE 51F-15
1.4 Фикс exppaycnfrm.cpp:35 — новая операция o.cap.expns (BLAGOROST_FUND → SOV_EXPENSES) 51F-16
1.5 Расходы программы: createpgexp/apprvpgexp/authpgexp/pgexppay/declpgexp + таблица progexpenses 51F-17
1.6 program_expense_pool + program_expense_reserved в global_state; action topupprogexp 51F-18
1.7 L2-роли: requestrole/approverole/declinerole/inviterole/acceptinvite/declinvite/requestrateu + rolerequests 51F-19
1.8 approved_rate_per_hour в segments; createcmmt берёт approved-ставку, fallback на contributors 51F-20
1.9 Чистка debug print() в startvoting.cpp; [[deprecated]] на program_invest (remove after 2026-07) 51F-21
1.10 require_recipient во всех новых actions (кроме batch markdebtoverd) 51F-22
1.11 Scaffold capital-blagorost.test.ts — 21 it.todo по новым ветвям 51F-23
1.13 capital::closedebt — закрытие долга через создание НМА (Дт 04/Кт 08) + write-off (Дт 80/Кт 58) 51F-25

Финальный канон debt-операций невозврата (commit c3a433b591)

Утверждено автором 2026-05-19. Семантика операции через имя, группировка по сущности (dbt для debt-операций):

место имя смысл
операция №1 (Дт 04 / Кт 08) o.cap.crtnma CREATE_NMA — создать НМА кооператива из коммитов-обеспечения долга
операция №2 (Дт 80 / Кт 58) o.cap.dbtwrf DEBT_WRITEOFF — списать долг (write-off)
кошелёк w.cap.nma НМА кооператива
action capital::closedebt закрыть долг (парный к settledebt)
статус долга writeoff стандарт бухучёта, запись в реестре для аудита

Семантика, важная для следующих эпиков

  • Имена action'ов сокращены под 12-символьный лимит EOSIO: declineinvitedeclinvite, программные расходы — createpgexp/apprvpgexp/authpgexp/pgexppay/declpgexp. Эпик 2 (cooptypes) должен использовать эти имена.
  • Новые процессы ledger2 p.cap.expns и операции o.cap.crtnma/o.cap.dbtwrf зарегистрированы в OPERATION_REGISTRY.
  • Двухуровневая модель ставок: contributors.rate_per_hour — глобальная (fallback), segments.approved_rate_per_hour — мастерская на проекте.
  • debt lifecycle: created → approved → authorized ↔ pay_pending → paid → (overdue) → settled | writeoff. Ветка writeoff — автоматическое закрытие через closedebt при project.status == cancelled, без подписи пайщика.

Не входит в этот PR

  • Story 1.10 (системные доделки allocate/diallocate/regshare/initprj/cmpltvoting/...) — вынесена за пределы эпика «Благорост» как нерелевантная PRD.
  • Реальная имплементация контрактных тестов из Story 1.11 / 1.13 — оставлено как it.todo, реализуется по мере появления helper'ов в Эпике 4 (controller).

Test plan

  • CDT-build контракта capital зелёный (собран через cdt-cpp v4.1.0)
  • Existing capital.test.ts не сломан
  • Smoke: setcode capital, createdebt → approvedebt → debtauthcnfr → debtpaydcln(test) → проверить last_pay_error и статус authorized
  • Smoke: closedebt при project.status == cancelled → переход долга в writeoff, операции o.cap.crtnma + o.cap.dbtwrf в ledger2
## Зонтичный PR Эпика 1 — контрактный слой Благорост Закрывает 11 stories из спринт-плана Благорост (blago issues 51F-13…51F-23). Целиком замыкает контрактный слой одной волной — дальше Эпики 2 (cooptypes) и 4 (controller) могут идти без возвратов в C++. Зеркало github PR coopenomics/mono#402 (миграция с github на gitea). ## Stories и commits | # | Story | blago | |---|---|---| | 1.1 | `debtpaydcln` + статус `PAY_PENDING` — откат outpay без удаления долга | 51F-13 | | 1.2 | `settledebt` — прямое погашение деньгами, REPAY 80/58, статус `SETTLED` | 51F-14 | | 1.3 | `markdebtoverd` + `due_at = now + 1 год` в `debtpaycnfrm`, статус `OVERDUE` | 51F-15 | | 1.4 | Фикс `exppaycnfrm.cpp:35` — новая операция `o.cap.expns` (`BLAGOROST_FUND → SOV_EXPENSES`) | 51F-16 | | 1.5 | Расходы программы: `createpgexp/apprvpgexp/authpgexp/pgexppay/declpgexp` + таблица `progexpenses` | 51F-17 | | 1.6 | `program_expense_pool` + `program_expense_reserved` в `global_state`; action `topupprogexp` | 51F-18 | | 1.7 | L2-роли: `requestrole/approverole/declinerole/inviterole/acceptinvite/declinvite/requestrateu` + `rolerequests` | 51F-19 | | 1.8 | `approved_rate_per_hour` в `segments`; `createcmmt` берёт approved-ставку, fallback на `contributors` | 51F-20 | | 1.9 | Чистка debug `print()` в `startvoting.cpp`; `[[deprecated]]` на `program_invest` (remove after 2026-07) | 51F-21 | | 1.10 | `require_recipient` во всех новых actions (кроме batch `markdebtoverd`) | 51F-22 | | 1.11 | Scaffold `capital-blagorost.test.ts` — 21 it.todo по новым ветвям | 51F-23 | | 1.13 | `capital::closedebt` — закрытие долга через создание НМА (Дт 04/Кт 08) + write-off (Дт 80/Кт 58) | 51F-25 | ## Финальный канон debt-операций невозврата (commit c3a433b591a) Утверждено автором 2026-05-19. Семантика операции через имя, группировка по сущности (`dbt` для debt-операций): | место | имя | смысл | | --- | --- | --- | | операция №1 (Дт 04 / Кт 08) | `o.cap.crtnma` | CREATE_NMA — создать НМА кооператива из коммитов-обеспечения долга | | операция №2 (Дт 80 / Кт 58) | `o.cap.dbtwrf` | DEBT_WRITEOFF — списать долг (write-off) | | кошелёк | `w.cap.nma` | НМА кооператива | | action | `capital::closedebt` | закрыть долг (парный к `settledebt`) | | статус долга | `writeoff` | стандарт бухучёта, запись в реестре для аудита | ## Семантика, важная для следующих эпиков - **Имена action'ов сокращены под 12-символьный лимит EOSIO**: `declineinvite` → `declinvite`, программные расходы — `createpgexp/apprvpgexp/authpgexp/pgexppay/declpgexp`. Эпик 2 (cooptypes) должен использовать эти имена. - **Новые процессы ledger2 `p.cap.expns` и операции `o.cap.crtnma/o.cap.dbtwrf`** зарегистрированы в `OPERATION_REGISTRY`. - **Двухуровневая модель ставок**: `contributors.rate_per_hour` — глобальная (fallback), `segments.approved_rate_per_hour` — мастерская на проекте. - **debt lifecycle**: `created → approved → authorized ↔ pay_pending → paid → (overdue) → settled | writeoff`. Ветка `writeoff` — автоматическое закрытие через `closedebt` при `project.status == cancelled`, без подписи пайщика. ## Не входит в этот PR - Story 1.10 (системные доделки `allocate/diallocate/regshare/initprj/cmpltvoting/...`) — вынесена за пределы эпика «Благорост» как нерелевантная PRD. - Реальная имплементация контрактных тестов из Story 1.11 / 1.13 — оставлено как `it.todo`, реализуется по мере появления helper'ов в Эпике 4 (controller). ## Test plan - [ ] CDT-build контракта capital зелёный (собран через cdt-cpp v4.1.0) - [ ] Existing capital.test.ts не сломан - [ ] Smoke: setcode capital, createdebt → approvedebt → debtauthcnfr → debtpaydcln(test) → проверить last_pay_error и статус authorized - [ ] Smoke: closedebt при project.status == cancelled → переход долга в writeoff, операции o.cap.crtnma + o.cap.dbtwrf в ledger2
ant added 18 commits 2026-05-20 11:59:30 +00:00
Story 1.1 эпика «Благорост» (blago issue 51F-13).

PRD-A3 говорит «отказ оплаты по реквизитам не приводит к удалению долга».
До правки debtpaydcln был stub'ом, статусы не различали «в полёте до gateway»
и «авторизован, ждёт повторной отправки».

Изменения в lifecycle долга:
- статус PAY_PENDING — долг отдан в gateway на outpay
- поле last_pay_error — текст ошибки от gateway (пусто, пока нет отказов)
- утилиты debts.hpp: start_pay() (approved/authorized → PAY_PENDING + сохранение
  решения совета + сброс last_pay_error) и mark_pay_declined() (PAY_PENDING →
  AUTHORIZED + last_pay_error)

Касается actions:
- debtauthcnfr теперь через start_pay() переводит долг в PAY_PENDING и
  параллельно фиксирует document решения совета как authorization
- debtpaycnfrm требует PAY_PENDING (а не AUTHORIZED) перед PAID
- debtpaydcln вызывает mark_pay_declined() — повторная outpay из AUTHORIZED
  доступна без новой авторизации совета

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Story 1.2 эпика «Благорост» (blago issue 51F-14).

settledebt был stub'ом. Реализация по PRD-A3: должник или председатель
вносит сумму, ровно равную остатку долга — заём закрывается.

Изменения:
- сигнатура: settledebt(coopname, debt_hash, amount, statement) — раньше
  принимался username, но идентифицировать долг надо по hash (у одного
  пайщика может быть несколько активных)
- статус SETTLED — финальное состояние погашенного долга (как через
  деньги settledebt, так и через результат signact2)
- helper debts.hpp::mark_settled — атомарный переход paid → settled с
  пометкой даты погашения и memo
- get_settle_debt_memo() — описание проводки в ledger2
- авторизация: has_auth(debt.username) || has_auth(coopname) — раньше
  было только coopname, что мешало пайщику самому погасить долг
- проводка REPAY (Dr 80 / Cr 58) на полную сумму долга
- contributors.decrease_debt_amount на ту же сумму

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Story 1.3 эпика «Благорост» (blago issue 51F-15).

PRD-A3, FR-A11: «срок погашения 3 месяца, дальше — overdue, без блокировки операций».
Поле due_at и статус OVERDUE добавлены, helper sweep_overdue вычитан в action.

Изменения:
- статус OVERDUE — выставляется markdebtoverd для долгов с now > due_at
- поле debt.due_at (time_point_sec) — на 3 месяца (DEFAULT_DEBT_TERM_SECONDS = 90 дней)
  от даты подтверждения выплаты gateway
- helper debts.hpp::confirm_paid — атомарный переход pay_pending → paid с
  проставлением due_at и сбросом last_pay_error
- helper debts.hpp::sweep_overdue(coopname, batch_limit) — итерация по debts
  в scope, перевод paid→overdue для просроченных, не более batch_limit записей
- markdebtoverd(coopname) action — backend-cron вызовет раз в сутки (Эпик 4),
  batch=100 чтобы уложиться в 30s транзакции; повтор до 0 изменений
- mark_settled расширен: SETTLED доступен также из OVERDUE
- debtpaycnfrm теперь использует confirm_paid (раньше update_debt_status +
  только PAID, без due_at)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Story 1.4 эпика «Благорост» (blago issue 51F-16).

Закрывает TODO в exppaycnfrm.cpp:35 — после подтверждения оплаты проектного
расхода списываем сумму с программного фонда «Благорост» в пул хозрасходов
из целевого финансирования.

Изменения:
- processes::capital::EXPENSE (p.cap.expns) — новый process_type для цикла
  целевого расхода (создание → одобрение → авторизация → outpay → подтверждение)
- operations::capital::PAY_EXPENSE (o.cap.expns) — TRANSFER BLAGOROST_FUND
  → SOV_EXPENSES, без Dr/Cr; параллельная бухпроводка Dr 26 / Cr 51 на
  расчётном делается gateway и здесь не дублируется
- запись 20 в OPERATION_REGISTRY (lib/core/ledger2/operations.hpp)
- get_expense_pay_memo() в Capital::Memo
- exppaycnfrm.cpp теперь после complete_expense вызывает Ledger2::apply
  до delete_expense (нужны expense.amount/username для проводки)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Story 1.6 эпика «Благорост» (blago issue 51F-18).

Программные расходы (Story 1.5) списываются из дедикейтнутого пула, а не из
аллокаций проектов. До этого коммита поля для такого пула в global_state не было.

Изменения:
- global_state.program_expense_pool — доступная сумма программы под расходы
- global_state.program_expense_reserved — зарезервированная под approved/authorized
  программные расходы (Story 1.5: createprogexp резервирует, declprogexp возвращает,
  exppaycnfrm списывает окончательно)
- State::topup_program_expense_pool / reserve_program_expense /
  release_program_expense / consume_program_expense — utility-функции для Story 1.5
- topupprogexp(coopname, amount) action — председатель переводит amount из
  global_available_invest_pool в program_expense_pool; деньги остаются на
  ledger2-кошельке BLAGOROST_FUND, меняется только разбивка по назначению

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Story 1.5 эпика «Благорост» (blago issue 51F-17).

Полный цикл целевого расхода программы «Благорост» (не привязанного к проекту):
от заявки председателя до подтверждения оплаты gateway. Источник средств —
program_expense_pool (Story 1.6), резерв снимается окончательно в pgexppay.

PRD-B1: «createprogexp/approveprogex/authprogexp/progexppay/declprogexp». Имена
действий сокращены под 12-символьный лимит EOSIO — семантика та же.

Изменения:
- expenses.hpp: struct program_expense + таблица progexpenses (scope coopname)
  + helper'ы create/get/set_program_approved/set_program_authorized/delete
- 5 новых cpp actions в expense_managment/program_expenses/:
  - createpgexp — резервирует в State::reserve_program_expense, создаёт запись
  - apprvpgexp — председатель → soviet::create_agenda с CAPITAL_RESOLVE_PROGRAM_EXPENSE
  - authpgexp — совет → ::Gateway::create_outpay (через Action::send), статус AUTHORIZED
  - pgexppay — gateway callback → consume_program_expense + Ledger2::apply PAY_EXPENSE + delete
  - declpgexp — отказ от coopname/_soviet/_gateway → release_program_expense + delete
- names.hpp: CREATE_PROGRAM_EXPENSE, AUTHORIZE_PROGRAM_EXPENSE, DECLINE_PROGRAM_EXPENSE,
  CONFIRM_PROGRAM_EXPENSE_PAYMENT, SovietActions::CAPITAL_RESOLVE_PROGRAM_EXPENSE

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Story 1.8 эпика «Благорост» (blago issue 51F-20).

PRD-D2: «approved_rate_per_hour хранится в сегменте; createcmmt использует
approved-ставку». До этого коммита createcmmt брал ставку из глобального
contributors.rate_per_hour — мастер компонента не мог дифференцировать.

Изменения:
- segments.hpp: поля approved_rate_per_hour, approved_hours_per_day (по умолчанию 0
  — fallback на глобальную ставку из contributors)
- segments.hpp::set_approved_rate(coopname, project_hash, username, rate, hours)
  — helper будет вызван из approverole (Story 1.7)
- createcmmt.cpp: если segment.approved_rate_per_hour.amount > 0 — используется
  approved; иначе contributor->rate_per_hour. Меняется и creator_base, и
  delta_amounts (через calculate_fact_generation_amounts)
- editcontrib (без изменения) — теперь не влияет на approved-ставки на
  сегментах: контракт берёт сегментные значения вперёд глобальных

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Story 1.7 эпика «Благорост» (blago issue 51F-19).

PRD-D1/D3/D4: «двухуровневые допуски, мастер компонента валидирует исполнителей
и ставку часа на сегменте». До этого коммита actions L2-цикла в контракте
не существовало; ставки фиксировались только глобально через editcontrib.

Изменения:
- domain/entities/role_requests.hpp: таблица rolerequests, статусы pending/approved/declined,
  направление request/invite, тип role/rateupdate, helpers create/approve/decline
- 7 новых cpp actions в app/participation_management/roles/:
  - requestrole — пайщик просит L2-роль (creator/author/contributor) + ставку
  - approverole — мастер одобряет: фиксация approved-ставки в сегменте через
    Capital::Segments::set_approved_rate (Story 1.8)
  - declinerole — мастер отклоняет с причиной (запись для аудита остаётся)
  - inviterole — мастер инициирует обратный поток, приглашает кандидата
  - acceptinvite — кандидат принимает инвайт → applied к сегменту
  - declinvite — кандидат отказывается (имя укорочено с declineinvite — лимит 12 chars)
  - requestrateu — пайщик просит обновить approved-ставку (тот же flow, RequestType::RATE_UPDATE);
    approverole с новой ставкой → новая approved в сегменте
- name declineinvite сокращена до declinvite (13 → 10 chars, eosio лимит 12)

Авторизация на текущем этапе — require_auth(coopname); backend проверяет
master-роль приглашающего/одобряющего перед отправкой action.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Story 1.9 эпика «Благорост» (blago issue 51F-21).

PRD: «удалить отладочные print() в startvoting.cpp; пометить progrinvests
deprecated до 07.2026». Логирующие print() в returntopool.cpp и lvlnotify.cpp
оставлены — они несут смысл, не отладочные маркеры.

Изменения:
- startvoting.cpp: убраны 5 маркеров print("z"/"a"/"b"/"c"/"d")
- program_invests.hpp: атрибут [[deprecated("remove after 2026-07")]] на struct
  program_invest + расширенный doxygen-комментарий с пояснением жизненного цикла

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Story 1.10 эпика «Благорост» (blago issue 51F-22).

Notification-services (Эпик 6) подписываются на chain через action traces
и триггерят Novu workflows. На каждой ветке переходов требуется явный
require_recipient, чтобы участники, не подписавшие транзакцию, всё равно
получили её в свою цепочку recipient — это и есть «event ridge» из PRD-E.

Покрыто:
- debtpaydcln  → должник + coopname
- settledebt   → должник + coopname
- createpgexp  → creator + coopname
- apprvpgexp   → expense.username + approver
- authpgexp    → expense.username + coopname
- pgexppay     → expense.username + coopname
- declpgexp    → expense.username + coopname
- requestrole  → username + master
- approverole  → req.username + req.master
- declinerole  → req.username + req.master
- inviterole   → candidate + master
- acceptinvite → req.username + req.master
- declinvite   → req.username + req.master
- requestrateu → username + master

markdebtoverd сознательно пропущен — это batch-action (до 100 записей
за вызов), массовая рассылка require_recipient ухудшит RAM-стоимость
транзакции. Novu по этому переходу подписывается на изменение статуса
debt записи через парсер.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Story 1.11 эпика «Благорост» (blago issue 51F-23).

Существующий capital.test.ts ≈2650 строк — раздувать монолит нерационально.
Новые сценарии вынесены в отдельный файл со структурой describe/it.todo() —
каркас под пошаговое наполнение реальными helper'ами по мере появления
backend-обвязки (Эпик 4: processDebtDeclinePay, processSettleDebt,
processMarkOverdue, processProgramExpense, processRequestRole, и т.д.).

Покрытые сценарии (it.todo):
- A: debtpaydcln (×2), settledebt (×3), markdebtoverd (×3) + due_at (×1)
- B: exppaycnfrm проводка, topupprogexp, createpgexp резервирование,
  полный цикл создания/одобрения/авторизации/оплаты, declpgexp на 3 фазах
- D: requestrole/approverole + approved_rate в createcmmt (1200 ≠ 1500),
  editcontrib не трогает approved, inviterole/acceptinvite/declinvite,
  declinerole, requestrateu
- E: require_recipient на всех новых actions (Story 1.10)

Существующие тесты не сломаны (отдельный файл, отдельный describe).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
— segments.approved_rate_per_hour/hours_per_day → binary_extension<> (прод-таблица)
— global_state.program_expense_pool/reserved → binary_extension<asset> (прод-таблица)
— settledebt: require_auth(coopname), убран has_auth(username) — backend подписывает
— markdebtoverd: batch_limit 100 → 25 (transaction time limit EOSIO 0.5с)
— approverole/inviterole/requestrole/requestrateu: норма часов 12 → 8 (фронт-ограничение)
— debtpayretry: новый action для повторной отправки outpay из AUTHORIZED + last_pay_error
— PAY_EXPENSE: проводка Dr 86 / Cr 51 (TARGET_RECEIPTS/BANK_ACCOUNT) — целевое финансирование, не общехоз 26
— memo.get_settle_debt_memo: "Погашение займа участником" (без "деньгами") — погашение не только наличными
role_requests:
— role строго {creator, author, master}; contributor — L1, в role_requests не попадает
— validate_role_or_fail() — валидация на уровне requestrole/inviterole
— description (std::string, может быть пустой) — текст заявки/приглашения от пайщика к мастеру и наоборот
— apply_role_to_project(): inline addauthor для role=author, setmaster для role=master,
  creator → no-op (фиксируется при первом createcmmt)

Actions:
— requestrole/inviterole/requestrateu: добавлено description в сигнатуру + проброс в RoleRequests::create
— approverole/acceptinvite: после approve/segment.set_approved_rate вызывают
  apply_role_to_project — заявленная роль немедленно отражается в реестре участников
  (раньше creator становился creator только после первого commit'а)
— inviterole: уточнено в комментарии что master-параметр может быть председателем
  при приглашении кандидата на роль master (когда мастера в проекте ещё нет)

Ставка часа всегда подаётся явно (rate_per_hour, hours_per_day) — это ставка пайщика
именно под этот проект; фронт по дефолту подставляет contributors.rate_per_hour,
но пайщик может изменить под конкретный проект. createcmmt берёт approved_rate
сегмента, не глобальную из contributors.
Ревью PR#402 (debtpaycnfrm:23): учёт займов должен быть централизован в одном контракте,
как Gateway для платежей. Существующий контракт loan/ уже задеплоен и в whitelist,
но capital его не вызывал — теперь вызывает.

loan-контракт:
— table_loan_debts: добавлено поле project_hash (binary_extension<checksum256> —
  таблица уже в продакшене, расширение схемы только хвостом) + индекс byuserproj
— Loan::create_debt: новая сигнатура с project_hash; createdebt сохраняет привязку к проекту
— Loan::count_user_project_debts: хелпер для лимита займов на проект в паевом взносе

capital:
— debtpaycnfrm: после confirm_paid дублирует запись в loan.debts через inline
  Loan::create_debt (с due_at из обновлённой capital-записи и project_hash)
— settledebt: дополнительно зовёт inline Loan::settle_debt; принимает также OVERDUE
  (не только PAID) — погашение просроченного через председателя
— signact2 (паевой взнос): собирает все PAID/OVERDUE займы пайщика на проекте
  из capital.debts, проверяет лимит 10 (transaction limit EOSIO), на каждый —
  inline Loan::settle_debt + capital.Debts::mark_settled. Никакого «боевого взноса» —
  только паевой по интеллектуальному результату

Boot/deploy не требует правок: loan/ уже в configs/contracts.ts и configs/index.ts,
аккаунт loan создаётся, контракт деплоится. capital уже в contracts_whitelist в lib/consts.

Тесты: добавлен describe «A2 — Централизованный учёт займов» с 6 it.todo
по новым сценариям (createdebt в loan, settle через capital, лимит 10, whitelist).

Полный перенос таблицы debts из capital в loan (с удалением capital.debts) —
отдельная Story 1.12, оставлена на следующую итерацию: текущий PR сохраняет
параллельный учёт в обеих таблицах для безопасной миграции прода.
Round 2 ревью PR #402 — 8 замечаний:

LOAN-контракт независим от прикладных (capital/marketplace/...):
- loan.debts: убран binary_extension<checksum256> project_hash и индекс byuserproj
- loan.debts: добавлен binary_extension<name> source_contract (контракт-источник)
- loan::createdebt(): убран параметр project_hash, payer пишется в source_contract
- Loan::create_debt(): убран project_hash из сигнатуры и хелпера
- Loan::count_user_project_debts(): удалена — capital считает у себя
- Локальный контекст (project_hash и т.п.) остаётся в Capital::Debts

CAPITAL — счётчик активных займов на сегменте через binary_extension:
- Segment::active_debts_count (binary_extension<uint32_t>, хвостом)
- increase/decrease/get_active_debts_count хелперы
- debtpaycnfrm: после confirm_paid +1 счётчика сегмента
- settledebt: после mark_settled -1 счётчика
- signact2: лимит «10 займов на проект в одном паевом взносе» проверяется
  по счётчику (не через обход byprojhash); сам перебор ограничен счётчиком,
  ранний выход после нужного числа активных записей

role_requests — бездокументарная схема и строгая типизация:
- namespace Role { CREATOR, AUTHOR, MASTER, NONE } в role_requests.hpp
- удалены поля statement и master_decision из role_request struct
- create()/approve() — без параметров document2
- validate_role_or_fail и apply_role_to_project — через Role:: namespace
- requestrole/inviterole/approverole/acceptinvite — без statement/master_decision
- requestrateu — role = Role::NONE (не маркер "rate"_n), request_type = RATE_UPDATE
- decline сохраняет только reason; решения подписываются coopname-транзакцией

prerequisite fix:
- names.hpp: убран дубль Names::Capital::CREATE_PROGRAM_EXPENSE (был объявлен
  и в callback-блоке line 55, и в акцепт-блоке line 130 — оставлен один)

Замечания закрыты:
- PR comment id=3264976591 (фу-фу byuserproj) — индекс удалён
- id=3264980890 (count_user_project_debts) — функция удалена
- id=3265021918 (loan независим от capital) — изоляция выполнена
- id=3265072380 (rate как name через namespace) — RequestType::RATE_UPDATE + Role::NONE
- id=3265080990 (счётчик в binary_extension вместо перебора) — active_debts_count
- id=3265088096 (namespace типа роли) — Role::{CREATOR,AUTHOR,MASTER}
- id=3265101459 (без statement/master_decision) — поля удалены
- id=3265104196 (через Namespace проверка) — validate_role_or_fail через Role::

Сборка: cdt-cpp v4.1.0 capital.wasm + loan.wasm без ошибок.
Тесты: capital-blagorost.test.ts — формулировки A2/D обновлены под счётчик
       и бездокументарную схему.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Утверждённые проводки и срок (обсуждение Matrix 2026-05-19, Игорь Смуров / Алексей Муравьев):

* Срок займа — фиксированный 1 год вместо 3 мес. (`DEFAULT_DEBT_TERM_SECONDS = 365 × 24 × 3600`).
* Новые операции ledger2:
  - `o.cap.dflt` — обращение взыскания на коммиты-обеспечение (Дт 04 / Кт 08, TRANSFER `w.cap.gen` → `w.cap.coopn`).
  - `o.cap.lnwoff` — списание займа в счёт обращения взыскания (Дт 80 / Кт 58, BURN `w.cap.loan`).
* Новый кошелёк `w.cap.coopn` (COOP_NMA, COOPERATIVE) — НМА кооператива, полученные из обеспечения.
* Кооперативный стандарт `p.cap.debt.standard.yaml` обновлён: состояние `defaulted`, шаг `dfltdebt`, две новые операции в секции 6, альтернативная ветка в сценарии.
* Тесты-заготовки в `capital-blagorost.test.ts` (Story 1.13).

Реализация самого action `capital::dfltdebt` остаётся за Story 1.13 — здесь только ontology + стандарт + проводки.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Канон имён в ledger2-онтологии — самодокументирующиеся английские термины
без аббревиатур. Префикс контракта (cap) не дублировать в имени.

Переименования (старые имена — отвергнутый рабочий драфт; нигде больше
не используются):
  o.cap.dflt    → o.cap.seize    (SEIZE_COLLATERAL — изъятие обеспечения)
  o.cap.lnwoff  → o.cap.wroff    (WRITE_OFF_LOAN — списание займа)
  w.cap.coopn   → w.cap.nma      (NMA — НМА кооператива)
  capital::dfltdebt → capital::seizecollat
  state defaulted (virtual) → seized (Status::SEIZED + mark_seized)

Story 1.13 — action capital::seizecollat:
  - guard: debt.status ∈ {paid, overdue} ∧ project.status == cancelled;
  - apply o.cap.seize  (TRANSFER w.cap.gen → w.cap.nma, Дт 04 / Кт 08);
  - apply o.cap.wroff  (BURN w.cap.loan,                Дт 80 / Кт 58);
  - decrease segment.debt_amount + active_debts_count;
  - subtract project.used_for_compensation;
  - decrease contributors.debt_amount;
  - mark debt SEIZED (mark_seized);
  - inline Loan::settle_debt (loan-контракт);
  - require_recipient(username, coopname);
  - require_auth(coopname) — без подписи пайщика, без сбора совета.

Решения 2026-05-19 (Игорь Смуров / Алексей Муравьёв):
  - срок займа фикс — 1 год;
  - проводки невозврата: Дт 04 / Кт 08 + Дт 80 / Кт 58;
  - автоматизм без подписи пайщика и без сбора совета.

Compile: cdt-cpp v4.1.0 OK (warnings only Ricardian + deprecation).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Утверждено автором 2026-05-19. Семантика операции через имя, группировка по
сущности (dbt для debt-операций), без аббревиатур типа seize/wroff/seizecollat.

Переименования (относительно предыдущего refactor-коммита):
  o.cap.seize       → o.cap.crtnma   (CREATE_NMA — создать НМА кооператива)
  o.cap.wroff       → o.cap.dbtwrf   (DEBT_WRITEOFF — списать долг)
  capital::seizecollat → capital::closedebt (закрыть долг)
  Status::SEIZED    → Status::WRITEOFF
  mark_seized       → mark_writeoff
  seizecollat.cpp   → closedebt.cpp

Кошелёк w.cap.nma и проводки (Дт 04 / Кт 08 + Дт 80 / Кт 58) — без изменений.

Канон утверждён ДО реализации (правило в auto-memory:
feedback-kanon-naming-soglasovyvat); предыдущий заход с собственными
именами отвергнут автором.

Compile: cdt-cpp v4.1.0 — exit 0, capital.abi содержит action closedebt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ant reviewed 2026-05-31 06:03:15 +00:00
ant left a comment
Author
Owner

Поправить

Поправить
@@ -7,1 +6,3 @@
# • o.cap.repay — возврат займа из акта-2 (TRANSFER w.cap.loan → w.wal.share, Дт 80 / Кт 58)
# Многоактовый процесс с авторизацией советом. Четыре операции:
# • o.cap.lend — выдача займа на момент авторизации (ISSUE → w.cap.loan, Дт 58 / Кт 51)
# • o.cap.repay — возврат займа из акта-2 (TRANSFER w.cap.loan → w.wal.share, Дт 80 / Кт 58)
Author
Owner

и никакого акта-2 у нас не существует. Есть один акт просто вторая подпись на нем.

и никакого акта-2 у нас не существует. Есть один акт просто вторая подпись на нем.
@@ -281,6 +356,39 @@ operations:
(Кт 58), сумма зачитывается в паевой фонд (Дт 80) и появляется как
доступный остаток на SHARE_FUND_PAY пайщика.
- ledger_code: o.cap.crtnma
Author
Owner

Стандарты должны описываться бизнес-языком для председателей и бухгалтеров! Смотри скилл /coop-standarts

Стандарты должны описываться бизнес-языком для председателей и бухгалтеров! Смотри скилл /coop-standarts
@@ -22,0 +24,4 @@
// loan не знает доменного контекста (проекта, программы) — это ответственность
// вызывающего контракта; ему же принадлежит и локальная привязка debt_hash.
// binary_extension: таблица в продакшене (пустая), новые поля только хвостом.
binary_extension<name> source_contract;
Author
Owner

Ну так если таблица пустая - зачем здесь расширение то? Не надо! просто name.

Ну так если таблица пустая - зачем здесь расширение то? Не надо! просто name.
claude added 1 commit 2026-05-31 06:25:05 +00:00
1. loan.debts.source_contract — убрана binary_extension-обёртка, простой
   name. Таблица в продакшене пустая, расширение не нужно.

2. p.cap.debt.standard.yaml переписан бизнес-языком для председателей /
   бухгалтеров / пайщиков. Из прозы убраны идентификаторы кошельков
   (w.cap.*), коды операций (o.cap.*), бухгалтерские проводки в шапке и
   action.purpose / state.description / scenario / alternatives. Технические
   детали остались только в structured-секциях 5 (documents) и 6 (operations).

3. «акт-2» убран как ошибочная терминология — у проекта один акт, на
   нём ставится вторая подпись. Поправлено везде в стандарте.

Compile: loan + capital собраны через cdt-cpp v4.1.0 без ошибок.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Owner

Раунд 1 фиксов в gitea (commit 24663bdaac3)

Три замечания закрыты.

1. loan.debts.source_contract — убрана binary_extension

components/contracts/cpp/lib/domain/table_loan_debts.hpp:27 — теперь просто name source_contract. Таблица в продакшене пустая, обёртка не нужна. Соответственно в loan::createdebt:39d.source_contract = payer; вместо .emplace(payer).

2. Стандарт p.cap.debt.standard.yaml — бизнес-язык

Полностью переписан под аудиторию председатель / бухгалтер / пайщик по правилам skill /coop-standards. Из прозаических полей (purpose, actions[].purpose, states[].description, scenario, alternatives) убраны:

  • идентификаторы кошельков (w.cap.*)
  • коды операций (o.cap.*)
  • бухгалтерские проводки (Дт 04 / Кт 08)
  • имена статусов латиницей внутри прозы
  • технические подсказки на ledger2/registry

Технические детали остались только в structured-секциях:

  • секция 5 (documents) — registry_id, stored_in;
  • секция 6 (operations) — ledger_code, wallet_op, debit/credit, wallet_from/wallet_to, user_wallet/user_ref/available_delta для USER_SHARED (добавил L3 в o.cap.repay — зачисление на w.wal.share пайщика).

В roles — русские названия (Пайщик / Председатель / Совет / Кассир) вместо ролевых кодов.

3. «акт-2» — убран

Терминология поправлена: у проекта один акт, на нём ставится вторая подпись. Везде в стандарте (states.paid.description, scenario.step 4, operations.o.cap.repay.description, related.note) сейчас «сдача результата работ по проекту (вторая подпись на акте)».

Сборка

cdt-cpp v4.1.0 через dicoop/blockchain:latest — exit 0 для loan и capital.

## Раунд 1 фиксов в gitea (commit `24663bdaac3`) Три замечания закрыты. ### 1. `loan.debts.source_contract` — убрана `binary_extension` `components/contracts/cpp/lib/domain/table_loan_debts.hpp:27` — теперь просто `name source_contract`. Таблица в продакшене пустая, обёртка не нужна. Соответственно в `loan::createdebt:39` — `d.source_contract = payer;` вместо `.emplace(payer)`. ### 2. Стандарт `p.cap.debt.standard.yaml` — бизнес-язык Полностью переписан под аудиторию председатель / бухгалтер / пайщик по правилам skill `/coop-standards`. Из прозаических полей (`purpose`, `actions[].purpose`, `states[].description`, `scenario`, `alternatives`) убраны: - идентификаторы кошельков (`w.cap.*`) - коды операций (`o.cap.*`) - бухгалтерские проводки (`Дт 04 / Кт 08`) - имена статусов латиницей внутри прозы - технические подсказки на ledger2/registry Технические детали остались только в structured-секциях: - секция 5 (documents) — `registry_id`, `stored_in`; - секция 6 (operations) — `ledger_code`, `wallet_op`, `debit/credit`, `wallet_from/wallet_to`, `user_wallet`/`user_ref`/`available_delta` для USER_SHARED (добавил L3 в `o.cap.repay` — зачисление на `w.wal.share` пайщика). В `roles` — русские названия (Пайщик / Председатель / Совет / Кассир) вместо ролевых кодов. ### 3. «акт-2» — убран Терминология поправлена: у проекта один акт, на нём ставится вторая подпись. Везде в стандарте (`states.paid.description`, `scenario.step 4`, `operations.o.cap.repay.description`, `related.note`) сейчас «сдача результата работ по проекту (вторая подпись на акте)». ### Сборка `cdt-cpp v4.1.0` через `dicoop/blockchain:latest` — exit 0 для loan и capital.
claude added 1 commit 2026-05-31 06:28:02 +00:00
Поправка терминологии в p.cap.debt.standard.yaml: «вторая подпись на
акте» → «подписанный акт приёма-передачи» — корректное юридическое
название документа, который завершает приём результата работ по проекту.

Затронуты: operations.o.cap.repay.description, related.p.cap.rid.note.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ant merged commit de55c3aa3e into blagorost 2026-06-02 06:17:25 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: C9S/mono#3