COOPENOMICS  v1
Кооперативная Экономика
marketplace.hpp
См. документацию.
1#pragma once
2
3#include <functional>
4#include <optional>
5#include <string>
6
7#include <eosio/crypto.hpp>
8#include <eosio/eosio.hpp>
9
10#include "../../consts.hpp"
11#include "../../domain/document_core.hpp"
12#include "../../domain/table_ledger2_userwallets.hpp"
13#include "../../domain/table_marketplace_fee_config.hpp"
14#include "../ledger2/ledger2.hpp"
15#include "../branch/branch.hpp"
16#include "memo.hpp"
17#include "../../domain/table_marketplace_orders.hpp"
18#include "../../domain/table_marketplace_return_requests.hpp"
19#include "../../domain/table_marketplace_writeoff_proposals.hpp"
20
32namespace Marketplace {
33
34using namespace eosio;
35
36// ── Количество как fixed-point asset (Эпик 17, L14) ─────────────────────
37//
38// quantity/actual_quantity — asset с символом единицы измерения (KG/LTR/PCS);
39// дробность веса/объёма выражается младшими единицами (0.500 KG = 500 г).
40// Штука (PCS, precision 0) неделима на уровне типа. Цена задаётся за одну
41// базовую единицу (кг/литр/штуку) money-asset'ом _root_govern_symbol.
42
43inline bool is_valid_unit_symbol(const eosio::symbol& sym) {
44 return sym == _unit_kg || sym == _unit_liter || sym == _unit_piece;
45}
46
48inline void check_quantity(const eosio::asset& quantity) {
49 eosio::check(quantity.is_valid() && is_valid_unit_symbol(quantity.symbol),
50 "Недопустимая единица измерения количества");
51 eosio::check(quantity.amount > 0, "Количество должно быть больше нуля");
52}
53
60inline void check_packaging(const eosio::asset& quantity, const eosio::asset& package_size) {
61 if (package_size.amount == 0) return; // отпуск по мере
62 eosio::check(package_size.is_valid() && package_size.symbol == quantity.symbol,
63 "Единица упаковки не совпадает с единицей количества");
64 eosio::check(package_size.amount > 0, "Размер упаковки должен быть больше нуля");
65 eosio::check(quantity.amount % package_size.amount == 0,
66 "Количество должно быть кратно размеру упаковки");
67}
68
77inline eosio::asset calc_cost(const eosio::asset& quantity, const eosio::asset& unit_price,
78 const eosio::asset& package_size = eosio::asset(0, _unit_piece)) {
79 if (package_size.amount > 0) {
80 const int64_t packages = quantity.amount / package_size.amount; // точно (кратность — check_packaging)
81 const uint128_t total = static_cast<uint128_t>(packages) *
82 static_cast<uint128_t>(unit_price.amount);
83 return eosio::asset(static_cast<int64_t>(total), unit_price.symbol);
84 }
85 int64_t scale = 1;
86 for (uint8_t i = 0; i < quantity.symbol.precision(); ++i) scale *= 10;
87 const uint128_t num = static_cast<uint128_t>(quantity.amount) *
88 static_cast<uint128_t>(unit_price.amount);
89 const int64_t amount = static_cast<int64_t>((num + static_cast<uint128_t>(scale) / 2) / scale);
90 return eosio::asset(amount, unit_price.symbol);
91}
92
98inline eosio::asset pro_rata(const eosio::asset& total, int64_t part, int64_t whole) {
99 eosio::check(whole > 0, "Некорректная база для расчёта пропорциональной доли");
100 const uint128_t num = static_cast<uint128_t>(total.amount) * static_cast<uint128_t>(part);
101 const int64_t amount =
102 static_cast<int64_t>((num + static_cast<uint128_t>(whole) / 2) / static_cast<uint128_t>(whole));
103 return eosio::asset(amount, total.symbol);
104}
105
106// ── Orders ──────────────────────────────────────────────────────────────
107
108inline std::optional<order> get_order_by_hash(eosio::name coopname, const checksum256& order_hash) {
109 orders_index orders(_marketplace, coopname.value);
110 auto idx = orders.get_index<"byhash"_n>();
111 auto it = idx.find(order_hash);
112 if (it == idx.end()) return std::nullopt;
113 return *it;
114}
115
116inline order get_order_by_hash_or_fail(eosio::name coopname, const checksum256& order_hash,
117 const std::string& msg = "Заказ не найден по хэшу") {
118 auto o = get_order_by_hash(coopname, order_hash);
119 eosio::check(o.has_value(), msg);
120 return *o;
121}
122
123inline void update_order(eosio::name coopname, uint64_t order_id, const std::function<void(order&)>& fn) {
124 orders_index orders(_marketplace, coopname.value);
125 auto it = orders.find(order_id);
126 eosio::check(it != orders.end(), "Заказ не найден по id");
127 orders.modify(it, _marketplace, [&](auto& o) { fn(o); });
128}
129
130// Терминал жизненного цикла: запись стирается из RAM, история процесса
131// остаётся в журнале действий (blockchain_actions парсера).
132inline void erase_order(eosio::name coopname, uint64_t order_id) {
133 orders_index orders(_marketplace, coopname.value);
134 auto it = orders.find(order_id);
135 eosio::check(it != orders.end(), "Заказ не найден по id");
136 orders.erase(it);
137}
138
139// ── Return requests ─────────────────────────────────────────────────────
140
141inline std::optional<return_request> get_return_request_by_hash(eosio::name coopname,
142 const checksum256& request_hash) {
143 return_requests_index requests(_marketplace, coopname.value);
144 auto idx = requests.get_index<"byhash"_n>();
145 auto it = idx.find(request_hash);
146 if (it == idx.end()) return std::nullopt;
147 return *it;
148}
149
151 const checksum256& request_hash,
152 const std::string& msg = "Заявление на возврат не найдено по хэшу") {
153 auto r = get_return_request_by_hash(coopname, request_hash);
154 eosio::check(r.has_value(), msg);
155 return *r;
156}
157
158inline void update_return_request(eosio::name coopname, uint64_t request_id,
159 const std::function<void(return_request&)>& fn) {
160 return_requests_index requests(_marketplace, coopname.value);
161 auto it = requests.find(request_id);
162 eosio::check(it != requests.end(), "Заявление на возврат не найдено по id");
163 requests.modify(it, _marketplace, [&](auto& r) { fn(r); });
164}
165
166// Терминал жизненного цикла: запись стирается из RAM (история — в журнале
167// действий). order.return_request_id НЕ сбрасывается — повторный возврат по
168// тому же заказу не открывается.
169inline void erase_return_request(eosio::name coopname, uint64_t request_id) {
170 return_requests_index requests(_marketplace, coopname.value);
171 auto it = requests.find(request_id);
172 eosio::check(it != requests.end(), "Заявление на возврат не найдено по id");
173 requests.erase(it);
174}
175
180inline void check_return_request_branch(eosio::name coopname, const return_request& r,
181 eosio::name braname) {
183 eosio::check(o.delivery_braname == braname,
184 "Заявление на возврат рассматривает кооперативный участок выдачи заказа");
185}
186
187// ── Writeoff proposals ──────────────────────────────────────────────────
188
189inline std::optional<writeoff_proposal> get_writeoff_proposal_by_hash(eosio::name coopname,
190 const checksum256& proposal_hash) {
191 writeoff_proposals_index proposals(_marketplace, coopname.value);
192 auto idx = proposals.get_index<"byhash"_n>();
193 auto it = idx.find(proposal_hash);
194 if (it == idx.end()) return std::nullopt;
195 return *it;
196}
197
199 const checksum256& proposal_hash,
200 const std::string& msg = "Проект списания не найден по хэшу") {
201 auto p = get_writeoff_proposal_by_hash(coopname, proposal_hash);
202 eosio::check(p.has_value(), msg);
203 return *p;
204}
205
206inline void update_writeoff_proposal(eosio::name coopname, uint64_t proposal_id,
207 const std::function<void(writeoff_proposal&)>& fn) {
208 writeoff_proposals_index proposals(_marketplace, coopname.value);
209 auto it = proposals.find(proposal_id);
210 eosio::check(it != proposals.end(), "Проект списания не найден по id");
211 proposals.modify(it, _marketplace, [&](auto& p) { fn(p); });
212}
213
214// Терминал жизненного цикла: запись стирается из RAM, история процесса —
215// в журнале действий (blockchain_actions парсера).
216inline void erase_writeoff_proposal(eosio::name coopname, uint64_t proposal_id) {
217 writeoff_proposals_index proposals(_marketplace, coopname.value);
218 auto it = proposals.find(proposal_id);
219 eosio::check(it != proposals.end(), "Проект списания не найден по id");
220 proposals.erase(it);
221}
222
223// ── Cross-contract read: ledger2 wallet/userwallet balances ─────────────
224//
225// Используется в createorder для guard'а Locked Decision L6 (без отрицательного
226// баланса) — проверка достаточности средств заказчика на паевом кошельке
227// перед вызовом o.mkt.lock.
228//
229// ВАЖНО: контракт marketplace не вызывает ledger2::walletop напрямую, а только
230// читает state (RAM-таблицы wallets2 / userwallets через cross-contract scope).
231// Все мутации идут через `Ledger2::apply` (см. lib/core/ledger2/ledger2.hpp).
232
234 eosio::asset available = eosio::asset(0, _root_govern_symbol);
235 eosio::asset blocked = eosio::asset(0, _root_govern_symbol);
236 bool exists = false;
237};
238
240 eosio::name wallet_id,
241 eosio::name username) {
242 // userwallets_index — глобальный typedef в lib/domain/table_ledger2_userwallets.hpp.
243 // Для cross-contract read берём scope = coopname.value, code = _ledger2.
244 userwallets_index user_wallets(_ledger2, coopname.value);
245 auto idx = user_wallets.get_index<"byuserwallet"_n>();
246 auto it = idx.find(combine_ids(wallet_id.value, username.value));
247 if (it == idx.end()) {
248 return UserWalletAvailable{};
249 }
250 return UserWalletAvailable{ it->available, it->blocked, true };
251}
252
253// ── Членский взнос «Стола заказов» (requirement b6 «Экономика КУ») ──────
254
259constexpr uint64_t DEFAULT_MEMBERSHIP_FEE_PERCENT = 300000;
260
263inline uint64_t get_membership_fee_percent(eosio::name coopname) {
264 mkt_config_singleton cfg(_marketplace, coopname.value);
265 return cfg.exists() ? cfg.get().membership_fee_percent : DEFAULT_MEMBERSHIP_FEE_PERCENT;
266}
267
269inline eosio::asset calc_membership_fee(const eosio::asset& base, uint64_t fee_percent) {
270 const int64_t amount = static_cast<int64_t>(
271 static_cast<uint128_t>(base.amount) * fee_percent / HUNDR_PERCENTS);
272 return eosio::asset(amount, _root_govern_symbol);
273}
274
276inline eosio::asset get_order_membership_fee(const order& o) {
277 return o.membership_fee;
278}
279
283inline void refund_membership_fee_if_any(eosio::name coopname, const order& o) {
284 const eosio::asset fee = get_order_membership_fee(o);
285 if (fee.amount <= 0) return;
289 fee, o.orderer, o.hash,
291}
292
296inline void refund_order_full(eosio::name coopname, const order& o) {
300 o.total_cost, o.orderer, o.hash,
302 refund_membership_fee_if_any(coopname, o);
303}
304
307inline constexpr uint64_t REFUSAL_PENALTY_PERCENT = 50;
308
310inline eosio::asset refusal_penalty_share(const eosio::asset& base) {
312}
313
321inline void retain_refusal_penalty(eosio::name coopname, const order& o) {
322 // ── Тело заказа 50/50 ──
323 const eosio::asset penalty_body = refusal_penalty_share(o.total_cost);
324 const eosio::asset refund_body = o.total_cost - penalty_body;
325
326 if (refund_body.amount > 0) {
330 refund_body, o.orderer, o.hash,
332 }
333 if (penalty_body.amount > 0) {
334 // Транзит: удержанная половина тела → пул членских взносов, откуда уйдёт в КУ.
338 penalty_body, o.orderer, o.hash,
340 }
341
342 // ── Членский взнос 50/50 ──
343 const eosio::asset fee = get_order_membership_fee(o);
344 const eosio::asset penalty_fee = refusal_penalty_share(fee);
345 const eosio::asset refund_fee = fee - penalty_fee;
346 if (refund_fee.amount > 0) {
350 refund_fee, o.orderer, o.hash,
352 }
353
354 // ── Удержанное (тело + взнос) — в общий кошелёк КУ выдачи ──
355 // Обе удержанные половины сейчас в пуле членских взносов: тело — транзитом
356 // выше, взнос — ещё с createorder; единым accrue зачисляются в w.brn.common.
357 const eosio::asset to_common = penalty_body + penalty_fee;
358 if (to_common.amount > 0) {
362 }
363}
364
365} // namespace Marketplace
static void apply(eosio::name actor, eosio::name coopname, eosio::name operation_code, eosio::name process_type, eosio::asset amount, eosio::name username, eosio::checksum256 process_hash, std::string memo)
Отправить inline action ledger2::apply.
Definition: ledger2.hpp:42
static constexpr eosio::symbol _unit_piece
Definition: consts.hpp:238
static constexpr eosio::symbol _root_govern_symbol
Definition: consts.hpp:231
#define HUNDR_PERCENTS
Definition: consts.hpp:136
static constexpr eosio::symbol _unit_liter
Definition: consts.hpp:237
static constexpr eosio::symbol _unit_kg
Definition: consts.hpp:236
static constexpr eosio::name _ledger2
Definition: consts.hpp:180
static constexpr eosio::name _marketplace
Definition: consts.hpp:171
share_type amount
Definition: eosio.token_tests.cpp:174
Человекочитаемые memo для marketplace ledger2-операций.
void accrue(eosio::name actor, eosio::name coopname, eosio::name braname, eosio::asset amount, eosio::name process_type, eosio::checksum256 process_hash, std::string memo)
Inline-вызов branch::accrue от контракта-источника членских взносов (requirement b6 «Экономика КУ»,...
Definition: branch.hpp:56
std::string get_refusal_penalty_distribute_memo(uint64_t order_id)
Definition: memo.hpp:74
std::string get_membership_fee_refund_memo(uint64_t order_id)
Definition: memo.hpp:50
std::string get_cancel_order_memo(uint64_t order_id)
Definition: memo.hpp:66
std::string get_refusal_penalty_transit_memo(uint64_t order_id)
Definition: memo.hpp:70
Canonical helpers контракта marketplace (Story 11.1).
Definition: marketplace.hpp:32
void check_return_request_branch(eosio::name coopname, const return_request &r, eosio::name braname)
Definition: marketplace.hpp:180
std::optional< order > get_order_by_hash(eosio::name coopname, const checksum256 &order_hash)
Definition: marketplace.hpp:108
return_request get_return_request_by_hash_or_fail(eosio::name coopname, const checksum256 &request_hash, const std::string &msg="Заявление на возврат не найдено по хэшу")
Definition: marketplace.hpp:150
void erase_order(eosio::name coopname, uint64_t order_id)
Definition: marketplace.hpp:132
eosio::multi_index< "orders"_n, order, eosio::indexed_by<"byhash"_n, eosio::const_mem_fun< order, checksum256, &order::by_hash > >, eosio::indexed_by<"byorderer"_n, eosio::const_mem_fun< order, uint64_t, &order::by_orderer > >, eosio::indexed_by<"byofferer"_n, eosio::const_mem_fun< order, uint64_t, &order::by_offerer > >, eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun< order, uint64_t, &order::by_status > >, eosio::indexed_by<"bybatch"_n, eosio::const_mem_fun< order, checksum256, &order::by_batch > >, eosio::indexed_by<"byoffer"_n, eosio::const_mem_fun< order, checksum256, &order::by_offer > >, eosio::indexed_by<"bydelivbra"_n, eosio::const_mem_fun< order, uint64_t, &order::by_delivery_bra > >, eosio::indexed_by<"byacceptbra"_n, eosio::const_mem_fun< order, uint64_t, &order::by_accept_bra > > > orders_index
Definition: table_marketplace_orders.hpp:193
eosio::asset calc_membership_fee(const eosio::asset &base, uint64_t fee_percent)
Сумма членского взноса от базы по ставке (целочисленно, вниз).
Definition: marketplace.hpp:269
std::optional< writeoff_proposal > get_writeoff_proposal_by_hash(eosio::name coopname, const checksum256 &proposal_hash)
Definition: marketplace.hpp:189
constexpr uint64_t DEFAULT_MEMBERSHIP_FEE_PERCENT
Definition: marketplace.hpp:259
UserWalletAvailable get_user_wallet_balance(eosio::name coopname, eosio::name wallet_id, eosio::name username)
Definition: marketplace.hpp:239
void erase_writeoff_proposal(eosio::name coopname, uint64_t proposal_id)
Definition: marketplace.hpp:216
constexpr uint64_t REFUSAL_PENALTY_PERCENT
Definition: marketplace.hpp:307
void check_quantity(const eosio::asset &quantity)
Валидация количества: корректный asset, известная единица, положительное.
Definition: marketplace.hpp:48
void update_writeoff_proposal(eosio::name coopname, uint64_t proposal_id, const std::function< void(writeoff_proposal &)> &fn)
Definition: marketplace.hpp:206
eosio::asset pro_rata(const eosio::asset &total, int64_t part, int64_t whole)
Definition: marketplace.hpp:98
eosio::multi_index< "wroffprops"_n, writeoff_proposal, eosio::indexed_by<"byhash"_n, eosio::const_mem_fun< writeoff_proposal, checksum256, &writeoff_proposal::by_hash > >, eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun< writeoff_proposal, uint64_t, &writeoff_proposal::by_status > > > writeoff_proposals_index
Definition: table_marketplace_writeoff_proposals.hpp:116
eosio::asset get_order_membership_fee(const order &o)
Членский взнос заказа (ноль — взнос не начислялся).
Definition: marketplace.hpp:276
eosio::asset refusal_penalty_share(const eosio::asset &base)
Удерживаемая часть суммы (округление вниз — остаток в пользу пайщика).
Definition: marketplace.hpp:310
void check_packaging(const eosio::asset &quantity, const eosio::asset &package_size)
Definition: marketplace.hpp:60
eosio::asset calc_cost(const eosio::asset &quantity, const eosio::asset &unit_price, const eosio::asset &package_size=eosio::asset(0, _unit_piece))
Definition: marketplace.hpp:77
void erase_return_request(eosio::name coopname, uint64_t request_id)
Definition: marketplace.hpp:169
writeoff_proposal get_writeoff_proposal_by_hash_or_fail(eosio::name coopname, const checksum256 &proposal_hash, const std::string &msg="Проект списания не найден по хэшу")
Definition: marketplace.hpp:198
void update_return_request(eosio::name coopname, uint64_t request_id, const std::function< void(return_request &)> &fn)
Definition: marketplace.hpp:158
order get_order_by_hash_or_fail(eosio::name coopname, const checksum256 &order_hash, const std::string &msg="Заказ не найден по хэшу")
Definition: marketplace.hpp:116
void refund_membership_fee_if_any(eosio::name coopname, const order &o)
Definition: marketplace.hpp:283
uint64_t get_membership_fee_percent(eosio::name coopname)
Definition: marketplace.hpp:263
void update_order(eosio::name coopname, uint64_t order_id, const std::function< void(order &)> &fn)
Definition: marketplace.hpp:123
void refund_order_full(eosio::name coopname, const order &o)
Definition: marketplace.hpp:296
eosio::multi_index< "retrequests"_n, return_request, eosio::indexed_by<"byhash"_n, eosio::const_mem_fun< return_request, checksum256, &return_request::by_hash > >, eosio::indexed_by<"byorderer"_n, eosio::const_mem_fun< return_request, uint64_t, &return_request::by_orderer > >, eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun< return_request, uint64_t, &return_request::by_status > >, eosio::indexed_by<"byorigorder"_n, eosio::const_mem_fun< return_request, uint64_t, &return_request::by_original_order > > > return_requests_index
Definition: table_marketplace_return_requests.hpp:109
void retain_refusal_penalty(eosio::name coopname, const order &o)
Definition: marketplace.hpp:321
std::optional< return_request > get_return_request_by_hash(eosio::name coopname, const checksum256 &request_hash)
Definition: marketplace.hpp:141
bool is_valid_unit_symbol(const eosio::symbol &sym)
Definition: marketplace.hpp:43
Definition: eosio.msig.hpp:34
constexpr eosio::name MEMBERSHIP_FEE_REFUND
Возврат неиспользованной части членского взноса (TRANSFER w.mkt.fee → w.mkt.member,...
Definition: operations.hpp:106
constexpr eosio::name UNLOCK_ORDER
Снятие резерва при отмене Order'а или недовыдаче (TRANSFER w.mkt.order → w.mkt.member,...
Definition: operations.hpp:98
constexpr eosio::name REFUSAL_PENALTY
Удержание 50% при отказе пайщика от получения после акцепта поставщиком (TRANSFER w....
Definition: operations.hpp:108
constexpr eosio::name SUPPLY
Прямая поставка-приобретение имущества (5 операций: o.mkt.lock + o.mkt.unlock + o....
Definition: processes.hpp:74
Definition: marketplace.hpp:233
bool exists
Definition: marketplace.hpp:236
eosio::asset blocked
Definition: marketplace.hpp:235
eosio::asset available
Definition: marketplace.hpp:234
On-chain Order — анкер процесса p.mkt.supply.
Definition: table_marketplace_orders.hpp:112
eosio::name orderer
пайщик-заказчик
Definition: table_marketplace_orders.hpp:116
checksum256 hash
process_hash для p.mkt.supply
Definition: table_marketplace_orders.hpp:114
eosio::asset membership_fee
Definition: table_marketplace_orders.hpp:164
uint64_t id
внутренний ID
Definition: table_marketplace_orders.hpp:113
eosio::asset total_cost
quantity * unit_price / 10^precision (заблокированная сумма)
Definition: table_marketplace_orders.hpp:128
eosio::name delivery_braname
КУ выдачи (выбран пайщиком на createorder); проверка signiss1/signiss2/p.mkt.return через Branch::is_...
Definition: table_marketplace_orders.hpp:120
On-chain Заявление на гарантийный возврат — анкер процесса p.mkt.return.
Definition: table_marketplace_return_requests.hpp:63
checksum256 original_order_hash
process_hash оригинального p.mkt.supply
Definition: table_marketplace_return_requests.hpp:70
On-chain Проект решения совета о списании скоропорта — анкер процесса p.mkt.wroff.
Definition: table_marketplace_writeoff_proposals.hpp:90
eosio::multi_index< "userwallets"_n, userwallet, eosio::indexed_by<"byuserwallet"_n, eosio::const_mem_fun< userwallet, uint128_t, &userwallet::by_userwallet > >, eosio::indexed_by<"byuser"_n, eosio::const_mem_fun< userwallet, uint64_t, &userwallet::by_user > >, eosio::indexed_by<"bywallet"_n, eosio::const_mem_fun< userwallet, uint64_t, &userwallet::by_wallet > > > userwallets_index
Definition: table_ledger2_userwallets.hpp:46
eosio::singleton<"config"_n, mkt_config > mkt_config_singleton
Definition: table_marketplace_fee_config.hpp:28
static uint128_t combine_ids(const uint64_t &x, const uint64_t &y)
Definition: utils.hpp:7