[C28-33] feat(blagorost-on-chassis): Капитал → шасси расходов через inter-порт #67

Closed
claude wants to merge 0 commits from feat/blagorost-on-chassis into feat/expense-chassis-mvp
Owner

Что это

PR2 над #61 (шасси-MVP): первый consumer шасси расходов — программные расходы Капитала.

Архитектура

capitalтолько инициатор + получатель callback'a. Весь lifecycle (approve / authorize / pay / report / close / decline) обслуживает шасси expense. Связь:

capital::createpgexp  ─ резерв пула + inline action ──► expense::createexp
                                                           │
                                          callback={capital, onpgexpdone}
                                                           │
expense (lifecycle: authorize/pay/report/close|decline) ───┘
                                                           │
expense::closeexp/declexp ─ inline action ──► capital::onpgexpdone

Backend: capital extension зовёт шасси не напрямую, а через порт @coopenomics/inter::InterExpenseChassisPort (read-only proposals для list+карточки; write идёт на capital). Тот же порт переиспользует marketplace, EMP и любой следующий consumer.

Состав PR

C++:

  • capital::createpgexp (initiator), capital::onpgexpdone (callback), capital::topupprogexp.
  • Из expense::declexp/closeexp теперь шлётся unified callback с status (CLOSED/DECLINED).
  • program_expense_pool + program_expense_reserved в global_state.
  • Удалены дубли actions: apprvpgexp/authpgexp/pgexppay/declpgexp — это работа шасси.

cooptypes:

  • Capital.Actions.{TopupProgramExpense, CreateProgramExpense, OnProgramExpenseDone} + Capital.Tables.ProgramExpenses.
  • Снесены устаревшие IClosedebt/IDebtpayretry/IMarkdebtoverd/IRequestrole/*/IRoleRequest интерфейсы и actions, которых нет в текущем contract.

inter (порт):

  • InterExpenseChassisPort + DI токен INTER_EXPENSE_CHASSIS.
  • Adapter в extensions/expenses/infrastructure/inter/.
  • Регистрация в InterCommunicationBridgeModule.

Backend (capital extension):

  • CapitalBlockchainPort.{createProgramExpense, topupProgramExpense}.
  • ProgramExpensesManagementService — write через capital, read через inter-порт (list по callback.contract='capital'+action='onpgexpdone' + by hash).
  • ProgramExpensesResolver — chairman/member guards.

SDK:

  • Zeus regen + manual wrappers Mutations.Capital.{CreateProgramExpense, TopupProgramExpense} + Queries.Capital.{GetProgramExpense, GetProgramExpenses} + селекторы.

Desktop:

  • extensions/capital/entities/ProgramExpense (store + api).
  • features/ProgramExpense/CreateProgramExpense — подпись documents registry 2010 (reuse шасси-пайплайна) → capitalCreateProgramExpense.
  • features/ProgramExpense/TopupPoolcapitalTopupProgramExpensePool.
  • pages/ProgramExpensesPage — list + 2 диалога, канон mono-platform.
  • Route /:coopname/capital/program-expenses (icon receipt, chairman+member).

Не в этом PR (отдельно)

  • factory templates 1010/1011 (старые Capital expense) остаются как есть — снесём отдельным PR, когда старый flow capital::createexpense будет выпилен.
  • onpgexpdone parser-зеркало progexpenses-таблицы (MVP читает данные через inter, on-chain progexpense — индекс владения).

План проверки

  • pnpm tsc --noEmit (controller) — чисто.
  • pnpm build (sdk) — чисто.
  • Quasar dev server подхватывает страницу /capital/program-expenses без ошибок резолва.
  • e2e: создать программный расход → авторизовать через шасси → выдать → отчитаться → закрыть → дебет пула.
## Что это PR2 над #61 (шасси-MVP): первый consumer шасси расходов — программные расходы Капитала. ## Архитектура `capital` — **только инициатор + получатель callback'a**. Весь lifecycle (approve / authorize / pay / report / close / decline) обслуживает шасси `expense`. Связь: ``` capital::createpgexp ─ резерв пула + inline action ──► expense::createexp │ callback={capital, onpgexpdone} │ expense (lifecycle: authorize/pay/report/close|decline) ───┘ │ expense::closeexp/declexp ─ inline action ──► capital::onpgexpdone ``` Backend: capital extension зовёт шасси не напрямую, а через порт `@coopenomics/inter::InterExpenseChassisPort` (read-only proposals для list+карточки; write идёт на capital). Тот же порт переиспользует marketplace, EMP и любой следующий consumer. ## Состав PR **C++:** - `capital::createpgexp` (initiator), `capital::onpgexpdone` (callback), `capital::topupprogexp`. - Из `expense::declexp`/`closeexp` теперь шлётся unified callback с `status` (CLOSED/DECLINED). - `program_expense_pool` + `program_expense_reserved` в global_state. - Удалены дубли actions: `apprvpgexp/authpgexp/pgexppay/declpgexp` — это работа шасси. **cooptypes:** - `Capital.Actions.{TopupProgramExpense, CreateProgramExpense, OnProgramExpenseDone}` + `Capital.Tables.ProgramExpenses`. - Снесены устаревшие `IClosedebt/IDebtpayretry/IMarkdebtoverd/IRequestrole/*/IRoleRequest` интерфейсы и actions, которых нет в текущем contract. **inter (порт):** - `InterExpenseChassisPort` + DI токен `INTER_EXPENSE_CHASSIS`. - Adapter в `extensions/expenses/infrastructure/inter/`. - Регистрация в `InterCommunicationBridgeModule`. **Backend (capital extension):** - `CapitalBlockchainPort.{createProgramExpense, topupProgramExpense}`. - `ProgramExpensesManagementService` — write через capital, read через inter-порт (list по `callback.contract='capital'+action='onpgexpdone'` + by hash). - `ProgramExpensesResolver` — chairman/member guards. **SDK:** - Zeus regen + manual wrappers `Mutations.Capital.{CreateProgramExpense, TopupProgramExpense}` + `Queries.Capital.{GetProgramExpense, GetProgramExpenses}` + селекторы. **Desktop:** - `extensions/capital/entities/ProgramExpense` (store + api). - `features/ProgramExpense/CreateProgramExpense` — подпись documents registry 2010 (reuse шасси-пайплайна) → `capitalCreateProgramExpense`. - `features/ProgramExpense/TopupPool` — `capitalTopupProgramExpensePool`. - `pages/ProgramExpensesPage` — list + 2 диалога, канон mono-platform. - Route `/:coopname/capital/program-expenses` (icon receipt, chairman+member). ## Не в этом PR (отдельно) - factory templates 1010/1011 (старые Capital expense) остаются как есть — снесём отдельным PR, когда старый flow `capital::createexpense` будет выпилен. - onpgexpdone parser-зеркало progexpenses-таблицы (MVP читает данные через inter, on-chain progexpense — индекс владения). ## План проверки - [ ] `pnpm tsc --noEmit` (controller) — чисто. - [ ] `pnpm build` (sdk) — чисто. - [ ] Quasar dev server подхватывает страницу `/capital/program-expenses` без ошибок резолва. - [ ] e2e: создать программный расход → авторизовать через шасси → выдать → отчитаться → закрыть → дебет пула.
claude added 7 commits 2026-06-03 13:06:20 +00:00
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>
capital теперь только инициатор+получатель результата для программного расхода —
весь flow обслуживает шасси expense. Раньше blagorost-ветка дублировала
approve/auth/pay/decline на стороне capital.

Изменения capital:
- удалены apprvpgexp/authpgexp/pgexppay/declpgexp (дубль шасси).
- createpgexp переписан: вместо самообслуживания резервирует пул и шлёт inline
  expense::createexp с callback={capital, onpgexpdone}.
- сигнатура createpgexp принимает operation_code+items[] (передаётся в шасси).
- добавлен onpgexpdone(coopname, hash, status, total_actual, data) — callback от
  шасси на CLOSED/DECLINED, обновляет program_expense_pool и удаляет progexpense.

Изменения expense (шасси):
- callback payload расширен полем status (ExpenseDomain::ProposalStatus) — инициатор
  отличает CLOSED от DECLINED.
- declexp теперь шлёт callback тоже (раньше только closeexp).

Изменения names.hpp:
- удалён CAPITAL_RESOLVE_PROGRAM_EXPENSE / AUTHORIZE_PROGRAM_EXPENSE /
  DECLINE_PROGRAM_EXPENSE / CONFIRM_PROGRAM_EXPENSE_PAYMENT.
- добавлен ON_PROGRAM_EXPENSE_DONE.
- добавлен External::CREATE_EXPENSE_PROPOSAL для inline в шасси.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Адаптация blagorost-cooptypes под архитектуру «capital = инициатор + callback,
шасси expense обслуживает весь flow». Из blagorost-E2 берётся минимальный набор
интерфейсов под программный расход, без debt/role/duplicate-action типов.

Изменения:
- ICreatepgexp: amount/description упрощены, добавлены operation_code+items[]
  (передаются inline в шасси expense::createexp).
- ITopupprogexp — без изменений (пополнение пула).
- IOnpgexpdone — НОВЫЙ callback от шасси, payload (hash, status, total_actual, data).
- IApprvpgexp/IAuthpgexp/IPgexppay/IDeclpgexp — УДАЛЕНЫ (шасси expense замещает).
- IProgramExpense table-interface — сохранён.
- import IItem из expense interface для типизации items[].

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Тонкое расширение Capital для программных расходов: чисто consumer над
шасси `expense` (только Капитал-специфика, остальное обслуживает шасси).

Чейн:
  - CapitalBlockchainPort.createProgramExpense → capital::createpgexp.
  - CapitalBlockchainPort.topupProgramExpense  → capital::topupprogexp.

Backend:
  - ProgramExpensesManagementService — write через capital, read через
    INTER_EXPENSE_CHASSIS (list по owner='capital'+action='onpgexpdone' +
    карточка по hash). Никакого собственного TypeORM-mirror.
  - ProgramExpensesResolver — chairman/member guards, mutation+query.
  - DTO: CreateProgramExpenseInputDTO (items+statement registry 2010),
    TopupProgramExpenseInputDTO, ProgramExpenseOutputDTO.
  - Регистрация в capital-extension.module.

actual_amount/status в createProgramExpense инициализируются 0/0 серверно
(их C++ дефолтит, но cooptypes требует все поля IItem).

PR2 (Капитал на шасси).
Регенерация после backend-расширения. Добавлены manual wrappers (SDK Mutations/
Queries.Capital — не auto-generated, обёртки над Zeus):

  Mutations.Capital.CreateProgramExpense (capitalCreateProgramExpense)
  Mutations.Capital.TopupProgramExpense  (capitalTopupProgramExpensePool)
  Queries.Capital.GetProgramExpenses     (capitalProgramExpenses + pagination)
  Queries.Capital.GetProgramExpense      (capitalProgramExpense by hash)

Селекторы:
  programExpenseSelector / programExpensesPaginationSelector
  + items/callback вложенные селекторы.
Тонкая consumer-страница Капитала над шасси расходов. Доступ chairman+member.

  entities/ProgramExpense — store + api (Queries.Capital.GetProgramExpense{s}).
  features/ProgramExpense/CreateProgramExpense — useCreateProgramExpense:
    generateUniqueHash → reuse `generateExpenseProposalStatementDocument` шасси
    → подпись через DigitalDocument (registry 2010, Эпик 4 pipeline)
    → Mutations.Capital.CreateProgramExpense.
  features/ProgramExpense/TopupPool — useTopupProgramExpensePool:
    Mutations.Capital.TopupProgramExpense.
  pages/ProgramExpensesPage — list по канону mono-platform (BaseCard/Chip/
    EmptyState/--p-* токены), два диалога в шапке.
  install.ts — route `/:coopname/capital/program-expenses` (name
    `capital-program-expenses`, иконка receipt, agreementsBase, roles=
    chairman+member).
claude added 1 commit 2026-06-03 13:15:13 +00:00
Why: ExpenseMechanics и ExpenseRecipientType — string enums; Number(str) даёт NaN, wharfkit не сериализует. Зеркалит маппинг из expenses-mutations.service.ts:57-63.
claude added 1 commit 2026-06-03 13:18:04 +00:00
Why: send_callback_if_any слал (proposal_hash, status, amount, data), а capital::onpgexpdone ждёт (coopname, expense_hash, status, total_actual, data). Скоуп таблицы progexpenses = coopname, без него инициатор не найдёт запись. Применимо ко всем будущим consumer'ам шасси (marketplace, EMP).
claude added 1 commit 2026-06-03 13:23:14 +00:00
Why: использовал несуществующие токены/классы — UI бы рендерился со сломанными цветами/иконками. Привёл к канону:
- BaseChip: `:variant` (pos/accent/warn/neg/info/neutral), не `:label/:tone` (нет такого API).
- BaseButton: иконка через `<template #icon-left>`, не `icon='X'` (нет такого пропа).
- CSS-токены: `--p-ink`/`--p-ink-2` (не `--p-text`/`--p-muted`), `--p-fs-meta` (не `--p-fs-body-xs`).
- Утилитарные классы: `t-h2`/`t-sm`/`t-eyebrow`/`t-muted` из components.css, не `t-section`/`t-body-sm`.
claude added 1 commit 2026-06-03 14:29:22 +00:00
ProgramExpenseItemOutputDTO теперь отдаёт ExpenseMechanics / ExpenseRecipientType /
ExpenseItemStatus вместо magic Int — это явное нарушение canon'а GraphQL "enum
вместо строковых литералов / number". Сервис маппит number→enum зеркально
expenses-extension. Резолвер возвращает PaginationResult<T> через canon-сигнатуру
вместо «as never». Десктоп подменил локальные string-union'ы на Zeus enum
(ExpenseRecipientType / ExpenseMechanics / ExpenseProposalStatus).

Schema regen + Zeus regen + SDK build.
claude added 2 commits 2026-06-03 16:17:08 +00:00
Вынес общий PaginationInput → PaginationResult helper из capital-service в
~/application/common/dto/pagination.dto. Consumer'ы inter-port'ов (без своего
Repository.findAndCount) получают канон-сборку без дубля логики totalPages/
currentPage.

Также `paginationInputToOffset(options)` — стандартная конверсия для адаптеров,
работающих offset-form (большинство inter-port read API).

Capital ProgramExpensesManagementService переведён на оба helper'а — ужал
listProgramExpenses до 10 строк.
R4: убрал хардкод "RUB" и "1000.0000 RUB" из feature ProgramExpense.
 - submitTopup принимает raw число "10000" → форматирует через formatToAsset
   с info.symbols.root_govern_symbol/precision.
 - submitProgramExpense форматирует total_amount + planned_amount каждого item
   через тот же helper (UX-долг "amount без IMask" закрыт по сумме).
 - Placeholder'ы Dialog'ов вычисляются computed'ом из system.info.symbols.

R1 (частично): тип `GenerateStatementInput = Mutations.Expense.GenerateExpense
ProposalStatementDocument.IInput['data']` + деструктуризация результата через
computed `[Mutations.X.Y.name]` — убраны 2 из 4 `as any` каста (input + index).
Оставшиеся (signed-as-any и create-input-as-any) — отдельный refactor PR через
signed-doc adapter в shared/lib/document.

Source-wallet вынесен в локальную map SOURCE_WALLET_BY_OPERATION с throw на
неизвестный код — backend-derivation (R3) отдельной задачей.
claude added 4 commits 2026-06-10 18:42:48 +00:00
- expense::createexp принимает контракты-инициаторы по contracts_whitelist
  (capital@eosio.code не имеет coopname@active — inline падал по authority);
  capital::createpgexp шлёт inline от _capital@active.
- capital::onpgexpdone: require_auth(_expense) вместо _capital — callback
  шасси шёл с authority expense@active и всегда падал.
- payexp: cap actual<=plan + DIRECT-item сразу REPORTED с пересчётом статуса
  proposal — DIRECT-only СЗ навсегда зависал в PARTIALLY_PAID.
- returnexp/overspendexp: settlement-семантика по PRD — статус item остаётся
  PAID, отчёт закрывает item штатным reportexp (раньше RETURNED/OVERSPENT
  были терминальными тупиками: перерасход блокировал closeexp навсегда).
- declexp: только CREATED/AUTHORIZED — decline после оплат разъезжался
  с учётом пула в capital (возвращал весь резерв при ушедших деньгах).
- onpgexpdone CLOSED: перерасход сверх резерва списывается из
  program_expense_pool (spend_program_expense_pool).
- names.hpp: redefinition CREATE_PROGRAM_EXPENSE + несуществующий
  Names::Capital::Callbacks — контракты не компилировались вовсе.
- verify_document_or_fail(statement, {creator}) в createpgexp; снос
  мёртвых set_program_approved/set_program_authorized.

Оба контракта собраны: expense.wasm + capital.wasm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
claude closed this pull request 2026-06-10 18:43:55 +00:00
claude reopened this pull request 2026-06-10 18:43:58 +00:00
claude added 2 commits 2026-06-10 18:50:02 +00:00
claude closed this pull request 2026-06-10 18:51:00 +00:00
claude reopened this pull request 2026-06-10 18:51:03 +00:00
claude added 1 commit 2026-06-10 19:12:02 +00:00
claude closed this pull request 2026-06-10 19:12:18 +00:00
claude reopened this pull request 2026-06-10 19:12:19 +00:00
claude added 1 commit 2026-06-10 19:24:32 +00:00
claude closed this pull request 2026-06-10 19:25:11 +00:00
claude reopened this pull request 2026-06-10 19:25:11 +00:00
claude closed this pull request 2026-06-11 04:38:04 +00:00

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: C9S/mono#67