- тост успеха: multiLine=false (заголовок+CTA+крестик в один ряд, без
прыгающей второй строки), CTA сокращён до «В корзину»;
- главный паевой кошелёк ЦК (w.wal.share) — снова первым в реестре после
минимального неснижаемого остатка (приоритет), карточка «Главный паевой
кошелёк» / «Цифровой кошелёк»; мини-кошелёк в rail — тот же заголовок.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- членский стола заказов: убрана подпись «возврату не подлежит»;
- главный паевой (w.wal.share) опущен вниз — рядом с программными ЦПП-кошельками,
минимальный неснижаемый остаток остаётся верхней карточкой;
- мини-кошелёк в rail (левый низ): label «Паевой кошелёк» вместо «Доступно»,
сумма берётся из сырого w.wal.share — членская часть ЦК больше не примешивается.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Кошельки на стол пайщика устанавливаются через декларации 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>
Унификация сделала немедленный он-чейн-путь выдачи мёртвым: его никто не зовёт.
Удалены 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>
Оператор подписывает signiss1 и кладёт всё (существующие заказы + докладку со
склада) в один оффчейн-бандл; на цепи до подписи пайщика ничего нет, поэтому
отмена пайщиком = отказ от бандла без он-чейн отката (никакого reopeniss и
второй ветки немедленного signiss1). Пайщик одной кнопкой контрподписывает
получение — backend проводит convert?→stockorder?→signiss1→signiss2. Снесён
мёртвый member-путь ordererTasks/listMyReadyToReceive в гейте.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Пайщику передаётся 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>
Оператор в окне выдачи («Со склада») теперь получает АПП-выдачи по строкам
корзины и подписывает их первой подписью (signiss1) сразу при формировании
докладки (getStockIssuancePayloads + createStockProposal с подписанными актами).
Пайщику докладка приходит в гейт готовым актом (пункты имущества, кол-во, цена,
две суммы: с членского / конвертация с паевого). Одна кнопка «Подписать»
(+«Отказаться»): подписывает Заявление о конвертации при дефиците и
контрподписывает signiss2 по строкам — finalizeStockIssuance создаёт заказы и
выдаёт имущество. Кнопка «Принять предложение» убрана: принятие = подпись акта.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Переработка докладки/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>
В гейте подписи на месте карточка предложения со склада теперь показывает
разложение ДО подписи: «спишется с членского «Стола заказов»» (member_amount)
и, при дефиците, «конвертация с паевого (по заявлению)» (convert_amount).
Суммы предзагружаются через getStockProposalSignablePayloads (кэш по proposal_id),
ушедшие предложения чистятся. Убрана устаревшая подпись «Итого (паевой взнос)».
Зачем: пайщик видит, что и откуда спишется, до согласия — как договорено.
Оператор в окне «Открытие выдачи пайщику» кнопкой «Со склада» открывает выбор
опубликованного остатка КУ (StockPickDialog), набирает позиции — они встают
секцией «Доложено со склада» в том же акте и при «Подписать и открыть выдачу»
создаются предложением пайщику (createStockProposal). Зачем: «это не пришло —
возьмите вот это» оформляется в одном окне выдачи, без отдельного экрана докладки.
Инкремент 1 (сторона оператора); две суммы и слияние подписи у пайщика — следом.
getAcceptSignablePayloads теперь отдаёт member_amount (сколько стоимости докладки
покроется уже внесёнными членскими «Стола заказов», включая высвобожденные
недосдачей) рядом с convert_amount (дефицит → конвертация с паевого по Заявлению).
Сумма обеих = полная стоимость докладки. Зачем: оператор/пайщик видят в акте
разложение «с членского / с паевого» до подписи — backend считает целочисленно,
фронт не дублирует. Прокинул через DTO, резолвер, SDK-селектор + codegen.
Стандарт описывал старую модель: при факте больше заказа доплата вносится
дополнительным паевым взносом с авто-конвертацией в членский. Контракт это
уже не делает — паевой на выдаче не списывается и не конвертируется; доплата
и недостающая часть взноса берутся из членских средств, а паевой пайщик
переводит в членский заранее отдельным Заявлением о конвертации. Подровнял
текст под код (дрейф стандарт↔код — прав код).
Комментарии называли дособираемую при факте>заказа часть «довзносом с паевого».
По сути это членский взнос (комиссия КУ, идёт в w.mkt.fee → w.brn.common), а
источник средств теперь членский (o.mkt.lockmf), не паевой. Правка только текста
комментариев — логика не меняется; чтобы код самодокументировался корректно.
Ветка signiss2 «факт больше заказа» конвертировала паевой в членский (o.mkt.conv)
и добирала довзнос с паевого (o.mkt.fee) автоматически, без Заявления о конвертации.
Это нарушало инвариант «паевой→членский только по заявлению»: на выдаче паевой
трогать нельзя нигде. Теперь доплата тела (o.mkt.lockm) и довзнос (o.mkt.lockmf)
берутся ТОЛЬКО с w.mkt.member; при нехватке — отказ с просьбой сконвертировать
заранее. Backend покажет пайщику разложение (с членского / с паевого по заявлению)
до подписи. Зачем: деньги пайщика на паевом не должны списываться помимо его
явного волеизъявления — конвертация всегда отдельный подписанный документ.
Поставщик не согласен со снятыми позициями целиком (повезёт замену позже):
до его подписи приёмка — черновик, оператор отменяет акт и пересобирает.
- 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>
Поставщик видит два блока: «Принимается» (факт>0, нулевые строки скрыты) и
«Отклоняется (некондиция)» — снятые оператором позиции с пояснением, что
подтверждая, он отменяет их поставку (заказчикам полный возврат, удержания нет).
Метка кнопки «Подтвердить отмену», если принимать нечего. Пустой список актов
(вся партия снята) больше не ошибка — мутация с пустыми документами уводит
приёмку в отмену с полным возвратом.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Разнос группы приёмки на принятые (факт>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>
Диалог финализации заказчика принимает refusedOrders (остаток ACCEPTED_TO_COOP
того же пункта — оператор не открыл по ним выдачу = отказ). Пайщик видит две
секции «Получаете / Отказываетесь (удержим 50%)» и подтверждает целиком одной
кнопкой: signiss2 по принятым + cancelOrder по отказным (его сессия, cancel:own).
Не согласен → закрывает, оператор переформировывает. Списание строго в момент
подтверждения. Документы/акт не трогаем — в акт идёт только принятое. ESLint ok.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Расширил 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>
Расширил declineorder с ACTIVE на ACCEPTED/SUPPLY_PREPARED: поставщик
подтверждает отмену поставки непринятой позиции (ноль единиц в факт), имущество
ещё не оприходовано (purch на закрывающей подписи) и поставщик не оплачен —
клоубэка нет, полный возврат стоимости и членского взноса заказчику (непринятие
не его вина). Зеркально отказу на выдаче, но инициатор — поставщик и без штрафа.
Обновлён стандарт p.mkt.supply.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Граница удержания — акцепт поставщиком. До акцепта отмена бесплатна (полный
возврат), после — 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>
В диалоге открытия выдачи добавлена колонка-чекбокс «Выдать»: оператор
отмечает позиции, которые пайщик забирает сейчас; снятые остаются на складе и
в выдачу не попадают (acts формируются только по выбранным — openIssuance
per-order, бэк без изменений). Позиции с принято=0 авто-исключены (выдать
нечего) и больше не блокируют всю выдачу. Суммы/возврат/валидность считаются
по выбранным; итоги показывают «к выдаче N из M» и «остаётся на складе».
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Три полировки стола заказов:
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>
Поставщик отклонял входящий заказ (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>
- 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>
- BaseDocument.shadowStyles: .digital-document h1 { text-align:center } — заголовок (ПРОТОКОЛ №, СЛУЖЕБНАЯ ЗАПИСКА и пр.) центрируется на слое рендера, как в других документах; инлайн-стили выживают санитайзер, а блочные правила <style> — нет, поэтому правка шаблона существующий подписанный документ не центрировала
- DocumentViewerDialog: убрал лишнюю обёртку-лист (ширину никто не просил, проблема была только в выравнивании заголовка)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 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>
- 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>
Протокол на столе ПВЗ НЕ регенерируем фабрикой: документ уже подписан советом и
лежит в реестре — 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>
Удалён 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>
Колонка 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>
В повестке совета документ рендерит 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>
Диалог подписи: заявление рендерилось через 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>
Документы 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>
Несколько партий (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>
Склонение «позиций» и имя проекта по дате повторялись в AdminWriteoffsPage и
WriteoffProposalDetailsDialog — вынес в ../lib/proposalDisplay по правилу DRY.
Лента: имя проекта по дате («Списание от ДД.ММ.ГГГГ») + корректное склонение
«N позиция/позиции/позиций» вместо «1 позиций». Детальный диалог: канон-таблица
позиций (.table-wrap + .col-num + BaseBadge статуса) вместо q-markup-table;
журнал решений — человеческие формулировки вместо кодов draft_created/
submitted_to_council; «Решение совета» — итог рассмотрения (Одобрено/Отклонено/
На рассмотрении) вместо служебного номера; on-chain идентификатор укорочен и
вынесен вниз отдельной подписью.
Проблема 1 (решение совета не отражалось): не было слушателя on-chain
callback'ов совета — onCouncilAuthorized/onCouncilDeclined звались только в
тестах. Добавлен MarketplaceWriteoffSyncService на action::marketplace::
onmktwoauth/onmktwodecl → проект ON_AGENDA → PENDING_CONFIRMATION, Протокол
сохраняется.
Проблема 2 (нельзя создать второй проект пока один в совете): снят гард
findOpenInCouncil в createDraft и частичный unique-индекс
UQ_..._active (несколько проектов в совете допустимы — разные партии
скоропорта независимы). Защита от двойного списания одной позиции перенесена
на уровень кандидатов: findActiveLockedInventoryIds исключает из списка
позиции, уже занятые в незавершённых проектах.
Путь списания — единственный marketplace-процесс через повестку совета,
поэтому в decisionFactory не было обработчика типа mktwroff: при «Утвердить»
process-decisions не мог сгенерировать документ-Решение и падал. Зарегистрировал
обработчик: собирает Action Протокола совета о списании (registry 1107) из
метаданных подписанного Заявления 1108 (proposal_hash, цикл, сумма, число
позиций) + decision_id и генерит через generic GenerateDocument — фабрика сама
резолвит состав совета, голоса и реквизиты по decision_id.
Рендерер 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 знака.
Страница списания переверстана по канону: вкладки 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>
Документ 1108: убрана колонка «КУ» (наименования участков в блокчейн не шлём —
юридически достаточно наименование/кол-во/сумма/причина) и строка про транзит
счёта 91; вёрстка переведена на канон без white-space:pre-wrap (убирает большие
отступы), суммы позиций рендерятся с символом валюты как ИТОГО, дата цикла — в
формате ДД.ММ.ГГГГ. Те же правки формата/вёрстки применены к Служебной записке
1111. Причина по умолчанию для просрочки и товара без гарантии — «Истёк срок
годности». Лоадер диалога подписи отцентрован. Требуется перезапуск контроллера
для подхвата нового шаблона (cooptypes/factory dist).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
В одношаговом потоке диалог подписи Заявления монтируется уже открытым
(modelValue=true задаётся одновременно с draft), поэтому watch без immediate не
срабатывал и previewDoc оставался null — белый экран без кнопок. Добавлен
immediate:true: загрузка документа идёт и при появлении уже открытого диалога.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Поток списания свёрнут до одного действия: председатель выделяет имущество на
складах, при необходимости пишет причину (одно поле, по умолчанию пусто →
причина по состоянию позиции) и жмёт «Подписать и отправить в совет» — черновик
собирается под капотом и сразу открывается подпись Заявления. Убраны
промежуточный экран «Открытый черновик», кнопка «Изменить состав» и
DraftEditorDialog. Висящий черновик (прерванная подпись/крон) снимается перед
сборкой свежего. Бэкенд не менялся.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>