Commit Graph

3349 Commits

Author SHA1 Message Date
ant 2777837e4a [598-50][@ant] fix: тост «в корзину» в одну строку + главный паевой кошелёк первым
- тост успеха: multiLine=false (заголовок+CTA+крестик в один ряд, без
  прыгающей второй строки), CTA сокращён до «В корзину»;
- главный паевой кошелёк ЦК (w.wal.share) — снова первым в реестре после
  минимального неснижаемого остатка (приоритет), карточка «Главный паевой
  кошелёк» / «Цифровой кошелёк»; мини-кошелёк в rail — тот же заголовок.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 12:55:43 +00:00
ant 2e99350761 [598-50][@ant] fix: кошельки пайщика — подпись/порядок карточек + мини-кошелёк только паевой
- членский стола заказов: убрана подпись «возврату не подлежит»;
- главный паевой (w.wal.share) опущен вниз — рядом с программными ЦПП-кошельками,
  минимальный неснижаемый остаток остаётся верхней карточкой;
- мини-кошелёк в rail (левый низ): label «Паевой кошелёк» вместо «Доступно»,
  сумма берётся из сырого w.wal.share — членская часть ЦК больше не примешивается.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 12:51:30 +00:00
ant cfcb435c94 [598-50][@ant] feat: реестр-фабрика кошельков на столе пайщика — расширения устанавливают свои кошельки через install (путь B)
Кошельки на стол пайщика устанавливаются через декларации walletCards в
install.ts расширений + core-набор; реестр дедупит по wallet_name. Состав:
главный паевой (core, w.wal.share), членский стола заказов (market,
w.mkt.member), Благорост (capital, w.cap.blago). Главный членский ЦК
(w.wal.member) и Генератор (w.cap.gen) не показываем; минимальный
неснижаемый остаток — отдельной карточкой как раньше.

Источник балансов — новый core-резолвер getUserWallets (сырой срез
ledger2::userwallets без сворачивания паевого и членского). Зачем: убрать
хардкод трёх программ в WalletProgramWidget и дать расширениям приносить
свои кошельки единым стандартным механизмом.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 12:00:15 +00:00
ant 6715bde58f [598-50][@ant] refactor: снести мёртвый legacy-путь выдачи после унификации в бандл
Унификация сделала немедленный он-чейн-путь выдачи мёртвым: его никто не зовёт.
Удалены GraphQL-эндпоинты marketplaceOpenIssuance / marketplaceFinalizeIssuance /
marketplaceListMyReadyToReceive / marketplaceIssueActOrdererSignablePayload,
сервис-метод getFinalizeIssuanceSignablePayload, repo-метод
listReadyToReceiveByOrderer, мёртвые DTO и SDK-операции/селектор. На фронте убран
OrdererFinalizeIssuanceDialog и подпись получения в «Мои заказы»/детали заказа +
экшен receive в OrderCard (получение оформляется у стойки в гейте «подпись на
месте»). Сервис-методы openIssuance/finalizeIssuance остались — зовутся изнутри
бандла. README расширения: быстрый контекст единого пути выдачи + карта файлов.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 09:53:53 +00:00
ant 7e9fa99b60 [598-50][@ant] feat: единый путь выдачи через бандл — обычные заказы и докладка одним пропозалом
Оператор подписывает signiss1 и кладёт всё (существующие заказы + докладку со
склада) в один оффчейн-бандл; на цепи до подписи пайщика ничего нет, поэтому
отмена пайщиком = отказ от бандла без он-чейн отката (никакого reopeniss и
второй ветки немедленного signiss1). Пайщик одной кнопкой контрподписывает
получение — backend проводит convert?→stockorder?→signiss1→signiss2. Снесён
мёртвый member-путь ordererTasks/listMyReadyToReceive в гейте.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 09:24:30 +00:00
ant 741103eb48 [598-50][@ant] fix: контрольная сверка подписи пайщика на докладке — защита от подмены акта
Пайщику передаётся signiss1-акт оператора на его устройство для контрподписи —
вектор: подменить тело акта (другой товар/количество) и вернуть подписанным.
Перед любыми транзакциями finalizeStockIssuance сверяет каждый контрподписанный
акт с сохранённым signiss1-актом оператора:
 - doc_hash и meta_hash совпадают с выданным к подписи (тело/мета не подменены);
 - подпись оператора сохранена нетронутой (signer+signature+public_key);
 - пайщик подписал своим ключом;
 - DocumentValidationService.validateSignedDocument: крипто + структура (recompute
   signed_hash из doc_hash+meta_hash) + сверка тела с оригиналом в сторе.
На подмене — ForbiddenException до блокировки средств. Defence-in-depth: контракт
signiss2 дополнительно режет подделку on-chain (verify_document_or_fail требует
валидную подпись оператора над телом).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 07:59:25 +00:00
ant 94c1ef77c9 [598-50][@ant] feat: докладка у пайщика — готовый акт и одна кнопка «Подписать», без «Принять»
Оператор в окне выдачи («Со склада») теперь получает АПП-выдачи по строкам
корзины и подписывает их первой подписью (signiss1) сразу при формировании
докладки (getStockIssuancePayloads + createStockProposal с подписанными актами).

Пайщику докладка приходит в гейт готовым актом (пункты имущества, кол-во, цена,
две суммы: с членского / конвертация с паевого). Одна кнопка «Подписать»
(+«Отказаться»): подписывает Заявление о конвертации при дефиците и
контрподписывает signiss2 по строкам — finalizeStockIssuance создаёт заказы и
выдаёт имущество. Кнопка «Принять предложение» убрана: принятие = подпись акта.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 07:40:56 +00:00
ant 5b9d8fad67 [598-50][@ant] feat: докладка со склада — единый поток «акт + одна подпись» через stockorder+signiss1+signiss2
Переработка докладки/QR-выдачи со склада кооператива под требование «пайщик видит
готовый акт и одну кнопку Подписать, без отдельного Принять» (вариант Б1).

Оператор при формировании докладки получает АПП-выдачи по строкам корзины
(marketplaceStockIssuancePayloads) и подписывает их первой подписью (signiss1) на
детерминированный order_hash; бандл хранит подписанные акты. Пайщик одной подписью
утверждает докладку (marketplaceFinalizeStockIssuance): при дефиците членских —
конвертация с паевого по Заявлению, затем по каждой строке заказ из остатка
(stockorder) → выдача (signiss1 оператора) → получение (контрподпись signiss2),
заказ сразу RECEIVED. Паевой не трогается без подписанного Заявления.

Убрана двухфазная семантика «Принять предложение» (marketplaceAcceptStockProposal):
принятие = подпись акта получения. SDK/codegen синхронизированы.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 07:37:19 +00:00
ant ae6d5e472a [598-50][@ant] feat: две суммы в карточке докладки у пайщика — с членского / конвертация с паевого
В гейте подписи на месте карточка предложения со склада теперь показывает
разложение ДО подписи: «спишется с членского «Стола заказов»» (member_amount)
и, при дефиците, «конвертация с паевого (по заявлению)» (convert_amount).
Суммы предзагружаются через getStockProposalSignablePayloads (кэш по proposal_id),
ушедшие предложения чистятся. Убрана устаревшая подпись «Итого (паевой взнос)».
Зачем: пайщик видит, что и откуда спишется, до согласия — как договорено.
2026-06-16 05:48:52 +00:00
ant b38773c647 [598-50][@ant] feat: докладка со склада в окне выдачи — плюсик «Со склада» + секция доложенного
Оператор в окне «Открытие выдачи пайщику» кнопкой «Со склада» открывает выбор
опубликованного остатка КУ (StockPickDialog), набирает позиции — они встают
секцией «Доложено со склада» в том же акте и при «Подписать и открыть выдачу»
создаются предложением пайщику (createStockProposal). Зачем: «это не пришло —
возьмите вот это» оформляется в одном окне выдачи, без отдельного экрана докладки.
Инкремент 1 (сторона оператора); две суммы и слияние подписи у пайщика — следом.
2026-06-16 05:25:53 +00:00
ant 91d5f33a79 [598-50][@ant] feat: две суммы в payload докладки — доплата с членского и с паевого (конвертация)
getAcceptSignablePayloads теперь отдаёт member_amount (сколько стоимости докладки
покроется уже внесёнными членскими «Стола заказов», включая высвобожденные
недосдачей) рядом с convert_amount (дефицит → конвертация с паевого по Заявлению).
Сумма обеих = полная стоимость докладки. Зачем: оператор/пайщик видят в акте
разложение «с членского / с паевого» до подписи — backend считает целочисленно,
фронт не дублирует. Прокинул через DTO, резолвер, SDK-селектор + codegen.
2026-06-16 05:15:36 +00:00
ant 214a921525 [598-50][@ant] docs: стандарт supply — доплата/довзнос по факту с членского, паевой на выдаче не трогаем
Стандарт описывал старую модель: при факте больше заказа доплата вносится
дополнительным паевым взносом с авто-конвертацией в членский. Контракт это
уже не делает — паевой на выдаче не списывается и не конвертируется; доплата
и недостающая часть взноса берутся из членских средств, а паевой пайщик
переводит в членский заранее отдельным Заявлением о конвертации. Подровнял
текст под код (дрейф стандарт↔код — прав код).
2026-06-16 05:02:19 +00:00
ant ef8f8aab30 [598-50][@ant] docs: уточнить терминологию в signiss2 — членский взнос (комиссия КУ), не «довзнос с паевого»
Комментарии называли дособираемую при факте>заказа часть «довзносом с паевого».
По сути это членский взнос (комиссия КУ, идёт в w.mkt.fee → w.brn.common), а
источник средств теперь членский (o.mkt.lockmf), не паевой. Правка только текста
комментариев — логика не меняется; чтобы код самодокументировался корректно.
2026-06-16 05:01:20 +00:00
ant 2d01dc18b2 [598-50][@ant] fix: убрать авто-конвертацию паевого в signiss2 — доплата/довзнос по факту только с членского
Ветка signiss2 «факт больше заказа» конвертировала паевой в членский (o.mkt.conv)
и добирала довзнос с паевого (o.mkt.fee) автоматически, без Заявления о конвертации.
Это нарушало инвариант «паевой→членский только по заявлению»: на выдаче паевой
трогать нельзя нигде. Теперь доплата тела (o.mkt.lockm) и довзнос (o.mkt.lockmf)
берутся ТОЛЬКО с w.mkt.member; при нехватке — отказ с просьбой сконвертировать
заранее. Backend покажет пайщику разложение (с членского / с паевого по заявлению)
до подписи. Зачем: деньги пайщика на паевом не должны списываться помимо его
явного волеизъявления — конвертация всегда отдельный подписанный документ.
2026-06-16 04:37:28 +00:00
ant 7cf326f26a [598][@ant] feat: одно Заявление о конвертации на весь дефицит вместо документа на строку + ноль документов при замене из высвобожденных средств — getAcceptSignablePayloads/acceptProposal на агрегат (order_lines + единый convert), гейт подписывает 0-или-1 документ; SDK/zeus regen 2026-06-15 18:43:58 +00:00
ant 7785c8cd88 [598][@ant] test: units↔asset и lineUnits economy + инварианты дефицита замены (членских хватает→0, не хватает→только дельта) — защита суммы convert от рассинхрона с on-chain 2026-06-15 17:29:08 +00:00
ant 0d0609ddb9 [598][@ant] feat: замена непоставленного без доплаты — докладка конвертирует только дефицit сверх членских средств; при замене высвобожденные отменой средства уже в членском → convert не зовётся, заказ из остатка из членских (помощники economy units↔asset/lineUnits + чтение w.mkt.member) 2026-06-15 17:26:56 +00:00
ant aaf5a7a854 [598][@ant] feat: backend-оркестрация заказа из остатка из членских — порт/адаптер convert (паевой→членский) + createStockOrder сперва convert на сумму строки, затем stockorder без Заявления; докладка/checkout проброшены через convert_amount 2026-06-15 17:20:48 +00:00
ant 11031ec736 [598][@ant] feat: stockorder фондируется из членских средств + новый action convert (паевой→членский) + операция o.mkt.lockmf — заказ из остатка всегда из членского начисто; паевой пополняет его отдельным Заявлением о конвертации; основа замены непоставленного без доплаты 2026-06-15 16:47:13 +00:00
ant c217e2da4e [598][@ant] feat: откат черновика приёмки оператором (поставщик не согласен) — пересборка акта
Поставщик не согласен со снятыми позициями целиком (повезёт замену позже):
до его подписи приёмка — черновик, оператор отменяет акт и пересобирает.
- service.cancelReception: только PENDING_SUPPLIER_SIGN, приёмка → CANCELLED,
  партия → SUPPLY_PREPARED; on-chain ничего не было, заказы не трогаем.
- findByShipmentId исключает CANCELLED → повторный create() по партии проходит.
- мутация marketplaceCancelAplReception (Receiving create, член КУ) + codegen.
- фронт: кнопка «Отменить и пересобрать» на карточке приёмки PENDING_SUPPLIER_SIGN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:33:31 +00:00
ant 31666b7ac1 [598][@ant] feat: стол поставщика — блок «Отклоняется (некондиция)» перед подписью приёмки
Поставщик видит два блока: «Принимается» (факт>0, нулевые строки скрыты) и
«Отклоняется (некондиция)» — снятые оператором позиции с пояснением, что
подтверждая, он отменяет их поставку (заказчикам полный возврат, удержания нет).
Метка кнопки «Подтвердить отмену», если принимать нечего. Пустой список актов
(вся партия снята) больше не ошибка — мутация с пустыми документами уводит
приёмку в отмену с полным возвратом.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:26:24 +00:00
ant b3f2b0e466 [598][@ant] feat: отказ в приёмке на подписи поставщика — снятые оператором позиции (факт=0) уходят declineorder с полным возвратом, председатель закрывает только принятые
Разнос группы приёмки на принятые (факт>0) и отклонённые (факт=0) на шаге
подписи поставщика: signsupp только по принятым, declineorder по нулям
(полный возврат стоимости и членского взноса заказчику без штрафа + erase).
Вся партия некондиция → приёмка CANCELLED + shipment CANCELLED. Председатель
(signchair/ACCEPTED_TO_COOP/склад/выплаты) работает только по принятым.
DRY: declineOrdersAtReception переиспользует runDeclineChain+уведомления.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:22:29 +00:00
ant 14582ba54e [598][@ant] feat: отказ пайщика на выдаче — подтверждение получения с отказными позициями (удержим 50%)
Диалог финализации заказчика принимает refusedOrders (остаток ACCEPTED_TO_COOP
того же пункта — оператор не открыл по ним выдачу = отказ). Пайщик видит две
секции «Получаете / Отказываетесь (удержим 50%)» и подтверждает целиком одной
кнопкой: signiss2 по принятым + cancelOrder по отказным (его сессия, cancel:own).
Не согласен → закрывает, оператор переформировывает. Списание строго в момент
подтверждения. Документы/акт не трогаем — в акт идёт только принятое. ESLint ok.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:14:40 +00:00
ant 97e3de1f5c [598][@ant] feat: заказчицкий cancelOrder разрешён до открытия акта выдачи — отказ от позиции на выдаче (acceptcoop, удержание 50% решает контракт)
Расширил CANCELABLE_STATUSES на ACCEPTED/SUPPLY_PREPARED/ACCEPTED_TO_COOP:
заказчик может отказаться от получения вплоть до открытия акта выдачи. Граница
бесплатно/50% — на контракте (до/после акцепта поставщиком), бэкенд лишь не
закрывает действие раньше. Закрыто на READY_TO_RECEIVE/RECEIVED. Спека: 15/15.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:04:20 +00:00
ant 71663edd5a [598][@ant] feat: отказ в приёмке поставщиком (declineorder до оприходования) — некондицию не принимаем без штрафа, полный возврат заказчику
Расширил declineorder с ACTIVE на ACCEPTED/SUPPLY_PREPARED: поставщик
подтверждает отмену поставки непринятой позиции (ноль единиц в факт), имущество
ещё не оприходовано (purch на закрывающей подписи) и поставщик не оплачен —
клоубэка нет, полный возврат стоимости и членского взноса заказчику (непринятие
не его вина). Зеркально отказу на выдаче, но инициатор — поставщик и без штрафа.
Обновлён стандарт p.mkt.supply.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 11:52:08 +00:00
ant aa565fc4eb [598][@ant] feat: отказ от получения с удержанием 50% (o.mkt.penal) — кооп несёт риск оплаты поставки после акцепта поставщиком
Граница удержания — акцепт поставщиком. До акцепта отмена бесплатна (полный
возврат), после — cancelorder удерживает 50% тела заказа и членского взноса в
общий кошелёк КУ (w.brn.common), имущество остаётся на складе КУ, вторая
половина возвращается пайщику. Новая ledger-операция o.mkt.penal: транзит
w.mkt.order → w.mkt.fee, далее в КУ существующим Branch::accrue — прямой перевод
между USER_SHARED-кошельками с разным разрезом невозможен (walletop держит один
username на обе стороны). Имя o.mkt.penal (не penalty) — 13-й символ eosio::name
не может быть позже 'j'. Helper retain_refusal_penalty + refund_order_full (DRY),
обновлён стандарт p.mkt.supply и cooptypes-зеркало операции.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 11:33:31 +00:00
ant 938f4b4d64 [598][@ant] feat: частичная выдача — оператор выдаёт только то, что пайщик забирает
В диалоге открытия выдачи добавлена колонка-чекбокс «Выдать»: оператор
отмечает позиции, которые пайщик забирает сейчас; снятые остаются на складе и
в выдачу не попадают (acts формируются только по выбранным — openIssuance
per-order, бэк без изменений). Позиции с принято=0 авто-исключены (выдать
нечего) и больше не блокируют всю выдачу. Суммы/возврат/валидность считаются
по выбранным; итоги показывают «к выдаче N из M» и «остаётся на складе».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 09:53:05 +00:00
ant 0844661d4d [598][@ant] feat: уведомление заказчику об отказе поставщика + CTA корзины + причина блокировки выдачи
Три полировки стола заказов:

1. Уведомление заказчику при отказе поставщика. Раньше decline слал только
   ws-сигнал — push/email заказчику не уходил. Новый Novu-workflow
   marketplace-order-declined-by-supplier (email/in-app/push с причиной +
   названием товара + КУ); событие MARKETPLACE_ORDER_DECLINED_BY_SUPPLIER
   эмитится из declineOrdersBatch по каждому заказу (имя товара батчем по
   offer_id), листенер в MarketplaceNotificationService резолвит имя заказчика
   и КУ и шлёт push. Best-effort, основной flow не блокирует.

2. Тост «Добавлено в корзину» получил CTA «Перейти в корзину» — быстрый переход
   к оформлению прямо из всплывашки, без поиска корзины отдельно.

3. Диалог открытия выдачи: кнопка «Подписать и открыть выдачу» дизейблилась
   без объяснения. Добавлен баннер с причиной (нет на складе / факт больше
   принятого / нулевые кол-во-цена) + отдельный статус строки «Нет на складе»
   (принято 0) в CorrectionTable вместо общей «Недостача».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 09:27:59 +00:00
ant eaf878c3a7 [598][@ant] fix: erase-дельта не воскрешает отклонённый заказ — terminal-resurrection guard в updateFromBlockchain
Поставщик отклонял входящий заказ (decline=erase строки в chain-RAM), но
заказ исчезал и тут же возвращался в «Ждут акцепта», а повторный
decline/accept падал «не найден по хэшу». Причина: парсер на erase шлёт
present=false с последним живым value строки ('active'); у терминального
CANCELLED_BY_SUPPLIER forward-rank=undefined, поэтому updateFromBlockchain
перетирал backend-статус обратно в ACTIVE. Guard: не менять статус, если
заказ уже в терминальной отмене/возврате (mapper выдаёт только живые
on-chain статусы, cancelled — KNOWN_UNMAPPED). +2 регрессионных теста.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 08:10:54 +00:00
ant ead5394f54 [598][@ant] feat: системный резолв имени аккаунта — кооперативный участок (account_kind) — единое человеческое имя во всех реестрах
- AccountDomainService.getAccount: распознаёт КУ (branchBlockchainPort.getBranch) и строит private_account из его орг-данных тем же каналом, что и кооператив; getDisplayName/сертификаты/фронтовый getAccount резолвят имя участка автоматически, без ветки у каждого потребителя
- enum AccountKind (participant|branch|cooperative|unknown) + поле account_kind в AccountDTO — дискриминатор субъекта, чтобы UI не путал КУ с организацией-пайщиком
- SDK: account_kind в accountSelector + zeus-клиент
- desktop useFioCache: убран костыль-fallback в branch-стор, опора на getAccount; отдаёт kindCache
- ProcessesPage (стол бухгалтера): имя участка вместо braname + метка «КУ» по account_kind=branch

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 07:26:59 +00:00
ant 54760d589f [598][@ant] fix: заголовок документа (h1) по центру в shadowStyles — DOMPurify режет <style>-блок, центрирование жило только там
- BaseDocument.shadowStyles: .digital-document h1 { text-align:center } — заголовок (ПРОТОКОЛ №, СЛУЖЕБНАЯ ЗАПИСКА и пр.) центрируется на слое рендера, как в других документах; инлайн-стили выживают санитайзер, а блочные правила <style> — нет, поэтому правка шаблона существующий подписанный документ не центрировала
- DocumentViewerDialog: убрал лишнюю обёртку-лист (ширину никто не просил, проблема была только в выравнивании заголовка)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 05:53:30 +00:00
ant 5e1f930c9d [598][@ant] fix: документ совета реально по центру (margin:auto) + имя КУ вместо braname в реестре процессов — стол бухгалтера
- DocumentViewerDialog: лист центрируется блочным margin:0 auto (max-width 900px) — прежний flex+:deep не применялся, протокол оставался прижат
- useFioCache: к ФИО пайщика добавлен fallback на имя КУ из branch-стора (short_name||full_name||full_address); процесс списания пишет на цепочке braname КУ — теперь показывается человеческое имя участка, а не «kaffjpgeznnu»

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 05:46:30 +00:00
ant b2ef59cc49 [598][@ant] fix: документ списания по центру + общий Loader + убрать выдуманный текст из записки/протокола — чище UX подтверждения
- DocumentViewerDialog: лист документа центрируется (max-width 900px), а не прижат влево на maximized-диалоге
- Loader (общий) вместо сырого q-spinner в ConfirmWriteoffDialog/SubmitToCouncilDialog и трёх Process*InfoWidget
- 1111 Служебная записка + 1107 Протокол: убраны строки «Идентификатор проекта списания» и бухпроводка Дт86/Кт10 — пользователь не просил этот текст

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 05:04:31 +00:00
ant 526ae1fc9e [598][@ant] fix: Протокол совета — отдавать агрегат подписанного документа + высота записки
Протокол на столе ПВЗ НЕ регенерируем фабрикой: документ уже подписан советом и
лежит в реестре — query marketplaceWriteoffProtocolDocument теперь собирает
DocumentAggregate из подписанного protocol_doc через buildDocumentAggregate
(тело по doc_hash + подписи), как issuance/return-claim. Возврат — DocumentAggregate;
фронт показывает канон-просмотрщиком DocumentViewerDialog. Codegen + SDK-обёртка
на documentAggregateSelector.

ConfirmWriteoffDialog: Служебная записка была обрезана (max-height:60vh + overflow)
— убрал ограничение высоты, документ листом фиксированной ширины (820px), как в
SubmitToCouncilDialog; прокручивается весь диалог.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 17:51:11 +00:00
ant 5e78385722 [598][@ant] feat: списание — убрать стол «Повестка совета — списания» + Протокол совета окном на ПВЗ
Удалён read-only стол BoardAgendaWriteoff (повестка совета по списаниям) —
страница не нужна: маршрут marketplace-board-writeoff из install.ts, сам пейдж,
ссылки в комментариях.

Протокол совета на столе ПВЗ: вместо инлайн-тоггла со «Протокол совета
недоступен» (в callback'е onmktwoauth html не приходит — хранится только
подпись) — кнопка открывает документ полноэкранным окном, как остальные
документы. Бэкенд регенерит Протокол 1107 фабрикой по decision_id (votes с
цепи) новым query marketplaceWriteoffProtocolDocument — тот же паттерн, что у
Заявления 1108 и Служебной записки 1111. Codegen + SDK-обёртка.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 17:32:02 +00:00
ant 44ac3d1a16 [598][@ant] fix: списание — расширить status до varchar(32) + реальные единицы измерения в документах
Колонка marketplace_writeoff_proposal.status была varchar(16), а статус
PENDING_CONFIRMATION = 20 символов: callback совета onmktwoauth отлавливался,
но UPDATE статуса молча падал «value too long» → проект навсегда застревал в
ON_AGENDA (решение совета «не отлавливалось»). Расширил до 32. На колонке индекс
(coopname,status), поэтому synchronize не смог изменить длину сам (recreate →
ADD NOT NULL на непустой таблице) — расширение выполнено ALTER COLUMN TYPE на месте.

Единицы измерения в Заявлении (1108) и Служебной записке (1111): вместо зашитой
«ед.» бэкенд резолвит реальную единицу товара по партиям позиции
(inventory → заказ/published_offer → offer.unit_of_measure → шт./кг/л/упак.),
прокидывает в документ; шаблоны рендерят {{ it.unit }}.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 16:59:29 +00:00
ant e9375ed244 [598][@ant] fix: списание — документ по эталону 1106 (инлайн-вёрстка под pre-wrap) + единицы измерения
В повестке совета документ рендерит BaseDocument: он прогоняет html через
sanitizeHtml (ВЫРЕЗАЕТ <style> документа) и форсит в Shadow DOM
.digital-document{white-space:pre-wrap}. Прежняя вёрстка 1108/1111 полагалась
на <style> с классами (.addressee/.title-block + margin'ы) — всё вырезалось,
оставался pre-wrap → огромные отступы + выравнивание влево.

Переверстано 1-в-1 по канон-документу 1106 (он корректен и в повестке, и в
предпросмотре): выравнивание инлайн (style=text-align), вертикальный ритм —
пустыми строками под pre-wrap, плотные абзацы — инлайн margin:0px,
центр-заголовок — h1.header (shadowStyles центрирует .header).

Добавлена колонка «Ед. изм.» (ед.) — у инвентаря нет поля единицы, склад
считается в дискретных единицах, поэтому константа.

Предпросмотр (SubmitToCouncilDialog): рендер html как есть, без класса
.statement (тянул глобальный h1{line-height:4.5rem}) и без нормализатора,
ломавшего pre-wrap — теперь идентичен повестке.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 16:20:57 +00:00
ant 39f076dfff [598][@ant] fix: списание — заявление и детальная страница по канон-паттерну
Диалог подписи: заявление рендерилось через DocumentHtmlReader, который
форсит h1 line-height 4.5rem и режет <style> документа DOMPurify'ем —
заявление «разлетается» по вертикали. Переведено 1-в-1 на эталон
заявления на вступление (Registrator/SignUp/ReadStatement): .statement +
:deep-нормализатор бекенд-HTML к канон-типографике.

Детальная страница проекта: maximized-диалог рендерил контент full-bleed
без контейнера (поля minmax растянуты на весь экран, всё скучено в углу).
Переверстано по эталону ProfilePage: центрированная колонка max-width 920,
секции в BaseCard, «Основные параметры» через канон-компонент DataRow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 13:18:59 +00:00
ant 7fb0c51d23 [598][@ant] fix: списание — вёрстка документа по эталону 1110 + детальная страница на канон-компоненты
Документы 1108/1111: убран white-space:pre-wrap (рендерил переносы строк исходника
и вместе с дефолтными margin'ами <p> удваивал разрывы — документ разъезжался по
вертикали); спейсинг переведён на явные margin'ы по эталону уплотнённого 1110;
'.digital-document { white-space: normal }' перебивает форсированный pre-wrap из
Shadow DOM рендерера. Подписант 1108 — 'от Председателя Совета'. Диалог подписи:
документ рендерится канон-компонентом DocumentHtmlReader в листе фиксированной
ширины (на мобильном — во всю), без 60vh-обрубка. Детальная страница: журнал
решений переведён на канон ActivityTimeline, убран служебный on-chain идентификатор.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 12:56:35 +00:00
ant f6fd706bf0 [598][@ant] fix: списание — учитывать legacy inventory_id при блокировке кандидатов — не разблокировать партии старых проектов после перехода на inventory_ids[]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 11:46:54 +00:00
ant a6f620304b [598][@ant] fix: списание — агрегация партий в кандидатах по (КУ+товар+состояние) — устранить «прыгающее количество» от неразличимых строк-партий
Несколько партий (inventory lots) одного товара на одном КУ показывались отдельными
визуально идентичными строками (та же дата годности, тот же КУ) → пайщик видел, как
количество «прыгает» 40↔100, и после списания одной партии вторая оставалась.
Модель позиции переведена с одиночного inventory_id на inventory_ids[]: кандидаты
агрегируются по (braname, asset_title, состояние), одна строка несёт все партии и
суммарное кол-во/сумму; исполнение и блокировка повторного списания идут по всем
партиям агрегата. Сквозь domain→service→dto→mapper→sdk(zeus)→desktop + codegen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 11:46:23 +00:00
ant 461f3b8db7 [598][@ant] refactor: списание — общий хелпер proposalDisplay (positionsLabel/proposalTitle) — убрать дубль из страницы и диалога
Склонение «позиций» и имя проекта по дате повторялись в AdminWriteoffsPage и
WriteoffProposalDetailsDialog — вынес в ../lib/proposalDisplay по правилу DRY.
2026-06-14 10:58:24 +00:00
ant e85d04a51e [598][@ant] fix: списание — детальная страница и лента проекта по канону — «1 позиций» и сырой журнал заменены на человеческий вид
Лента: имя проекта по дате («Списание от ДД.ММ.ГГГГ») + корректное склонение
«N позиция/позиции/позиций» вместо «1 позиций». Детальный диалог: канон-таблица
позиций (.table-wrap + .col-num + BaseBadge статуса) вместо q-markup-table;
журнал решений — человеческие формулировки вместо кодов draft_created/
submitted_to_council; «Решение совета» — итог рассмотрения (Одобрено/Отклонено/
На рассмотрении) вместо служебного номера; on-chain идентификатор укорочен и
вынесен вниз отдельной подписью.
2026-06-14 10:56:21 +00:00
ant f20e416a60 [598][@ant] fix: списание — решение совета прицепляется + несколько проектов одновременно — без этого совет одобрял «вхолостую» и второй проект не создать
Проблема 1 (решение совета не отражалось): не было слушателя on-chain
callback'ов совета — onCouncilAuthorized/onCouncilDeclined звались только в
тестах. Добавлен MarketplaceWriteoffSyncService на action::marketplace::
onmktwoauth/onmktwodecl → проект ON_AGENDA → PENDING_CONFIRMATION, Протокол
сохраняется.

Проблема 2 (нельзя создать второй проект пока один в совете): снят гард
findOpenInCouncil в createDraft и частичный unique-индекс
UQ_..._active (несколько проектов в совете допустимы — разные партии
скоропорта независимы). Защита от двойного списания одной позиции перенесена
на уровень кандидатов: findActiveLockedInventoryIds исключает из списка
позиции, уже занятые в незавершённых проектах.
2026-06-14 10:52:46 +00:00
ant 65551dfe45 [598][@ant] fix: списание — обработчик решения совета mktwroff (Протокол 1107) — без него утверждение повестки падало «Не найден обработчик»
Путь списания — единственный marketplace-процесс через повестку совета,
поэтому в decisionFactory не было обработчика типа mktwroff: при «Утвердить»
process-decisions не мог сгенерировать документ-Решение и падал. Зарегистрировал
обработчик: собирает Action Протокола совета о списании (registry 1107) из
метаданных подписанного Заявления 1108 (proposal_hash, цикл, сумма, число
позиций) + decision_id и генерит через generic GenerateDocument — фабрика сама
резолвит состав совета, голоса и реквизиты по decision_id.
2026-06-14 08:42:15 +00:00
ant 00dddc3861 [598][@ant] fix: списание — вёрстка документов 1108/1111 строго по канону фабрики — чтобы заявление не «уезжало» и совпадало с соседними документами
Рендерер BaseDocument навязывает в Shadow DOM white-space: pre-wrap и
th{width:30% !important}; пятиколоночный thead давал 5×30%=150% и таблица
уезжала вправо («Причина» схлопывалась в столбик), а кастомные margin'ы вместо
канонных переносов давали огромные вертикальные зазоры. Привёл оба документа
1-в-1 к style-блоку и структуре сиблингов (эталон 1106): addressee справа,
заголовок по центру, город под подзаголовком справа; список позиций —
многоколоночная таблица без <th> (шапка через <td><b>), суммы 2 знака.
2026-06-14 07:58:11 +00:00
ant 95dafbfe3d [598][@ant] feat: списание — страница в табы по канону + документ 1108 (сумма 2 знака, без «цикла», город)
Страница списания переверстана по канону: вкладки PageTabs «Кандидаты / На
повестке / Архив» вместо россыпи карточек и списков; ленты проектов — канон-
таблица .table-wrap с BaseBadge-статусами и EmptyState; CTA только на вкладке
кандидатов. Документ Заявления 1108: сумма в формате «1 020,00 RUB» (2 знака,
разделители), из преамбулы убрано «по итогам цикла», под заголовком добавлен
город «г. {{ coop.city }}». Те же суммы 2-знака применены к Записке 1111.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 07:43:16 +00:00
ant 1b527b35f7 [598][@ant] fix: документ Заявления о списании (1108) — убрать КУ и строку транзита, формат суммы/даты, канон-вёрстка + центр лоадера
Документ 1108: убрана колонка «КУ» (наименования участков в блокчейн не шлём —
юридически достаточно наименование/кол-во/сумма/причина) и строка про транзит
счёта 91; вёрстка переведена на канон без white-space:pre-wrap (убирает большие
отступы), суммы позиций рендерятся с символом валюты как ИТОГО, дата цикла — в
формате ДД.ММ.ГГГГ. Те же правки формата/вёрстки применены к Служебной записке
1111. Причина по умолчанию для просрочки и товара без гарантии — «Истёк срок
годности». Лоадер диалога подписи отцентрован. Требуется перезапуск контроллера
для подхвата нового шаблона (cooptypes/factory dist).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 06:30:49 +00:00
ant f7e1a05bc6 [598][@ant] fix: пустой экран подписи списания — watch с immediate, документ грузится при монтировании открытого диалога
В одношаговом потоке диалог подписи Заявления монтируется уже открытым
(modelValue=true задаётся одновременно с draft), поэтому watch без immediate не
срабатывал и previewDoc оставался null — белый экран без кнопок. Добавлен
immediate:true: загрузка документа идёт и при появлении уже открытого диалога.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 06:08:15 +00:00
ant 34d306f9b6 [598][@ant] feat: списание в один шаг — выделил имущество + причина → подпись и в совет, убрать черновик-экран
Поток списания свёрнут до одного действия: председатель выделяет имущество на
складах, при необходимости пишет причину (одно поле, по умолчанию пусто →
причина по состоянию позиции) и жмёт «Подписать и отправить в совет» — черновик
собирается под капотом и сразу открывается подпись Заявления. Убраны
промежуточный экран «Открытый черновик», кнопка «Изменить состав» и
DraftEditorDialog. Висящий черновик (прерванная подпись/крон) снимается перед
сборкой свежего. Бэкенд не менялся.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 05:56:10 +00:00