Добавляем расчётный флаг settings.is_registration_open в getSystemInfo и заглушку на странице signup, чтобы пайщик не попадал в ошибку генерации документов. Параллельные утверждения решений больше не затирают vars при read-modify-write.
Co-authored-by: Cursor <cursoragent@cursor.com>
Кнопки «Отменить» и «Завершить протокол» показываются только после окончания
голосования — в зависимости от принятия повестки. Председатель участка
выбирается только среди физических лиц, с проверкой на backend.
Co-authored-by: Cursor <cursoragent@cursor.com>
Удалён шаблон 321 и заявление при присоединении к собранию; joindec без document.
В протокол 325 добавлено приложение с положением участка, factory-тесты 320–330.
Экспорт getPaymentMethods.name в SDK; пункт меню «Выйти из кооператива».
Co-authored-by: Cursor <cursoragent@cursor.com>
Resolve conflict in process-decisions handlers — keep both
Ku establishment and membership exit decision generators.
Co-authored-by: Cursor <cursoragent@cursor.com>
Изменены компоненты, связанные с выходом из кооператива. В меню заменены иконки и пути для поддержки и выхода. Обновлены метаданные и названия для кнопок и страниц, чтобы улучшить пользовательский интерфейс. В ExitButton добавлены параметры для настройки иконки и метки. Упрощена структура MembershipExitPage с акцентом на ключевые шаги выхода. Исправлены комментарии и типы для лучшего понимания кода.
Поля под ручное заполнение (банковский счёт в 329, № протокола/доверенности
в 329 и 330) — линия подчёркивания «______________» вместо em-dash, чтобы
вписать значение ручкой на распечатанном экземпляре.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Текст генерируемых документов учреждения кооперативного участка по
замечаниям методолога. Структура он-чейн пакетов не затронута.
- 323 Протокол собрания: убрано «кооперативного участка» из названия и
meeting_name (просто «собрание пайщиков»); литерация «Протокол № КУ-N»
сохранена.
- Повестка собрания (desktop, KuMeetingDetails/onStart + KuMeetings):
в каждый вопрос добавлена принадлежность «...потребительского
кооператива «Восход»»; полномочие обратиться в совет вшито во второй
вопрос (третий вопрос не нужен — методолог «три в два»). Имя кооператива
из System store (vars.name / full_abbr_genitive).
- 322 Бюллетень: в текст добавлено «потребительского кооператива «{name}»».
- 324 Заявление в совет: «прошу Совет согласовать создание участка ... и
согласовать кандидатуру избранного председателя».
- 325 Решение совета: повестка разложена на 2 вопроса (учреждение +
утверждение председателя и поручение Совету организовать сопровождение —
банковский счёт, доверенность).
- 327/328 Договоры матответственности: из названия убрано «ПОЛНОЙ
ИНДИВИДУАЛЬНОЙ» (тело без изменений).
- 329 Доверенность председателю КУ: переработана по эталону —
реквизиты ПК в шапку, паспорт+регистрация инлайн в первый абзац,
банковский счёт «№ —» (прочерк до открытия счёта в банке), ссылка на
«Протокол № —» совета (прочерк), убран нижний блок «Уполномоченный: ФИО»,
подпись доверителя + М.П.
- 330 Доверенность оператору: переработана по эталону — шапка
«Кооперативный участок ... ПК ...», доверитель — председатель КУ,
паспорт оператора инлайн, без банковского счёта, подпись + М.П. (КУ).
Эталоны: ~/blago/production/shared/ШАБЛОН_Доверенность_*.pdf,
ШАБЛОН_ДОГОВОР_О_МАТЕРИАЛЬНОЙ_ОТВЕТСТВЕННОСТИ_*.pdf
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
confirmexit теперь обходит сет LEDGER2_EXIT_REFUND_WALLETS (w.reg.minshr +
w.wal.share + w.cap.blago): собирает доступные L3-балансы каждого (>0),
консолидирует на главный паевой (w.reg.minshr→o.reg.mvmin, w.cap.blago→
o.cap.wthcap) и ставит полную сумму на возврат единым платежом. Раньше
хардкодил только minshr+share — паевой в Благоросте оставался висеть.
Сет задан один раз в wallets.hpp (источник истины), генерируется gen:from-cpp
в cooptypes (LEDGER2_EXIT_REFUND_WALLETS / EXIT_REFUND_WALLET_NAMES) и обходится
backend-preview (getReturnPreview) — расчёт на фронте всегда совпадает с тем,
что реально вернёт контракт. Подпись «планируемая сумма / итог фиксирует Совет»
убрана из ExitButton и ExitOverlay (сумма теперь авторитетна).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- статус: вышедший пайщик (participant_account стёрт delpartcpnt + user_account
blocked) больше не показывается «Активный пайщик» — добавлен терминал
«Вышел из кооператива» по user_account.status
- дата вступления: фолбэк participant_account.created_at → user_account.registered_at
(у вышедших пайщик-запись стёрта, дата больше не «отсутствует»)
- убрана кнопка удаления пайщика (таблица + карточка + диалог + машинерия):
пайщика из реестра не удаляют
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Раньше всё (протокол, бюллетени, заявление в совет, договор, доверенность,
решение совета) висело на одном якоре hash → получались две почти одинаковые
карточки одного пакета (протокол и заявление), документы перетекали между ними.
Разводим на два процесса по разным package-хэшам:
- Пакет СОБРАНИЯ пайщиков (якорь = proposal.hash): протокол (closedec) + бюллетени
(votedec). Появляется в реестре сразу при закрытии собрания.
- Пакет СОВЕТА (якорь = hash процесса): заявление в совет + договор матответственности
+ доверенность + решение совета. Советская часть (createagenda/confirmdec/declinedec/
одобрения) НЕ тронута — нулевой риск.
Меняются только package-аргументы в closedec (make_complete_document) и votedec (newlink).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
closedec при типе createbranch заводил отдельный complete-document (newsubmitted)
на протокол собрания пайщиков, а exec/create_agenda — второй на заявление в совет.
Один и тот же package=hash, но два newsubmitted → две почти одинаковые карточки
в реестре документов.
Теперь для createbranch протокол линкуется (newlink) по якорному хэшу к пакету
заявления председателя в совет: заявление — единственный statement пакета, внутри
него протокол собрания пайщиков, бюллетени, договор матответственности, доверенность
и решение совета. Для free-решений протокол остаётся самостоятельным документом.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Культура денег: входящие — подтверждением, исходящие — чеком. Единый механизм
на уровне ядра (gateway), привязка по payment_hash — один на все исходящие
(возврат паевого/withdrawal/registration-refund/аванс расхода), переиспользуется
расширениями. Контроль мягкий: статус не трогаем, но зеркалим proof_count →
реестр рисует «чек приложен / не приложен».
Backend (core gateway):
- таблица payment_files + бакет gateway:files (@UseBucket), PaymentFilesService
(upload/read-url/list/delete + зеркалирование proof_count в платёж)
- enum PaymentFileKind (PAYMENT_PROOF), DTO, резолвер uploadPaymentProof /
paymentProofs / paymentFile; провайдеры в typeorm.module + gateway.module
SDK: selectors/mutations/queries gateway + zeus regen
Desktop:
- features/Payment/AttachPaymentProof — панель чека по payment-hash
- реестр: панель + индикатор «чек приложен» для ЛЮБОГО исходящего (PAID/COMPLETED)
- конвергенция expense: чек ушёл в ядро, AttachExpenseProof оставляет только
закрывающие документы (DIRECT)
Терминология: «чек об оплате» (не «платёжка»/«первичка»).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
После completexit строка registrator::exits стирается (терминал=erase) →
getMembershipExit возвращал null → оверлей пропадал, кабинет разблокировался,
пайщик видел стол и мог подать выход повторно (гарда не было).
- enum MembershipExitStatus += COMPLETED
- getMembershipExit: терминальная фаза по blocked-аккаунту + персистентному
MEMBERSHIP_EXIT-платежу (сумма возврата + статус 'Оплачено')
- createMembershipExit: гард — заблокированный аккаунт не может выйти повторно
(контракт уже блокирует через get_participant_or_fail; отсекаем раньше)
- ExitOverlay: терминальная фаза 'Вы вышли из кооператива' (сумма + 'Оплачено'
+ ожидайте поступления), она же UX-гард — перекрывает кабинет
- SDK Zeus regen под новый enum
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Инцидент 2026-06-16: коммит d02c0ce (18 мая) добавил program_expense_pool и
program_expense_reserved в global_state ПЕРЕД полем config напрямую, без
binary_extension. После деплоя на прод таблица state перестала читаться:
запись сериализована старым layout (config сразу после
program_membership_cumulative_reward_per_share), а новый ABI ждёт два asset
перед config → unpack натыкается на байты config (get table → "Invalid
symbol ...Y@" = double 100.0 = config.expense_pool_percent). Любой action,
читающий global_state, падал.
Фикс: оба поля перенесены в ХВОСТ struct (после config) и обёрнуты в
eosio::binary_extension<asset>. Старая запись прода читается без изменений
(хвостовые extension опциональны → пусто = 0), новая логика программных
расходов сохранена. Поля в таблицу EOSIO дописываются только в конец и только
binary_extension'ом.
utility-функции State:: (topup/reserve/release/consume/spend) переведены на
optional-семантику через ext_or_zero(); инвариант — оба поля материализуются
вместе, чтобы порядок хвостовых extension был консистентным. Прямого доступа к
полям вне State:: нет. Собрано в CDT-докере: capital.wasm слинкован, в ABI
program_expense_* → asset$ после config.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
По решению пользователя заход долей Благороста — только в активные проекты.
В pending больше не заводим: при переходе pending→active придёт новая дельта
capital::projects со status=active, на ней листенер и зарегистрирует.
- Листенер ProgramShareRegistrationOnProjectDeltaListener: гейт pending|active → только active.
- ProgramShareRegistrationService.findActiveProjects (путь крона и wallet-листенера): фильтр pending|active → только active.
- Тест: pending теперь НЕ реагирует; active — позитивный кейс.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Дефолт promote.sh — текущий HEAD чекаута; второй аргумент по-прежнему задаёт явный
ref (ветка/тег/sha). Типичный путь: cut на dev → promote testnet → promote main,
все три от того же HEAD. testnet/main остаются независимыми FF-указателями.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ссылка из письма открывается часто без сессии кабинета (другой браузер/инкогнито/
после logout). Подтверждение работает по токену без входа, но wallet защищён —
гард молча редиректил на login-redirect, и пайщик не понимал, что произошло.
- есть сессия → 'Перейти в кабинет' → wallet (глобальный ExitOverlay сам покажет
'на рассмотрении Совета')
- нет сессии → 'Войти в кабинет' → signin + пояснение, что войти нужно для слежения
за статусом и суммой возврата
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Доли пайщиков Благороста регистрировались в проект только периодическим
scheduler'ом (regshare) и точечным листенером на дельту кошелька. Новый
проект баланс не меняет → wallet-листенер молчит, и наполнение нового
проекта зависело ТОЛЬКО от крона. Если компонент быстро прогнали
pending→…→result до тика крона, пайщики в него не попадали, а откат
result→active контракт не допускает — доли в компоненте терялись
безвозвратно (voskhod, компонент 011bcd92…, 2026-06-16).
- Новый ProgramShareRegistrationOnProjectDeltaListener на
delta::capital::projects: при появлении проекта в статусе pending|active
сразу регистрирует доли всех активных пайщиков (неблокирующе, fire-and-forget,
переиспользуя ядро syncContributor — тот же путь, что и крон).
- ProgramShareRegistrationService.syncProgramSharesForProject — обход пайщиков
по одному проекту; syncContributor переведён с ProjectDomainEntity[] на
projectHashes: string[].
- Крон ОСТАВЛЕН как reconciliation-бэкстоп: он ещё и ДОобновляет уже
зарегистрированные доли при дрейфе баланса (контракт upsert_contributor_segment
это допускает) и подбирает события, потерянные при downtime контроллера.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Паровоз dev→testnet→main заменён независимыми указателями: каждое окружение —
самостоятельный fast-forward-указатель на выбранный релизный тег dev. Прод и тест
катятся разным темпом и на разные версии одновременно, без диверджа и конфликтов
(оба — FF-указатели на линейную историю dev).
- promote.sh <testnet|main> [ref]: FF выбранного тега (по умолчанию — последний на
origin/dev) на ветку окружения; main больше не зависит от testnet; только вперёд.
- release.yaml: workflow_dispatch получил inputs environment+ref — откат на старую
версию / редеплой без бампа / hotfix в один контур (то, что FF не умеет). Сборка
идёт из вычекнутого ref, окружение — из inputs.environment. Авто-триггер по
изменению lerna.json сохранён.
- RELEASE.md: модель и инварианты переписаны под независимые указатели.
Принципы деплоя сохранены: бамп версии один раз на dev, образы по версии,
BUILD_MODE по окружению, webhooks per env, npm publish/доки только на main.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- заголовок документа из DocumentHtmlReader (3rem) прижат к h2 по канону,
:deep + !important как в ReadStatement.vue
- 'Сумма к возврату' оформлена soft-панелью (label+hint слева, mono-значение
справа) вместо висящего снизу текста
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Суммы к возврату форматируются канонной formatAsset2Digits ("300.0000 RUB"
→ "300,00 RUB") в диалоге заявления и в оверлее — вместо сырого asset.
- Убрана разбивка "целевой + минимальный" (оба паевые, суммируются): показываем
одну строку "Сумма к возврату".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Было две крутилки рядом («Формирование заявления…» + «Расчёт суммы…»).
Теперь заявление и сумма грузятся параллельно (Promise.allSettled) под одним
центрированным лоадером и показываются вместе. Тот же стиль лоадера — на фазе
проверки реквизитов. Сумма некритична: документ подписывается и без предрасчёта.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Пункт меню «Выход из кооператива» перенесён в самый низ (после «Поддержки»),
иконка group_remove вместо logout — чтобы не сливаться с кнопкой «Выйти»
(выход из кабинета, дверь-logout) внизу панели.
- Гейт реквизитов сделан fail-open: блокируем выход ТОЛЬКО при достоверно
пустом списке методов; null/непонятный ответ не блокирует. + реактивный
фолбэк: если бэкенд отклонил подачу из-за отсутствия реквизитов — диалог
переключается на баннер. Авторитетный источник — бэкенд (createMembershipExit).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Счёт регистрационного взноса создаётся со статусом pending сразу при заходе на
шаг оплаты («счёт выставлен», деньги ещё не получены). При возврате на страницу
(перезагрузка/повторный вход) роутинг считал сам факт наличия платежа за «этап
оплаты пройден» и вёл на экран ожидания, где дефолтная ветка показывала «Ваш
платеж принят» — хотя оплаты не было и статус не менялся.
- SignUp: на экран ожидания/отказа ведём только при PAID/COMPLETED либо
терминальном статусе (отказ/отмена/возврат); pending → шаг оплаты (QR + поллинг).
- WaitingRegistration: для pending — отдельная ветка «Ожидаем поступление оплаты»
+ кнопка «Перейти к оплате»; «платёж принят» остаётся только для PAID/COMPLETED.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ExitOverlay: чип статуса исходящего платежа у суммы (ожидает оплаты/оплачивается/
оплачено/ошибка) — берётся из payment_status, меняется по мере обработки кассиром.
- MembershipExitPage переверстана на AuthCard (иконка-предупреждение + заголовок +
ключевые шаги процедуры + запуск подачи) — единый канон с оверлеем.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
getMembershipExit подтягивает платёж возврата по hash=exit_hash и отдаёт его
payment_status (PaymentStatus enum, nullable). Фронт показывает реальный статус
платежа кассира (ожидает оплаты → оплачено), а не только статус процесса выхода.
Сумма к возврату — зафиксированная советом on-chain (= сумма платежа), не preview.
+ codegen (controller/sdk zeus, селектор MembershipExit).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Контент больше не плавает по пустому экрану: статус выхода — в центрированной
карточке AuthCard (accent-стрип + мягкая тень, канонный hero-контейнер).
Иконка статуса в soft-плитке (primary/pos), заголовок, пояснение, сумма к
возврату на surface-2, действия в футере с разделителем. Всё на токенах --p-*.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- ExitOverlay переверстан на канонный EmptyState (иконка-плитка + заголовок +
приглушённый текст + слот действий) вместо россыпи text-h5/64px-иконки.
- Добавлена кнопка «Выйти из личного кабинета» (logout) на оба экрана выхода:
при активном выходе аккаунт заблокирован, и выйти из сессии было нечем.
После logout gate обнуляется, редирект на signin; при повторном входе экран
выхода снова показывается (статус живёт on-chain).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Перед подписанием заявления о выходе проверяем наличие реквизитов пайщика
(getPaymentMethods). Если их нет — вместо формы показываем баннер «установите
реквизиты для получения возврата паевого взноса» + кнопку перехода на страницу
реквизитов (payment-methods). Бэкенд проверяет то же при подаче (страховка).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Закрываем пробел: при одобрении советом выхода (on-chain confirmexit) кассир
не видел платёж возврата — его никто не создавал в реестре gateway.
- Гейт реквизитов: createMembershipExit отклоняет подачу, если у пайщика нет
ни одного платёжного метода («установите реквизиты для возврата паевого»).
- MembershipExitAuthorizationListener (@OnEvent action::registrator::confirmexit):
по exit_hash берёт из таблицы exits username + сумму возврата, заводит
исходящий платёж MEMBERSHIP_EXIT (PENDING — совет одобрил, сразу кассиру) по
реквизитам пайщика (метод по умолчанию). hash платежа = exit_hash, поэтому
подтверждение кассой через default-ветку processOutgoingPayment вызовет
completeOutcome → registrator::completexit (списание + блокировка).
- getExitByHash в account-порт/адаптер: confirmexit отдаёт только coopname+exit_hash.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
После merge dev zeus-клиенты были взяты из dev (без операций выхода).
Интроспекция живого coopback → schema.gql → generate-client вернула обе
группы операций (createMembershipExit/confirm/cancel + expense-шасси).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Регенерация Zeus-клиента контроллера под GraphQL-операции выхода
(CreateMembershipExitInput, MembershipExitStatus, signed-document inputs).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Маршрут membership-exit/confirm авторизуется токеном из ссылки (мутация
confirmMembershipExit публичная), поэтому навигационный гард не должен
редиректить его на login-redirect. requiresAuth:false исключает страницу из
auth-гейта — ссылку из письма можно открыть без активной сессии.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Черновик заявления сохраняется до отправки письма; если провайдер писем
недоступен, приём заявления не падает (try/catch + error-лог), пайщик может
отменить. Ссылка подтверждения пишется в debug-лог для диагностики.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- ExitButton: диалог теперь показывает сгенерированное заявление (DocumentHtmlReader)
с предупреждением «читайте внимательно, необратимо» + сумма к возврату, затем
«Подписать и подать» (generateApplication → показ → submitSignedApplication).
- ExitOverlay: новое состояние AWAITING_CONFIRMATION — «пройдите по ссылке из письма»
+ кнопка «Отменить выход»; после перехода/отмены overlay переключается на
ончейн-статус (рассмотрение Совета → одобрено → выплаты).
- useExitGate: isAwaitingConfirmation + cancelExit (Mutations.MembershipExit.CancelMembershipExit).
- model: generateApplication / submitSignedApplication / confirmExit вместо единого processMembershipExit.
- MembershipExitConfirmPage + маршрут membership-exit/confirm (цель ссылки из письма):
onMounted confirmExit(token) → loadExitStatus.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>