COOPENOMICS  v1
Кооперативная Экономика
Класс marketplace

Контракт marketplace — кооперативный «Стол заказов» в режиме членских взносов. Подробнее...

#include <marketplace.hpp>

Граф наследования:marketplace:

Открытые члены

 marketplace (eosio::name receiver, eosio::name code, eosio::datastream< const char * > ds)
 
void createorder (eosio::name coopname, eosio::name orderer, checksum256 order_hash, checksum256 offer_hash, eosio::name offerer, eosio::name delivery_braname, eosio::asset quantity, eosio::asset unit_price, eosio::asset package_size, uint32_t warranty_period_secs, checksum256 batch_hash, document2 convert_statement)
 Заказчик размещает заказ на товар из каталога (Story 4.1). Один шаг ledger2: o.mkt.lock (TRANSFER w.wal.share → w.mkt.order). convert_statement — подписанное заказчиком заявление о конвертации паевого взноса в членский по программе «Стол заказов»; публикуется в реестр документов отдельным пакетом (package = hash заявления). Подробнее...
 
void stockorder (eosio::name coopname, eosio::name orderer, checksum256 order_hash, checksum256 offer_hash, eosio::name delivery_braname, eosio::asset quantity, eosio::asset unit_price, eosio::asset package_size, uint32_t warranty_period_secs, checksum256 batch_hash)
 Заказ из обезличенного остатка склада кооператива (requirement 76). Продавец — сам кооператив (offerer == coopname), имущество уже на счёте 10 после ранее закрытых приёмок, поэтому Order создаётся сразу в acceptcoop и идёт только через выдачу signiss1/signiss2. Этапы поставки и выплата поставщику для такого заказа не существуют. Подробнее...
 
void convert (eosio::name coopname, eosio::name orderer, eosio::asset amount, document2 convert_statement)
 Конвертация паевого взноса пайщика в членский кошелёк «Стола заказов» (requirement 76, заказ из остатка из членских средств). Один шаг ledger2: o.mkt.conv (TRANSFER w.wal.share → w.mkt.member, Дт 80 / Кт 86). convert_statement — подписанное заказчиком Заявление о конвертации (шаблон 1110), публикуется в реестр документов отдельным пакетом (package = hash заявления). Выполняется перед stockorder, когда членских средств пайщика не хватает на заказ из остатка (на полную сумму или только на дельту превышения над высвобождённым при замене). Подробнее...
 
void cancelorder (eosio::name coopname, eosio::name orderer, checksum256 order_hash)
 Заказчик отменяет заказ до акцепта (Story 4.4). Триггерит o.mkt.unlock. Заказ из остатка кооператива (offerer == coopname) отменяется и в acceptcoop — до первой подписи акта выдачи (откат оператора, requirement 76 решение 11). Подробнее...
 
void expireorder (eosio::name coopname, checksum256 order_hash)
 Backend закрывает Order по таймауту цикла отсечки (Story 4.3). Per-Order: o.mkt.unlock + статус active → cancelled. Backend вычисляет threshold по batch'у вне контракта; для каждого истёкшего Order'а вызывается отдельный expireorder. Подробнее...
 
void closeorder (eosio::name coopname, checksum256 order_hash)
 Закрыть выданный заказ после выхода гарантийного срока: терминал жизненного цикла, запись стирается из RAM. Вызывается автоматизированной службой по расписанию; до выхода гарантийного срока, при незавершённой выплате поставщику или открытом возврате закрытие отклоняется. Подробнее...
 
void acceptorder (eosio::name coopname, eosio::name offerer, checksum256 order_hash)
 Поставщик акцептует один Order (Story 4.5). Без ledger2-операций — статус active → accepted. Backend проходит циклом по orders соответствующего batch'а, вызывая acceptorder per Order. Подробнее...
 
void declineorder (eosio::name coopname, eosio::name offerer, checksum256 order_hash)
 Поставщик отказывается от одного Order'а до акцепта (Story 4.5). Per-Order: o.mkt.unlock на total_cost + статус active → cancelled. Backend проходит циклом по orders батча, вызывая declineorder per Order. Подробнее...
 
void signsupp (eosio::name coopname, eosio::name offerer, checksum256 order_hash, eosio::name accept_braname, document2 act)
 Поставщик первой подписью на АПП приёмки фиксирует партию по одному Order'у (Story 5.3/5.4). Без ledger2-операций — статус accepted → supply_prepared. Параметр accept_braname указывает приёмный КУ; запись в Order. Подпись валидируется как verify_document_or_fail(act, {offerer}). Backend проходит циклом по orders батча с одинаковым act. Подробнее...
 
void signchair (eosio::name coopname, eosio::name signer, checksum256 order_hash, eosio::asset actual_quantity, eosio::asset actual_unit_price, document2 act)
 Председатель приёмного КУ ставит закрывающую подпись на АПП приёмки одного Order'а (Story 5.3/5.4). Per-Order: только o.mkt.purch (Дт 10 / Кт 86). Выплата поставщику (o.mkt.payout) — отдельным lazy action'ом payout после подтверждения кассиром фактического банковского перевода (E11 техдолг 598-16, Locked Decision L12). Авторизация подписи: председатель / trustee / trusted ∈ branches[o.accept_braname]. Backend проходит циклом по orders батча с одинаковым act. Подробнее...
 
void payout (eosio::name coopname, checksum256 order_hash)
 Инициация исходящей выплаты поставщику через контракт gateway по одному Order'у (E11 техдолг 598-16, Locked Decision L12). Per-Order: inline-вызов gateway::createoutpay с callback'ами на payconfirm / paydecline. Ledger2-операция o.mkt.payout (Дт 86 / Кт 51) применяется НЕ здесь, а в callback'е payconfirm после действия кассира. Статус Order'а не меняется; защита от двойного запроса — через order.payout_status (NONE/DECLINED → PENDING). Подробнее...
 
void payconfirm (eosio::name coopname, checksum256 outcome_hash)
 Callback от gateway::outcomplete — кассир подтвердил банковский перевод поставщику (E11 техдолг 598-16, Locked Decision L12). Здесь применяется o.mkt.payout (Дт 86 / Кт 51) и payout_status переходит PENDING → COMPLETED. Авторизация: _gateway. outcome_hash совпадает с order.hash (так задано при payout). Подробнее...
 
void paydecline (eosio::name coopname, checksum256 outcome_hash, std::string reason)
 Callback от gateway::outdecline — кассир отметил, что банковский перевод не состоялся (E11 техдолг 598-16, Locked Decision L12). Ledger2-операция НЕ применяется; обязательство Кт 86 остаётся открытым. payout_status PENDING → DECLINED; payout_decline_reason сохраняется. Авторизация: _gateway. Подробнее...
 
void markdown (eosio::name coopname, checksum256 order_hash, eosio::asset amount)
 Списание уценки по заказу из остатка кооператива (requirement 76). Разница между стоимостью прибытия выданного и фактической суммой выдачи выбывает со счёта 10 в прочие расходы: o.mkt.loss (NONE, Дт 91 / Кт 10). Вместе с o.mkt.consum даёт выбытие по полной стоимости прибытия — на складе ничего не зависает. Вызывает backend после финализации выдачи (сумму считает по выданным позициям). Погашение накопленного на 91 (Дт 86 / Кт 91) — будущий процесс по образцу списания скоропорта. Подробнее...
 
void signiss1 (eosio::name coopname, eosio::name signer, checksum256 order_hash, document2 act)
 Председатель КУ выдачи открывает выдачу первой подписью АПП-выдачи (Story 6.1). Без ledger2-операций — статус ready_to_receive. Авторизация: подписант ∈ branches[o.delivery_braname]. Подробнее...
 
void signiss2 (eosio::name coopname, eosio::name orderer, checksum256 order_hash, eosio::asset actual_quantity, eosio::asset actual_unit_price, eosio::name delivery_signer, document2 act)
 Заказчик ставит финальную подпись АПП-выдачи (Story 6.3). Per-Order с поддержкой actual_quantity ≠ ordered (Story 6.2). Atomic: [o.mkt.unlock на разницу если actual<ordered | o.mkt.lock на разницу если actual>ordered]. Подробнее...
 
void setfee (eosio::name coopname, uint64_t membership_fee_percent)
 Установка единой ставки членского взноса «Стола заказов» (requirement b6). Одна ставка на весь кооператив (HUNDR_PERCENTS = 100%); задаёт администратор. Применяется к новым заказам; в созданных заказах взнос зафиксирован полем Order.membership_fee. Подробнее...
 
void submretrn (eosio::name coopname, eosio::name orderer, checksum256 request_hash, checksum256 original_order_hash, eosio::asset actual_quantity, std::string reason_text, std::vector< checksum256 > photos, document2 statement)
 Пайщик подаёт заявление на гарантийный возврат (Story 7.1). Подробнее...
 
void aprretrem (eosio::name coopname, eosio::name signer, eosio::name braname, checksum256 request_hash)
 Председатель удалённо одобряет очный визит (Story 7.2). Авторизация: подписант ∈ branches[braname]; параметр braname фиксирует КУ, в котором рассматривается заявление. Подробнее...
 
void rejretrem (eosio::name coopname, eosio::name signer, eosio::name braname, checksum256 request_hash, std::string reason)
 Председатель удалённо отказывает (Story 7.2). Авторизация: подписант ∈ branches[braname]. Подробнее...
 
void accretrn (eosio::name coopname, eosio::name signer, eosio::name braname, checksum256 request_hash, document2 statement)
 Председатель принимает возврат на очном осмотре (Story 7.4). Один шаг: o.mkt.return (compensating forward к o.mkt.consum). Председатель накладывает вторую подпись на заявление пайщика (statement несёт обе подписи — пайщика и председателя), отдельного документа решения нет. Авторизация: подписант ∈ branches[braname]. Подробнее...
 
void rejretrn (eosio::name coopname, eosio::name signer, eosio::name braname, checksum256 request_hash, std::string reason)
 Председатель отказывает на очном осмотре (Story 7.3). Авторизация: подписант ∈ branches[braname]. Подробнее...
 
void propwroff (eosio::name coopname, eosio::name proposed_by, checksum256 proposal_hash, std::vector< wroff_item > items, document2 statement, std::string meta)
 Backend выносит проект списания на повестку совета (Story 8.1). Без ledger2-операций — только создание proposal с N позициями (статус proposed) и тем же inline-вызовом ставит повестку: soviet::createagenda от permission_level{_marketplace, active} с callback_contract=marketplace, confirm_callback=onmktwoauth, decline_callback=onmktwodecl, type=mktwroff, hash=proposal_hash, statement (Заявление 1106). Проект может подаваться председателем (за подписью) либо автоматически — мост повестки целиком на контракте, без участия backend. Подробнее...
 
void onmktwoauth (eosio::name coopname, checksum256 hash, document2 authorization)
 Callback от soviet::exec после авторизации Протокола совета (registry 1105) председателем (Story 8.4). PROPOSED → AUTHORIZED; сохраняется authorization в wroffprops.protocol. Цикл per-item списания запускает backend через execwroff после получения этой дельты. Подробнее...
 
void onmktwodecl (eosio::name coopname, checksum256 hash, std::string reason)
 Callback от soviet::cancelexprd (или от любого decline-эффекта в soviet) — повестка отклонена или просрочена (Story 8.4). PROPOSED → REJECTED; reason сохраняется в wroffprops.reject_reason. Без ledger2-движений. Подробнее...
 
void execwroff (eosio::name coopname, eosio::name signer, checksum256 proposal_hash, uint64_t item_index)
 Backend исполняет одну позицию авторизованного проекта списания (Story 8.4). Per-item: o.mkt.wroff, items[item_index].executed = true. Когда все items исполнены, статус AUTHORIZED → EXECUTED. Подробнее...
 
void confirmwroff (eosio::name coopname, eosio::name signer, checksum256 proposal_hash, eosio::name braname, document2 memo)
 Председатель кооперативного участка подтверждает фактическое списание со склада своего КУ по авторизованному советом проекту (ручной шаг стола ПВЗ). Подробнее...
 
void migrate ()
 Заглушка миграции — donor-таблиц нет, мигрировать нечего. Оставлена для совместимости с CMake-build и прежним ABI. Подробнее...
 

Подробное описание

Контракт marketplace — кооперативный «Стол заказов» в режиме членских взносов.

Реализует canonical actions трёх процессов из YAML-стандартов:

  • p.mkt.supply (13 actions): createorder, stockorder, cancelorder, expireorder, acceptorder, declineorder, signsupp, signchair, signiss1, signiss2, closeorder, markdown, setfee.
  • p.mkt.return (5 actions): submretrn, aprretrem, rejretrem, accretrn, rejretrn.
  • p.mkt.wroff (4 actions): propwroff, execwroff, onmktwoauth, onmktwodecl. Cписание скоропорта идёт через канонический паттерн «решение совета»: backend подписывает Заявление о списании (registry 1106) ключом кооператива, вызывает propwroff (запись wroffprops::proposed) + soviet::createagenda(type=mktwroff, callback=onmktwoauth/onmktwodecl). После голосования совета и подписи Протокола (registry 1105) chairman'ом soviet::exec автоматически вызывает callback onmktwoauth (PROPOSED → AUTHORIZED, сохраняется protocol2) или onmktwodecl (PROPOSED → REJECTED). Только после AUTHORIZED backend циклом по items вызывает execwroff per-item (o.mkt.wroff на каждой позиции).

Все per-batch операции на on-chain выполняются per-Order (бэкенд проходит циклом по Order'ам соответствующего batch'а, объединяя их по batch_hash). Векторов order_hashes в actions нет — это ограничение на размер транзакции в Antelope (тысячи orders в одной транзакции не пройдут).

Все ledger2-движения средств — через Ledger2::apply(_marketplace, …), никаких прямых wallet/account-операций. 13 marketplace-операций зарегистрированы в lib/core/ledger2/operations.hpp (OPERATION_REGISTRY).

Composite-операции (consum+consum2, return+return2, wroff+wroff2) — последовательные Ledger2::apply в одной транзакции Antelope (атомарность через single-action wrapper).

Авторизация подписей под актами / решениями привязана к кооперативному участку (КУ) через контракт branch и helper Branch::is_user_authorized(coopname, braname, signer) — председатель КУ может делегировать подпись доверенному лицу из coobranch.trusted[].

Источник правды по логике actions, гардам, state-переходам и операциям — три YAML-файла рядом с этим .hpp:

  • p.mkt.supply.standard.yaml
  • p.mkt.return.standard.yaml
  • p.mkt.wroff.standard.yaml

Donor-actions старой клиринговой модели (FR19a, AR30) удалены вместе с соответствующими таблицами Marketplace::request/segment/shipment.

Конструктор(ы)

◆ marketplace()

marketplace::marketplace ( eosio::name  receiver,
eosio::name  code,
eosio::datastream< const char * >  ds 
)
inline

Методы

◆ acceptorder()

void marketplace::acceptorder ( eosio::name  coopname,
eosio::name  offerer,
checksum256  order_hash 
)

Поставщик акцептует один Order (Story 4.5). Без ledger2-операций — статус active → accepted. Backend проходит циклом по orders соответствующего batch'а, вызывая acceptorder per Order.

Поставщик акцептует один Order (Story 4.5, p.mkt.supply).

Без ledger2-операций — статус active → accepted. Backend проходит циклом по orders соответствующего batch'а, вызывая acceptorder per Order (контракт не принимает векторов order_hash — единичные транзакции масштабируются на любой размер batch'а).

После акцепта поставщик считается обязанным доставить партию: отдельная подпись «готов отгрузить» (бывший prepship) удалена из процесса — следующий шаг сразу signsupp с актом приёмки.

Guards:

  • Order существует и в статусе active.
  • actor == order.offerer.

◆ accretrn()

void marketplace::accretrn ( eosio::name  coopname,
eosio::name  signer,
eosio::name  braname,
checksum256  request_hash,
document2  statement 
)

Председатель принимает возврат на очном осмотре (Story 7.4). Один шаг: o.mkt.return (compensating forward к o.mkt.consum). Председатель накладывает вторую подпись на заявление пайщика (statement несёт обе подписи — пайщика и председателя), отдельного документа решения нет. Авторизация: подписант ∈ branches[braname].

Председатель принимает гарантийный возврат на очном осмотре (Story 7.4, p.mkt.return).

  • Ledger2::apply(o.mkt.return, fact_cost, orderer, hash=request.hash) — ISSUE w.mkt.member, Дт 10 / Кт 86. Восстановление средств на членском «Стола заказов» заказчика и возврат имущества на склад через целевое финансирование.

Compensating forward, не revert (Locked Decision L3 — AR14): новое событие в journal с прикладным полем original_consume_op_id (заполняется backend'ом в submretrn) для трассировки. Исходная o.mkt.consum в журнале НЕ модифицируется.

Терминал жизненного цикла: запись заявления стирается из RAM, история (включая со-подписанное заявление) остаётся в журнале действий. Имущество возвращается на склад КУ; средства восстанавливаются на w.mkt.member.available заказчика.

Принятие возврата оформляется второй подписью председателя на заявлении пайщика (канон двухподписных актов): на вход приходит тот же документ заявления (registry 1104) с двумя подписями — пайщика и председателя; обе проверяются, поле statement перезаписывается со-подписанной версией. Отдельного документа решения нет.

Guards:

  • Указанный КУ (braname) — участок выдачи исходного заказа.
  • Подписант (signer) авторизован для указанного КУ (braname).
  • return_request.status == approved_for_visit.
  • statement подписан пайщиком (orderer) и председателем (signer).

◆ aprretrem()

void marketplace::aprretrem ( eosio::name  coopname,
eosio::name  signer,
eosio::name  braname,
checksum256  request_hash 
)

Председатель удалённо одобряет очный визит (Story 7.2). Авторизация: подписант ∈ branches[braname]; параметр braname фиксирует КУ, в котором рассматривается заявление.

Председатель удалённо одобряет очный визит (Story 7.2, p.mkt.return).

Без ledger2-операций и без документов. Процедурное действие: статус return_request pending_review → approved_for_visit фиксируется вместе с actor + blockchain_actions[at]. Подпись на документе на удалённом этапе не требуется — она нужна только при принятии возврата (accretrn).

Guards:

  • Указанный КУ (braname) — участок выдачи исходного заказа.
  • Подписант (signer) авторизован для указанного КУ (braname): председатель / trustee / trusted в branches[braname].
  • return_request.status == pending_review.

◆ cancelorder()

void marketplace::cancelorder ( eosio::name  coopname,
eosio::name  orderer,
checksum256  order_hash 
)

Заказчик отменяет заказ до акцепта (Story 4.4). Триггерит o.mkt.unlock. Заказ из остатка кооператива (offerer == coopname) отменяется и в acceptcoop — до первой подписи акта выдачи (откат оператора, requirement 76 решение 11).

Заказчик отменяет заказ / отказывается от получения позиции (Story 4.4 + удержание при отказе, p.mkt.supply).

Граница удержания — акцепт поставщиком (acceptorder). До акцепта поставщик ещё не взял обязательство и ничего не везёт — отмена бесплатна (полный возврат резерва и членского взноса). После акцепта поставщик уже принял заявку и доставляет имущество, кооператив несёт риск его оплаты — отказ пайщика удерживает 50% (тела заказа и взноса) в общий кошелёк КУ; имущество остаётся на складе КУ, вторая половина возвращается пайщику.

Guards:

  • Order существует, actor == order.orderer.
  • active → бесплатно (полный возврат).
  • accepted / supplyprep / acceptcoop → удержание 50% (после акцепта).
  • readyrecv / received → закрыто (акт выдачи уже открыт).
  • Заказ из остатка кооператива (offerer == coopname, см. stockorder): поставщика и его риска нет — отмена бесплатна в acceptcoop до первой подписи акта выдачи (requirement 76, решение 11); после signiss1 закрыта.

◆ closeorder()

void marketplace::closeorder ( eosio::name  coopname,
checksum256  order_hash 
)

Закрыть выданный заказ после выхода гарантийного срока: терминал жизненного цикла, запись стирается из RAM. Вызывается автоматизированной службой по расписанию; до выхода гарантийного срока, при незавершённой выплате поставщику или открытом возврате закрытие отклоняется.

Backend закрывает выданный заказ после выхода гарантийного срока (p.mkt.supply; принцип конечного жизненного цикла RAM-записей).

Терминал жизненного цикла выданного заказа: запись стирается из RAM, история заказа (акты, выплата, гарантия) остаётся в журнале действий. Вызывается автоматизированной службой по расписанию; контракт — финальный судья условий закрытия, до их наступления закрытие отклоняется:

Guards:

  • require_auth(coopname) — backend от имени кооператива;
  • заказ в статусе received (выдан заказчику);
  • гарантийный срок вышел: warranty_until ≤ now. Для заказов без гарантии (warranty_period_secs == 0) условие считается выполненным сразу;
  • гарантийный возврат не в работе: если по заказу подавалось заявление (return_request_id != 0), его запись должна быть уже стёрта терминалом процесса возврата;
  • выплата поставщику завершена (payout_status == completed). Заказ из остатка кооператива (offerer == coopname) выплаты не предполагает.

◆ confirmwroff()

void marketplace::confirmwroff ( eosio::name  coopname,
eosio::name  signer,
checksum256  proposal_hash,
eosio::name  braname,
document2  memo 
)

Председатель кооперативного участка подтверждает фактическое списание со склада своего КУ по авторизованному советом проекту (ручной шаг стола ПВЗ).

Председатель кооперативного участка подтверждает фактическое списание со склада своего КУ по авторизованному советом проекту (ручной шаг стола ПВЗ, p.mkt.wroff).

Совет лишь принимает решение о допустимости списания (proposed → authorized); фактическое выбытие имущества со склада инициирует председатель КУ, подписывая Служебную записку о списании (registry 1111). Один вызов закрывает все неисполненные позиции одного КУ (braname) за одну транзакцию: per-КУ гранулярность разбивает большой проект так, чтобы протокол поместился в лимит транзакции, и привязывает выбытие к подписи ответственного за склад.

Эффект:

  • Ledger2::apply(o.mkt.wroff) по каждой неисполненной позиции с этим braname (Дт 86 / Кт 10), как в execwroff.
  • Служебная записка memo публикуется в реестр документов (make_complete_document, package = proposal_hash).
  • Позиции КУ помечаются executed; когда исполнены все позиции проекта, запись proposal стирается из RAM (терминал жизненного цикла).

Guards:

  • proposal.status == AUTHORIZED (совет уже одобрил);
  • signer уполномочен для КУ braname (branches[braname].is_user_authorized) — председатель КУ или его доверенное лицо;
  • у проекта есть хотя бы одна неисполненная позиция этого КУ.

Совет своим решением (proposed → authorized через onmktwoauth) лишь признаёт списание допустимым. Имущество физически выбывает со склада только после того, как ответственный за склад председатель КУ подпишет Служебную записку о списании (registry 1111) — это и делает данный action.

Гранулярность — по одному КУ (braname) за вызов: проект списания может охватывать несколько участков, и каждый председатель закрывает только свою часть. Все неисполненные позиции данного КУ списываются в одной транзакции (Ledger2::apply o.mkt.wroff, Дт 86 / Кт 10 — как в execwroff). Служебная записка публикуется в реестр документов в пакете процесса списания (package = proposal_hash). Исполнение последней позиции проекта (по всем КУ) стирает запись из RAM — терминал жизненного цикла.

◆ convert()

void marketplace::convert ( eosio::name  coopname,
eosio::name  orderer,
eosio::asset  amount,
document2  convert_statement 
)

Конвертация паевого взноса пайщика в членский кошелёк «Стола заказов» (requirement 76, заказ из остатка из членских средств). Один шаг ledger2: o.mkt.conv (TRANSFER w.wal.share → w.mkt.member, Дт 80 / Кт 86). convert_statement — подписанное заказчиком Заявление о конвертации (шаблон 1110), публикуется в реестр документов отдельным пакетом (package = hash заявления). Выполняется перед stockorder, когда членских средств пайщика не хватает на заказ из остатка (на полную сумму или только на дельту превышения над высвобождённым при замене).

Конвертация паевого взноса пайщика в членский кошелёк «Стола заказов» (requirement 76, заказ из остатка из членских средств).

Заказ из остатка (stockorder) фондируется ВСЕГДА из членского кошелька пайщика начисто. Этот action — отдельный шаг пополнения членского кошелька с паевого: пайщик подаёт Заявление о конвертации (шаблон 1110) с просьбой транслировать паевой взнос с программы «Цифровой кошелёк» в программу «Стол заказов». Зеркало capital::convertsegm в Благорост.

Одна ledger2-операция:

  • o.mkt.conv (TRANSFER w.wal.share → w.mkt.member, Дт 80 / Кт 86) — паевой переходит в целевое финансирование на членский кошелёк программы.

Когда вызывается (оркестрация на backend, всё в одной транзакции с stockorder):

  • докладка у стойки / обычный заказ из остатка, когда членских средств не хватает: конвертируется вся требуемая сумма;
  • замена непоставленного на свободный остаток, когда замена дороже высвобожденного отменой бюджета: конвертируется только дельта превышения (в рамках высвобожденного доплаты с паевого нет).

Guards:

  • amount > 0 в _root_govern_symbol; подпись Заявления валидна (orderer).
  • Заказчик — активный пайщик кооператива.
  • w.wal.share.available заказчика >= amount.

◆ createorder()

void marketplace::createorder ( eosio::name  coopname,
eosio::name  orderer,
checksum256  order_hash,
checksum256  offer_hash,
eosio::name  offerer,
eosio::name  delivery_braname,
eosio::asset  quantity,
eosio::asset  unit_price,
eosio::asset  package_size,
uint32_t  warranty_period_secs,
checksum256  batch_hash,
document2  convert_statement 
)

Заказчик размещает заказ на товар из каталога (Story 4.1). Один шаг ledger2: o.mkt.lock (TRANSFER w.wal.share → w.mkt.order). convert_statement — подписанное заказчиком заявление о конвертации паевого взноса в членский по программе «Стол заказов»; публикуется в реестр документов отдельным пакетом (package = hash заявления).

Заказчик размещает заказ на товар (Story 4.1, p.mkt.supply шаг 1).

Одна ledger2-операция:

  • o.mkt.lock (TRANSFER w.wal.share → w.mkt.order пайщика на total_cost, Дт 80 / Кт 86) — резерв средств заказчика под этот Order. Паевой пайщика переходит в целевое финансирование на резерв-кошелёк.

Guards (из p.mkt.supply.standard.yaml + Locked Decision L6):

  • quantity > 0; unit_price > 0 в _root_govern_symbol.
  • Order с таким hash ещё не создан (idempotency).
  • Заказчик — активный пайщик кооператива (get_participant_or_fail).
  • delivery_braname существует в branches (КУ выдачи задаётся пайщиком из доступных и неизменен после создания Order'а).
  • w.wal.share.available заказчика >= total_cost; иначе createorder фейлится без создания Order'а.
  • Подписка пайщика на оферту ЦПП «Стол заказов» (L2/L3 онбординг) — автоматически проверяется в ledger2::walletop через assert_program_signed (cross-contract в wallet::users.programs[]) при первом TRANSFER на USER_SHARED-кошельке программы (w.mkt.order).

Сообщения проверок — для прямого показа пользователю (UI ловит check'ом).

◆ declineorder()

void marketplace::declineorder ( eosio::name  coopname,
eosio::name  offerer,
checksum256  order_hash 
)

Поставщик отказывается от одного Order'а до акцепта (Story 4.5). Per-Order: o.mkt.unlock на total_cost + статус active → cancelled. Backend проходит циклом по orders батча, вызывая declineorder per Order.

Поставщик отказывается от одного Order'а (Story 4.5 + отказ в приёмке, p.mkt.supply).

Два случая, оба со стороны поставщика и оба без штрафа — полный возврат резерва и членского взноса заказчику (вина не его):

  1. До акцепта (active) — поставщик не берёт заявку в работу.
  2. Отказ в приёмке (accepted / supply_prepared) — поставщик привёз некондицию; кооператив не принимает позицию (ноль единиц в факт), поставщик подтверждает отмену поставки этой позиции и забирает имущество обратно. Имущество ещё не оприходовано на склад (purch на закрывающей подписи приёмки) и поставщик ещё не оплачен — клоубэка нет.

Per-Order: o.mkt.unlock на total_cost (TRANSFER w.mkt.order → w.mkt.member — резерв возвращается на членский «Стола заказов» заказчика) + полный возврат взноса + erase. Backend проходит циклом по неотмеченным в приёмке orders — векторов order_hash нет.

Guards:

  • Order существует, actor == order.offerer.
  • Статус active / accepted / supply_prepared (до оприходования имущества).

◆ execwroff()

void marketplace::execwroff ( eosio::name  coopname,
eosio::name  signer,
checksum256  proposal_hash,
uint64_t  item_index 
)

Backend исполняет одну позицию авторизованного проекта списания (Story 8.4). Per-item: o.mkt.wroff, items[item_index].executed = true. Когда все items исполнены, статус AUTHORIZED → EXECUTED.

Backend исполняет одну позицию авторизованного советом проекта списания скоропорта (Story 8.4, p.mkt.wroff).

Защита от газового лимита (тысячи позиций в одной транзакции Antelope не помещаются) — backend проходит цикл и вызывает execwroff per item.

Guards:

  • proposal.status == AUTHORIZED (callback onmktwoauth уже отработал);
  • подписант (signer) авторизован для КУ-источника (branches[items[item_index].braname]).

Per-item операция:

  • Ledger2::apply(o.mkt.wroff, item.amount, …, hash=proposal.hash) — Дт 86 / Кт 10 (списание со склада через целевое финансирование).

Вызывается ТОЛЬКО после того, как совет авторизовал проект (status = AUTHORIZED через callback onmktwoauth). Backend проходит циклом по неисполненным позициям, вызывая execwroff(proposal_hash, item_index) per item — это снимает ограничение на максимальный размер протокола.

Исполнение последней позиции (все items[i].executed == true) — терминал жизненного цикла: запись проекта стирается из RAM, история — в журнале действий и решении совета.

Подписанный советом protocol уже лежит в proposal.protocol (положен callback'ом onmktwoauth), отдельно его передавать не нужно.

◆ expireorder()

void marketplace::expireorder ( eosio::name  coopname,
checksum256  order_hash 
)

Backend закрывает Order по таймауту цикла отсечки (Story 4.3). Per-Order: o.mkt.unlock + статус active → cancelled. Backend вычисляет threshold по batch'у вне контракта; для каждого истёкшего Order'а вызывается отдельный expireorder.

Backend закрывает один Order по таймауту цикла отсечки заявок (Story 4.3, p.mkt.supply).

Вызывается бэкендом после расчёта по batch'у: если за время цикла Offer'а threshold не достигнут, бэкенд проходит циклом по всем active Order'ам этого batch'а и для каждого вызывает expireorder. Контракт не знает про threshold — это вычисление backend'а; on-chain — только закрытие конкретного Order'а с возвратом резерва.

Per-Order: o.mkt.unlock на total_cost (TRANSFER w.mkt.order → w.mkt.member — возврат резерва на членский «Стола заказов» заказчика) + статус active → cancelled.

Guards:

  • Order существует и в статусе active (после акцепта поставщика expireorder не применим — поставщик уже взял обязательство; такие Order'ы должны идти через signiss2 обычным порядком либо через отдельный механизм просрочки доставки).
  • require_auth(coopname) — backend от имени кооператива.

◆ markdown()

void marketplace::markdown ( eosio::name  coopname,
checksum256  order_hash,
eosio::asset  amount 
)

Списание уценки по заказу из остатка кооператива (requirement 76). Разница между стоимостью прибытия выданного и фактической суммой выдачи выбывает со счёта 10 в прочие расходы: o.mkt.loss (NONE, Дт 91 / Кт 10). Вместе с o.mkt.consum даёт выбытие по полной стоимости прибытия — на складе ничего не зависает. Вызывает backend после финализации выдачи (сумму считает по выданным позициям). Погашение накопленного на 91 (Дт 86 / Кт 91) — будущий процесс по образцу списания скоропорта.

Списание уценки по заказу из остатка кооператива (requirement 76, вопрос 4, p.mkt.supply).

Имущество остатка оприходовано на счёт 10 по цене прибытия, а выдано пайщику могло быть с уценкой. signiss2 (o.mkt.consum) закрывает Кт 10 только на фактическую сумму выдачи — без этого действия разница зависала бы на складе, которого физически уже нет. Здесь разница выбывает в прочие расходы:

  • o.mkt.loss (NONE, Дт 91 / Кт 10) — кошельки не двигаются, чисто бухгалтерская проводка на сумму уценки.

Вместе consum + loss дают выбытие по полной стоимости прибытия. Накопленный расход на счёте 91 погашается ПОЗЖЕ отдельным процессом (Дт 86 / Кт 91) по образцу списания скоропорта через решение совета — этот шаг зафиксирован и пока не реализован.

Вызывает backend сразу после финализации выдачи: сумму уценки он считает точно по выданным складским позициям (цены прибытия у позиций одного заказа могут отличаться — FIFO-резерв), контракту эта аналитика недоступна.

Guards:

  • заказ существует и сделан из остатка кооператива (offerer == coopname);
  • выдача завершена (status == received) — основание: двухподписный акт;
  • amount > 0 в валюте кооператива;
  • идемпотентность: уценка по заказу ещё не списывалась (markdown_cost == 0).

◆ migrate()

void marketplace::migrate ( )

Заглушка миграции — donor-таблиц нет, мигрировать нечего. Оставлена для совместимости с CMake-build и прежним ABI.

◆ onmktwoauth()

void marketplace::onmktwoauth ( eosio::name  coopname,
checksum256  hash,
document2  authorization 
)

Callback от soviet::exec после авторизации Протокола совета (registry 1105) председателем (Story 8.4). PROPOSED → AUTHORIZED; сохраняется authorization в wroffprops.protocol. Цикл per-item списания запускает backend через execwroff после получения этой дельты.

Callback от soviet::exec после авторизации Протокола совета о списании скоропорта (registry 1105) председателем (Story 8.4, p.mkt.wroff).

Авторизация: контракт _soviet (require_auth(_soviet)); сигнатура соответствует authorize_action_effect в soviet — (coopname, hash, authorization).

Соглашение о сигнатуре — (coopname, hash, authorization) — задано в soviet::createagenda::authorize_action_effect. Контракт soviet зовёт marketplace::onmktwoauth от своего имени, поэтому единственно допустимая авторизация — _soviet.

Эффект:

  • Находит wroffprops по proposal_hash == hash.
  • Проверяет, что текущий статус == PROPOSED (запрет повторного callback'а).
  • Записывает подписанный советом protocol2 в proposal.protocol.
  • Переводит статус PROPOSED → AUTHORIZED.

Дальнейший шаг — backend через дельту/мониторинг видит AUTHORIZED и проходит циклом execwroff per-item (см. execwroff.cpp).

◆ onmktwodecl()

void marketplace::onmktwodecl ( eosio::name  coopname,
checksum256  hash,
std::string  reason 
)

Callback от soviet::cancelexprd (или от любого decline-эффекта в soviet) — повестка отклонена или просрочена (Story 8.4). PROPOSED → REJECTED; reason сохраняется в wroffprops.reject_reason. Без ledger2-движений.

Callback от soviet после отказа в Протоколе совета о списании скоропорта или истечения срока повестки (Story 8.4, p.mkt.wroff).

Сигнатура (coopname, hash, reason) соответствует DECLINE_CALLBACK_SIGNATURE в lib/core/soviet/soviet.hpp:19. Авторизация: _soviet.

Сигнатура (coopname, hash, reason) соответствует DECLINE_CALLBACK_SIGNATURE (см. cpp/lib/core/soviet/soviet.hpp:19). Контракт soviet вызывает action из cancelexprd (повестка просрочена) или вручную через decline* actions от своего имени, поэтому единственно допустимая авторизация — _soviet.

Эффект:

  • Находит wroffprops по proposal_hash == hash, проверяет статус PROPOSED.
  • Терминал жизненного цикла: запись проекта стирается из RAM; причина отказа остаётся в журнале действий (аргумент reason) и решении совета.

Без ledger2-движений.

◆ payconfirm()

void marketplace::payconfirm ( eosio::name  coopname,
checksum256  outcome_hash 
)

Callback от gateway::outcomplete — кассир подтвердил банковский перевод поставщику (E11 техдолг 598-16, Locked Decision L12). Здесь применяется o.mkt.payout (Дт 86 / Кт 51) и payout_status переходит PENDING → COMPLETED. Авторизация: _gateway. outcome_hash совпадает с order.hash (так задано при payout).

Callback от gateway о фактическом подтверждении исходящей выплаты поставщику (E11 техдолг 598-16, Locked Decision L12, p.mkt.supply).

Inline-action отправляется контрактом gateway из gateway::outcomplete после того, как кассир в админке подтвердил реальный банковский перевод. Здесь — единственное место, где применяется бухгалтерская проводка выплаты:

  • Ledger2::apply(o.mkt.payout, fact_cost, …, hash=order.hash) — Дт 86 / Кт 51.

Сумма — o.fact_cost (фактически принятое после отбраковки на приёмке), а не исходный o.total_cost: проводка должна совпадать с приходованием имущества (Кт 86 = fact_cost из signchair) и с реальной суммой банковского перевода.

outcome_hash приходит из gateway и равен order.hash (так его задал marketplace::payout). Поиск Order'а — по индексу byhash.

Guards:

  • require_auth(_gateway) — callback легитимен только от gateway-контракта.
  • Order найден по outcome_hash.
  • payout_status == PENDING — на NONE/COMPLETED/DECLINED callback не ждём.

◆ paydecline()

void marketplace::paydecline ( eosio::name  coopname,
checksum256  outcome_hash,
std::string  reason 
)

Callback от gateway::outdecline — кассир отметил, что банковский перевод не состоялся (E11 техдолг 598-16, Locked Decision L12). Ledger2-операция НЕ применяется; обязательство Кт 86 остаётся открытым. payout_status PENDING → DECLINED; payout_decline_reason сохраняется. Авторизация: _gateway.

Callback от gateway об отклонении исходящей выплаты поставщику (E11 техдолг 598-16, Locked Decision L12, p.mkt.supply).

Inline-action отправляется контрактом gateway из gateway::outdecline — кассир отметил, что банковский перевод не прошёл (нет реквизитов, платёж отменён банком, ошибка ввода). Бухгалтерия не двигается — обязательство Кт 86 перед поставщиком остаётся открытым. Backend может повторно вызвать marketplace::payout после исправления реквизитов; gateway-запись по этому outcome_hash уже стёрта на outdecline, поэтому повторная инициация проходит штатно (см. payout-гард payout_status ∈ { NONE, DECLINED }).

Guards:

  • require_auth(_gateway) — callback легитимен только от gateway-контракта.
  • Order найден по outcome_hash.
  • payout_status == PENDING.

◆ payout()

void marketplace::payout ( eosio::name  coopname,
checksum256  order_hash 
)

Инициация исходящей выплаты поставщику через контракт gateway по одному Order'у (E11 техдолг 598-16, Locked Decision L12). Per-Order: inline-вызов gateway::createoutpay с callback'ами на payconfirm / paydecline. Ledger2-операция o.mkt.payout (Дт 86 / Кт 51) применяется НЕ здесь, а в callback'е payconfirm после действия кассира. Статус Order'а не меняется; защита от двойного запроса — через order.payout_status (NONE/DECLINED → PENDING).

Инициация исходящей выплаты поставщику по одному Order'у через gateway (E11 техдолг 598-16, Locked Decision L12, p.mkt.supply).

Backend дёргает это действие, когда кассир в админке отметил готовность проводить выплату поставщику. Действие НЕ применяет ledger2 — оно лишь inline-вызовом регистрирует в gateway::outcomes запись типа «исходящий платёж» со статусом pending и привязанным callback'ом на marketplace. Сам Дт 86 / Кт 51 произойдёт уже в callback'е payconfirm после фактического банковского перевода (gateway::outcomplete вызывает кассир через свой стол), либо отменится в paydecline (gateway::outdecline).

Inline-вызов: gateway::createoutpay с callback_contract = _marketplace, confirm_callback = "payconfirm"_n, decline_callback = "paydecline"_n, outcome_hash = order.hash (уникальность гарантирована индексом orders).

Сумма выплаты — o.fact_cost (фактически принятое после приёмки), а НЕ o.total_cost (исходный заказ): при отбраковке части поставки на приёмке (signchair снизил факт) кооператив должен поставщику ровно принятое. fact_cost зафиксирован на приёмке (статус-гард ниже гарантирует, что приёмка уже прошла), совпадает с приходованием имущества Кт 86 и с суммой платежа в реестре платежей кооператива.

Status Order'а не меняется (выплата может идти параллельно шагам выдачи). payout_status переходит NONE/DECLINED → PENDING; declined-кейс — повторная попытка после исправления реквизитов (gateway-запись была стёрта на outdecline).

Guards:

  • Order существует и приёмка завершена (статус ∈ accepted_to_coop / ready_to_receive / received).
  • payout_status ∈ { NONE, DECLINED } — нельзя инициировать выплату поверх pending или completed.

◆ propwroff()

void marketplace::propwroff ( eosio::name  coopname,
eosio::name  proposed_by,
checksum256  proposal_hash,
std::vector< wroff_item >  items,
document2  statement,
std::string  meta 
)

Backend выносит проект списания на повестку совета (Story 8.1). Без ledger2-операций — только создание proposal с N позициями (статус proposed) и тем же inline-вызовом ставит повестку: soviet::createagenda от permission_level{_marketplace, active} с callback_contract=marketplace, confirm_callback=onmktwoauth, decline_callback=onmktwodecl, type=mktwroff, hash=proposal_hash, statement (Заявление 1106). Проект может подаваться председателем (за подписью) либо автоматически — мост повестки целиком на контракте, без участия backend.

Выносит проект списания скоропорта на повестку совета (Story 8.1, p.mkt.wroff).

Без ledger2-операций. Создаётся writeoff_proposal в статусе proposed; total_amount = Σ items.amount; все items создаются с executed=false. Тем же action'ом контракт ставит повестку: inline-вызов soviet::createagenda от permission_level{_marketplace, active} (marketplace в contracts_whitelist) с type=mktwroff, hash=proposal_hash, callback_contract=_marketplace, confirm_callback=onmktwoauth, decline_callback=onmktwodecl, statement (подписанное Заявление 1106). Мост повестки целиком на контракте — backend не подписывает createagenda отдельно (кооператив не в whitelist). Списание выполняется per-item через execwroff только после callback'а onmktwoauth (status proposed → authorized).

Guards:

  • actor coopname (require_auth).
  • items.size() > 0.
  • Все items.amount > 0 в _root_govern_symbol.
  • Все items.braname существуют в branches[coopname].
  • proposal_hash уникален.

◆ rejretrem()

void marketplace::rejretrem ( eosio::name  coopname,
eosio::name  signer,
eosio::name  braname,
checksum256  request_hash,
std::string  reason 
)

Председатель удалённо отказывает (Story 7.2). Авторизация: подписант ∈ branches[braname].

Председатель удалённо отказывает в гарантийном возврате (Story 7.2, p.mkt.return).

Финальное решение, без ledger2-операций — терминал жизненного цикла: запись заявления стирается из RAM, причина отказа остаётся в журнале действий (аргумент reason).

Guards:

  • Указанный КУ (braname) — участок выдачи исходного заказа.
  • Подписант (signer) авторизован для указанного КУ (braname).
  • return_request.status == pending_review.
  • reason.size() > 0.

◆ rejretrn()

void marketplace::rejretrn ( eosio::name  coopname,
eosio::name  signer,
eosio::name  braname,
checksum256  request_hash,
std::string  reason 
)

Председатель отказывает на очном осмотре (Story 7.3). Авторизация: подписант ∈ branches[braname].

Председатель отказывает в гарантийном возврате на очном осмотре (Story 7.3, p.mkt.return).

Финальное решение, без ledger2-операций — терминал жизненного цикла: запись заявления стирается из RAM, причина отказа остаётся в журнале действий (аргумент reason).

Guards:

  • Указанный КУ (braname) — участок выдачи исходного заказа.
  • Подписант (signer) авторизован для указанного КУ (braname).
  • return_request.status == approved_for_visit.
  • reason.size() > 0.

◆ setfee()

void marketplace::setfee ( eosio::name  coopname,
uint64_t  membership_fee_percent 
)

Установка единой ставки членского взноса «Стола заказов» (requirement b6). Одна ставка на весь кооператив (HUNDR_PERCENTS = 100%); задаёт администратор. Применяется к новым заказам; в созданных заказах взнос зафиксирован полем Order.membership_fee.

Установка единой ставки членского взноса «Стола заказов» (requirement b6 «Экономика КУ»).

Ставка одна на весь кооператив — не per-КУ и не per-категория: один и тот же членский взнос вне зависимости от того, на какой кооперативный участок заказ (против спекуляций и конкуренции между участками — решение владельца 2026-06-10). Применяется к заказам, созданным ПОСЛЕ установки: в уже созданных заказах взнос зафиксирован полем Order.membership_fee.

Проценты — в долях HUNDR_PERCENTS (1000000 = 100%). Ставка 0 отключает начисление взноса.

Заметки
Авторизация требуется от аккаунта: coopname (устанавливает администратор со стола администратора).

◆ signchair()

void marketplace::signchair ( eosio::name  coopname,
eosio::name  signer,
checksum256  order_hash,
eosio::asset  actual_quantity,
eosio::asset  actual_unit_price,
document2  act 
)

Председатель приёмного КУ ставит закрывающую подпись на АПП приёмки одного Order'а (Story 5.3/5.4). Per-Order: только o.mkt.purch (Дт 10 / Кт 86). Выплата поставщику (o.mkt.payout) — отдельным lazy action'ом payout после подтверждения кассиром фактического банковского перевода (E11 техдолг 598-16, Locked Decision L12). Авторизация подписи: председатель / trustee / trusted ∈ branches[o.accept_braname]. Backend проходит циклом по orders батча с одинаковым act.

Председатель приёмного КУ ставит закрывающую подпись на АПП приёмки по одному Order'у (Story 5.3/5.4, p.mkt.supply).

Per-Order — только бухгалтерская приёмка имущества:

  • Ledger2::apply(o.mkt.purch, fact_cost, …, hash=order.hash) — Дт 10 / Кт 86.

Факт приёмки (кол-во и цена за единицу) корректируется оператором при открытии приёмки и зашивается в акт, который утверждает поставщик: привезли меньше / другого качества → принимаем со скидкой. Кооператив приходует поставщику итоговую fact_cost = actual_quantity × actual_unit_price, а не исходную o.total_cost. Резерва пайщика на приёмке нет, поэтому веток возврата/доплаты (как в signiss2) здесь не требуется — это просто итоговая стоимость к получению поставщиком.

Имущество приходуется на склад приёмного КУ (accept_braname); у кооператива возникает обязательство Кт 86 перед поставщиком. Фактическая выплата деньгами (Дт 86 / Кт 51) — отдельным lazy action'ом marketplace::payout после подтверждения кассиром реального банковского перевода (Locked Decision L12, E11 техдолг 598-16).

Status: supply_prepared → accepted_to_coop. acceptance_act_signchair сохраняется. payout_done не выставляется — это атрибут payout-действия.

Guards:

  • Order существует и в статусе supply_prepared.
  • Подписант (signer) авторизован для приёмного КУ — председатель, trustee либо доверенное лицо в branches[accept_braname].trusted[].
  • На акте есть подписи поставщика и подписанта приёмки.
  • actual_quantity > 0; actual_unit_price > 0 и в валюте кооператива.

◆ signiss1()

void marketplace::signiss1 ( eosio::name  coopname,
eosio::name  signer,
checksum256  order_hash,
document2  act 
)

Председатель КУ выдачи открывает выдачу первой подписью АПП-выдачи (Story 6.1). Без ledger2-операций — статус ready_to_receive. Авторизация: подписант ∈ branches[o.delivery_braname].

Председатель КУ выдачи открывает выдачу первой подписью АПП-выдачи (Story 6.1, signiss1).

Без ledger2-операций. Per-Order: статус accepted_to_coop → ready_to_receive; issue_act_signiss1 сохраняется; current_warehouse_braname обновляется на delivery_braname (фиксация факта логистической передачи имущества на склад выдачи — промежуточные перемещения по заготовочным КУ контрактом не подписываются, точка хранения переходит «скачком» в этот момент). Нотификация заказчику — post-effect в backend через ParserClient.

Guards:

  • Order существует и в статусе accepted_to_coop.
  • Подписант (signer) авторизован для КУ выдачи (o.delivery_braname): председатель / trustee / trusted в branches[delivery_braname].
  • verify_document_or_fail(act, {signer}).
  • Idempotency: is_empty_document(issue_act_signiss1).

◆ signiss2()

void marketplace::signiss2 ( eosio::name  coopname,
eosio::name  orderer,
checksum256  order_hash,
eosio::asset  actual_quantity,
eosio::asset  actual_unit_price,
eosio::name  delivery_signer,
document2  act 
)

Заказчик ставит финальную подпись АПП-выдачи (Story 6.3). Per-Order с поддержкой actual_quantity ≠ ordered (Story 6.2). Atomic: [o.mkt.unlock на разницу если actual<ordered | o.mkt.lock на разницу если actual>ordered].

Заказчик закрывающей подписью АПП-выдачи получает имущество (Story 6.3, signiss2).

  • o.mkt.consum. Подпись акта: orderer + любой авторизованный из branches[o.delivery_braname].

Per-Order атомарная транзакция с поддержкой actual_quantity ≠ ordered (Story 6.2):

1) actual == ordered: Ledger2::apply(o.mkt.consum, fact_cost) — BURN w.mkt.order, Дт 86 / Кт 10 (сжигание резерва заказа и выбытие имущества со склада через целевое финансирование).

2) actual < ordered (выдано меньше — остаток резерва возвращается): Ledger2::apply(o.mkt.unlock, ordered_cost - fact_cost) — TRANSFER w.mkt.order → w.mkt.member на разницу (остаток резерва на членский «Стола заказов»). затем — consum на fact_cost.

3) actual > ordered (доплата по факту — ТОЛЬКО с членского, паевой не трогаем): diff = fact_cost - ordered_cost проверка достаточности diff на w.mkt.member (членский «Стола заказов»). o.mkt.lockm(diff) — TRANSFER w.mkt.member → w.mkt.order (без проводки) — добор резерва на этот же Order с членского программы. Затем — consum на fact_cost. Недостающее на членском пайщик переводит ЗАРАНЕЕ отдельным Заявлением о конвертации (action convert, o.mkt.conv) — авто-конвертации паевого на выдаче нет НИГДЕ; при нехватке членского транзакция фейлится.

Status: ready_to_receive → received. actual_quantity, fact_cost, issue_act_signiss2, warranty_until заполняются.

Guards:

  • actor == order.orderer; Order в ready_to_receive.
  • Подписант со стороны кооператива (delivery_signer) авторизован для КУ выдачи (o.delivery_braname).
  • verify_document_or_fail(act, {delivery_signer, orderer}).
  • actual_quantity > 0; actual_unit_price > 0 и в валюте кооператива. fact_cost = actual_quantity × actual_unit_price (цена скорректирована оператором при открытии выдачи, заказчик факт не редактирует).
  • При actual > ordered и нехватке средств — транзакция фейлится с человеческим сообщением, которое UI показывает напрямую.
  • Idempotency: is_empty_document(issue_act_signiss2).

◆ signsupp()

void marketplace::signsupp ( eosio::name  coopname,
eosio::name  offerer,
checksum256  order_hash,
eosio::name  accept_braname,
document2  act 
)

Поставщик первой подписью на АПП приёмки фиксирует партию по одному Order'у (Story 5.3/5.4). Без ledger2-операций — статус accepted → supply_prepared. Параметр accept_braname указывает приёмный КУ; запись в Order. Подпись валидируется как verify_document_or_fail(act, {offerer}). Backend проходит циклом по orders батча с одинаковым act.

Поставщик первой подписью на АПП приёмки фиксирует партию по одному Order'у (Story 5.3/5.4, p.mkt.supply).

Без ledger2-операций (имущество физически на складе, но юридически не оприходовано до signchair). Per-Order: статус accepted → supply_prepared, параметр accept_braname указывает приёмный КУ — куда поставщик сдаёт партию. Документ acceptance_act_signsupp сохраняется (копия per Order — допустимо для on-chain).

Backend проходит циклом по orders соответствующего batch'а с одним и тем же актом, вызывая signsupp per Order — это позволяет масштабировать batch на любой размер без риска превысить лимит транзакции Antelope.

Guards:

  • Order существует и в статусе accepted.
  • actor == order.offerer.
  • accept_braname существует в branches[coopname].
  • verify_document_or_fail(act, {offerer}) — поставщик подписал.
  • Idempotency: повторный вызов запрещён (is_empty_document(acceptance_act_signsupp)).

◆ stockorder()

void marketplace::stockorder ( eosio::name  coopname,
eosio::name  orderer,
checksum256  order_hash,
checksum256  offer_hash,
eosio::name  delivery_braname,
eosio::asset  quantity,
eosio::asset  unit_price,
eosio::asset  package_size,
uint32_t  warranty_period_secs,
checksum256  batch_hash 
)

Заказ из обезличенного остатка склада кооператива (requirement 76). Продавец — сам кооператив (offerer == coopname), имущество уже на счёте 10 после ранее закрытых приёмок, поэтому Order создаётся сразу в acceptcoop и идёт только через выдачу signiss1/signiss2. Этапы поставки и выплата поставщику для такого заказа не существуют.

Заказ имущества из обезличенного остатка склада кооператива (requirement 76 «Склад кооператива на КУ», p.mkt.supply).

Заказ из остатка ВСЕГДА фондируется из членского кошелька «Стола заказов» пайщика начисто (без паевого): o.mkt.lockm (тело, w.mkt.member → w.mkt.order)

  • o.mkt.lockmf (взнос, w.mkt.member → w.mkt.fee). Паевой пополняет членский кошелёк заранее отдельным действием convert (Заявление о конвертации). При замене непоставленного на свободный остаток высвобожденные отменой средства уже лежат в w.mkt.member — заказ создаётся без конвертации и доплаты.

Продавец — сам кооператив: имущество уже принято на склад по ранее закрытым АПП приёмки (числится на счёте 10 после o.mkt.purch) и перепредлагается пайщикам после недовыдачи / отказа от излишка. Поэтому Order создаётся сразу в статусе acceptcoop — этапы поставки (acceptorder / signsupp / signchair) и выплата поставщику (payout) для него не существуют: дальше работает только штатная выдача signiss1 → signiss2 (o.mkt.consum, Дт 86 / Кт 10).

Маркер заказа из остатка — order.offerer == coopname (кооператив-продавец). По нему cancelorder разрешает отмену в acceptcoop (откат оператора до своей подписи), а payout отклоняет попытку выплаты.

Фондируется ВСЕГДА из членского кошелька «Стола заказов» пайщика начисто (без паевого), две ledger2-операции (оба кошелька на 86, без проводок):

  • o.mkt.lockm (TRANSFER w.mkt.member → w.mkt.order на total_cost) — тело;
  • o.mkt.lockmf (TRANSFER w.mkt.member → w.mkt.fee на взнос) — взнос. Паевой взнос пополняет членский кошелёк ЗАРАНЕЕ отдельным действием convert (Заявление о конвертации). При замене непоставленного на свободный остаток высвобожденные отменой средства уже лежат в w.mkt.member — заказ создаётся без конвертации и без доплаты с паевого.

Guards (зеркало createorder + специфика остатка):

  • quantity > 0; unit_price > 0 в _root_govern_symbol (цена публикации остатка: цена прибытия либо уценка — requirement 76, решение 12).
  • Order с таким hash ещё не создан (idempotency).
  • Заказчик — активный пайщик кооператива.
  • delivery_braname существует: КУ, на складе которого лежит остаток; он же — КУ выдачи (accept_braname и current_warehouse_braname равны ему: имущество уже на месте, логистики нет).
  • w.mkt.member.available заказчика >= total_cost + взнос (членских средств должно хватать; недостающее конвертируется заранее через convert).

◆ submretrn()

void marketplace::submretrn ( eosio::name  coopname,
eosio::name  orderer,
checksum256  request_hash,
checksum256  original_order_hash,
eosio::asset  actual_quantity,
std::string  reason_text,
std::vector< checksum256 >  photos,
document2  statement 
)

Пайщик подаёт заявление на гарантийный возврат (Story 7.1).

Пайщик подаёт заявление на гарантийный возврат (Story 7.1, p.mkt.return).

Без ledger2-операций. Создаётся return_request в pending_review; order.return_request_id ставится для двусторонней связи. Привязка к конкретному КУ не сохраняется — каждое последующее действие (aprretrem/rejretrem/accretrn/rejretrn) принимает braname параметром и валидирует его через Branch::is_user_authorized.

Guards (из p.mkt.return.standard.yaml):

  • actor == original_order.orderer.
  • original_order.status == received.
  • original_order.warranty_until > now() (гарантийный срок не истёк, если warranty_period_secs > 0; иначе возврат запрещён).
  • photos.size() > 0 (фото приложены).
  • actual_quantity > 0 && <= original_order.actual_quantity.
  • request_hash уникален.
  • Active возврат на этот order ещё не открыт (idempotency через order.return_request_id == 0).

Объявления и описания членов классов находятся в файлах: