[@ant] feat(ledger2): членская модель «Стола заказов» — 12 операций marketplace, счета 10/91, кошелёк w.mkt.member, WalletOp REVOKE; удалены legacy o.mkt.supply/recv (клиринг)

- operations.hpp: enum WalletOp + REVOKE (списание blocked + обяз. Dr/Cr); удалены CONFIRM_SUPPLY/CONFIRM_RECEIPT; добавлены 12 операций членской модели — CONVERT_TO_MEMBER в operations::wallet и 11 в operations::marketplace (ASSIGN_TO_PROGRAM, BLOCK_FOR_ORDER, UNBLOCK_ON_CANCEL, RECALL_TO_UNIVERSAL, PURCHASE_FROM_SUPPLIER, PAY_SUPPLIER, CONSUME_BY_MEMBER + CONSUME_TRANSIT_CLOSE, RETURN_BY_MEMBER + RETURN_TRANSIT_CLOSE, WRITE_OFF_PERISHABLE + WRITE_OFF_TRANSIT_CLOSE); composite-проводки через транзит счёта 91 «Прочие доходы и расходы» разбиты на пары операций (часть 1 + часть 2, *2-суффикс), вызываемые последовательно в одной транзакции Antelope — атомарность через транзакцию.
- accounts.hpp: добавлены счета 10 «Материалы» (А) и 91 «Прочие доходы и расходы» (А/П).
- wallets.hpp: добавлен USER_SHARED-кошелёк w.mkt.member («ЦПП Стол Заказов — программный членский») с program_id=2 (marketplace).
- processes.hpp: удалён p.mkt.reqst (клиринговый), добавлены p.mkt.supply / p.mkt.return / p.mkt.wroff.
- p.mkt.{supply,return,wroff}.standard.yaml: composite-проводки разнесены на пары operations[]; транзит через 91 явно описан в комментариях и description (нетто-эффект — Дт 86 / Кт 10 на выдаче/утилизации и Дт 10 / Кт 86 на возврате).

Согласовано с Ангелиной 2026-05-11.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
coopops
2026-05-11 17:56:02 +00:00
parent e9fca4138b
commit 0a8e226417
7 changed files with 339 additions and 93 deletions
@@ -32,17 +32,22 @@ enum class AccountType : uint8_t {
* состояния «принятый коммит» (паевой взнос имуществом в переходе РИД
* в программу Благорост): commit → Dr 08 / Cr 80, accept → Dr 04 / Cr 08.
*
* Состав (6 счетов):
* Состав (8 счетов):
*
* - 04 — Нематериальные активы (РИД, принятые в паевой фонд)
* - 08 — Вложения во внеоборотные активы (промежуточное состояние)
* - 10 — Материалы (склад имущества на кооперативных участках; per-КУ субсчета)
* - 51 — Расчётный счёт
* - 58 — Финансовые вложения (выданные пайщикам беспроцентные займы)
* - 80 — Паевой фонд (складочный капитал)
* - 86 — Целевое финансирование (без субсчетов)
* - 91 — Прочие доходы и расходы (транзит при выдаче имущества пайщику
* и при утилизации скоропорта по «Столу заказов»)
*
* Счёт 67 удалён: беспроцентные займы пайщикам идут через 58/51, без 67.
*
* Счета 10 и 91 добавлены 2026-05-11 под членскую модель «Стола заказов».
*
* Правило кодирования id: integer(code) * 1000.
* Детализация — через wallets (см. wallets.hpp).
*
@@ -52,12 +57,16 @@ struct ledger2_accounts {
// Активы
static constexpr uint64_t INTANGIBLE_ASSETS = 4 * 1000; ///< 04 — Нематериальные активы (А)
static constexpr uint64_t NON_CURRENT_INVESTMENTS = 8 * 1000; ///< 08 — Вложения во внеоборотные активы (А)
static constexpr uint64_t MATERIALS = 10 * 1000; ///< 10 — Материалы (А; склад имущества на КУ, per-КУ субсчета)
static constexpr uint64_t BANK_ACCOUNT = 51 * 1000; ///< 51 — Расчётный счёт (А)
static constexpr uint64_t FINANCIAL_INVESTMENTS = 58 * 1000; ///< 58 — Финансовые вложения (А)
// Пассивы
static constexpr uint64_t SHARE_FUND = 80 * 1000; ///< 80 — Паевой фонд (П)
static constexpr uint64_t TARGET_RECEIPTS = 86 * 1000; ///< 86 — Целевое финансирование (П)
// Активно-пассивный
static constexpr uint64_t OTHER_INCOME_EXPENSES = 91 * 1000; ///< 91 — Прочие доходы и расходы (А/П; транзит на выдаче и при утилизации скоропорта)
};
/**
@@ -74,18 +83,20 @@ struct Ledger2AccountMeta {
};
/**
* @brief Хардкод-справочник плана счетов (MVP, 7 записей).
* @brief Хардкод-справочник плана счетов (MVP, 8 записей).
*
* `constexpr std::array` + `string_view` — чтобы не было dynamic init
* при загрузке контракта и тип был полностью заморожен на этапе сборки.
*/
inline constexpr std::array<Ledger2AccountMeta, 6> LEDGER2_ACCOUNT_MAP = {{
inline constexpr std::array<Ledger2AccountMeta, 8> LEDGER2_ACCOUNT_MAP = {{
{ ledger2_accounts::INTANGIBLE_ASSETS, "Нематериальные активы", AccountType::ACTIVE },
{ ledger2_accounts::NON_CURRENT_INVESTMENTS, "Вложения во внеоборотные активы", AccountType::ACTIVE },
{ ledger2_accounts::MATERIALS, "Материалы", AccountType::ACTIVE },
{ ledger2_accounts::BANK_ACCOUNT, "Расчётный счёт", AccountType::ACTIVE },
{ ledger2_accounts::FINANCIAL_INVESTMENTS, "Финансовые вложения", AccountType::ACTIVE },
{ ledger2_accounts::SHARE_FUND, "Паевой фонд (складочный капитал)", AccountType::PASSIVE },
{ ledger2_accounts::TARGET_RECEIPTS, "Целевое финансирование", AccountType::PASSIVE },
{ ledger2_accounts::OTHER_INCOME_EXPENSES, "Прочие доходы и расходы", AccountType::ACTIVE_PASSIVE },
}};
static constexpr size_t LEDGER2_ACCOUNT_MAP_SIZE = LEDGER2_ACCOUNT_MAP.size();
@@ -64,6 +64,7 @@ namespace operations {
inline constexpr eosio::name COMPLETE_WITHDRAW = "o.wal.wthcpl"_n; ///< Завершение возврата паевого взноса (Dr 80 / Cr 51, TRANSFER SHARE_FUND_PAY → WITHDRAWALS_SINK).
inline constexpr eosio::name REQUEST_WITHDRAW = "o.wal.wthreq"_n; ///< Запрос на возврат паевого: BLOCK на SHARE_FUND_PAY (без Dr/Cr).
inline constexpr eosio::name DECLINE_WITHDRAW = "o.wal.wthdec"_n; ///< Отклонение запроса на возврат: UNBLOCK на SHARE_FUND_PAY (без Dr/Cr).
inline constexpr eosio::name CONVERT_TO_MEMBER = "o.wal.conv"_n; ///< Конвертация цифрового рубля в универсальный членский кошелёк (Dr 80 / Cr 86, TRANSFER SHARE_FUND_PAY → CK_MEMBER). Conditional-шаг серии createorder в «Столе заказов».
}
// capital
@@ -82,10 +83,26 @@ namespace operations {
inline constexpr eosio::name CONVERT_TO_BLAGO = "o.cap.cnvbl"_n; ///< Конвертация сегмента: РИД → ЦПП «Благорост» (TRANSFER GENERATOR_FUND → BLAGOROST_FUND, без Dr/Cr — бухпроводка уже была сделана в ACCEPT_RID).
}
// marketplace
// marketplace — членская модель «Стола заказов» (refactor 2026-05-11; старые
// o.mkt.supply / o.mkt.recv — клиринговые — удалены).
//
// Составные бухгалтерские проводки через транзит счёта 91 «Прочие доходы
// и расходы» разбиты на пары операций (часть 1 + часть 2). Контракт
// вызывает обе последовательно в одной транзакции Antelope — атомарность
// обеспечивается транзакцией. Имена с суффиксом «2» — закрытие транзита.
namespace marketplace {
inline constexpr eosio::name CONFIRM_SUPPLY = "o.mkt.supply"_n; ///< Подтверждение поставки (Dr 51 / Cr 80, ISSUE SHARE_FUND_PAY).
inline constexpr eosio::name CONFIRM_RECEIPT = "o.mkt.recv"_n; ///< Подтверждение получения (Dr 80 / Cr 51, TRANSFER SHARE_FUND_PAY → SUPPLIER_PAYMENTS).
inline constexpr eosio::name ASSIGN_TO_PROGRAM = "o.mkt.assign"_n; ///< Целевое назначение членского взноса пайщика в программу Marketplace (TRANSFER CK_MEMBER → MARKETPLACE_MEMBER, без проводки — оба кошелька на счёте 86). Conditional-шаг серии createorder.
inline constexpr eosio::name BLOCK_FOR_ORDER = "o.mkt.block"_n; ///< Блокировка членского взноса заказчика под конкретный Order (BLOCK на MARKETPLACE_MEMBER, без Dr/Cr).
inline constexpr eosio::name UNBLOCK_ON_CANCEL = "o.mkt.unblk"_n; ///< Разблокировка членского взноса при отмене Order'а (UNBLOCK на MARKETPLACE_MEMBER, без Dr/Cr). Сумма остаётся на .available и может быть потрачена на следующий заказ в программе.
inline constexpr eosio::name RECALL_TO_UNIVERSAL = "o.mkt.recall"_n; ///< Вывод программного членского взноса в универсальный членский кошелёк пайщика (TRANSFER MARKETPLACE_MEMBER → CK_MEMBER, без проводки — оба кошелька на счёте 86). Явное действие пайщика, не часть авто-flow отмены.
inline constexpr eosio::name PURCHASE_FROM_SUPPLIER = "o.mkt.purch"_n; ///< Приёмка имущества кооперативом по АПП приёмки от поставщика (Dr 10 / Cr 86, NONE — только бухпроводка, кошельки не двигаются; имущество — аналитика по 10). Атомарно с PAY_SUPPLIER на закрывающей подписи председателя.
inline constexpr eosio::name PAY_SUPPLIER = "o.mkt.payout"_n; ///< Оплата поставщику с расчётного счёта по факту приёмки (Dr 86 / Cr 51, ISSUE ∅ → SUPPLIER_PAYMENTS). Атомарно с PURCHASE_FROM_SUPPLIER.
inline constexpr eosio::name CONSUME_BY_MEMBER = "o.mkt.consum"_n; ///< Выдача имущества пайщику по АПП выдачи (часть 1 композитной проводки через транзит 91): Dr 91 / Cr 10, REVOKE MARKETPLACE_MEMBER.blocked → 0 — выбытие имущества со склада на «прочие». Закрытие транзита — отдельной операцией CONSUME_TRANSIT_CLOSE, атомарно в той же транзакции signiss2.
inline constexpr eosio::name CONSUME_TRANSIT_CLOSE = "o.mkt.consum2"_n; ///< Выдача имущества пайщику (часть 2 композитной проводки): Dr 86 / Cr 91, NONE — закрытие транзита 91 на счёт ЦФ программы. Срабатывает после CONSUME_BY_MEMBER в той же транзакции.
inline constexpr eosio::name RETURN_BY_MEMBER = "o.mkt.return"_n; ///< Гарантийный возврат имущества пайщиком — compensating forward к CONSUME_BY_MEMBER (часть 1 композитной проводки): Dr 91 / Cr 86, ISSUE ∅ → MARKETPLACE_MEMBER — восстановление «прочих» за счёт ЦФ + восстановление .available заказчика. Реверты ledger2::revert в Столе заказов не используются.
inline constexpr eosio::name RETURN_TRANSIT_CLOSE = "o.mkt.return2"_n; ///< Гарантийный возврат (часть 2 композитной проводки): Dr 10 / Cr 91, NONE — имущество назад на склад через закрытие транзита. Срабатывает после RETURN_BY_MEMBER в той же транзакции decretvisit.
inline constexpr eosio::name WRITE_OFF_PERISHABLE = "o.mkt.wroff"_n; ///< Утилизация скоропорта со склада (часть 1 композитной проводки): Dr 91 / Cr 10, NONE — выбытие со склада на «прочие». По протоколу совета.
inline constexpr eosio::name WRITE_OFF_TRANSIT_CLOSE = "o.mkt.wroff2"_n; ///< Утилизация скоропорта (часть 2 композитной проводки): Dr 86 / Cr 91, NONE — закрытие транзита 91 на счёт ЦФ программы. Срабатывает после WRITE_OFF_PERISHABLE в той же транзакции execwroff.
}
// soviet
@@ -134,8 +151,9 @@ enum class WalletOp : uint8_t {
TRANSFER = 1, ///< перемещение wallet_from → wallet_to (с Dr/Cr ИЛИ без — по парам account_id)
BLOCK = 2, ///< available-=amount, blocked+=amount на wallet_from
UNBLOCK = 3, ///< blocked-=amount, available+=amount на wallet_from
BURN = 4, ///< изъятие amount с wallet_from, без wallet_to. Покрывает оба кейса: (a) штатное сжигание как бизнес-операция в OPERATION_REGISTRY; (b) зеркало ISSUE при `ledger2::revert` (различие — через operation_code: `o.adj.rev` для adjustment-mirror).
BURN = 4, ///< изъятие amount с wallet_from.available, без wallet_to. Покрывает оба кейса: (a) штатное сжигание как бизнес-операция в OPERATION_REGISTRY; (b) зеркало ISSUE при `ledger2::revert` (различие — через operation_code: `o.adj.rev` для adjustment-mirror).
NONE = 5, ///< только бухпроводка без перемещения средств (wallet_from = empty, wallet_to = empty, debit ≠ 0, credit ≠ 0). Покрывает кейсы внутрибалансовых проводок типа Dr 04 / Cr 08 (приём РИД в НМА), когда кошелёк уже на нужном программном фонде.
REVOKE = 6, ///< изъятие amount с wallet_from.blocked в пустоту, без wallet_to. Обязательная пара Dr/Cr. Отличие от BURN — списание из blocked, а не из available; применяется на целевом расходовании предварительно заблокированной суммы (например, выдача имущества пайщику по АПП выдачи). Введён 2026-05-11 под членскую модель «Стола заказов» (o.mkt.consum).
};
/**
@@ -252,16 +270,115 @@ static constexpr OperationRegistryEntry OPERATION_REGISTRY[] = {
ledger2_accounts::SHARE_FUND, ledger2_accounts::FINANCIAL_INVESTMENTS,
"Возврат беспроцентного займа пайщика по акту-2" },
// 12. Подтверждение поставки: Dr 51 / Cr 80, ISSUE SHARE_FUND_PAY
{ operations::marketplace::CONFIRM_SUPPLY, processes::marketplace::REQUEST, WalletOp::ISSUE, eosio::name{}, ledger2_wallets::SHARE_FUND_PAY,
ledger2_accounts::BANK_ACCOUNT, ledger2_accounts::SHARE_FUND,
"Подтверждение поставки товара/услуги" },
// ===== Marketplace: членская модель «Стола заказов» (refactor 2026-05-11) =====
// Старые операции o.mkt.supply (CONFIRM_SUPPLY) и o.mkt.recv (CONFIRM_RECEIPT)
// удалены вместе с клиринговой моделью (паевой взнос имуществом от
// поставщика + возврат паевого имуществом заказчику) — она объявлена
// out-of-MVP до изменений в законодательстве.
// 13. Подтверждение получения: Dr 80 / Cr 51, TRANSFER SHARE_FUND_PAY → SUPPLIER_PAYMENTS
{ operations::marketplace::CONFIRM_RECEIPT, processes::marketplace::REQUEST, WalletOp::TRANSFER,
ledger2_wallets::SHARE_FUND_PAY, ledger2_wallets::SUPPLIER_PAYMENTS,
ledger2_accounts::SHARE_FUND, ledger2_accounts::BANK_ACCOUNT,
"Подтверждение получения товара/услуги — выплата поставщику" },
// 12a. p.mkt.supply: Конвертация цифрового рубля заказчика в универсальный
// членский кошелёк пайщика (Dr 80 / Cr 86, TRANSFER w.wal.share → w.wal.member).
// Conditional-шаг серии createorder — выполняется только при недостаче
// на w.wal.member.available.
{ operations::wallet::CONVERT_TO_MEMBER, processes::marketplace::SUPPLY, WalletOp::TRANSFER,
ledger2_wallets::SHARE_FUND_PAY, ledger2_wallets::CK_MEMBER,
ledger2_accounts::SHARE_FUND, ledger2_accounts::TARGET_RECEIPTS,
"Конвертация цифрового рубля в членский кошелёк пайщика" },
// 12b. p.mkt.supply: Целевое назначение членского в программу Marketplace
// (TRANSFER w.wal.member → w.mkt.member, без Dr/Cr — оба на 86, аналитика на L2).
// Conditional-шаг серии createorder.
{ operations::marketplace::ASSIGN_TO_PROGRAM, processes::marketplace::SUPPLY, WalletOp::TRANSFER,
ledger2_wallets::CK_MEMBER, ledger2_wallets::MARKETPLACE_MEMBER,
0, 0,
"Целевое назначение членского взноса в программу «Стол заказов»" },
// 12c. p.mkt.supply: Блокировка под Order (BLOCK на w.mkt.member, без Dr/Cr).
{ operations::marketplace::BLOCK_FOR_ORDER, processes::marketplace::SUPPLY, WalletOp::BLOCK,
ledger2_wallets::MARKETPLACE_MEMBER, eosio::name{},
0, 0,
"Блокировка членского взноса под заказ" },
// 12d. p.mkt.supply: Разблокировка при отмене Order'а (UNBLOCK, без Dr/Cr).
// Срабатывает на cancelorder / expirecycle / declinebatch. Сумма остаётся
// на w.mkt.member.available и может быть потрачена на следующий заказ.
{ operations::marketplace::UNBLOCK_ON_CANCEL, processes::marketplace::SUPPLY, WalletOp::UNBLOCK,
ledger2_wallets::MARKETPLACE_MEMBER, eosio::name{},
0, 0,
"Разблокировка членского взноса при отмене заказа" },
// 12e. p.mkt.supply: Вывод программного членского в универсальный членский
// (TRANSFER w.mkt.member → w.wal.member, без Dr/Cr — оба на 86).
// Явное действие пайщика — не часть авто-flow отмены.
{ operations::marketplace::RECALL_TO_UNIVERSAL, processes::marketplace::SUPPLY, WalletOp::TRANSFER,
ledger2_wallets::MARKETPLACE_MEMBER, ledger2_wallets::CK_MEMBER,
0, 0,
"Вывод членского взноса в универсальный членский кошелёк" },
// 12f. p.mkt.supply: Приёмка имущества кооперативом по АПП приёмки
// (Dr 10 / Cr 86, NONE — только бухпроводка, кошельки не двигаются).
// Имущество — аналитикой по счёту 10 (per-КУ субсчета), без отдельного кошелька.
// Атомарно с PAYOUT на закрывающей подписи председателя АПП приёмки.
{ operations::marketplace::PURCHASE_FROM_SUPPLIER, processes::marketplace::SUPPLY, WalletOp::NONE,
eosio::name{}, eosio::name{},
ledger2_accounts::MATERIALS, ledger2_accounts::TARGET_RECEIPTS,
"Приёмка имущества кооперативом по АПП приёмки" },
// 12g. p.mkt.supply: Оплата поставщику с расчётного счёта
// (Dr 86 / Cr 51, ISSUE ∅ → w.mkt.payout). Атомарно с PURCH.
{ operations::marketplace::PAY_SUPPLIER, processes::marketplace::SUPPLY, WalletOp::ISSUE,
eosio::name{}, ledger2_wallets::SUPPLIER_PAYMENTS,
ledger2_accounts::TARGET_RECEIPTS, ledger2_accounts::BANK_ACCOUNT,
"Оплата поставщику с расчётного счёта по факту приёмки" },
// 12h. p.mkt.supply: Выдача имущества пайщику по АПП выдачи — часть 1.
// REVOKE на w.mkt.member.blocked → 0 (целевой расход членского) +
// Dr 91 / Cr 10 (выбытие имущества со склада на «прочие»).
// Согласовано с Ангелиной 2026-05-11. Атомарность с CONSUME_TRANSIT_CLOSE
// обеспечивается транзакцией Antelope — контракт вызывает обе операции
// последовательно в signiss2.
{ operations::marketplace::CONSUME_BY_MEMBER, processes::marketplace::SUPPLY, WalletOp::REVOKE,
ledger2_wallets::MARKETPLACE_MEMBER, eosio::name{},
ledger2_accounts::OTHER_INCOME_EXPENSES, ledger2_accounts::MATERIALS,
"Выдача имущества пайщику по АПП выдачи — выбытие со склада" },
// 12h2. p.mkt.supply: Выдача — часть 2. NONE Dr 86 / Cr 91 (закрытие
// транзита 91 на счёт ЦФ программы). Кошельки не двигаются.
{ operations::marketplace::CONSUME_TRANSIT_CLOSE, processes::marketplace::SUPPLY, WalletOp::NONE,
eosio::name{}, eosio::name{},
ledger2_accounts::TARGET_RECEIPTS, ledger2_accounts::OTHER_INCOME_EXPENSES,
"Выдача имущества пайщику — закрытие транзита на ЦФ программы" },
// 12i. p.mkt.return: Гарантийный возврат — часть 1. ISSUE ∅ → w.mkt.member
// (восстановление средств заказчика в программе) + Dr 91 / Cr 86
// (восстановление «прочих» за счёт ЦФ — зеркало 12h2). Compensating
// forward к CONSUM; ledger2::revert в Столе заказов не используется.
// Атомарность с RETURN_TRANSIT_CLOSE — транзакция Antelope в decretvisit.
{ operations::marketplace::RETURN_BY_MEMBER, processes::marketplace::RETURN, WalletOp::ISSUE,
eosio::name{}, ledger2_wallets::MARKETPLACE_MEMBER,
ledger2_accounts::OTHER_INCOME_EXPENSES, ledger2_accounts::TARGET_RECEIPTS,
"Гарантийный возврат — восстановление средств в программе" },
// 12i2. p.mkt.return: Гарантийный возврат — часть 2. NONE Dr 10 / Cr 91
// (имущество назад на склад через транзит — зеркало 12h).
{ operations::marketplace::RETURN_TRANSIT_CLOSE, processes::marketplace::RETURN, WalletOp::NONE,
eosio::name{}, eosio::name{},
ledger2_accounts::MATERIALS, ledger2_accounts::OTHER_INCOME_EXPENSES,
"Гарантийный возврат — имущество назад на склад через транзит" },
// 12j. p.mkt.wroff: Утилизация скоропорта — часть 1. NONE Dr 91 / Cr 10
// (выбытие со склада на «прочие»). По протоколу совета.
{ operations::marketplace::WRITE_OFF_PERISHABLE, processes::marketplace::WRITEOFF, WalletOp::NONE,
eosio::name{}, eosio::name{},
ledger2_accounts::OTHER_INCOME_EXPENSES, ledger2_accounts::MATERIALS,
"Утилизация скоропорта — выбытие со склада" },
// 12j2. p.mkt.wroff: Утилизация скоропорта — часть 2. NONE Dr 86 / Cr 91
// (закрытие транзита). Атомарно с WROFF в execwroff.
{ operations::marketplace::WRITE_OFF_TRANSIT_CLOSE, processes::marketplace::WRITEOFF, WalletOp::NONE,
eosio::name{}, eosio::name{},
ledger2_accounts::TARGET_RECEIPTS, ledger2_accounts::OTHER_INCOME_EXPENSES,
"Утилизация скоропорта — закрытие транзита на ЦФ программы" },
// 14. Конвертация в AXN: Dr 80 / Cr 86, TRANSFER SHARE_FUND_PAY → DELEGATE_FEES
{ operations::soviet::CONVERT_AXN, processes::soviet::AXN_CONVERT, WalletOp::TRANSFER,
@@ -391,6 +508,22 @@ namespace ledger2_registry_detail {
return true;
}
// Правило 6b: REVOKE — wallet_from required, wallet_to == 0,
// обязательная пара Dr/Cr ≠ 0 (списание blocked в пустоту требует
// фиксации целевого расхода и выбытия актива). Введён 2026-05-11
// под `o.mkt.consum` (выдача имущества пайщику по АПП выдачи).
constexpr bool revoke_pattern_correct() {
for (size_t i = 0; i < OPERATION_REGISTRY_SIZE; ++i) {
const auto& e = OPERATION_REGISTRY[i];
if (e.wallet_op != WalletOp::REVOKE) continue;
if (e.wallet_from.value == 0) return false;
if (e.wallet_to.value != 0) return false;
if (e.debit_account_id == 0) return false;
if (e.credit_account_id == 0) return false;
}
return true;
}
// Правило 8: NONE — оба wallet пустые, обе проводки обязательны (Dr ≠ 0, Cr ≠ 0).
// Семантика: только бухпроводка, кошельковое движение отсутствует.
constexpr bool none_pattern_correct() {
@@ -437,6 +570,8 @@ static_assert(ledger2_registry_detail::transfer_wallet_from_ne_to(),
"OPERATION_REGISTRY: TRANSFER с wallet_from == wallet_to или одним из них == 0");
static_assert(ledger2_registry_detail::burn_pattern_correct(),
"OPERATION_REGISTRY: BURN требует wallet_from ≠ 0 и wallet_to == 0");
static_assert(ledger2_registry_detail::revoke_pattern_correct(),
"OPERATION_REGISTRY: REVOKE требует wallet_from ≠ 0, wallet_to == 0 и обе проводки Dr/Cr ≠ 0");
static_assert(ledger2_registry_detail::none_pattern_correct(),
"OPERATION_REGISTRY: NONE требует wallet_from == 0, wallet_to == 0 и обе проводки заполненными");
static_assert(ledger2_registry_detail::accounts_exist_in_map(),
@@ -15,7 +15,11 @@
* явно разрешённая модель мульти-операционных процессов:
* - processes::registrator::ACCEPT ← o.reg.payent + o.reg.putmin
* - processes::capital::RID ← o.cap.commit + o.cap.accept (+ o.cap.repay) + o.cap.cnvshr/o.cap.cnvbl
* - processes::marketplace::REQUEST ← o.mkt.supply + o.mkt.recv
* - processes::marketplace::SUPPLY ← o.wal.conv + o.mkt.assign + o.mkt.block + o.mkt.unblk +
* o.mkt.recall + o.mkt.purch + o.mkt.payout +
* o.mkt.consum + o.mkt.consum2 (composite через 91)
* - processes::marketplace::RETURN ← o.mkt.return + o.mkt.return2 (composite через 91)
* - processes::marketplace::WRITEOFF ← o.mkt.wroff + o.mkt.wroff2 (composite через 91)
*
* Одноактовые процессы: `capital::IMPORT`, `capital::PROPERTY`,
* `capital::INVEST`, `soviet::AXN_CONVERT` (process_type совпадает с
@@ -57,7 +61,9 @@ namespace processes {
// marketplace
namespace marketplace {
inline constexpr eosio::name REQUEST = "p.mkt.reqst"_n; ///< Цикл запроса маркетплейса (o.mkt.supply + o.mkt.recv).
inline constexpr eosio::name SUPPLY = "p.mkt.supply"_n; ///< Прямая поставка-приобретение имущества (9 операций: o.wal.conv + o.mkt.assign + o.mkt.block + o.mkt.unblk + o.mkt.recall + o.mkt.purch + o.mkt.payout + o.mkt.consum + o.mkt.consum2 — последние две композитная проводка через транзит 91). Старый p.mkt.reqst (клиринговый) удалён 2026-05-11.
inline constexpr eosio::name RETURN = "p.mkt.return"_n; ///< Гарантийный возврат имущества пайщиком — compensating forward к o.mkt.consum (o.mkt.return + o.mkt.return2 — композитная проводка через транзит 91).
inline constexpr eosio::name WRITEOFF = "p.mkt.wroff"_n; ///< Утилизация скоропорта со склада КУ (o.mkt.wroff + o.mkt.wroff2 — композитная проводка через транзит 91, по протоколу совета).
}
// soviet
@@ -63,8 +63,9 @@ struct ledger2_wallets {
static constexpr eosio::name GENERATOR_FUND = "w.cap.gen"_n; ///< Генератор — единый агрегированный кошелёк программы (USER_SHARED; ADR-009)
static constexpr eosio::name PREIMP_FUND = "w.cap.preimp"_n; ///< Первичный учёт РИД-взносов до перехода на электронный учёт (USER_SHARED; o.cap.preimp / o.cap.drppre)
// marketplace — выплаты
static constexpr eosio::name SUPPLIER_PAYMENTS = "w.mkt.payout"_n; ///< Выплаты поставщикам (sink RECEIVE_CONFIRM, COOPERATIVE)
// marketplace — программный членский + выплаты
static constexpr eosio::name MARKETPLACE_MEMBER = "w.mkt.member"_n; ///< ЦПП «Стол Заказов» — программный членский кошелёк пайщика (USER_SHARED; BLOCK/UNBLOCK/REVOKE под Order)
static constexpr eosio::name SUPPLIER_PAYMENTS = "w.mkt.payout"_n; ///< Выплаты поставщикам (sink PAYOUT, COOPERATIVE)
};
/**
@@ -93,14 +94,15 @@ struct Ledger2WalletMeta {
WalletKind kind;
};
inline constexpr std::array<Ledger2WalletMeta, 13> LEDGER2_WALLET_REGISTRY = {{
// USER_SHARED (6) — L3-разрез по пайщику
inline constexpr std::array<Ledger2WalletMeta, 14> LEDGER2_WALLET_REGISTRY = {{
// USER_SHARED (7) — L3-разрез по пайщику
{ ledger2_wallets::MIN_SHARE_FUND, "Минимальный паевой взнос", WalletKind::USER_SHARED },
{ ledger2_wallets::SHARE_FUND_PAY, "Паевой взнос пайщика", WalletKind::USER_SHARED },
{ ledger2_wallets::CK_MEMBER, "ЦК — членская часть пайщика", WalletKind::USER_SHARED },
{ ledger2_wallets::BLAGOROST_FUND, "ЦПП «Благорост» — единый кошелёк программы у пайщика", WalletKind::USER_SHARED },
{ ledger2_wallets::GENERATOR_FUND, "ЦПП «Генератор» — единый кошелёк программы у пайщика", WalletKind::USER_SHARED },
{ ledger2_wallets::PREIMP_FUND, "Первичный учёт РИД-взносов до перехода на электронный учёт", WalletKind::USER_SHARED },
{ ledger2_wallets::MARKETPLACE_MEMBER, "ЦПП «Стол Заказов» — программный членский у пайщика", WalletKind::USER_SHARED },
// COOPERATIVE (7) — единый кооперативный баланс, без L3
{ ledger2_wallets::ENTRANCE_FEES, "Вступительные взносы", WalletKind::COOPERATIVE },
@@ -222,13 +224,14 @@ struct Ledger2WalletProgramMapping {
uint64_t required_program_id; // 0 = исключение (без проверки)
};
inline constexpr std::array<Ledger2WalletProgramMapping, 6> LEDGER2_USER_SHARED_PROGRAM_MAPPING = {{
inline constexpr std::array<Ledger2WalletProgramMapping, 7> LEDGER2_USER_SHARED_PROGRAM_MAPPING = {{
{ ledger2_wallets::MIN_SHARE_FUND, 0 /* w.reg.minshr — без проверки */ },
{ ledger2_wallets::SHARE_FUND_PAY, 1 /* ЦК */ },
{ ledger2_wallets::CK_MEMBER, 1 /* ЦК */ },
{ ledger2_wallets::BLAGOROST_FUND, 4 /* Благорост */ },
{ ledger2_wallets::GENERATOR_FUND, 3 /* Генератор */ },
{ ledger2_wallets::PREIMP_FUND, 0 /* w.cap.preimp — РИД-учёт до перехода на электронный учёт, без проверки */ },
{ ledger2_wallets::MARKETPLACE_MEMBER, 2 /* Marketplace */ },
}};
/**
@@ -21,11 +21,14 @@
# вернувшееся имущество остаётся на складе КУ как материальный остаток
# до отдельной процедуры по регламенту кооператива.
#
# В процессе участвует 1 ledger2-операция:
# • o.mkt.return — compensating forward для отката выдачи имущества
# (ISSUE: восстановление .available на пайщикском
# w.mkt.member; проводка Дт 10 / Кт 86
# обратная к o.mkt.consum, имущество назад на склад)
# В процессе участвуют 2 ledger2-операции (compensating-forward пара, зеркало
# выдачи на supply — через транзит счёта 91):
# • o.mkt.return — часть 1: ISSUE ∅ → w.mkt.member (восстановление
# .available заказчика в программе) + Дт 91 / Кт 86
# (восстановление «прочих» за счёт ЦФ)
# • o.mkt.return2 — часть 2: NONE Дт 10 / Кт 91 (имущество назад на склад
# через закрытие транзита). Атомарно с o.mkt.return
# в той же транзакции decretvisit.
#
# Канон формата:
# coopenomics-docs/docs/standards/_spec/canon.md
@@ -37,7 +40,9 @@
# • cpp/lib/core/ledger2/processes.hpp — processes::marketplace::RETURN
# • cpp/lib/core/ledger2/wallets.hpp — w.mkt.member (programmatic)
# • cpp/lib/core/ledger2/accounts.hpp — Целевое финансирование (86),
# Материалы (10)
# Материалы (10),
# Прочие доходы и расходы (91 — NEW,
# транзит при возврате o.mkt.return)
# ─────────────────────────────────────────────────────────────────────────────
# ── Секция 1. Паспорт ───────────────────────────────────────────────────────
@@ -174,6 +179,7 @@ transitions:
ledger_code: p.mkt.return
operations:
- o.mkt.return
- o.mkt.return2
guards:
- Пайщик прибыл на КУ с продукцией.
- Председатель очно осмотрел имущество и принял решение «Принять возврат».
@@ -298,10 +304,14 @@ documents:
# к выдаче).
operations:
- ledger_code: o.mkt.return
human_name: Гарантийный возврат имущества
human_name: Гарантийный возврат — восстановление средств в программе
wallet_op: ISSUE
# L1 — обратная проводка к o.mkt.consum
debit: 10 # Материалы — имущество назад на склад
# L1 — часть 1 композитной проводки через транзит счёта 91 (зеркало
# выдачи): Дт 91 / Кт 86 — восстановление «прочих» за счёт ЦФ.
# Закрытие транзита выполняется отдельной операцией o.mkt.return2
# (Дт 10 / Кт 91), атомарно с o.mkt.return в той же транзакции
# decretvisit. Согласовано с Ангелиной 2026-05-11.
debit: 91 # Прочие доходы и расходы (NEW)
credit: 86 # Целевое финансирование
# L2 — восстановление средств в программе
wallet_from: null # эмиссия — источника нет
@@ -314,20 +324,45 @@ operations:
amount_ref: order.fact_cost
triggered_by: marketplace::decretvisit
description: >
Compensating forward к выдаче имущества (o.mkt.consum). Атомарно
выполняет два эффекта в одной операции: (1) ISSUE на пайщикском
w.mkt.member — .available +fact_cost (восстановление ранее
списанной суммы); (2) проводка Дт 10 / Кт 86 — имущество возвращается
в учёт по счёту 10 КУ-получателя (обратная к проводке o.mkt.consum
Дт 86 / Кт 10). Журнал содержит payload original_consume_op_id
(ссылка на исходный o.mkt.consum для трассировки) — это **прикладное
поле**, не часть инфраструктуры revert. Дальнейшая судьба
возвращённого имущества — определяется регламентом кооператива через
отдельную процедуру вне Стола заказов. Пайщик может потратить
возвращённую сумму на следующий заказ в Столе заказов либо вывести
в общий членский кошелёк (w.wal.member) через отдельную операцию
o.mkt.recall — там средства универсальны между программами.
(TBD-Standardization: проводка Дт 10 / Кт 86 — baseline, обратная
к o.mkt.consum; финальное решение — при формализации стандарта
совместно с Ангелиной.)
Compensating forward к выдаче имущества (o.mkt.consum), часть 1
композитной бухгалтерской проводки через транзит счёта 91. ISSUE на
пайщикском w.mkt.member — .available +fact_cost (восстановление ранее
списанной суммы); проводка Дт 91 / Кт 86 — восстановление «прочих»
за счёт ЦФ (зеркало 91-стороны от o.mkt.consum). Сразу после этой
операции в той же транзакции decretvisit контракт вызывает
o.mkt.return2 (Дт 10 / Кт 91, NONE) — имущество назад на склад через
закрытие транзита. Транзит через 91 симметричен выбытию на выдаче —
согласовано с Ангелиной 2026-05-11. Журнал содержит payload
original_consume_op_id (ссылка на исходный o.mkt.consum для
трассировки) — это **прикладное поле**, не часть инфраструктуры
revert. Дальнейшая судьба возвращённого имущества — определяется
регламентом кооператива через отдельную процедуру вне Стола заказов.
Пайщик может потратить возвращённую сумму на следующий заказ в Столе
заказов либо вывести в общий членский кошелёк (w.wal.member) через
отдельную операцию o.mkt.recall — там средства универсальны между
программами.
- ledger_code: o.mkt.return2
human_name: Гарантийный возврат — закрытие транзита
wallet_op: NONE
# L1 — часть 2 композитной проводки: Дт 10 / Кт 91 (имущество назад
# на склад через закрытие транзита). Зеркало 91-стороны от o.mkt.csmcls.
debit: 10 # Материалы (NEW) — имущество на складе
credit: 91 # Прочие доходы и расходы (NEW)
# L2 / L3 — без движения по кошелькам (склад — аналитика по счёту 10)
wallet_from: null
wallet_to: null
user_wallet: null
user_ref: null
available_delta: null
blocked_delta: null
amount_ref: order.fact_cost
triggered_by: marketplace::decretvisit
description: >
Закрытие транзита счёта 91 после восстановления членского взноса
пайщика. NONE — только бухпроводка Дт 10 / Кт 91, кошельки не
двигаются. Срабатывает атомарно с o.mkt.return в той же транзакции
decretvisit. По итогу пары operations нетто-эффект — Дт 10 / Кт 86
(имущество возвращается на склад + восстановление обязательства
по целевой программе), 91 проходит нулевым транзитом.
@@ -30,7 +30,7 @@
# (рефакторинг 2026-04-27 на ветке reports). null для незатронутых уровней;
# sentinel '' — legacy-допуск (рендерится как ∅).
#
# В процессе участвуют 8 ledger2-операций (часть — из контракта wallet):
# В процессе участвуют 9 ledger2-операций (часть — из контракта wallet):
# • o.wal.conv — конвертация цифрового рубля → общий членский пайщика
# (TRANSFER, w.wal.share → w.wal.member,
# Дт 80 / Кт 86) — conditional, только если на
@@ -52,9 +52,13 @@
# (ACCOUNT_ONLY, только проводка, Дт 10 / Кт 86)
# • o.mkt.payout — оплата поставщику с расчётного счёта
# (TRANSFER, ∅ → w.mkt.payout, Дт 86 / Кт 51)
# • o.mkt.consum — выдача имущества пайщику по АПП выдачи
# (REVOKE, w.mkt.member .blocked → 0,
# Дт 86 / Кт 10)
# • o.mkt.consum — выдача имущества пайщику по АПП выдачи, часть 1
# (REVOKE, w.mkt.member.blocked → 0, Дт 91 / Кт 10 —
# выбытие имущества со склада на «прочие»)
# • o.mkt.consum2 — выдача имущества пайщику, часть 2 — закрытие транзита
# (NONE, Дт 86 / Кт 91; кошельки не двигаются).
# Атомарно с o.mkt.consum в signiss2 — обе операции
# вызываются последовательно в одной транзакции Antelope.
#
# Канон формата:
# coopenomics-docs/docs/standards/_spec/canon.md
@@ -70,7 +74,9 @@
# • cpp/lib/core/ledger2/accounts.hpp — Расчётный (51),
# Паевой фонд (80),
# Целевое финансирование (86),
# Материалы (10 — NEW)
# Материалы (10 — NEW),
# Прочие доходы и расходы (91 — NEW,
# транзит при выдаче o.mkt.consum)
# ─────────────────────────────────────────────────────────────────────────────
# ── Секция 1. Паспорт ───────────────────────────────────────────────────────
@@ -362,6 +368,7 @@ transitions:
ledger_code: p.mkt.supply
operations:
- o.mkt.consum
- o.mkt.consum2
guards:
- Заказчик присутствует на КУ; оператор сверил факт vs заказ.
- При расхождении факт vs заказ сначала выполнены корректирующие операции (o.mkt.unblk на остаток если факт меньше; o.wal.conv + o.mkt.assign + o.mkt.block на разницу если факт больше — первые две conditional; в пределах допустимого отрицательного баланса паевого ≤5%).
@@ -833,11 +840,15 @@ operations:
финальное решение — при формализации стандарта совместно с Ангелиной.)
- ledger_code: o.mkt.consum
human_name: Выдача имущества пайщику
human_name: Выдача имущества пайщику — выбытие со склада
wallet_op: REVOKE
# L1 — выбытие имущества за счёт целевых средств
debit: 86 # Целевое финансирование
credit: 10 # Материалы
# L1 — часть 1 композитной проводки через транзит счёта 91:
# Дт 91 / Кт 10 — выбытие имущества со склада на «прочие». Закрытие
# транзита выполняется отдельной операцией o.mkt.consum2 (Дт 86 / Кт 91),
# атомарно с o.mkt.consum в той же транзакции Antelope (вызывается
# сразу после в действии signiss2). Согласовано с Ангелиной 2026-05-11.
debit: 91 # Прочие доходы и расходы (NEW)
credit: 10 # Материалы (NEW)
# L2 — без перевода между кошельками кооператива (REVOKE списывает
# blocked в пустоту, это L3-семантика)
wallet_from: null
@@ -851,15 +862,41 @@ operations:
triggered_by: marketplace::signiss2
description: >
Выдача имущества заказчику-пайщику по АПП выдачи (финальная подпись
заказчика). REVOKE на программном кошельке пайщика — заблокированная
сумма списывается без увеличения куда-либо (целевое расходование
членского взноса). Двойная запись Дт 86 / Кт 10 — целевое использование
средств с одновременным выбытием имущества со склада (обратная к
o.mkt.purch). При расхождении факт vs заказ предшествуют корректирующие
операции (o.mkt.unblk на остаток если факт меньше; o.wal.conv +
o.mkt.assign + o.mkt.block на разницу если факт больше — первые две
conditional). (TBD-Standardization:
проводка Дт 86 / Кт 10 фиксируется как baseline; Игорь предлагал
альтернативу с участием 10-го счёта в иной роли — точная формулировка
утеряна, восстанавливается при стандартизации.)
заказчика), часть 1 композитной бухгалтерской проводки через транзит
счёта 91 «Прочие доходы и расходы». REVOKE на программном кошельке
пайщика — заблокированная сумма списывается без увеличения куда-либо
(целевое расходование членского взноса). Проводка Дт 91 / Кт 10 —
выбытие имущества со склада на «прочие». Сразу после этой операции
в той же транзакции signiss2 контракт вызывает o.mkt.consum2
(Дт 86 / Кт 91, NONE) — закрытие транзита 91 на счёт ЦФ программы.
Транзит через 91 отделяет момент выбытия имущества от момента
признания целевого расхода — согласовано с Ангелиной 2026-05-11.
При расхождении факт vs заказ предшествуют корректирующие операции
(o.mkt.unblk на остаток если факт меньше; o.wal.conv + o.mkt.assign +
o.mkt.block на разницу если факт больше — первые две conditional).
- ledger_code: o.mkt.consum2
human_name: Выдача имущества пайщику — закрытие транзита
wallet_op: NONE
# L1 — часть 2 композитной проводки: Дт 86 / Кт 91 (закрытие «прочих»
# на счёт ЦФ программы). Зеркало 91-стороны от o.mkt.consum.
debit: 86 # Целевое финансирование
credit: 91 # Прочие доходы и расходы (NEW)
# L2 / L3 — без движения по кошелькам
wallet_from: null
wallet_to: null
user_wallet: null
user_ref: null
available_delta: null
blocked_delta: null
amount_ref: order.fact_cost
triggered_by: marketplace::signiss2
description: >
Закрытие транзита счёта 91 после выдачи имущества пайщику. NONE —
только бухпроводка Дт 86 / Кт 91, кошельки не двигаются. Срабатывает
атомарно с o.mkt.consum в той же транзакции signiss2 (контракт
выполняет обе операции последовательно). По итогу пары operations
нетто-эффект — Дт 86 / Кт 10 (закрытие обязательства по целевой
программе + выбытие имущества со склада), 91 проходит нулевым
транзитом.
@@ -22,10 +22,10 @@
# reports). Sentinel '' (пустая строка) — «кошелёк вне системы» для
# операций ACCOUNT_ONLY.
#
# В процессе участвует 1 ledger2-операция:
# • o.mkt.wroff — списание скоропорта со склада через транзит счёта 91
# (ACCOUNT_ONLY с составной проводкой:
# Дт 91 / Кт 10 + Дт 86 / Кт 91)
# В процессе участвуют 2 ledger2-операции (композитная проводка через
# транзит счёта 91 разбита на пару, срабатывают в одной транзакции execwroff):
# • o.mkt.wroff — часть 1: NONE Дт 91 / Кт 10 (выбытие со склада на «прочие»)
# • o.mkt.wroff2 — часть 2: NONE Дт 86 / Кт 91 (закрытие транзита на ЦФ программы)
#
# Канон формата:
# coopenomics-docs/docs/standards/_spec/canon.md
@@ -140,6 +140,7 @@ transitions:
ledger_code: p.mkt.wroff
operations:
- o.mkt.wroff
- o.mkt.wroff2
guards:
- Совет принял положительное решение по протоколу (sov.decision).
- Решение содержит подписанный документ authorization.
@@ -232,11 +233,14 @@ documents:
# ledger2 для операций без движения по кошелькам.
operations:
- ledger_code: o.mkt.wroff
human_name: Списание скоропорта со склада
wallet_op: ACCOUNT_ONLY
# L1 — составная проводка через транзит счёта 91 (см. description)
debit: 91 # Прочие доходы и расходы (NEW в LEDGER2_ACCOUNT_REGISTRY)
credit: 10 # Материалы (NEW)
human_name: Утилизация скоропорта — выбытие со склада
wallet_op: NONE # NONE = только бухпроводка без движения по кошелькам
# L1 — часть 1 композитной проводки: Дт 91 / Кт 10 (выбытие имущества
# со склада на «прочие»). Закрытие транзита выполняется отдельной
# операцией o.mkt.wroff2 (Дт 86 / Кт 91), атомарно с o.mkt.wroff
# в той же транзакции execwroff (контракт вызывает обе последовательно).
debit: 91 # Прочие доходы и расходы (NEW)
credit: 10 # Материалы (NEW в LEDGER2_ACCOUNT_REGISTRY)
# L2 — без перевода между кошельками кооператива
wallet_from: null
wallet_to: null
@@ -248,20 +252,35 @@ operations:
amount_ref: writeoff_item.cost
triggered_by: marketplace::execwroff
description: >
Списание имущества со склада КУ через транзит счёта 91 «Прочие доходы
и расходы». В контракте реализовано как составная операция: первая
проводка Дт 91 / Кт 10 (списание имущества с активного счёта на
транзит 91), вторая проводка Дт 86 / Кт 91 (закрытие 91 на счёт
ЦФ программы). По кошелькам пайщиков движений не происходит —
это чисто кооперативный расход в рамках условий ЦПП «Стол заказов».
Применяется по каждой позиции из списка writeoff_proposal — может
быть несколько последовательных вызовов в одной транзакции execwroff.
WalletOp ACCOUNT_ONLY — proposed расширение enum'а ledger2 для
операций, выполняющих только бухгалтерскую проводку без движения
по кошелькам. (TBD-Standardization: транзит через 91 фиксируется
как baseline согласно решению с Ангелиной 2026-04-27; финальное
решение — при формализации стандарта; для составной проводки
потребуется либо расширение схемы ledger2_operation_record под
композитные проводки, либо два последовательных apply с общим
transaction_id.)
Списание имущества со склада КУ — часть 1 композитной бухгалтерской
проводки через транзит счёта 91 «Прочие доходы и расходы». NONE —
только бухпроводка Дт 91 / Кт 10, кошельки не двигаются. Сразу
после этой операции в той же транзакции execwroff контракт вызывает
o.mkt.wroff2 (Дт 86 / Кт 91, NONE) — закрытие транзита на счёт ЦФ
программы. Применяется по каждой позиции из списка writeoff_proposal —
может быть несколько последовательных пар в одной транзакции
execwroff. Согласовано с Ангелиной 2026-04-27.
- ledger_code: o.mkt.wroff2
human_name: Утилизация скоропорта — закрытие транзита
wallet_op: NONE
# L1 — часть 2 композитной проводки: Дт 86 / Кт 91 (закрытие «прочих»
# на счёт ЦФ программы).
debit: 86 # Целевое финансирование
credit: 91 # Прочие доходы и расходы (NEW)
# L2 / L3 — без движения по кошелькам
wallet_from: null
wallet_to: null
user_wallet: null
user_ref: null
available_delta: null
blocked_delta: null
amount_ref: writeoff_item.cost
triggered_by: marketplace::execwroff
description: >
Закрытие транзита счёта 91 после выбытия имущества со склада. NONE —
только бухпроводка Дт 86 / Кт 91, кошельки не двигаются. Срабатывает
атомарно с o.mkt.wroff в той же транзакции execwroff. По итогу пары
operations нетто-эффект — Дт 86 / Кт 10 (закрытие ЦФ программы +
выбытие имущества со склада), 91 проходит нулевым транзитом.