Compare commits
10 Commits
main
...
marketplace
| Author | SHA1 | Date | |
|---|---|---|---|
| 64c86de033 | |||
| 4ea4298cb1 | |||
| 3dc0592d72 | |||
| 7ad2806059 | |||
| 725b804481 | |||
| 1080a29ab9 | |||
| 4eb6888b1f | |||
| 48c41937e5 | |||
| b6771e43a9 | |||
| 507e694477 |
@@ -1,3 +1,25 @@
|
||||
# v2025.7.15-1
|
||||
|
||||
Релиз закрывает собой большой цикл фундаментальной разработки, предоставляя кооперативам мобильные PWA-приложения, которые быстро и легко устанавливаются на все телефоны и компьютеры через добавление на домашний экран. После добавления на домашний экран приложение запрашивает доступ к системным уведомлениям и становится способным оповещать пайщиков о самом важном через PUSH-сообщения.
|
||||
|
||||
Также, данный релиз содержит прототип виджета, который позволит разработчикам в несколько строк кода отображать на своих сайтах визуальный интерфейс цифрового кооператива MONO и безопасно взаимодействовать с ним без необходимости тяжелой разработки. Реализация данного виджета открывает дорогу на все существующие платформы-конструкторы, такие как Tilda, Wordpress, Joomla, а также, на любые сайты, где страницы с мини-приложениями кооперативной экономики встроятся "нативно", - без необходимости для пользователя переходить куда-либо и разрывать опыт.
|
||||
|
||||
|
||||
✨ Новые функции
|
||||
- [#293](https://github.com/coopenomics/mono/issues/293): Разработан прототип виджета MONO для быстрого запуска
|
||||
- [#291](https://github.com/coopenomics/mono/issues/291): Реализован и настроен центр уведомлений
|
||||
- [#290](https://github.com/coopenomics/mono/issues/290): Создан пакет фабрики оповещений (email, push, inApp)
|
||||
- [#289](https://github.com/coopenomics/mono/issues/289): Настроены push-уведомления для всех поддерживаемых устройств и браузеров
|
||||
- [#288](https://github.com/coopenomics/mono/issues/288): Добавлено окно для запроса разрешения на PUSH уведомления
|
||||
- [#287](https://github.com/coopenomics/mono/issues/287): Создана PWA-конфигурация для приложения MONO
|
||||
|
||||
🐛 Исправления ошибок
|
||||
- [#292](https://github.com/coopenomics/mono/issues/292): Исправлен баг "Неизвестный подписант" для организаций
|
||||
|
||||
#releases
|
||||
|
||||
---
|
||||
|
||||
# v2025.7.1-1
|
||||
|
||||
В этом релизе реализованы механизмы возврата паевого взноса пайщика и инструменты управления этим процессом для членов совета. Также внесены визуальные доработки интерфейсов для улучшения восприятия информации.
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
---
|
||||
Длинна типа name не должна превышать 12 символов и не должна содержать специальных знаков.
|
||||
@@ -30,6 +30,7 @@ macro(add_contract_build name)
|
||||
endmacro()
|
||||
|
||||
# Добавляем все контракты
|
||||
add_contract_build(ledger)
|
||||
add_contract_build(meet)
|
||||
add_contract_build(loan)
|
||||
add_contract_build(wallet)
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -6,6 +6,7 @@
|
||||
#include "src/addaccum.cpp"
|
||||
#include "src/addcirculate.cpp"
|
||||
#include "src/addexpense.cpp"
|
||||
#include "src/accumfee.cpp"
|
||||
#include "src/addinitial.cpp"
|
||||
#include "src/authorize.cpp"
|
||||
#include "src/delfund.cpp"
|
||||
|
||||
@@ -44,10 +44,10 @@ class [[eosio::contract(FUND)]] fund : public eosio::contract {
|
||||
eosio::name type, uint64_t fund_id);
|
||||
|
||||
// атомарные транзакции фондового кошелька
|
||||
// паевый фонд
|
||||
// паевой фонд
|
||||
[[eosio::action]] void addcirculate(
|
||||
eosio::name coopname,
|
||||
eosio::asset quantity); /// < добавить сумму в паевый фонд
|
||||
eosio::asset quantity); /// < добавить сумму в паевой фонд
|
||||
[[eosio::action]] void subcirculate(
|
||||
eosio::name coopname,
|
||||
eosio::asset quantity,
|
||||
@@ -65,6 +65,9 @@ class [[eosio::contract(FUND)]] fund : public eosio::contract {
|
||||
[[eosio::action]] void addexpense(eosio::name coopname, uint64_t fund_id,
|
||||
eosio::asset quantity);
|
||||
|
||||
// добавить членский взнос на накопительный счет кооператива для дальнейшего управления
|
||||
[[eosio::action]] void accumfee(eosio::name coopname, eosio::asset quantity);
|
||||
|
||||
//счет вступительных взносов
|
||||
[[eosio::action]] void addinitial(eosio::name coopname,
|
||||
eosio::asset quantity);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @brief Атомарный метод добавления членского взноса на накопительный счет кооператива.
|
||||
* Минует процесс spreadamount и добавляет средства напрямую на accumulative_expense_account
|
||||
* для дальнейшего управления кооперативом (распределение по фондам или использование на расходы).
|
||||
*
|
||||
* @param coopname Имя кооператива
|
||||
* @param quantity Сумма членского взноса
|
||||
*/
|
||||
[[eosio::action]] void fund::accumfee(eosio::name coopname, eosio::asset quantity) {
|
||||
name payer = check_auth_and_get_payer_or_fail(contracts_whitelist);
|
||||
|
||||
auto cooperative = get_cooperative_or_fail(coopname);
|
||||
|
||||
coopwallet_index coopwallet(_fund, coopname.value);
|
||||
auto wal = coopwallet.find(0);
|
||||
|
||||
eosio::check(wal != coopwallet.end(), "Кошелёк кооператива не найден");
|
||||
|
||||
// Добавление членского взноса на накопительный счет кооператива
|
||||
coopwallet.modify(wal, payer, [&](auto &w) {
|
||||
w.accumulative_expense_account.available += quantity;
|
||||
});
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
// атомарные транзакции фондового кошелька
|
||||
// паевый счет
|
||||
// паевой счет
|
||||
[[eosio::action]] void fund::addcirculate(
|
||||
eosio::name coopname,
|
||||
eosio::asset quantity) /// < добавить сумму в паевый фонд
|
||||
eosio::asset quantity) /// < добавить сумму в паевой фонд
|
||||
{
|
||||
name payer = check_auth_and_get_payer_or_fail(contracts_whitelist);
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Проект
|
||||
project(ledger)
|
||||
|
||||
find_package(cdt)
|
||||
|
||||
# Добавление контракта
|
||||
add_contract(ledger ledger ledger.cpp)
|
||||
target_compile_definitions(ledger PUBLIC IS_TESTNET=${IS_TESTNET})
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "ledger.hpp"
|
||||
#include <ctime>
|
||||
#include <eosio/transaction.hpp>
|
||||
|
||||
#include "src/initialize/init.cpp"
|
||||
#include "src/common/add.cpp"
|
||||
#include "src/common/sub.cpp"
|
||||
#include "src/common/transfer.cpp"
|
||||
#include "src/writeoff/create.cpp"
|
||||
#include "src/writeoff/auth.cpp"
|
||||
#include "src/writeoff/complete.cpp"
|
||||
#include "src/writeoff/decline.cpp"
|
||||
#include "src/migrate/migrate.cpp"
|
||||
@@ -0,0 +1,46 @@
|
||||
#include <eosio/asset.hpp>
|
||||
#include <eosio/contract.hpp>
|
||||
#include <eosio/crypto.hpp>
|
||||
#include <eosio/eosio.hpp>
|
||||
#include <eosio/multi_index.hpp>
|
||||
#include <eosio/system.hpp>
|
||||
#include <eosio/time.hpp>
|
||||
|
||||
#include "../lib/common.hpp"
|
||||
|
||||
using namespace eosio;
|
||||
using namespace Ledger;
|
||||
|
||||
/**
|
||||
* @ingroup public_contracts
|
||||
* @brief Контракт ledger для управления бухгалтерской книгой кооператива
|
||||
* @details Обеспечивает ведение счетов согласно стандартам кооперативной отчетности
|
||||
*/
|
||||
class [[eosio::contract(LEDGER)]] ledger : public eosio::contract {
|
||||
|
||||
public:
|
||||
ledger(eosio::name receiver, eosio::name code,
|
||||
eosio::datastream<const char *> ds)
|
||||
: eosio::contract(receiver, code, ds) {}
|
||||
|
||||
void apply(uint64_t receiver, uint64_t code, uint64_t action);
|
||||
[[eosio::action]] void migrate();
|
||||
|
||||
// Инициализация счетов
|
||||
[[eosio::action]] void init(eosio::name coopname);
|
||||
|
||||
// Операции со счетами (common процесс)
|
||||
[[eosio::action]] void add(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
|
||||
[[eosio::action]] void sub(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
|
||||
[[eosio::action]] void transfer(eosio::name coopname, uint64_t from_account_id, uint64_t to_account_id, eosio::asset quantity, std::string comment);
|
||||
|
||||
// Операции списания через совет (writeoff процесс)
|
||||
[[eosio::action]] void create(eosio::name coopname, eosio::name username, uint64_t account_id, eosio::asset quantity, std::string reason, document2 document, checksum256 writeoff_hash);
|
||||
[[eosio::action]] void auth(eosio::name coopname, checksum256 writeoff_hash);
|
||||
|
||||
// Коллбэки от gateway
|
||||
[[eosio::action]] void complete(eosio::name coopname, checksum256 writeoff_hash);
|
||||
[[eosio::action]] void decline(eosio::name coopname, checksum256 writeoff_hash, std::string reason);
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @brief Пополнение счета
|
||||
* @param coopname - имя кооператива
|
||||
* @param account_id - идентификатор счета
|
||||
* @param quantity - сумма пополнения
|
||||
* @param comment - комментарий к операции
|
||||
*/
|
||||
[[eosio::action]]
|
||||
void ledger::add(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
|
||||
if (!has_auth(coopname)) {
|
||||
check_auth_and_get_payer_or_fail(contracts_whitelist);
|
||||
}
|
||||
|
||||
eosio::check(quantity.is_valid(), "Некорректная сумма");
|
||||
eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
|
||||
eosio::check(quantity.symbol == _root_govern_symbol, "Некорректный символ валюты");
|
||||
|
||||
laccounts_index accounts(_ledger, coopname.value);
|
||||
auto account_iter = accounts.find(account_id);
|
||||
eosio::check(account_iter != accounts.end(), "Счет не найден");
|
||||
|
||||
// Обновляем баланс счета
|
||||
accounts.modify(account_iter, coopname, [&](auto& acc) {
|
||||
acc.allocation += quantity;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @brief Списание со счета
|
||||
* @param coopname - имя кооператива
|
||||
* @param account_id - идентификатор счета
|
||||
* @param quantity - сумма списания
|
||||
* @param comment - комментарий к операции
|
||||
*/
|
||||
[[eosio::action]]
|
||||
void ledger::sub(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
|
||||
|
||||
if (!has_auth(coopname)) {
|
||||
check_auth_and_get_payer_or_fail(contracts_whitelist);
|
||||
}
|
||||
|
||||
eosio::check(quantity.is_valid(), "Некорректная сумма");
|
||||
eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
|
||||
eosio::check(quantity.symbol == _root_govern_symbol, "Некорректный символ валюты");
|
||||
|
||||
laccounts_index accounts(_ledger, coopname.value);
|
||||
auto account_iter = accounts.find(account_id);
|
||||
eosio::check(account_iter != accounts.end(), "Счет не найден");
|
||||
|
||||
// Проверяем достаточность средств
|
||||
eosio::check(account_iter->allocation >= quantity, "Недостаточно средств для списания");
|
||||
|
||||
// Обновляем баланс счета
|
||||
accounts.modify(account_iter, coopname, [&](auto& acc) {
|
||||
acc.allocation -= quantity;
|
||||
acc.writeoff += quantity;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @brief Перевод средств между счетами
|
||||
* @param coopname - имя кооператива
|
||||
* @param from_account_id - идентификатор счета списания
|
||||
* @param to_account_id - идентификатор счета зачисления
|
||||
* @param quantity - сумма перевода
|
||||
* @param comment - комментарий к операции
|
||||
*/
|
||||
[[eosio::action]]
|
||||
void ledger::transfer(eosio::name coopname, uint64_t from_account_id, uint64_t to_account_id, eosio::asset quantity, std::string comment) {
|
||||
if (!has_auth(coopname)) {
|
||||
check_auth_and_get_payer_or_fail(contracts_whitelist);
|
||||
}
|
||||
|
||||
eosio::check(quantity.is_valid(), "Некорректная сумма");
|
||||
eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
|
||||
eosio::check(quantity.symbol == _root_govern_symbol, "Некорректный символ валюты");
|
||||
eosio::check(from_account_id != to_account_id, "Счета списания и зачисления должны отличаться");
|
||||
|
||||
laccounts_index accounts(_ledger, coopname.value);
|
||||
|
||||
auto from_account_iter = accounts.find(from_account_id);
|
||||
eosio::check(from_account_iter != accounts.end(), "Счет списания не найден");
|
||||
|
||||
auto to_account_iter = accounts.find(to_account_id);
|
||||
eosio::check(to_account_iter != accounts.end(), "Счет зачисления не найден");
|
||||
|
||||
// Проверяем достаточность средств
|
||||
eosio::check(from_account_iter->allocation >= quantity, "Недостаточно средств для перевода");
|
||||
|
||||
// Списываем с источника
|
||||
accounts.modify(from_account_iter, coopname, [&](auto& acc) {
|
||||
acc.allocation -= quantity;
|
||||
});
|
||||
|
||||
// Зачисляем на получателя
|
||||
accounts.modify(to_account_iter, coopname, [&](auto& acc) {
|
||||
acc.allocation += quantity;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @brief Инициализация всех счетов кооператива согласно плану счетов
|
||||
* @param coopname - имя кооператива
|
||||
*/
|
||||
[[eosio::action]]
|
||||
void ledger::init(eosio::name coopname) {
|
||||
require_auth(coopname);
|
||||
|
||||
// Проверяем что кооператив существует
|
||||
cooperatives2_index coops(_registrator, _registrator.value);
|
||||
auto coop_iter = coops.find(coopname.value);
|
||||
eosio::check(coop_iter != coops.end(), "Кооператив не найден");
|
||||
|
||||
laccounts_index accounts(_ledger, coopname.value);
|
||||
|
||||
// Проверяем что счета еще не инициализированы
|
||||
auto existing = accounts.begin();
|
||||
eosio::check(existing == accounts.end(), "Счета уже инициализированы для данного кооператива");
|
||||
|
||||
// Инициализируем все счета из карты с нулевыми значениями
|
||||
for (const auto& account_data : ACCOUNT_MAP) {
|
||||
uint64_t id = std::get<0>(account_data);
|
||||
std::string name = std::get<1>(account_data);
|
||||
|
||||
accounts.emplace(coopname, [&](auto& acc) {
|
||||
acc.id = id;
|
||||
acc.name = name;
|
||||
acc.allocation = eosio::asset(0, _root_govern_symbol);
|
||||
acc.writeoff = eosio::asset(0, _root_govern_symbol);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @brief Миграция данных из контракта fund
|
||||
* Переносит данные из фондов fund в счета ledger
|
||||
*/
|
||||
void ledger::migrate(){
|
||||
require_auth(_ledger);
|
||||
|
||||
// Получаем все кооперативы
|
||||
cooperatives2_index coops(_registrator, _registrator.value);
|
||||
|
||||
for (auto coop_iter = coops.begin(); coop_iter != coops.end(); ++coop_iter) {
|
||||
eosio::name coopname = coop_iter->username;
|
||||
|
||||
// Проверяем что счета еще не инициализированы
|
||||
laccounts_index accounts(_ledger, coopname.value);
|
||||
auto existing = accounts.begin();
|
||||
if (existing != accounts.end()) {
|
||||
continue; // Пропускаем уже инициализированные кооперативы
|
||||
}
|
||||
|
||||
// Инициализируем все счета с нулевыми значениями
|
||||
for (const auto& account_data : ACCOUNT_MAP) {
|
||||
uint64_t id = std::get<0>(account_data);
|
||||
std::string name = std::get<1>(account_data);
|
||||
|
||||
accounts.emplace(_ledger, [&](auto& acc) {
|
||||
acc.id = id;
|
||||
acc.name = name;
|
||||
acc.allocation = eosio::asset(0, _root_govern_symbol);
|
||||
acc.writeoff = eosio::asset(0, _root_govern_symbol);
|
||||
});
|
||||
}
|
||||
|
||||
// Мигрируем данные из fund контракта
|
||||
// Читаем кошелек кооператива
|
||||
coopwallet_index coopwallets(_fund, coopname.value);
|
||||
auto wallet_iter = coopwallets.find(0); // ID всегда 0
|
||||
|
||||
if (wallet_iter != coopwallets.end()) {
|
||||
// Паевой фонд (circulating_account) -> счет 80
|
||||
auto account_iter = accounts.find(80);
|
||||
if (account_iter != accounts.end()) {
|
||||
accounts.modify(account_iter, _ledger, [&](auto& acc) {
|
||||
acc.allocation = wallet_iter->circulating_account.available;
|
||||
});
|
||||
}
|
||||
|
||||
// Вступительные взносы (initial_account) -> счет 801
|
||||
account_iter = accounts.find(801);
|
||||
if (account_iter != accounts.end()) {
|
||||
accounts.modify(account_iter, _ledger, [&](auto& acc) {
|
||||
acc.allocation = wallet_iter->initial_account.available;
|
||||
});
|
||||
}
|
||||
|
||||
// Накопительный счет членских взносов (accumulative_expense_account) -> счет 86
|
||||
account_iter = accounts.find(86);
|
||||
if (account_iter != accounts.end()) {
|
||||
accounts.modify(account_iter, _ledger, [&](auto& acc) {
|
||||
acc.allocation = wallet_iter->accumulative_expense_account.available;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @brief Авторизация операции после положительного решения совета
|
||||
* @param coopname - имя кооператива
|
||||
* @param writeoff_hash - хэш операции списания
|
||||
*/
|
||||
[[eosio::action]]
|
||||
void ledger::auth(eosio::name coopname, checksum256 writeoff_hash) {
|
||||
require_auth(_soviet);
|
||||
|
||||
auto writeoff_opt = get_writeoff_by_hash(writeoff_hash);
|
||||
eosio::check(writeoff_opt.has_value(), "Операция не найдена");
|
||||
|
||||
auto writeoff = writeoff_opt.value();
|
||||
eosio::check(writeoff.coopname == coopname, "Неверный кооператив");
|
||||
eosio::check(writeoff.status == "pending"_n, "Операция уже обработана");
|
||||
|
||||
// Обновляем статус операции
|
||||
writeoffs_index writeoffs(_ledger, _ledger.value);
|
||||
auto writeoff_iter = writeoffs.find(writeoff.id);
|
||||
writeoffs.modify(writeoff_iter, _soviet, [&](auto& w) {
|
||||
w.status = "approved"_n;
|
||||
});
|
||||
|
||||
// Отправляем запрос на создание исходящего платежа в gateway через интерфейс
|
||||
Action::send<createoutpay_interface>(
|
||||
_gateway,
|
||||
"createoutpay"_n,
|
||||
_ledger,
|
||||
coopname,
|
||||
writeoff.username,
|
||||
writeoff_hash,
|
||||
writeoff.quantity,
|
||||
_ledger, // callback_contract
|
||||
"complete"_n, // confirm_callback
|
||||
"decline"_n // decline_callback
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @brief Коллбэк при успешном завершении платежа
|
||||
* @param coopname - имя кооператива
|
||||
* @param writeoff_hash - хэш операции списания
|
||||
*/
|
||||
[[eosio::action]]
|
||||
void ledger::complete(eosio::name coopname, checksum256 writeoff_hash) {
|
||||
require_auth(_gateway);
|
||||
|
||||
auto writeoff_opt = get_writeoff_by_hash(writeoff_hash);
|
||||
eosio::check(writeoff_opt.has_value(), "Операция не найдена");
|
||||
|
||||
auto writeoff = writeoff_opt.value();
|
||||
eosio::check(writeoff.coopname == coopname, "Неверный кооператив");
|
||||
eosio::check(writeoff.status == "approved"_n, "Операция не одобрена");
|
||||
|
||||
// Обновляем статус операции
|
||||
writeoffs_index writeoffs(_ledger, _ledger.value);
|
||||
auto writeoff_iter = writeoffs.find(writeoff.id);
|
||||
writeoffs.modify(writeoff_iter, _gateway, [&](auto& w) {
|
||||
w.status = "paid"_n;
|
||||
});
|
||||
|
||||
// Списываем средства со счета
|
||||
laccounts_index accounts(_ledger, coopname.value);
|
||||
auto account_iter = accounts.find(writeoff.account_id);
|
||||
eosio::check(account_iter != accounts.end(), "Счет не найден");
|
||||
|
||||
accounts.modify(account_iter, _gateway, [&](auto& acc) {
|
||||
acc.allocation -= writeoff.quantity;
|
||||
acc.writeoff += writeoff.quantity;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @brief Создание заявления в совет для списания средств
|
||||
* @param coopname - имя кооператива
|
||||
* @param username - инициатор заявления
|
||||
* @param account_id - идентификатор счета для списания
|
||||
* @param quantity - сумма списания
|
||||
* @param reason - обоснование списания
|
||||
* @param document - документ обоснования
|
||||
* @param writeoff_hash - внешний хэш операции списания
|
||||
*/
|
||||
[[eosio::action]]
|
||||
void ledger::create(eosio::name coopname, eosio::name username, uint64_t account_id, eosio::asset quantity, std::string reason, document2 document, checksum256 writeoff_hash) {
|
||||
require_auth(username);
|
||||
|
||||
eosio::check(quantity.is_valid(), "Некорректная сумма");
|
||||
eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
|
||||
eosio::check(quantity.symbol == _root_govern_symbol, "Некорректный символ валюты");
|
||||
eosio::check(reason.length() > 0, "Обоснование должно быть от 1 символа");
|
||||
|
||||
// Проверяем что кооператив существует
|
||||
cooperatives2_index coops(_registrator, _registrator.value);
|
||||
auto coop_iter = coops.find(coopname.value);
|
||||
eosio::check(coop_iter != coops.end(), "Кооператив не найден");
|
||||
|
||||
// Проверяем что счет существует
|
||||
laccounts_index accounts(_ledger, coopname.value);
|
||||
auto account_iter = accounts.find(account_id);
|
||||
eosio::check(account_iter != accounts.end(), "Счет не найден");
|
||||
|
||||
// Проверяем достаточность средств
|
||||
eosio::check(account_iter -> allocation >= quantity, "Недостаточно средств на счету");
|
||||
|
||||
// Проверяем уникальность writeoff_hash
|
||||
auto existing_writeoff = get_writeoff_by_hash(writeoff_hash);
|
||||
eosio::check(!existing_writeoff.has_value(), "Операция с таким хэшем уже существует");
|
||||
|
||||
// Создаем операцию ожидающую решения совета
|
||||
auto operation_id = get_global_id_in_scope(_ledger, _ledger, "writeoffs"_n);
|
||||
|
||||
writeoffs_index writeoffs(_ledger, _ledger.value);
|
||||
writeoffs.emplace(username, [&](auto& op) {
|
||||
op.id = operation_id;
|
||||
op.coopname = coopname;
|
||||
op.username = username;
|
||||
op.account_id = account_id;
|
||||
op.quantity = quantity;
|
||||
op.reason = reason;
|
||||
op.document = document;
|
||||
op.writeoff_hash = writeoff_hash;
|
||||
op.status = "pending"_n;
|
||||
});
|
||||
|
||||
// Создаем агенду в совете через Action::send
|
||||
Action::send<createagenda_interface>(
|
||||
_soviet,
|
||||
"createagenda"_n,
|
||||
username,
|
||||
coopname,
|
||||
username,
|
||||
get_valid_soviet_action("ledgerwthd"_n), // тип решения
|
||||
writeoff_hash,
|
||||
_ledger, // callback_contract
|
||||
"auth"_n, // confirm_callback
|
||||
"decline"_n, // decline_callback
|
||||
document,
|
||||
reason
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @brief Коллбэк при отклонении платежа или решения совета
|
||||
* @param coopname - имя кооператива
|
||||
* @param writeoff_hash - хэш операции списания
|
||||
* @param reason - причина отклонения
|
||||
*/
|
||||
[[eosio::action]]
|
||||
void ledger::decline(eosio::name coopname, checksum256 writeoff_hash, std::string reason) {
|
||||
// Может быть вызван от _soviet (отклонение решения) или _gateway (отклонение платежа)
|
||||
eosio::check(has_auth(_soviet) || has_auth(_gateway), "Недостаточно прав для отклонения операции");
|
||||
|
||||
auto writeoff_opt = get_writeoff_by_hash(writeoff_hash);
|
||||
eosio::check(writeoff_opt.has_value(), "Операция не найдена");
|
||||
|
||||
auto writeoff = writeoff_opt.value();
|
||||
eosio::check(writeoff.coopname == coopname, "Неверный кооператив");
|
||||
|
||||
// Обновляем статус операции
|
||||
writeoffs_index writeoffs(_ledger, _ledger.value);
|
||||
auto writeoff_iter = writeoffs.find(writeoff.id);
|
||||
writeoffs.erase(writeoff_iter);
|
||||
}
|
||||
@@ -164,10 +164,10 @@ struct org_data {
|
||||
std::string announce; ///< Анонс
|
||||
std::string description; ///< Описание
|
||||
eosio::asset initial; ///< Вступительный взнос физического лица / ип
|
||||
eosio::asset minimum; ///< Минимальный паевый взнос физического лица / ип
|
||||
eosio::asset minimum; ///< Минимальный паевой взнос физического лица / ип
|
||||
|
||||
eosio::asset org_initial; ///< Вступительный взнос юридического лица
|
||||
eosio::asset org_minimum; ///< Минимальный паевый взнос юридического лица
|
||||
eosio::asset org_minimum; ///< Минимальный паевой взнос юридического лица
|
||||
|
||||
};
|
||||
|
||||
@@ -192,11 +192,11 @@ struct [[eosio::table, eosio::contract(REGISTRATOR)]] cooperative {
|
||||
|
||||
eosio::asset registration; ///< Регистрационный взнос физического лица / ип
|
||||
eosio::asset initial; ///< Вступительный членский взнос физического лица / ип
|
||||
eosio::asset minimum; ///< Минимальный паевый взнос физического лица / ип
|
||||
eosio::asset minimum; ///< Минимальный паевой взнос физического лица / ип
|
||||
|
||||
eosio::binary_extension<eosio::asset> org_registration; ///< Регистрационный взнос юридического лица
|
||||
eosio::binary_extension<eosio::asset> org_initial; ///< Вступительный членский взнос юридического лица
|
||||
eosio::binary_extension<eosio::asset> org_minimum; ///< Минимальный паевый взнос юридического лица
|
||||
eosio::binary_extension<eosio::asset> org_minimum; ///< Минимальный паевой взнос юридического лица
|
||||
|
||||
eosio::binary_extension<eosio::name> status; ///< Статус процесса подключения
|
||||
eosio::binary_extension<eosio::time_point_sec> created_at; ///< Дата поступления заявки на подключение (pending | ... | active | blocked)
|
||||
@@ -303,11 +303,11 @@ struct [[eosio::table, eosio::contract(REGISTRATOR)]] cooperative2 {
|
||||
|
||||
eosio::asset registration; ///< Регистрационный взнос физического лица / ип
|
||||
eosio::asset initial; ///< Вступительный членский взнос физического лица / ип
|
||||
eosio::asset minimum; ///< Минимальный паевый взнос физического лица / ип
|
||||
eosio::asset minimum; ///< Минимальный паевой взнос физического лица / ип
|
||||
|
||||
eosio::binary_extension<eosio::asset> org_registration; ///< Регистрационный взнос юридического лица
|
||||
eosio::binary_extension<eosio::asset> org_initial; ///< Вступительный членский взнос юридического лица
|
||||
eosio::binary_extension<eosio::asset> org_minimum; ///< Минимальный паевый взнос юридического лица
|
||||
eosio::binary_extension<eosio::asset> org_minimum; ///< Минимальный паевой взнос юридического лица
|
||||
|
||||
eosio::binary_extension<eosio::name> status; ///< Статус процесса подключения
|
||||
eosio::binary_extension<eosio::time_point_sec> created_at; ///< Дата поступления заявки на подключение (pending | ... | active | blocked)
|
||||
|
||||
@@ -76,6 +76,21 @@ struct [[eosio::table, eosio::contract(BRANCH)]] coobranch {
|
||||
bool is_account_in_trusted(const eosio::name& account) const {
|
||||
return std::find(trusted.begin(), trusted.end(), account) != trusted.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Проверяет права доступа пользователя к данному кооперативному участку
|
||||
* @param username Имя пользователя для проверки
|
||||
* @return true если пользователь имеет права доступа, false в противном случае
|
||||
*/
|
||||
bool is_user_authorized(const eosio::name& username) const {
|
||||
// Проверяем является ли пользователь доверенным лицом (trustee)
|
||||
if (trustee == username) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Проверяем находится ли пользователь в списке доверенных (trusted)
|
||||
return is_account_in_trusted(username);
|
||||
}
|
||||
};
|
||||
|
||||
typedef eosio::multi_index<"branches"_n, coobranch,
|
||||
|
||||
@@ -1,157 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
/**
|
||||
\ingroup public_tables
|
||||
\brief Параметры заявки на обмен.
|
||||
*
|
||||
* Эта структура предоставляет набор данных, которые требуются для создания или обновления заявки на обмен в контракте "marketplace".
|
||||
*
|
||||
* @details Следующие параметры определяют заявку:
|
||||
* - username: Имя пользователя, инициирующего или обновляющего заявку.
|
||||
* - parent_id: Идентификатор родительской заявки (если есть).
|
||||
* - coopname: Имя кооператива.
|
||||
* - units: Количество частей (штук) товара или услуги для обмена.
|
||||
* - unit_cost: Цена за единицу (штуку) товара или услуги, выраженная в определенном формате asset.
|
||||
* - data: Содержит дополнительные данные, специфичные для заявки (например, условия обмена).
|
||||
* - meta: Метаданные, предоставляющие дополнительную информацию о заявке (например, описание товара или условия обмена).
|
||||
*/
|
||||
struct exchange_params {
|
||||
eosio::name username; /*!< Имя пользователя, инициирующего или обновляющего заявку */
|
||||
uint64_t parent_id; /*!< Идентификатор родительской заявки */
|
||||
uint64_t program_id; /*!< Идентификатор программы */
|
||||
eosio::name coopname; /*!< Имя кооператива */
|
||||
uint64_t units; /*!< Количество частей (штук) товара или услуги */
|
||||
eosio::asset unit_cost; /*!< Цена за единицу (штуку) товара или услуги */
|
||||
uint64_t product_lifecycle_secs; /*!< Время жизни продукта, заявляемое поставщиком */
|
||||
std::optional<document2> document; /*!< Сопутствующий подписанный документ на взнос или возврат взноса */
|
||||
std::string data; /*!< Дополнительные данные, специфичные для заявки */
|
||||
std::string meta; /*!< Метаданные о заявке */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Таблица обменов для контракта "marketplace"
|
||||
* @ingroup public_tables
|
||||
* Эта таблица используется для хранения информации об обменных заявках в системе.
|
||||
*
|
||||
* @param id Идентификатор обмена, используемый как первичный ключ
|
||||
* @param parent_id Идентификатор родительской заявки (если есть)
|
||||
* @param type Тип обмена (покупка, продажа и т.д.)
|
||||
* @param status Статус обмена (например, "опубликовано", "на модерации" и т.д.)
|
||||
* @param username Имя пользователя, создавшего заявку
|
||||
* @param token_contract Имя контракта токена
|
||||
* @param unit_cost Цена за единицу товара в заявке
|
||||
* @param remain_units Оставшееся количество товара
|
||||
* @param blocked_units Заблокированное количество товара
|
||||
* @param delivered_units Количество доставленного товара
|
||||
* @param data Дополнительные данные, связанные с заявкой
|
||||
* @param meta Метаданные заявки
|
||||
*
|
||||
* Дополнительные индексы по status, type и parent_id позволяют искать заявки по этим полям.
|
||||
*
|
||||
* Пример использования:
|
||||
* @code
|
||||
* requests_index exchange(_me, coopname.value);
|
||||
* auto exchange_order = exchange.find(id);
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
struct [[eosio::table, eosio::contract(MARKETPLACE)]] request {
|
||||
uint64_t id; /*!< идентификатор обмена */
|
||||
uint64_t parent_id; /*!< идентификатор родительской заявки */
|
||||
uint64_t program_id; /*!< идентификатор программы */
|
||||
eosio::name coopname; /*!< имя аккаунта кооператива */
|
||||
eosio::name type; /*!< тип обмена */
|
||||
eosio::name status; /*!< статус обмена */
|
||||
eosio::name username; /*!< имя аккаунта владельца заявки */
|
||||
eosio::name parent_username; /*!< имя аккаунта владельца объявления */
|
||||
eosio::name token_contract; /*!< имя контракта токена */
|
||||
|
||||
eosio::asset unit_cost;/*!< себестоимость единицы товара от поставщика */
|
||||
eosio::asset supplier_amount; /*!< сумма взноса поставщика */
|
||||
eosio::asset total_cost; /*!< сумма взноса заказчика */
|
||||
eosio::asset membership_fee; /*!< членский взнос заказчика */
|
||||
|
||||
uint64_t remain_units; /*!< оставшееся количество товара */
|
||||
uint64_t blocked_units; /*!< заблокированное количество товара */
|
||||
uint64_t delivered_units; /*!< количество доставленного товара */
|
||||
std::string data; /*!< дополнительные данные */
|
||||
std::string meta; /*!< метаданные заявки */
|
||||
|
||||
eosio::name money_contributor;
|
||||
eosio::name product_contributor;
|
||||
|
||||
document2 contribute_product_statement;
|
||||
document2 return_product_statement;
|
||||
|
||||
uint64_t contribution_product_decision_id;
|
||||
document2 contribution_product_authorization;
|
||||
|
||||
uint64_t return_product_decision_id;
|
||||
document2 return_product_authorization;
|
||||
|
||||
document2 product_contribution_act_validation;
|
||||
document2 product_contribution_act;
|
||||
|
||||
document2 product_recieve_act;
|
||||
document2 product_recieve_act_validation;
|
||||
|
||||
uint64_t product_lifecycle_secs;
|
||||
uint64_t cancellation_fee; //up to 100
|
||||
eosio::asset cancellation_fee_amount;
|
||||
|
||||
eosio::time_point_sec created_at;
|
||||
eosio::time_point_sec accepted_at;
|
||||
eosio::time_point_sec supplied_at;
|
||||
eosio::time_point_sec delivered_at;
|
||||
eosio::time_point_sec recieved_at;
|
||||
eosio::time_point_sec completed_at;
|
||||
eosio::time_point_sec declined_at;
|
||||
eosio::time_point_sec disputed_at;
|
||||
eosio::time_point_sec canceled_at;
|
||||
// eosio::time_point_sec expired_at;
|
||||
|
||||
eosio::time_point_sec warranty_delay_until;
|
||||
eosio::time_point_sec deadline_for_receipt;
|
||||
|
||||
bool is_warranty_return = false;
|
||||
uint64_t warranty_return_id;
|
||||
|
||||
uint64_t primary_key() const { return id; } /*!< return id - primary_key */
|
||||
uint64_t by_coop() const {return coopname.value;} /*!< кооператив */
|
||||
uint64_t by_status() const { return status.value; } /*!< индекс по статусу */
|
||||
uint64_t by_program() const { return program_id; } /*!< индекс по программе */
|
||||
uint64_t by_type() const { return type.value; } /*!< индекс по типу */
|
||||
uint64_t by_parent() const { return parent_id; } /*!< индекс по родительскому ID */
|
||||
uint64_t by_username() const { return username.value;} /*!< индекс по имени аккаунта */
|
||||
uint64_t by_parent_username() const { return parent_username.value;} /*!< индекс по имени аккаунта владельца объявления */
|
||||
|
||||
uint64_t by_created() const { return created_at.sec_since_epoch();}
|
||||
uint64_t by_completed() const { return completed_at.sec_since_epoch();}
|
||||
uint64_t by_declined() const { return declined_at.sec_since_epoch();}
|
||||
uint64_t by_canceled() const { return canceled_at.sec_since_epoch();}
|
||||
uint64_t by_warranty_id() const { return warranty_return_id;}
|
||||
// uint64_t by_expired() const { return expired_at.sec_since_epoch();}
|
||||
};
|
||||
|
||||
typedef eosio::multi_index<
|
||||
"requests"_n, request,
|
||||
eosio::indexed_by<"bycoop"_n, eosio::const_mem_fun<request, uint64_t, &request::by_coop>>,
|
||||
eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun<request, uint64_t, &request::by_status>>,
|
||||
eosio::indexed_by<"bytype"_n, eosio::const_mem_fun<request, uint64_t, &request::by_type>>,
|
||||
eosio::indexed_by<"byprogram"_n, eosio::const_mem_fun<request, uint64_t, &request::by_program>>,
|
||||
eosio::indexed_by<"byparent"_n, eosio::const_mem_fun<request, uint64_t, &request::by_parent>>,
|
||||
eosio::indexed_by<"byusername"_n, eosio::const_mem_fun<request, uint64_t, &request::by_username>>,
|
||||
eosio::indexed_by<"bypausername"_n, eosio::const_mem_fun<request, uint64_t, &request::by_parent_username>>,
|
||||
eosio::indexed_by<"bycreated"_n, eosio::const_mem_fun<request, uint64_t, &request::by_created>>,
|
||||
eosio::indexed_by<"bycompleted"_n, eosio::const_mem_fun<request, uint64_t, &request::by_completed>>,
|
||||
eosio::indexed_by<"bydeclined"_n, eosio::const_mem_fun<request, uint64_t, &request::by_declined>>,
|
||||
eosio::indexed_by<"bycanceled"_n, eosio::const_mem_fun<request, uint64_t, &request::by_canceled>>,
|
||||
eosio::indexed_by<"bywarrantyid"_n, eosio::const_mem_fun<request, uint64_t, &request::by_warranty_id>>
|
||||
// eosio::indexed_by<"byexpired"_n, eosio::const_mem_fun<request, uint64_t, &request::by_expired>>
|
||||
>
|
||||
requests_index; /*!< Тип мультииндекса для таблицы обменов */
|
||||
// Структуры exchange_params и request перенесены в shared_marketplace.hpp
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "utils.hpp"
|
||||
#include "drafts.hpp"
|
||||
#include "accounts.hpp"
|
||||
#include "changes.hpp"
|
||||
#include "coops.hpp"
|
||||
#include "counts.hpp"
|
||||
#include "permissions.hpp"
|
||||
@@ -17,10 +16,13 @@
|
||||
#include "shared_actions.hpp"
|
||||
#include "shared_soviet.hpp"
|
||||
#include "shared_wallet.hpp"
|
||||
#include "shared_fund.hpp"
|
||||
#include "shared_gateway.hpp"
|
||||
#include "shared_ledger.hpp"
|
||||
#include "shared_approver.hpp"
|
||||
#include "shared_registrator.hpp"
|
||||
#include "shared_loan.hpp"
|
||||
#include "shared_branch.hpp"
|
||||
#include "shared_marketplace.hpp"
|
||||
#include "approves.hpp"
|
||||
#include "debts.hpp"
|
||||
@@ -93,7 +93,7 @@ static constexpr eosio::name _result_action = "result"_n;
|
||||
|
||||
//program_types
|
||||
static constexpr eosio::name _wallet_program = "wallet"_n;
|
||||
static constexpr eosio::name _sosedi_program = "cooplace"_n;
|
||||
static constexpr eosio::name _marketplace_program = "marketplace"_n;
|
||||
static constexpr eosio::name _source_program = "source"_n;
|
||||
static constexpr eosio::name _capital_program = "capital"_n;
|
||||
|
||||
@@ -117,11 +117,18 @@ static const std::set<eosio::name> soviet_actions = {
|
||||
|
||||
//WALLET
|
||||
"createwthd"_n, //создать заявление на возврат паевого взноса
|
||||
|
||||
//LEDGER
|
||||
"ledgerwthd"_n, //заявление на списание со счета через ledger
|
||||
|
||||
//MARKETPLACE
|
||||
"authoffs2c"_n, //заявление на взнос имуществом
|
||||
"authoffc2r"_n, //заявление на возврат паевого взноса имуществом
|
||||
};
|
||||
|
||||
//program_ids
|
||||
static constexpr uint64_t _wallet_program_id = 1;
|
||||
static constexpr uint64_t _sosedi_program_id = 2;
|
||||
static constexpr uint64_t _marketplace_program_id = 2;
|
||||
static constexpr uint64_t _capital_program_id = 3;
|
||||
|
||||
static constexpr int64_t REWARD_SCALE = 100000000; ///< Масштабный коэффициент для вознаграждений (1e8).
|
||||
@@ -143,7 +150,10 @@ static constexpr int64_t REWARD_SCALE = 100000000; ///< Масштабный к
|
||||
#define CAPITAL "capital"
|
||||
#define WALLET "wallet"
|
||||
#define LOAN "loan"
|
||||
#define LEDGER "ledger"
|
||||
#define MEET "meet"
|
||||
#define LEDGER "ledger"
|
||||
|
||||
/**
|
||||
* @ingroup public_consts
|
||||
* @{
|
||||
@@ -164,6 +174,7 @@ static constexpr int64_t REWARD_SCALE = 100000000; ///< Масштабный к
|
||||
static constexpr eosio::name _branch = "branch"_n;
|
||||
static constexpr eosio::name _loan = "loan"_n;
|
||||
static constexpr eosio::name _meet = "meet"_n;
|
||||
static constexpr eosio::name _ledger = "ledger"_n;
|
||||
static constexpr eosio::name _power_account = "eosio.power"_n;
|
||||
static constexpr eosio::name _saving_account = "eosio.saving"_n;
|
||||
|
||||
@@ -188,7 +199,8 @@ static constexpr int64_t REWARD_SCALE = 100000000; ///< Масштабный к
|
||||
"eosio.msig"_n,
|
||||
"eosio.wrap"_n,
|
||||
"eosio.power"_n,
|
||||
"eosio.saving"_n
|
||||
"eosio.saving"_n,
|
||||
"ledger"_n
|
||||
// Добавьте остальные стандартные или пользовательские контракты по необходимости
|
||||
};
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ struct [[eosio::table, eosio::contract(SOVIET)]] participant {
|
||||
eosio::name status; //accepted | blocked
|
||||
|
||||
bool is_initial = true; ///< флаг, указывающий, внесен ли регистрационный взнос.
|
||||
bool is_minimum = true; ///< флаг, указывающий, внесен ли минимальный паевый взнос.
|
||||
bool is_minimum = true; ///< флаг, указывающий, внесен ли минимальный паевой взнос.
|
||||
bool has_vote = true; ///< флаг, указывающий, имеет ли член право голоса.
|
||||
|
||||
eosio::binary_extension<eosio::name> type; ///< individual | entrepreneur | organization
|
||||
|
||||
@@ -30,6 +30,8 @@ struct document2 {
|
||||
// Версия стандарта документа
|
||||
std::string version;
|
||||
|
||||
// uint64_t template_id; ///< было бы удобно но этого нет
|
||||
|
||||
// Хэши
|
||||
eosio::checksum256 hash; // общий хэш (doc_hash + meta_hash)
|
||||
eosio::checksum256 doc_hash; // хэш содержимого документа
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
struct [[eosio::table, eosio::contract(SOVIET)]] program {
|
||||
uint64_t id; /*!< идентификатор обмена */
|
||||
uint64_t draft_id; ///< Ссылка на шаблон условий
|
||||
eosio::name program_type; /*!< тип кооперативной программы (wallet | market | ...) */
|
||||
eosio::name program_type; /*!< тип кооперативной программы (wallet | market | capital) */
|
||||
|
||||
eosio::name coopname; /*!< имя аккаунта кооператива */
|
||||
bool is_active;
|
||||
@@ -17,9 +17,9 @@ struct [[eosio::table, eosio::contract(SOVIET)]] program {
|
||||
std::string images;
|
||||
std::string meta;
|
||||
|
||||
eosio::name calculation_type; /*!< тип настройки платежей по программе ( absolute | relative | free ) */
|
||||
uint64_t membership_percent_fee; /*!< процент комиссии со взноса */
|
||||
eosio::asset fixed_membership_contribution; /*!< Членский взнос */
|
||||
eosio::name calculation_type; /*!< LEGACY тип настройки платежей по программе ( absolute | relative | free ) */
|
||||
uint64_t membership_percent_fee; /*!< LEGACY процент комиссии со взноса */
|
||||
eosio::asset fixed_membership_contribution; /*!< LEGACY Членский взнос */
|
||||
|
||||
eosio::time_point_sec start_at; /*!< Время открытия */
|
||||
eosio::time_point_sec expired_at; /*!< Временное ограничение */
|
||||
@@ -120,7 +120,7 @@ struct ProgramInfo {
|
||||
|
||||
static const std::map<eosio::name, ProgramInfo> program_map = {
|
||||
{_wallet_program, {1, 1}}, // program_id = 1, draft_id = 1
|
||||
{_sosedi_program, {2, 699}}, // program_id = 2, draft_id = 699
|
||||
{_marketplace_program, {2, 699}}, // program_id = 2, draft_id = 699
|
||||
{_source_program, {3, 0}}, // program_id = 3, draft_id = 0
|
||||
{_capital_program, {4, 1000}} // program_id = 4, draft_id = 1000
|
||||
};
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
namespace Fund {
|
||||
|
||||
/**
|
||||
* @brief Добавляет средства в циркулирующий фонд кооператива
|
||||
* @param contract Контракт-отправитель (обычно _marketplace)
|
||||
* @param coopname Имя кооператива
|
||||
* @param amount Сумма для добавления
|
||||
*/
|
||||
inline void add_circulating_funds(eosio::name contract, eosio::name coopname, eosio::asset amount) {
|
||||
action(
|
||||
permission_level{ contract, "active"_n },
|
||||
_fund,
|
||||
"addcirculate"_n,
|
||||
std::make_tuple(coopname, amount)
|
||||
).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Списывает средства из циркулирующего фонда кооператива
|
||||
* @param contract Контракт-отправитель (обычно _marketplace)
|
||||
* @param coopname Имя кооператива
|
||||
* @param amount Сумма для списания
|
||||
* @param skip_available_check Пропустить проверку доступных средств
|
||||
*/
|
||||
inline void sub_circulating_funds(eosio::name contract, eosio::name coopname, eosio::asset amount, bool skip_available_check = false) {
|
||||
action(
|
||||
permission_level{ contract, "active"_n },
|
||||
_fund,
|
||||
"subcirculate"_n,
|
||||
std::make_tuple(coopname, amount, skip_available_check)
|
||||
).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Добавляет средства на счёт вступительных взносов кооператива
|
||||
* @param contract Контракт-отправитель (обычно _registrator)
|
||||
* @param coopname Имя кооператива
|
||||
* @param amount Сумма для добавления
|
||||
*/
|
||||
inline void add_initial_funds(eosio::name contract, eosio::name coopname, eosio::asset amount) {
|
||||
action(
|
||||
permission_level{ contract, "active"_n },
|
||||
_fund,
|
||||
"addinitial"_n,
|
||||
std::make_tuple(coopname, amount)
|
||||
).send();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @brief Добавляет членский взнос на накопительный счет кооператива для дальнейшего управления
|
||||
// * @param contract Контракт-отправитель
|
||||
// * @param coopname Имя кооператива
|
||||
// * @param amount Сумма членского взноса
|
||||
// */
|
||||
// inline void accumulate_fee(eosio::name contract, eosio::name coopname, eosio::asset amount) {
|
||||
// action(
|
||||
// permission_level{ contract, "active"_n },
|
||||
// _fund,
|
||||
// "accumfee"_n,
|
||||
// std::make_tuple(coopname, amount)
|
||||
// ).send();
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief Валидные действия ledger для интеграции с другими контрактами
|
||||
*/
|
||||
namespace Ledger {
|
||||
|
||||
static const std::set<eosio::name> ledger_actions = {
|
||||
"add"_n, // пополнение счета
|
||||
"sub"_n, // списание со счета
|
||||
"transfer"_n, // перевод между счетами
|
||||
"create"_n, // создание заявления на списание
|
||||
"auth"_n, // авторизация списания
|
||||
"complete"_n, // завершение операции
|
||||
"decline"_n // отклонение операции
|
||||
};
|
||||
|
||||
inline eosio::name get_valid_ledger_action(const eosio::name& action) {
|
||||
eosio::check(ledger_actions.contains(action), "Недопустимое имя действия ledger");
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Проверка корректности символа валюты для операций ledger
|
||||
* @param amount - проверяемая сумма
|
||||
*/
|
||||
inline void check_ledger_symbol(const eosio::asset& amount) {
|
||||
eosio::check(amount.symbol == _root_govern_symbol, "Некорректный символ валюты для операций ledger");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Проверка положительности суммы для операций ledger
|
||||
* @param amount - проверяемая сумма
|
||||
*/
|
||||
inline void check_positive_amount(const eosio::asset& amount) {
|
||||
eosio::check(amount.is_valid(), "Некорректная сумма");
|
||||
eosio::check(amount.amount > 0, "Сумма должна быть положительной");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Структура бухгалтерского счета
|
||||
*/
|
||||
struct [[eosio::table, eosio::contract(LEDGER)]] laccount {
|
||||
uint64_t id; ///< Идентификатор счета
|
||||
std::string name; ///< Название счета
|
||||
eosio::asset allocation; ///< Аллоцированные средства
|
||||
eosio::asset writeoff; ///< Списанные средства
|
||||
|
||||
uint64_t primary_key() const { return id; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Типы индексов для таблиц ledger
|
||||
*/
|
||||
typedef eosio::multi_index<"accounts"_n, laccount> laccounts_index;
|
||||
|
||||
/**
|
||||
* @brief Интерфейс для пополнения счета ledger из других контрактов
|
||||
* @param coopname - имя кооператива
|
||||
* @param account_id - ID счета
|
||||
* @param quantity - сумма пополнения
|
||||
* @param comment - комментарий
|
||||
*/
|
||||
inline void add(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
|
||||
eosio::action(
|
||||
eosio::permission_level{coopname, "active"_n},
|
||||
_ledger,
|
||||
"add"_n,
|
||||
std::make_tuple(coopname, account_id, quantity, comment)
|
||||
).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Интерфейс для списания со счета ledger из других контрактов
|
||||
* @param coopname - имя кооператива
|
||||
* @param account_id - ID счета
|
||||
* @param quantity - сумма списания
|
||||
* @param comment - комментарий
|
||||
*/
|
||||
inline void sub(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
|
||||
eosio::action(
|
||||
eosio::permission_level{coopname, "active"_n},
|
||||
_ledger,
|
||||
"sub"_n,
|
||||
std::make_tuple(coopname, account_id, quantity, comment)
|
||||
).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Интерфейс для перевода между счетами ledger из других контрактов
|
||||
* @param coopname - имя кооператива
|
||||
* @param from_account_id - ID счета списания
|
||||
* @param to_account_id - ID счета зачисления
|
||||
* @param quantity - сумма перевода
|
||||
* @param comment - комментарий
|
||||
*/
|
||||
inline void transfer(eosio::name coopname, uint64_t from_account_id, uint64_t to_account_id, eosio::asset quantity, std::string comment) {
|
||||
eosio::action(
|
||||
eosio::permission_level{coopname, "active"_n},
|
||||
_ledger,
|
||||
"transfer"_n,
|
||||
std::make_tuple(coopname, from_account_id, to_account_id, quantity, comment)
|
||||
).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Структура для операций ожидающих решения совета
|
||||
*/
|
||||
struct [[eosio::table, eosio::contract(LEDGER)]] writeoff_op {
|
||||
uint64_t id; ///< Идентификатор операции
|
||||
eosio::name coopname; ///< Имя кооператива
|
||||
eosio::name username; ///< Инициатор операции
|
||||
uint64_t account_id; ///< Счет для операции
|
||||
eosio::asset quantity; ///< Сумма операции
|
||||
std::string reason; ///< Обоснование
|
||||
document2 document; ///< Документ обоснования
|
||||
checksum256 writeoff_hash; ///< Хэш операции списания
|
||||
eosio::name status; ///< Статус: pending, approved, declined, paid
|
||||
|
||||
uint64_t primary_key() const { return id; }
|
||||
uint64_t by_coop() const { return coopname.value; }
|
||||
checksum256 by_hash() const { return writeoff_hash; }
|
||||
};
|
||||
|
||||
typedef eosio::multi_index<"writeoffs"_n, writeoff_op,
|
||||
eosio::indexed_by<"bycoop"_n, eosio::const_mem_fun<writeoff_op, uint64_t, &writeoff_op::by_coop>>,
|
||||
eosio::indexed_by<"byhash"_n, eosio::const_mem_fun<writeoff_op, checksum256, &writeoff_op::by_hash>>
|
||||
> writeoffs_index;
|
||||
|
||||
/**
|
||||
* @brief Получает операцию списания по хэшу
|
||||
* @param writeoff_hash Хэш операции списания
|
||||
* @return std::optional<writeoff_op> - найденная операция или std::nullopt, если её нет
|
||||
*/
|
||||
inline std::optional<writeoff_op> get_writeoff_by_hash(const checksum256 &writeoff_hash) {
|
||||
writeoffs_index writeoffs(_ledger, _ledger.value);
|
||||
auto hash_idx = writeoffs.get_index<"byhash"_n>();
|
||||
|
||||
auto op_iter = hash_idx.find(writeoff_hash);
|
||||
if (op_iter == hash_idx.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return *op_iter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Константы счетов для использования в других контрактах
|
||||
*/
|
||||
namespace accounts {
|
||||
// Денежные средства
|
||||
static constexpr uint64_t CASH = 50; // Касса
|
||||
static constexpr uint64_t BANK_ACCOUNT = 51; // Расчетный счет
|
||||
|
||||
// Расчеты с пайщиками
|
||||
static constexpr uint64_t MEMBER_SETTLEMENTS = 75; // Расчеты с пайщиками по внесению/возврату паевых взносов
|
||||
static constexpr uint64_t MEMBER_DEBT = 751; // Задолженность пайщиков по внесению взносов в паевой фонд
|
||||
static constexpr uint64_t INCOME_ACCRUALS = 752; // Начисление доходов участникам от предпринимательской деятельности ПО (кооперативные выплаты)
|
||||
|
||||
// Паевой фонд
|
||||
static constexpr uint64_t SHARE_FUND = 80; // Паевой фонд (складочный капитал)
|
||||
|
||||
// Расчеты с пайщиками (дебиторами и кредиторами)
|
||||
static constexpr uint64_t MEMBER_FEES = 761; // По членским взносам
|
||||
static constexpr uint64_t PROPERTY_TRANSFER = 762; // По передаче имущества для некоммерческой деятельности
|
||||
static constexpr uint64_t OTHER_SETTLEMENTS = 763; // Другие расчеты
|
||||
|
||||
// Расчеты по займам
|
||||
static constexpr uint64_t LOANS_ISSUED = 583; // Расчеты по выданным займам
|
||||
static constexpr uint64_t LOAN_INTEREST = 911; // Внесение процентов за пользование займами
|
||||
|
||||
// Финансовые вложения из средств ПО
|
||||
static constexpr uint64_t SHARES_AND_STAKES = 581; // Доли, паи и акции в организациях, где участвует ПО
|
||||
static constexpr uint64_t SECURITIES = 582; // Облигации (государственные ценные бумаги)
|
||||
|
||||
// Запасы, затраты, расчеты, собственные средства
|
||||
static constexpr uint64_t FIXED_ASSETS = 1; // Основные средства
|
||||
static constexpr uint64_t INTANGIBLE_ASSETS = 4; // Нематериальные активы
|
||||
static constexpr uint64_t MATERIALS_GOODS = 10; // Материалы, товары
|
||||
static constexpr uint64_t MAIN_PRODUCTION = 20; // Основное производство
|
||||
static constexpr uint64_t NON_PROFIT_ACTIVITY = 201; // Некоммерческая деятельность
|
||||
static constexpr uint64_t GENERAL_EXPENSES = 26; // Общехозяйственные расходы (содержание ПО)
|
||||
static constexpr uint64_t RESERVES = 63; // Резервы по сомнительным долгам
|
||||
static constexpr uint64_t LONG_TERM_LOANS = 67; // Расчеты по долгосрочным кредитам и займам
|
||||
static constexpr uint64_t TAXES_FEES = 68; // Расчеты с бюджетом по налогам и сборам
|
||||
static constexpr uint64_t SOCIAL_INSURANCE = 69; // Расчеты по социальному страхованию и обеспечению
|
||||
static constexpr uint64_t SALARY = 70; // Заработная плата
|
||||
static constexpr uint64_t ACCOUNTABLE_PERSONS = 71; // Расчеты с подотчетными лицами
|
||||
static constexpr uint64_t ADDITIONAL_CAPITAL = 83; // Добавочный капитал
|
||||
static constexpr uint64_t FUNDS_PO_1 = 831; // Фонды ПО (вариант пополнения фондов ПО)
|
||||
static constexpr uint64_t CURRENT_YEAR_PROFIT = 841; // Нераспределенная прибыль (убыток) отчетного года
|
||||
static constexpr uint64_t PREVIOUS_YEARS_PROFIT = 842; // Нераспределенная прибыль (непокрытый убыток) прошлых лет
|
||||
static constexpr uint64_t FUNDS_PO_2 = 843; // Фонды ПО (вариант пополнения фондов ПО)
|
||||
static constexpr uint64_t TARGET_RECEIPTS = 86; // Целевые поступления
|
||||
static constexpr uint64_t ENTRANCE_FEES = 861; // Вступительные взносы
|
||||
static constexpr uint64_t RESERVE_FUND = 862; // Резервный фонд
|
||||
static constexpr uint64_t INDIVISIBLE_FUND = 863; // Неделимый фонд
|
||||
static constexpr uint64_t ECONOMIC_ACTIVITY_FUND = 864; // Фонд обеспечения хозяйственной деятельности
|
||||
static constexpr uint64_t MUTUAL_SECURITY_FUND = 865; // Фонд взаимного обеспечения
|
||||
static constexpr uint64_t DEVELOPMENT_FUND = 866; // Фонд развития потребительской кооперации
|
||||
static constexpr uint64_t OTHER_INCOME_EXPENSES = 91; // Прочие доходы и расходы
|
||||
static constexpr uint64_t FUTURE_EXPENSES_RESERVE = 96; // Резерв предстоящих расходов
|
||||
static constexpr uint64_t FREE_RECEIPT = 981; // Безвозмездное получение имущества
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Карта счетов для инициализации бухгалтерской книги
|
||||
*/
|
||||
static const std::vector<std::tuple<uint64_t, std::string>> ACCOUNT_MAP = {
|
||||
{accounts::FIXED_ASSETS, "Основные средства"},
|
||||
{accounts::INTANGIBLE_ASSETS, "Нематериальные активы"},
|
||||
{accounts::MATERIALS_GOODS, "Материалы, товары"},
|
||||
{accounts::MAIN_PRODUCTION, "Основное производство"},
|
||||
{accounts::NON_PROFIT_ACTIVITY, "Некоммерческая деятельность"},
|
||||
{accounts::GENERAL_EXPENSES, "Общехозяйственные расходы"},
|
||||
{accounts::CASH, "Касса"},
|
||||
{accounts::BANK_ACCOUNT, "Расчетный счет"},
|
||||
{accounts::RESERVES, "Резервы по сомнительным долгам"},
|
||||
{accounts::LONG_TERM_LOANS, "Расчеты по долгосрочным кредитам и займам"},
|
||||
{accounts::TAXES_FEES, "Расчеты с бюджетом по налогам и сборам"},
|
||||
{accounts::SOCIAL_INSURANCE, "Расчеты по социальному страхованию и обеспечению"},
|
||||
{accounts::SALARY, "Заработная плата"},
|
||||
{accounts::ACCOUNTABLE_PERSONS, "Расчеты с подотчетными лицами"},
|
||||
{accounts::MEMBER_SETTLEMENTS, "Расчеты с пайщиками по внесению/возврату паевых взносов"},
|
||||
{accounts::SHARE_FUND, "Паевой фонд (складочный капитал)"},
|
||||
{accounts::ADDITIONAL_CAPITAL, "Добавочный капитал"},
|
||||
{accounts::TARGET_RECEIPTS, "Целевые поступления"},
|
||||
{accounts::OTHER_INCOME_EXPENSES, "Прочие доходы и расходы"},
|
||||
{accounts::FUTURE_EXPENSES_RESERVE, "Резерв предстоящих расходов"},
|
||||
{accounts::SHARES_AND_STAKES, "Доли, паи и акции в организациях"},
|
||||
{accounts::SECURITIES, "Облигации (государственные ценные бумаги)"},
|
||||
{accounts::LOANS_ISSUED, "Расчеты по выданным займам"},
|
||||
{accounts::MEMBER_DEBT, "Задолженность пайщиков по внесению взносов в паевой фонд"},
|
||||
{accounts::INCOME_ACCRUALS, "Начисление доходов участникам от предпринимательской деятельности ПО"},
|
||||
{accounts::MEMBER_FEES, "По членским взносам"},
|
||||
{accounts::PROPERTY_TRANSFER, "По передаче имущества для некоммерческой деятельности"},
|
||||
{accounts::OTHER_SETTLEMENTS, "Другие расчеты"},
|
||||
{accounts::ENTRANCE_FEES, "Вступительные взносы"},
|
||||
{accounts::FUNDS_PO_1, "Фонды ПО (вариант пополнения фондов ПО)"},
|
||||
{accounts::CURRENT_YEAR_PROFIT, "Нераспределенная прибыль (убыток) отчетного года"},
|
||||
{accounts::PREVIOUS_YEARS_PROFIT, "Нераспределенная прибыль (непокрытый убыток) прошлых лет"},
|
||||
{accounts::FUNDS_PO_2, "Фонды ПО (вариант пополнения фондов ПО)"},
|
||||
{accounts::RESERVE_FUND, "Резервный фонд"},
|
||||
{accounts::INDIVISIBLE_FUND, "Неделимый фонд"},
|
||||
{accounts::ECONOMIC_ACTIVITY_FUND, "Фонд обеспечения хозяйственной деятельности"},
|
||||
{accounts::MUTUAL_SECURITY_FUND, "Фонд взаимного обеспечения"},
|
||||
{accounts::DEVELOPMENT_FUND, "Фонд развития потребительской кооперации"},
|
||||
{accounts::LOAN_INTEREST, "Внесение процентов за пользование займами"},
|
||||
{accounts::FREE_RECEIPT, "Безвозмездное получение имущества"}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
#pragma once
|
||||
|
||||
// Сигнатуры для callback действий marketplace
|
||||
#define AUTH_SIGNATURE eosio::name coopname, checksum256 request_hash, document2 authorization
|
||||
|
||||
using auth_interface = void(AUTH_SIGNATURE);
|
||||
|
||||
|
||||
namespace Marketplace {
|
||||
|
||||
/**
|
||||
* @brief Сегмент поставки - унифицированная структура для работы с документами
|
||||
* @ingroup public_tables
|
||||
*
|
||||
* Сегмент представляет собой путь поставки от пайщика в кооператив или из кооператива до пайщика
|
||||
*
|
||||
* Типы сегментов:
|
||||
* - "contribute" - сегмент поставки имущества в кооператив
|
||||
* - "return" - сегмент возврата имущества из кооператива
|
||||
* - "wreturn" - сегмент возврата по гарантии (от заказчика в кооператив)
|
||||
* - "wsupply" - сегмент выдачи по гарантии (из кооператива поставщику)
|
||||
*/
|
||||
struct [[eosio::table, eosio::contract(MARKETPLACE)]] segment {
|
||||
uint64_t id; /*!< идентификатор сегмента */
|
||||
uint64_t request_id; /*!< идентификатор заявки, к которой относится сегмент */
|
||||
eosio::name type; /*!< тип сегмента: "contribute", "return", "wreturn", "wsupply" */
|
||||
eosio::name status; /*!< статус сегмента */
|
||||
|
||||
document2 convert_in; /*!< заявление на конвертацию из кошелька */
|
||||
document2 statement; /*!< заявление/документ на взнос или возврат */
|
||||
uint64_t decision_id; /*!< идентификатор решения */
|
||||
document2 authorization; /*!< документ авторизации */
|
||||
document2 act1; /*!< акт приёма-передачи */
|
||||
document2 act2; /*!< акт приёма-передачи */
|
||||
document2 convert_out; /*!< заявление на конвертацию в кошелек */
|
||||
|
||||
// Документы транспортировки
|
||||
document2 transport_act_1; /*!< акт товарно-транспортной накладной - передача имущества на транспортировку */
|
||||
document2 transport_act_2; /*!< акт товарно-транспортной накладной - прием имущества водителем */
|
||||
document2 transport_act_3; /*!< акт товарно-транспортной накладной - доставка до КУ получателя */
|
||||
document2 transport_act_4; /*!< акт товарно-транспортной накладной - прием имущества председателем КУ */
|
||||
|
||||
eosio::name coopactor; /*!< представитель кооператива, который действует в этом сегменте */
|
||||
eosio::name username; /*!< пользователь, который действует в этом сегменте */
|
||||
eosio::name driver_username; /*!< водитель-пайщик, который принимает имущество на транспортировку */
|
||||
eosio::name receive_from_driver_coopactor; /*!< председатель КУ, который принимает имущество от водителя */
|
||||
|
||||
eosio::time_point_sec created_at;
|
||||
eosio::time_point_sec updated_at;
|
||||
|
||||
uint64_t primary_key() const { return id; }
|
||||
uint64_t by_request() const { return request_id; }
|
||||
uint64_t by_type() const { return type.value; }
|
||||
uint64_t by_status() const { return status.value; }
|
||||
};
|
||||
|
||||
typedef eosio::multi_index<
|
||||
"segments"_n, segment,
|
||||
eosio::indexed_by<"byrequest"_n, eosio::const_mem_fun<segment, uint64_t, &segment::by_request>>,
|
||||
eosio::indexed_by<"bytype"_n, eosio::const_mem_fun<segment, uint64_t, &segment::by_type>>,
|
||||
eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun<segment, uint64_t, &segment::by_status>>
|
||||
> segments_index;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Таблица обменов для контракта "marketplace"
|
||||
* @ingroup public_tables
|
||||
*/
|
||||
struct [[eosio::table, eosio::contract(MARKETPLACE)]] request {
|
||||
uint64_t id; /*!< идентификатор обмена */
|
||||
checksum256 hash; /*!< хэш заявки */
|
||||
uint64_t parent_id; /*!< идентификатор родительской заявки */
|
||||
checksum256 parent_hash; /*!< хэш родительской заявки */
|
||||
eosio::name coopname; /*!< имя аккаунта кооператива */
|
||||
eosio::name type; /*!< тип обмена */
|
||||
eosio::name status; /*!< статус обмена */
|
||||
eosio::name username; /*!< имя аккаунта владельца заявки */
|
||||
eosio::name parent_username; /*!< имя аккаунта владельца объявления */
|
||||
eosio::name braname; /*!< имя кооперативного участка */
|
||||
eosio::name token_contract; /*!< имя контракта токена */
|
||||
|
||||
eosio::asset unit_cost;/*!< себестоимость единицы товара от поставщика */
|
||||
eosio::asset base_cost; /*!< базовая стоимость заявки */
|
||||
eosio::asset membership_fee_amount; /*!< членский взнос заказчика */
|
||||
eosio::asset total_cost; /*!< общая сумма заявки */
|
||||
|
||||
uint64_t remaining_units; /*!< оставшееся количество единиц товара */
|
||||
uint64_t blocked_units; /*!< заблокированное количество единиц товара */
|
||||
uint64_t supplied_units; /*!< количество доставленных и принятых единиц товара */
|
||||
std::string meta; /*!< метаданные заявки */
|
||||
|
||||
eosio::name money_contributor; /*!< имя аккаунта, который вносит средства */
|
||||
eosio::name product_contributor; /*!< имя аккаунта, который передаёт товар */
|
||||
|
||||
uint64_t product_lifecycle_secs; // жизненный цикл продукта
|
||||
uint64_t warranty_period_secs; // гарантийный срок в секундах
|
||||
eosio::asset cancellation_fee_amount; // сумма штрафа за отмену заявки
|
||||
|
||||
eosio::time_point_sec warranty_delay_until;
|
||||
eosio::time_point_sec deadline_for_receipt;
|
||||
|
||||
bool is_warranty_return = false;
|
||||
uint64_t warranty_return_id;
|
||||
|
||||
eosio::time_point_sec created_at;
|
||||
eosio::time_point_sec accepted_at;
|
||||
eosio::time_point_sec supplied_at;
|
||||
eosio::time_point_sec delivered_at;
|
||||
eosio::time_point_sec received_at;
|
||||
eosio::time_point_sec completed_at;
|
||||
eosio::time_point_sec declined_at;
|
||||
eosio::time_point_sec disputed_at;
|
||||
eosio::time_point_sec canceled_at;
|
||||
|
||||
|
||||
uint64_t primary_key() const { return id; }
|
||||
uint64_t by_coop() const {return coopname.value;}
|
||||
uint64_t by_status() const { return status.value; }
|
||||
uint64_t by_type() const { return type.value; }
|
||||
uint64_t by_parent() const { return parent_id; }
|
||||
checksum256 by_hash() const { return hash; }
|
||||
checksum256 by_parent_hash() const { return parent_hash; }
|
||||
uint64_t by_username() const { return username.value;}
|
||||
uint64_t by_parent_username() const { return parent_username.value;}
|
||||
|
||||
uint64_t by_created() const { return created_at.sec_since_epoch();}
|
||||
uint64_t by_completed() const { return completed_at.sec_since_epoch();}
|
||||
uint64_t by_declined() const { return declined_at.sec_since_epoch();}
|
||||
uint64_t by_canceled() const { return canceled_at.sec_since_epoch();}
|
||||
uint64_t by_warranty_id() const { return warranty_return_id;}
|
||||
};
|
||||
|
||||
typedef eosio::multi_index<
|
||||
"requests"_n, request,
|
||||
eosio::indexed_by<"bycoop"_n, eosio::const_mem_fun<request, uint64_t, &request::by_coop>>,
|
||||
eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun<request, uint64_t, &request::by_status>>,
|
||||
eosio::indexed_by<"bytype"_n, eosio::const_mem_fun<request, uint64_t, &request::by_type>>,
|
||||
eosio::indexed_by<"byparent"_n, eosio::const_mem_fun<request, uint64_t, &request::by_parent>>,
|
||||
eosio::indexed_by<"byhash"_n, eosio::const_mem_fun<request, checksum256, &request::by_hash>>,
|
||||
eosio::indexed_by<"byparenthash"_n, eosio::const_mem_fun<request, checksum256, &request::by_parent_hash>>,
|
||||
eosio::indexed_by<"byusername"_n, eosio::const_mem_fun<request, uint64_t, &request::by_username>>,
|
||||
eosio::indexed_by<"bypausername"_n, eosio::const_mem_fun<request, uint64_t, &request::by_parent_username>>,
|
||||
eosio::indexed_by<"bycreated"_n, eosio::const_mem_fun<request, uint64_t, &request::by_created>>,
|
||||
eosio::indexed_by<"bycompleted"_n, eosio::const_mem_fun<request, uint64_t, &request::by_completed>>,
|
||||
eosio::indexed_by<"bydeclined"_n, eosio::const_mem_fun<request, uint64_t, &request::by_declined>>,
|
||||
eosio::indexed_by<"bycanceled"_n, eosio::const_mem_fun<request, uint64_t, &request::by_canceled>>,
|
||||
eosio::indexed_by<"bywarrantyid"_n, eosio::const_mem_fun<request, uint64_t, &request::by_warranty_id>>
|
||||
> requests_index;
|
||||
|
||||
static const std::set<eosio::name> marketplace_callback_actions = {
|
||||
"authoffs2c"_n, // авторизация сегмента contribute для направления OFFER → ORDER
|
||||
"authoffc2r"_n, // авторизация сегмента return для направления OFFER → ORDER
|
||||
"authordcont"_n, // авторизация сегмента contribute для направления ORDER → OFFER
|
||||
"authordret"_n, // авторизация сегмента return для направления ORDER → OFFER
|
||||
"declineacc"_n, // отклонение принятия заявки
|
||||
};
|
||||
|
||||
inline eosio::name get_valid_marketplace_action(const eosio::name& action) {
|
||||
eosio::check(marketplace_callback_actions.contains(action), "Недопустимое имя действия marketplace");
|
||||
return action;
|
||||
}
|
||||
|
||||
// Вспомогательные функции для поиска заявок по хэшу
|
||||
static std::optional<request> get_request_by_hash(eosio::name coopname, checksum256 request_hash) {
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto idx = requests.get_index<"byhash"_n>();
|
||||
auto req = idx.find(request_hash);
|
||||
|
||||
if (req != idx.end()) {
|
||||
return *req;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static request get_request_by_hash_or_fail(eosio::name coopname, checksum256 request_hash, const std::string& error_msg = "Заявка не найдена по хэшу") {
|
||||
auto request_opt = get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(request_opt.has_value(), error_msg);
|
||||
return *request_opt;
|
||||
}
|
||||
|
||||
static std::vector<request> get_requests_by_parent_hash(eosio::name coopname, checksum256 parent_hash) {
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto idx = requests.get_index<"byparenthash"_n>();
|
||||
auto lower = idx.lower_bound(parent_hash);
|
||||
auto upper = idx.upper_bound(parent_hash);
|
||||
|
||||
std::vector<request> result;
|
||||
for (auto itr = lower; itr != upper; ++itr) {
|
||||
result.push_back(*itr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Marketplace
|
||||
@@ -123,6 +123,33 @@ namespace Wallet {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Добавляет членский взнос в программу для дальнейшего списания
|
||||
* @param contract Контракт-отправитель
|
||||
* @param coopname Имя кооператива
|
||||
* @param username Имя пользователя
|
||||
* @param program_id ID программы
|
||||
* @param amount Сумма членского взноса
|
||||
* @param memo Мемо для операции
|
||||
*/
|
||||
inline void add_member_fee(eosio::name contract, eosio::name coopname, eosio::name username, uint64_t program_id, eosio::asset amount, std::string memo) {
|
||||
// Сначала добавляем в программе для учета
|
||||
action(
|
||||
permission_level{ contract, "active"_n },
|
||||
_soviet,
|
||||
"addmemberfee"_n,
|
||||
std::make_tuple(coopname, username, program_id, amount, memo)
|
||||
).send();
|
||||
|
||||
// Затем добавляем на накопительный счет кооператива для дальнейшего управления
|
||||
action(
|
||||
permission_level{ contract, "active"_n },
|
||||
_fund,
|
||||
"accumfee"_n,
|
||||
std::make_tuple(coopname, amount)
|
||||
).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup public_tables
|
||||
* @brief Таблица `deposits` отслеживает депозиты в контракте WALLET.
|
||||
|
||||
@@ -57,4 +57,15 @@ uint64_t generate() {
|
||||
}
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
std::string checksum256_to_hex(const eosio::checksum256& hash) {
|
||||
auto hash_bytes = hash.extract_as_byte_array();
|
||||
std::string hash_hex;
|
||||
for (const auto& byte : hash_bytes) {
|
||||
char hex_char[3];
|
||||
sprintf(hex_char, "%02x", byte);
|
||||
hash_hex += hex_char;
|
||||
}
|
||||
return hash_hex;
|
||||
}
|
||||
@@ -11,17 +11,8 @@
|
||||
### Структуры
|
||||
|
||||
- `balance`: Описывает баланс пользователя.
|
||||
- `exchange`: Описывает информацию об обмене.
|
||||
- `exchange_params`: Параметры обмена, включая имя пользователя, контракт, количество единиц, цену за штуку и дополнительные данные и метаданные.
|
||||
|
||||
### Действия
|
||||
|
||||
Контракт включает в себя следующие действия:
|
||||
|
||||
- **Действия обмена**: `offer`, `order`, `complete`, `decline`, `accept`.
|
||||
- **Административные действия**: `moderate`, `prohibit`, `unpublish`, `publish`.
|
||||
- **Действия авторизации**: `authorize`.
|
||||
- **Прочие действия**: `newid` (используется для возврата информации из контракта), `update`, `addpieces`.
|
||||
- `requests`: Описывает информацию об обмене.
|
||||
- `requests_params`: Параметры обмена, включая имя пользователя, контракт, количество единиц, цену за штуку и дополнительные данные и метаданные.
|
||||
|
||||
## Использование
|
||||
|
||||
|
||||
@@ -1,36 +1,46 @@
|
||||
#include "marketplace.hpp"
|
||||
#include <eosio/transaction.hpp>
|
||||
#include "src/accept.cpp"
|
||||
#include "src/addunits.cpp"
|
||||
#include "src/authorize.cpp"
|
||||
#include "src/cancel.cpp"
|
||||
#include "src/change.cpp"
|
||||
#include "src/complete.cpp"
|
||||
#include "src/decline.cpp"
|
||||
#include "src/delivered.cpp"
|
||||
#include "src/dispute.cpp"
|
||||
#include "src/moderate.cpp"
|
||||
#include "src/offer.cpp"
|
||||
#include "src/order.cpp"
|
||||
#include "src/prohibit.cpp"
|
||||
#include "src/publish.cpp"
|
||||
#include "src/recieve.cpp"
|
||||
#include "src/recievecnfrm.cpp"
|
||||
#include "src/supply.cpp"
|
||||
#include "src/supplycnfrm.cpp"
|
||||
#include "src/unpublish.cpp"
|
||||
#include "src/update.cpp"
|
||||
|
||||
/**
|
||||
* @brief Пустой метод регистрации нового идентификатора
|
||||
* @ingroup public_actions
|
||||
* Этот метод используется для возврата информации из контракта.
|
||||
* @param id идентификатор
|
||||
* @param type тип идентификатора
|
||||
*/
|
||||
[[eosio::action]] void marketplace::newid(uint64_t id, eosio::name type) {
|
||||
require_auth(_marketplace);
|
||||
};
|
||||
|
||||
// Процесс поставки OFFER → ORDER (deliver_on_offer)
|
||||
#include "src/deliver_on_offer/parentoffer.cpp"
|
||||
#include "src/deliver_on_offer/childorder.cpp"
|
||||
#include "src/deliver_on_offer/acceptoff.cpp"
|
||||
#include "src/deliver_on_offer/authoffs2c.cpp"
|
||||
#include "src/deliver_on_offer/authoffc2r.cpp"
|
||||
#include "src/deliver_on_offer/declineacc.cpp"
|
||||
#include "src/deliver_on_offer/supplyoff.cpp"
|
||||
#include "src/deliver_on_offer/supplcnfoff.cpp"
|
||||
#include "src/deliver_on_offer/deliveroff1.cpp"
|
||||
#include "src/deliver_on_offer/deliveroff2.cpp"
|
||||
#include "src/deliver_on_offer/deliveroff3.cpp"
|
||||
#include "src/deliver_on_offer/deliveroff4.cpp"
|
||||
#include "src/deliver_on_offer/recvoff.cpp"
|
||||
#include "src/deliver_on_offer/recvcnfoff.cpp"
|
||||
#include "src/deliver_on_offer/completeoff.cpp"
|
||||
#include "src/deliver_on_offer/declineoff.cpp"
|
||||
#include "src/deliver_on_offer/cancel.cpp"
|
||||
// Процесс поставки ORDER → OFFER (deliver_on_order)
|
||||
// #include "src/deliver_on_order/parentorder.cpp"
|
||||
// #include "src/deliver_on_order/childoffer.cpp"
|
||||
// #include "src/deliver_on_order/acceptord.cpp"
|
||||
// #include "src/deliver_on_order/authords2c.cpp"
|
||||
// #include "src/deliver_on_order/authordc2r.cpp"
|
||||
// #include "src/deliver_on_order/supplyord.cpp"
|
||||
// #include "src/deliver_on_order/supplcnford.cpp"
|
||||
// #include "src/deliver_on_order/deliverord.cpp"
|
||||
// #include "src/deliver_on_order/recvord.cpp"
|
||||
// #include "src/deliver_on_order/recvcnford.cpp"
|
||||
// #include "src/deliver_on_order/completeord.cpp"
|
||||
// #include "src/deliver_on_order/declineord.cpp"
|
||||
|
||||
|
||||
// Диспуты
|
||||
#include "src/dispute_on_offer/dispute.cpp"
|
||||
#include "src/dispute_on_offer/wauthorize.cpp"
|
||||
#include "src/dispute_on_offer/wreturn.cpp"
|
||||
#include "src/dispute_on_offer/woffer.cpp"
|
||||
#include "src/dispute_on_offer/waccept.cpp"
|
||||
|
||||
|
||||
[[eosio::action]] void marketplace::migrate(){
|
||||
|
||||
@@ -8,6 +8,16 @@
|
||||
|
||||
#include "../lib/common.hpp"
|
||||
|
||||
using namespace Marketplace;
|
||||
|
||||
// Инициализация статических констант
|
||||
const std::vector<eosio::name> valid_segment_types = {
|
||||
"s2c"_n, // supplier to coop (поставщик → кооператив)
|
||||
"c2r"_n, // coop to receiver (кооператив → получатель)
|
||||
"wreturn"_n, // warranty return (гарантийный возврат)
|
||||
"wsupply"_n // warranty supply (гарантийная поставка)
|
||||
};
|
||||
|
||||
/**
|
||||
* \ingroup public_contracts
|
||||
* @brief Класс `marketplace` предоставляет функционал кооперативного маркетплейса, позволяя пользователям
|
||||
@@ -20,7 +30,20 @@
|
||||
* - Модерация и управление публикацией заявок на обмен.
|
||||
* - Административные функции, такие как создание идентификаторов и авторизация операций.
|
||||
*
|
||||
* ## Направления процессов поставки:
|
||||
*
|
||||
* ### OFFER → ORDER (deliver_on_offer)
|
||||
* - Parent: имущественный паевой взнос (поставщик создает предложение)
|
||||
* - Child: денежный паевой взнос (заказчик создает встречную заявку)
|
||||
* - При accept заказчик подает: заявление на конвертацию + заявление на возврат паевого взноса
|
||||
*
|
||||
* ### ORDER → OFFER (deliver_on_order)
|
||||
* - Parent: денежный паевой взнос (заказчик создает заказ)
|
||||
* - Child: имущественный паевой взнос (поставщик создает встречную заявку)
|
||||
* - При accept поставщик подает: заявление на взнос имуществом + заявление на конвертацию
|
||||
*
|
||||
* \note Контракт маркетплейса является центральной точкой экономической активности на платформе.
|
||||
* \note Каждое направление имеет свой набор методов с префиксами "offer" и "order" соответственно.
|
||||
*/
|
||||
class [[eosio::contract(MARKETPLACE)]] marketplace : public eosio::contract {
|
||||
|
||||
@@ -29,54 +52,202 @@ public:
|
||||
eosio::datastream<const char *> ds)
|
||||
: eosio::contract(receiver, code, ds) {}
|
||||
|
||||
|
||||
void apply(uint64_t receiver, uint64_t code, uint64_t action);
|
||||
[[eosio::action]] void migrate();
|
||||
|
||||
// Утилитарные функции для работы с сегментами
|
||||
static segment get_segment(eosio::name coopname, uint64_t segment_id) {
|
||||
segments_index segments(_marketplace, coopname.value);
|
||||
auto seg = segments.find(segment_id);
|
||||
eosio::check(seg != segments.end(), "Сегмент не найден");
|
||||
return *seg;
|
||||
}
|
||||
|
||||
static segment get_segment_by_request_and_type(eosio::name coopname, uint64_t request_id, eosio::name type) {
|
||||
segments_index segments(_marketplace, coopname.value);
|
||||
auto idx = segments.get_index<"byrequest"_n>();
|
||||
auto lower = idx.lower_bound(request_id);
|
||||
auto upper = idx.upper_bound(request_id);
|
||||
|
||||
for (auto itr = lower; itr != upper; ++itr) {
|
||||
if (itr->type == type) {
|
||||
return *itr;
|
||||
}
|
||||
}
|
||||
|
||||
eosio::check(false, "Сегмент не найден для заявки и типа");
|
||||
return segment{}; // никогда не достигнется
|
||||
}
|
||||
|
||||
static uint64_t create_segment(eosio::name coopname, uint64_t request_id, eosio::name type) {
|
||||
segments_index segments(_marketplace, coopname.value);
|
||||
uint64_t id = get_global_id(_marketplace, "segment"_n);
|
||||
|
||||
segments.emplace(_marketplace, [&](auto &s) {
|
||||
s.id = id;
|
||||
s.request_id = request_id;
|
||||
s.type = type;
|
||||
s.status = "created"_n;
|
||||
s.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
s.updated_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static uint64_t create_segment(eosio::name coopname, uint64_t request_id, eosio::name type, eosio::name username) {
|
||||
segments_index segments(_marketplace, coopname.value);
|
||||
uint64_t id = get_global_id(_marketplace, "segment"_n);
|
||||
|
||||
segments.emplace(_marketplace, [&](auto &s) {
|
||||
s.id = id;
|
||||
s.request_id = request_id;
|
||||
s.type = type;
|
||||
s.status = "created"_n;
|
||||
s.username = username;
|
||||
s.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
s.updated_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static void update_segment(eosio::name coopname, uint64_t segment_id, std::function<void(segment&)> updater) {
|
||||
segments_index segments(_marketplace, coopname.value);
|
||||
auto seg = segments.find(segment_id);
|
||||
eosio::check(seg != segments.end(), "Сегмент не найден");
|
||||
|
||||
segments.modify(seg, _marketplace, [&](auto &s) {
|
||||
updater(s);
|
||||
s.updated_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
}
|
||||
|
||||
static void update_segment_by_request_and_type(eosio::name coopname, uint64_t request_id, eosio::name type, std::function<void(segment&)> updater) {
|
||||
segments_index segments(_marketplace, coopname.value);
|
||||
auto idx = segments.get_index<"byrequest"_n>();
|
||||
auto lower = idx.lower_bound(request_id);
|
||||
auto upper = idx.upper_bound(request_id);
|
||||
|
||||
for (auto itr = lower; itr != upper; ++itr) {
|
||||
if (itr->type == type) {
|
||||
idx.modify(itr, _marketplace, [&](auto &s) {
|
||||
updater(s);
|
||||
s.updated_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
eosio::check(false, "Сегмент не найден для обновления");
|
||||
}
|
||||
|
||||
static void delete_segments_by_request(eosio::name coopname, uint64_t request_id) {
|
||||
segments_index segments(_marketplace, coopname.value);
|
||||
auto idx = segments.get_index<"byrequest"_n>();
|
||||
auto lower = idx.lower_bound(request_id);
|
||||
auto upper = idx.upper_bound(request_id);
|
||||
|
||||
// Создаем вектор для хранения итераторов, чтобы избежать инвалидации
|
||||
std::vector<segments_index::const_iterator> to_delete;
|
||||
for (auto itr = lower; itr != upper; ++itr) {
|
||||
to_delete.push_back(segments.find(itr->id));
|
||||
}
|
||||
|
||||
// Удаляем все найденные сегменты
|
||||
for (auto itr : to_delete) {
|
||||
segments.erase(itr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Функция валидации имени сегмента
|
||||
static eosio::name valid_segment(const std::string& segment_name) {
|
||||
eosio::name segment_type(segment_name);
|
||||
|
||||
// Проверяем, что сегмент входит в список допустимых
|
||||
bool is_valid = false;
|
||||
for (const auto& valid_type : valid_segment_types) {
|
||||
if (valid_type == segment_type) {
|
||||
is_valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
eosio::check(is_valid, "Недопустимый тип сегмента: " + segment_name);
|
||||
return segment_type;
|
||||
}
|
||||
|
||||
// Проверка корректности единиц товара (инварианты)
|
||||
static void check_units_invariant(const request& req, const std::string& operation) {
|
||||
// Проверяем, что количество единиц не отрицательное
|
||||
eosio::check(req.remaining_units >= 0,
|
||||
"Инвариант нарушен в операции " + operation + ": remaining_units не может быть отрицательным");
|
||||
|
||||
eosio::check(req.blocked_units >= 0,
|
||||
"Инвариант нарушен в операции " + operation + ": blocked_units не может быть отрицательным");
|
||||
|
||||
eosio::check(req.supplied_units >= 0,
|
||||
"Инвариант нарушен в операции " + operation + ": supplied_units не может быть отрицательным");
|
||||
}
|
||||
|
||||
// ... определение методов контракта ...
|
||||
|
||||
// Действия для создания заявок
|
||||
[[eosio::action]] void parentoffer(eosio::name coopname, eosio::name braname, eosio::name username, checksum256 hash, uint64_t units, eosio::asset unit_cost, uint32_t product_lifecycle_secs, uint32_t warranty_period_secs, eosio::asset membership_fee_amount, eosio::asset cancellation_fee_amount, std::string meta);
|
||||
[[eosio::action]] void childorder(eosio::name coopname, eosio::name braname, eosio::name username, checksum256 hash, checksum256 parent_hash, uint64_t units, eosio::asset unit_cost, document2 statement, document2 convert_in, std::string meta);
|
||||
|
||||
//marketplace.cpp
|
||||
[[eosio::action]] void newid(uint64_t id, eosio::name type);
|
||||
|
||||
//soviet.cpp
|
||||
[[eosio::action]] void authorize(eosio::name coopname, uint64_t exchange_id, uint64_t contribution_product_decision_id, document2 contribution_product_authorization, uint64_t return_product_decision_id, document2 return_product_authorization);
|
||||
// [[eosio::action]] void parentorder(eosio::name coopname, eosio::name braname, eosio::name username, checksum256 hash, uint64_t units, eosio::asset unit_cost, eosio::asset membership_fee_amount, eosio::asset cancellation_fee_amount, document2 convert_in, std::string meta);
|
||||
// [[eosio::action]] void childoffer(eosio::name coopname, eosio::name braname, eosio::name username, checksum256 hash, checksum256 parent_hash, uint64_t units, eosio::asset unit_cost, uint32_t product_lifecycle_secs, document2 document, std::string meta);
|
||||
static void cancel_parent(eosio::name coopname, eosio::name username, checksum256 request_hash);
|
||||
static void cancel_child(eosio::name coopname, eosio::name username, checksum256 request_hash);
|
||||
|
||||
//change.cpp
|
||||
[[eosio::action]] void offer(const exchange_params& params);
|
||||
[[eosio::action]] void order(const exchange_params& params);
|
||||
// Статические методы для отклонения заявок
|
||||
static void decline_child_request(eosio::name coopname, const request& child_change);
|
||||
static void decline_parent_request(eosio::name coopname, const request& parent_change);
|
||||
static void decline_parent_and_children(eosio::name coopname, const request& parent_change);
|
||||
static void decline_child_simple(eosio::name coopname, const request& child_change);
|
||||
|
||||
static void create(eosio::name type, const exchange_params& params);
|
||||
static void create_parent(eosio::name type, const exchange_params& params);
|
||||
static void create_child(eosio::name type, const exchange_params& params);
|
||||
static void cancel_parent(eosio::name coopname, eosio::name username, uint64_t exchange_id);
|
||||
static void cancel_child(eosio::name coopname, eosio::name username, uint64_t exchange_id);
|
||||
// Методы для направления OFFER → ORDER (deliver_on_offer)
|
||||
[[eosio::action]] void acceptoff(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 convert_out, document2 return_document);
|
||||
[[eosio::action]] void authoffs2c(eosio::name coopname, checksum256 request_hash, document2 authorization);
|
||||
[[eosio::action]] void authoffc2r(eosio::name coopname, checksum256 request_hash, document2 authorization);
|
||||
[[eosio::action]] void declineacc(eosio::name coopname, checksum256 hash, std::string reason);
|
||||
[[eosio::action]] void supplyoff(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 act1);
|
||||
[[eosio::action]] void supplcnfoff(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 act2);
|
||||
[[eosio::action]] void deliveroff1(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 transport_act_1);
|
||||
[[eosio::action]] void deliveroff2(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 transport_act_2);
|
||||
[[eosio::action]] void deliveroff3(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 transport_act_3);
|
||||
[[eosio::action]] void deliveroff4(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 transport_act_4);
|
||||
[[eosio::action]] void recvoff(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
|
||||
[[eosio::action]] void recvcnfoff(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
|
||||
[[eosio::action]] void completeoff(eosio::name coopname, eosio::name username, checksum256 request_hash);
|
||||
[[eosio::action]] void declineoff(eosio::name coopname, eosio::name username, checksum256 request_hash, std::string meta);
|
||||
|
||||
[[eosio::action]] void accept(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document);
|
||||
[[eosio::action]] void decline(eosio::name coopname, eosio::name username, uint64_t exchange_id, std::string meta);
|
||||
// Методы для направления ORDER → OFFER (deliver_on_order)
|
||||
// [[eosio::action]] void acceptord(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 contribution_document, document2 conversion_document);
|
||||
// [[eosio::action]] void authords2c(eosio::name coopname, checksum256 request_hash, document2 authorization);
|
||||
// [[eosio::action]] void authordc2r(eosio::name coopname, checksum256 request_hash, document2 authorization);
|
||||
// [[eosio::action]] void supplyord(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
|
||||
// [[eosio::action]] void supplcnford(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
|
||||
// [[eosio::action]] void deliverord(eosio::name coopname, eosio::name username, checksum256 request_hash);
|
||||
// [[eosio::action]] void recvord(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
|
||||
// [[eosio::action]] void recvcnford(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
|
||||
// [[eosio::action]] void completeord(eosio::name coopname, eosio::name username, checksum256 request_hash);
|
||||
// [[eosio::action]] void declineord(eosio::name coopname, eosio::name username, checksum256 request_hash, std::string meta);
|
||||
|
||||
[[eosio::action]] void supplycnfrm(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document);
|
||||
[[eosio::action]] void supply(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document);
|
||||
[[eosio::action]] void delivered(eosio::name coopname, eosio::name username, uint64_t exchange_id);
|
||||
[[eosio::action]] void recieve(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document);
|
||||
[[eosio::action]] void recievecnfrm(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document);
|
||||
|
||||
[[eosio::action]] void dispute(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document);
|
||||
|
||||
[[eosio::action]] void complete(eosio::name coopname, eosio::name username, uint64_t exchange_id);
|
||||
|
||||
[[eosio::action]] void cancel(eosio::name coopname, eosio::name username, uint64_t exchange_id);
|
||||
|
||||
[[eosio::action]] void update(eosio::name coopname, eosio::name username, uint64_t exchange_id, uint64_t remain_units, eosio::asset unit_cost, std::string data, std::string meta);
|
||||
[[eosio::action]] void addunits(eosio::name coopname, eosio::name username, uint64_t exchange_id, uint64_t units);
|
||||
|
||||
//admins.cpp
|
||||
[[eosio::action]] void moderate(eosio::name coopname, eosio::name username, uint64_t exchange_id, uint64_t cancellation_fee);
|
||||
[[eosio::action]] void prohibit(eosio::name coopname, eosio::name username, uint64_t exchange_id, std::string meta);
|
||||
[[eosio::action]] void unpublish(eosio::name coopname, eosio::name username, uint64_t exchange_id);
|
||||
[[eosio::action]] void publish(eosio::name coopname, eosio::name username, uint64_t exchange_id);
|
||||
// Диспуты
|
||||
[[eosio::action]] void dispute(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
|
||||
|
||||
[[eosio::action]] void cancel(eosio::name coopname, eosio::name username, checksum256 request_hash);
|
||||
|
||||
// Методы для работы с диспутом (гарантийный возврат)
|
||||
[[eosio::action]] void wauthorize(eosio::name coopname, checksum256 request_hash, uint64_t wreturn_decision_id, document2 wreturn_authorization, uint64_t wsupply_decision_id, document2 wsupply_authorization);
|
||||
[[eosio::action]] void wreturn(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
|
||||
[[eosio::action]] void woffer(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
|
||||
[[eosio::action]] void waccept(eosio::name coopname, eosio::name username, checksum256 request_hash, bool accept, document2 document);
|
||||
|
||||
struct [[eosio::table, eosio::contract("marketplace")]] balances : balances_base {};
|
||||
struct [[eosio::table, eosio::contract("marketplace")]] counts : counts_base {};
|
||||
|
||||
};
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Подтверждение готовности выполнить заявку.
|
||||
|
||||
@details Данный метод позволяет пользователю, который получил предложение по своей заявке, подтвердить свою готовность его принять и выполнить. При этом формируется пакет документов, который отправляется в совет на утверждение.
|
||||
|
||||
@param username Имя пользователя, подтверждающего готовность выполнить предложение.
|
||||
@param exchange_id ID предложения, которое следует подтвердить.
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p username
|
||||
*/
|
||||
[[eosio::action]] void marketplace::accept(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Заявка не найдена");
|
||||
eosio::check(change -> status == "published"_n, "Только заявка в статусе ожидания может быть принята");
|
||||
|
||||
auto parent_change = exchange.find(change -> parent_id);
|
||||
eosio::check(parent_change != exchange.end(), "Родительская заявка не найдена");
|
||||
eosio::check(parent_change -> username == username, "Недостаточно прав доступа");
|
||||
eosio::check(parent_change -> remain_units >= change -> remain_units, "Недостаточно объектов для поставки");
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
exchange.modify(parent_change, _marketplace, [&](auto &i) {
|
||||
i.remain_units -= change -> remain_units;
|
||||
i.supplier_amount = (parent_change -> remain_units - change -> remain_units ) * parent_change -> unit_cost;
|
||||
i.blocked_units += change -> remain_units;
|
||||
});
|
||||
|
||||
exchange.modify(change, _marketplace, [&](auto &o){
|
||||
o.status = "accepted"_n;
|
||||
o.blocked_units += change -> remain_units;
|
||||
o.remain_units = 0;
|
||||
o.accepted_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
|
||||
if (change -> type == "order"_n) {
|
||||
o.contribute_product_statement = document;
|
||||
} else if (change -> type == "offer"_n) {
|
||||
o.return_product_statement = document;
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_soviet,
|
||||
_change_action,
|
||||
std::make_tuple(change -> coopname, username, change -> username, exchange_id, change -> money_contributor, change -> product_contributor)
|
||||
).send();
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Добавление единиц товара к заявке.
|
||||
|
||||
@details Метод позволяет владельцу заявки дополнительно увеличить количество товара, доступное для обмена в рамках указанной заявки.
|
||||
Используется, когда у продавца появляется дополнительное количество товара, которое он хочет добавить к существующей заявке.
|
||||
|
||||
@param username Имя пользователя, инициировавшего добавление.
|
||||
@param exchange_id Идентификатор заявки, к которой добавляются единицы товара.
|
||||
@param units Количество новых единиц товара, которые следует добавить к заявке.
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p username
|
||||
*/
|
||||
[[eosio::action]] void marketplace::addunits(eosio::name coopname, eosio::name username, uint64_t exchange_id, uint64_t units) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
|
||||
eosio::check(change -> username == username, "У вас нет прав на редактирование данной заявки");
|
||||
eosio::check(change -> parent_id == 0, "Нельзя отредактировать количество единиц во встречной заявке. Отмените её и пересоздайте");
|
||||
|
||||
exchange.modify(change, _marketplace, [&](auto &c){
|
||||
c.remain_units += units;
|
||||
c.supplier_amount = (change -> remain_units + units) * change -> unit_cost;
|
||||
});
|
||||
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Авторизация обмена советом.
|
||||
|
||||
@details Метод используется для подтверждения согласия совета на заявленный обмен.
|
||||
Обычно этот метод вызывается после прохождения определенного процесса голосования или принятия решения советом.
|
||||
Авторизованный обмен считается утвержденным и может быть выполнен.
|
||||
|
||||
@param exchange_id Идентификатор заявки на обмен, которую следует авторизовать.
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p _soviet
|
||||
*/
|
||||
[[eosio::action]] void marketplace::authorize(eosio::name coopname, uint64_t exchange_id, uint64_t contribution_product_decision_id, document2 contribution_product_authorization, uint64_t return_product_decision_id, document2 return_product_authorization) {
|
||||
require_auth(_soviet);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
|
||||
eosio::check(change != exchange.end(), "Ордер не найден");
|
||||
|
||||
exchange.modify(change, _soviet, [&](auto &o) {
|
||||
o.status = "authorized"_n;
|
||||
|
||||
o.contribution_product_decision_id = contribution_product_decision_id;
|
||||
o.contribution_product_authorization = contribution_product_authorization;
|
||||
|
||||
o.return_product_decision_id = return_product_decision_id;
|
||||
o.return_product_authorization = return_product_authorization;
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Отмена заявки и возврат токенов.
|
||||
|
||||
@details Позволяет пользователю отменить родительскую или дочернюю заявку, а также обеспечивает возврат токенов владельцу (если применимо). При отмене проверяется наличие заявки и её текущий статус.
|
||||
|
||||
@param username Имя пользователя, инициировавшего отмену.
|
||||
@param exchange_id Идентификатор заявки для отмены.
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p username
|
||||
*/
|
||||
[[eosio::action]] void marketplace::cancel(eosio::name coopname, eosio::name username, uint64_t exchange_id) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Заявка не найдена");
|
||||
|
||||
//TODO перенести под проверку пользователя и вообще сценарий отмены проверить полностью
|
||||
eosio::check(change -> status != "accepted"_n, "Заявка не может быть отменена сейчас");
|
||||
|
||||
if (change -> parent_id == 0) {
|
||||
marketplace::cancel_parent(coopname, username, exchange_id);
|
||||
} else {
|
||||
marketplace::cancel_child(coopname, username, exchange_id);
|
||||
};
|
||||
}
|
||||
@@ -1,325 +0,0 @@
|
||||
// /**
|
||||
// * @mainpage Описание системы заявок
|
||||
// *
|
||||
// * В системе имеются два типа заявок: order (заказ) и offer (предложение), а также два уровня заявок: родительская (base) и встречная (quote).
|
||||
// * Используется одна таблица заявок: exchange(_marketplace, _marketplace). Встречная заявка всегда должна быть противополжного типа к родительской.
|
||||
// *
|
||||
// * <b>Группы заявок:</b>
|
||||
// *
|
||||
// * <b>1. Группа предложений от родителя:</b>
|
||||
// * - Parent: type == 'offer' && (parent_id == 0) => имущественный паевый взнос
|
||||
// * - Child: type == 'order' && (parent_id > 0) => денежный паевый взнос
|
||||
// *
|
||||
// * <b>2. Группа заказа от родителя:</b>
|
||||
// * - Parent: type == "order" && (parent_id == 0) => денежный паевый взнос
|
||||
// * - Child: type == "offer" && (parent_id > 0) => имущественный паевый взнос
|
||||
// *
|
||||
// * <b>Обозначения:</b>
|
||||
// * - order => денежный паевый взнос
|
||||
// * - offer => имущественный паевый взнос
|
||||
// */
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Создание заявки на обмен
|
||||
*
|
||||
* @param type Тип заявки
|
||||
* @param params Параметры заявки
|
||||
*
|
||||
* Общая функция для создания как родительских, так и дочерних заявок.
|
||||
*/
|
||||
void marketplace::create (eosio::name type, const exchange_params& params) {
|
||||
cooperatives2_index coops(_registrator, _registrator.value);
|
||||
auto coop = coops.find(params.coopname.value);
|
||||
eosio::check(coop != coops.end() && coop -> is_coop(), "Кооператив не найден");
|
||||
eosio::check(params.unit_cost.symbol == coop -> initial.symbol, "Неверный символ токен");
|
||||
eosio::check(params.units > 0, "Количество единиц в заявке должно быть больше нуля");
|
||||
eosio::check(params.unit_cost.amount >= 0, "Цена не может быть отрицательной");
|
||||
|
||||
if (params.parent_id == 0) {
|
||||
marketplace::create_parent(type, params);
|
||||
} else {
|
||||
marketplace::create_child(type, params);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Создание родительской заявки
|
||||
*
|
||||
* @param type Тип заявки
|
||||
* @param params Параметры заявки
|
||||
*
|
||||
* Специализированная функция для создания родительской заявки.
|
||||
*/
|
||||
void marketplace::create_parent(eosio::name type, const exchange_params& params) {
|
||||
eosio::check(type == "offer"_n, "В родительском заявке может быть только предложение");
|
||||
|
||||
requests_index exchange(_marketplace, params.coopname.value);
|
||||
uint64_t id = get_global_id(_marketplace, "exchange"_n);
|
||||
|
||||
eosio::check(params.parent_id == 0, "Родительская заявка создаётся без указания родителя");
|
||||
|
||||
cooperatives2_index coops(_registrator, _registrator.value);
|
||||
auto coop = coops.find(params.coopname.value);
|
||||
eosio::check(coop != coops.end(), "Кооператив не найден");
|
||||
eosio::check(coop -> is_coop() == true, "Организация - не кооператив");
|
||||
|
||||
participants_index participants(_soviet, params.coopname.value);
|
||||
auto participant = participants.find(params.username.value);
|
||||
eosio::check(participant != participants.end(), "Вы не являетесь членом указанного кооператива");
|
||||
|
||||
auto program = get_program_or_fail(params.coopname, params.program_id);
|
||||
|
||||
//срок гарантийного возврата должен быть установлен
|
||||
eosio::check(params.product_lifecycle_secs > 0, "Гарантийный срок возврата для имущества должен быть установлен");
|
||||
|
||||
exchange.emplace(_marketplace, [&](auto &i) {
|
||||
i.id = id;
|
||||
i.type = type;
|
||||
i.program_id = params.program_id;
|
||||
i.username = params.username;
|
||||
i.coopname = params.coopname;
|
||||
i.status = "moderation"_n;
|
||||
i.remain_units = params.units;
|
||||
i.unit_cost = params.unit_cost;
|
||||
i.supplier_amount = params.unit_cost * params.units;
|
||||
|
||||
i.membership_fee = asset(0, coop -> initial.symbol);
|
||||
i.total_cost = asset(0, coop -> initial.symbol);
|
||||
|
||||
i.product_lifecycle_secs = params.product_lifecycle_secs;
|
||||
i.data = params.data;
|
||||
i.meta = params.meta;
|
||||
i.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
i.cancellation_fee_amount = asset(0, params.unit_cost.symbol);
|
||||
});
|
||||
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_marketplace,
|
||||
"newid"_n,
|
||||
std::make_tuple(id, type)
|
||||
).send();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Создание дочерней заявки
|
||||
*
|
||||
* @param type Тип заявки
|
||||
* @param params Параметры заявки
|
||||
*
|
||||
* Специализированная функция для создания дочерних заявок, связанных с родительской заявкой.
|
||||
*/
|
||||
void marketplace::create_child(eosio::name type, const exchange_params& params) {
|
||||
eosio::check(type == "order"_n, "В дочерней заявки может быть только заказ");
|
||||
|
||||
requests_index exchange(_marketplace, params.coopname.value);
|
||||
auto parent_change = exchange.find(params.parent_id);
|
||||
eosio::check(parent_change != exchange.end(), "Заявка не обнаружена");
|
||||
eosio::check(parent_change -> status == "published"_n, "Заявка не опубликована или не прошла модерацию");
|
||||
|
||||
cooperatives2_index coops(_registrator, _registrator.value);
|
||||
auto coop = coops.find(params.coopname.value);
|
||||
eosio::check(coop != coops.end(), "Кооператив не найден");
|
||||
eosio::check(coop -> is_coop() == true, "Организация - не кооператив");
|
||||
|
||||
eosio::check(parent_change -> unit_cost.amount == params.unit_cost.amount, "Торги запрещены");
|
||||
|
||||
eosio::check(params.parent_id > 0, "Встречная заявка создаётся с указанием родителя");
|
||||
eosio::check(params.document.has_value(), "Документ должен быть приложен к транзакции");
|
||||
|
||||
//проводим проверку подписи документа
|
||||
verify_document_or_fail(*params.document);
|
||||
|
||||
uint64_t id = get_global_id(_marketplace, "exchange"_n);
|
||||
|
||||
auto program = get_program_or_fail(params.coopname, params.program_id);
|
||||
|
||||
eosio::check(parent_change -> program_id == params.program_id, "Целевые программы должны совпадать");
|
||||
|
||||
participants_index participants(_soviet, params.coopname.value);
|
||||
auto participant = participants.find(params.username.value);
|
||||
eosio::check(participant != participants.end(), "Вы не являетесь членом указанного кооператива");
|
||||
|
||||
uint64_t product_lifecycle_secs = 0;
|
||||
|
||||
eosio::asset membership_fee;
|
||||
eosio::asset supplier_amount = params.unit_cost * params.units;
|
||||
|
||||
if (program.calculation_type == "absolute"_n) {
|
||||
membership_fee = program.fixed_membership_contribution;
|
||||
} else if (program.calculation_type == "relative"_n) {
|
||||
membership_fee = supplier_amount * HUNDR_PERCENTS / program.membership_percent_fee;
|
||||
} else if (program.calculation_type == "free"_n) {
|
||||
membership_fee = asset(0, _root_govern_symbol);
|
||||
};
|
||||
|
||||
eosio::asset total_cost = params.unit_cost * params.units + membership_fee;
|
||||
|
||||
//Специальные проверки
|
||||
if (type == "offer"_n) {
|
||||
//родительская заявка должна быть противоположного типа
|
||||
eosio::check(parent_change -> type == "order"_n, "Неверный тип родительской заявки");
|
||||
|
||||
} else if(type == "order"_n) {
|
||||
//родительская заявка должна быть противоположного типа
|
||||
eosio::check(parent_change -> type == "offer"_n, "Неверный тип родительской заявки");
|
||||
|
||||
std::string memo = "Начало поставки по программе №" + std::to_string(params.program_id) + " с ID: " + std::to_string(id);
|
||||
|
||||
//Для блокировки средств необходимо их иметь на ЦПП, т.е. предварительно необходимо сконвертировать их с ЦПП кошелька
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_soviet,
|
||||
"blockbal"_n,
|
||||
std::make_tuple(params.coopname, params.username, params.program_id, total_cost, memo)
|
||||
).send();
|
||||
|
||||
}
|
||||
|
||||
exchange.emplace(_marketplace, [&](auto &i) {
|
||||
i.id = id;
|
||||
i.parent_id = params.parent_id;
|
||||
i.parent_username = parent_change -> username;
|
||||
i.type = type;
|
||||
i.program_id = parent_change -> program_id;
|
||||
i.coopname = params.coopname;
|
||||
i.username = params.username;
|
||||
i.status = "published"_n;
|
||||
i.remain_units = params.units;
|
||||
i.unit_cost = params.unit_cost;
|
||||
i.membership_fee = membership_fee;
|
||||
|
||||
i.supplier_amount = supplier_amount;
|
||||
i.total_cost = total_cost;
|
||||
|
||||
// не дублируем информацию
|
||||
// i.data = params.data;
|
||||
// i.meta = params.meta;
|
||||
|
||||
i.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
i.cancellation_fee_amount = asset(0, params.unit_cost.symbol);
|
||||
|
||||
|
||||
if (type == "order"_n) {
|
||||
print("on create child order");
|
||||
i.return_product_statement = *params.document;
|
||||
i.money_contributor = params.username;
|
||||
i.product_contributor = parent_change -> username;
|
||||
i.product_lifecycle_secs = parent_change -> product_lifecycle_secs;
|
||||
} else if (type == "offer"_n) {
|
||||
print("on create child offer");
|
||||
i.contribute_product_statement = *params.document;
|
||||
i.money_contributor = parent_change -> username;
|
||||
i.product_contributor = params.username;
|
||||
i.product_lifecycle_secs = params.product_lifecycle_secs;
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_marketplace,
|
||||
"newid"_n,
|
||||
std::make_tuple(id, type)
|
||||
).send();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Отмена родительской заявки.
|
||||
*
|
||||
* Вызывается из `cancel`, если заявка является родительской.
|
||||
* Выполняется проверка, что заявка не имеет заблокированных единиц товара, и удаляется из хранилища.
|
||||
*
|
||||
* @param username Имя пользователя, осуществляющего отмену заявки.
|
||||
* @param exchange_id ID родительской заявки, которую нужно отменить.
|
||||
*/
|
||||
void marketplace::cancel_parent(eosio::name coopname, eosio::name username, uint64_t exchange_id) {
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
|
||||
//Удаление, если заблокированных объектов на поставке - нет.
|
||||
eosio::check(change -> remain_units + change -> blocked_units == 0, "Заявка не может быть отменена из-за наличия заблокированных единиц товара");
|
||||
exchange.erase(change);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Отмена дочерней заявки.
|
||||
*
|
||||
* Вызывается из `cancel`, если заявка является дочерней.
|
||||
* Обновляет количество оставшихся и заблокированных единиц товара в родительской заявке и удаляет дочернюю заявку из хранилища.
|
||||
* В зависимости от статуса и типа заявки возможен возврат токенов "покупателю".
|
||||
*
|
||||
* @param username Имя пользователя, осуществляющего отмену заявки.
|
||||
* @param exchange_id ID дочерней заявки, которую нужно отменить.
|
||||
*/
|
||||
void marketplace::cancel_child(eosio::name coopname, eosio::name username, uint64_t exchange_id) {
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
auto parent_change = exchange.find(change -> parent_id);
|
||||
eosio::asset quantity = change -> unit_cost * change -> blocked_units;
|
||||
|
||||
|
||||
// оповещаем совет об отмене и разблокируем средства
|
||||
if (change -> type == "order"_n) {
|
||||
std::string memo = "Отмена поставки по программе №" + std::to_string(change -> program_id) + " с ID: " + std::to_string(change -> id);
|
||||
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_soviet,
|
||||
"unblockbal"_n,
|
||||
std::make_tuple(coopname, change -> money_contributor, change -> program_id, change -> total_cost, memo)
|
||||
).send();
|
||||
|
||||
};
|
||||
|
||||
if (change -> status == "authorized"_n) {
|
||||
//возвращаем единицы товара в родительскую заявку
|
||||
exchange.modify(parent_change, _marketplace, [&](auto &e) {
|
||||
e.remain_units += change -> blocked_units;
|
||||
e.blocked_units -= change -> blocked_units;
|
||||
e.supplier_amount += change -> supplier_amount;
|
||||
});
|
||||
|
||||
exchange.modify(change, _marketplace, [&](auto &c){
|
||||
c.status = "canceled"_n;
|
||||
c.canceled_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
|
||||
//удаляем дочернюю заявку
|
||||
// exchange.erase(change);
|
||||
} else if (change -> status == "published"_n) {
|
||||
|
||||
exchange.modify(change, _marketplace, [&](auto &c){
|
||||
c.status = "canceled"_n;
|
||||
c.canceled_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
|
||||
//удаляем дочернюю заявку
|
||||
// exchange.erase(change);
|
||||
} else {
|
||||
//TODO здесь должно быть допустимо, но для каждого статуса по-своему
|
||||
eosio::check(false, "Заявка находится в недопустимом статусе для отмены");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Подписание акта о приёме-передаче имущества.
|
||||
|
||||
* @details После успешного получения товара, получатель подписывает акт о приёме-передаче, что свидетельствует о юридическом завершении сделки. Этот акт делает пакет документов по данной сделке полным. После проведения ряда проверок, обновляются статусы и количество объектов в основной заявке и предложении. Если все объекты основной заявки обработаны, заявка удаляется из публикации. В зависимости от типа предложения, может осуществляться перевод токенов.
|
||||
|
||||
* @param username Имя пользователя-получателя товара.
|
||||
* @param exchange_id ID предложения, под которым следует подписать акт.
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p username
|
||||
*/
|
||||
[[eosio::action]] void marketplace::complete(eosio::name coopname, eosio::name username, uint64_t exchange_id) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Заявка не найдена");
|
||||
|
||||
eosio::check(change -> parent_id > 0, "У указанной заявки нет встречной заявки");
|
||||
|
||||
auto parent_change = exchange.find(change -> parent_id);
|
||||
eosio::check(parent_change != exchange.end(), "Родительская заявка не найдена");
|
||||
|
||||
// eosio::check(change -> username == username, "Вы не можете подтвердить исполнение заявки");
|
||||
eosio::check(change -> status == "recieved2"_n, "Заявка находится в неверном статусе для утверждения обмена");
|
||||
|
||||
eosio::check(change -> warranty_delay_until.sec_since_epoch() < eosio::current_time_point().sec_since_epoch(), "Время гарантийной задержки еще не истекло");
|
||||
|
||||
exchange.modify(parent_change, _marketplace, [&](auto &i) {
|
||||
i.delivered_units += change -> blocked_units;
|
||||
i.blocked_units -= change -> blocked_units;
|
||||
|
||||
if (i.blocked_units + parent_change -> remain_units == 0) {
|
||||
i.status = "unpublished"_n; //снимаем родительскую заявку с публикации, если она исполнена
|
||||
};
|
||||
});
|
||||
|
||||
exchange.modify(change, _marketplace, [&](auto &o) {
|
||||
o.status = "completed"_n;
|
||||
o.delivered_units += change -> blocked_units;
|
||||
o.blocked_units = 0;
|
||||
o.completed_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
|
||||
auto program = get_program_or_fail(coopname, change -> program_id);
|
||||
|
||||
std::string memo = "Успешное завершение поставки имущества по программе №" + std::to_string(change -> program_id) + " с ID: " + std::to_string(change -> id);
|
||||
|
||||
//Заказчику разблокируем баланс ЦПП кооплекса и списываем его
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_soviet,
|
||||
"unblockbal"_n,
|
||||
std::make_tuple(coopname, change -> money_contributor, change -> program_id, change -> total_cost, memo)
|
||||
).send();
|
||||
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_soviet,
|
||||
"subbal"_n,
|
||||
std::make_tuple(coopname, change -> money_contributor, change -> program_id, change -> total_cost, false, memo)
|
||||
).send();
|
||||
|
||||
|
||||
//Поставщику разблокируем средства в программе кооплейса
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_soviet,
|
||||
"unblockbal"_n,
|
||||
std::make_tuple(coopname, change -> product_contributor, change -> program_id, change -> supplier_amount, memo)
|
||||
).send();
|
||||
|
||||
|
||||
if (change -> membership_fee.amount > 0) {
|
||||
//распределяем членские взносы по фондам
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_fund,
|
||||
"spreadamount"_n,
|
||||
std::make_tuple(coopname, change -> membership_fee)
|
||||
).send();
|
||||
|
||||
// отмечаем распределение членских взносов в программе и кошельке пользователя
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_soviet,
|
||||
"addmemberfee"_n,
|
||||
std::make_tuple(coopname, change -> money_contributor, change -> program_id, change -> membership_fee, memo)
|
||||
).send();
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Отказ от предложения.
|
||||
|
||||
* @details Этот метод позволяет пользователю отклонить предложение, представленное к его заявке.
|
||||
* Выполняются следующие проверки:
|
||||
* - Существование предложения с указанным ID.
|
||||
* - Существование основной заявки.
|
||||
* - Предложение находится в статусе "ожидание".
|
||||
*
|
||||
* Если отклонено предложение к заявке типа "order", осуществляется возврат токенов пользователю, которому были заблокированы токены при создании предложения.
|
||||
*
|
||||
* @param username Имя пользователя, отклоняющего предложение.
|
||||
* @param exchange_id ID предложения, которое следует отклонить.
|
||||
* @param meta Дополнительные метаданные, связанные с отказом.
|
||||
*
|
||||
* @note Авторизация требуется от аккаунта: @p username
|
||||
*/
|
||||
[[eosio::action]] void marketplace::decline(eosio::name coopname, eosio::name username, uint64_t exchange_id, std::string meta) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
auto parent_change = exchange.find(change -> parent_id);
|
||||
|
||||
eosio::check(change != exchange.end(), "Заявка не найдена");
|
||||
eosio::check(parent_change != exchange.end(), "Родительская заявка не найдена");
|
||||
eosio::check(change -> status == "published"_n, "Только заявка в статусе ожидания может быть отклонена");
|
||||
|
||||
exchange.modify(change, coopname, [&](auto &o){
|
||||
o.status = "declined"_n;
|
||||
o.declined_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
o.meta = meta;
|
||||
});
|
||||
|
||||
if (change -> type == "order"_n) {
|
||||
std::string memo = "Отказ в поставке по программе №" + std::to_string(change -> program_id) + " с ID: " + std::to_string(change -> id);
|
||||
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_soviet,
|
||||
"unblockbal"_n,
|
||||
std::make_tuple(coopname, change -> money_contributor, change -> program_id, change -> total_cost, memo)
|
||||
).send();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Принятие встречной заявки поставщиком в направлении deliver_on_offer (acceptoff).
|
||||
|
||||
@details Поставщик принимает встречную заявку (child order) на поставку имущества, создает сегменты и подготавливает заявления для совета.
|
||||
|
||||
Процесс: OFFER (parent) → ORDER (child)
|
||||
- Parent: имущественный паевой взнос (поставщик)
|
||||
- Child: денежный паевой взнос (заказчик)
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя поставщика
|
||||
@param request_hash Хэш встречной заявки (order)
|
||||
@param conversion_document Заявление на конвертацию
|
||||
@param product_contribution_statement Заявление на возврат
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::acceptoff(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 convert_out, document2 product_contribution_statement) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "active"_n, "Только активная заявка может быть принята");
|
||||
eosio::check(change.type == "order"_n, "Метод offeraccept применим только к встречным заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
eosio::check(parent_change.username == username, "Недостаточно прав доступа");
|
||||
eosio::check(parent_change.remaining_units >= change.remaining_units, "Недостаточно объектов для поставки");
|
||||
|
||||
// Проверяем подписи документов
|
||||
verify_document_or_fail(convert_out);
|
||||
verify_document_or_fail(product_contribution_statement);
|
||||
|
||||
// Обновляем родительскую заявку (offer)
|
||||
auto parent_itr = requests.find(parent_change.id);
|
||||
eosio::check(parent_itr != requests.end(), "Родительская заявка не найдена для обновления");
|
||||
|
||||
requests.modify(parent_itr, _marketplace, [&](auto &i) {
|
||||
i.remaining_units -= change.remaining_units;
|
||||
i.base_cost = (parent_change.remaining_units - change.remaining_units) * parent_change.unit_cost;
|
||||
i.blocked_units += change.remaining_units;
|
||||
});
|
||||
|
||||
// Проверяем инварианты родительской заявки
|
||||
auto updated_parent = requests.find(parent_change.id);
|
||||
marketplace::check_units_invariant(*updated_parent, "offeraccept_parent_update");
|
||||
|
||||
// Обновляем встречную заявку (order)
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o){
|
||||
o.status = "accepted"_n;
|
||||
o.blocked_units += change.remaining_units;
|
||||
o.remaining_units = 0;
|
||||
o.accepted_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "offeraccept_child_update");
|
||||
|
||||
// Сохраняем заявление на конвертацию в contribute сегменте
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.convert_out = convert_out;
|
||||
s.statement = product_contribution_statement;
|
||||
s.status = "statement"_n;
|
||||
});
|
||||
|
||||
// Извлекаем заявления из сегментов
|
||||
auto s2c_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"));
|
||||
auto c2r_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"));
|
||||
|
||||
// Используем хэш встречной заявки как идентификатор пакета решений
|
||||
checksum256 agenda_hash = change.hash;
|
||||
|
||||
// Отправляем заявление на конвертацию (из contribute сегмента) в совет
|
||||
Action::send<createagenda_interface>(
|
||||
_soviet,
|
||||
"createagenda"_n,
|
||||
_marketplace,
|
||||
coopname,
|
||||
username,
|
||||
get_valid_soviet_action("authoffs2c"_n),
|
||||
agenda_hash,
|
||||
_marketplace,
|
||||
Marketplace::get_valid_marketplace_action("authoffs2c"_n),
|
||||
"declineacc"_n,
|
||||
s2c_segment.statement,
|
||||
std::string("")
|
||||
);
|
||||
|
||||
// Отправляем заявление на возврат (из return сегмента) в совет
|
||||
Action::send<createagenda_interface>(
|
||||
_soviet,
|
||||
"createagenda"_n,
|
||||
_marketplace,
|
||||
coopname,
|
||||
username,
|
||||
get_valid_soviet_action("authoffc2r"_n),
|
||||
agenda_hash,
|
||||
_marketplace,
|
||||
Marketplace::get_valid_marketplace_action("authoffc2r"_n),
|
||||
"declineacc"_n,
|
||||
c2r_segment.statement,
|
||||
std::string("")
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Авторизация сегмента "return" для направления deliver_on_offer (authoffc2r).
|
||||
|
||||
@details Метод используется для подтверждения согласия совета на заявление на возврат в направлении OFFER → ORDER.
|
||||
Заявка становится авторизованной только когда оба сегмента (contribute и return) получили авторизацию.
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param request_hash Хэш встречной заявки на обмен (order)
|
||||
@param authorization Документ авторизации
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p _soviet
|
||||
*/
|
||||
[[eosio::action]] void marketplace::authoffc2r(eosio::name coopname, checksum256 request_hash, document2 authorization) {
|
||||
require_auth(_soviet);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.type == "order"_n, "Метод authoffc2r применим только к заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
|
||||
// Обновляем return сегмент
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"), [&](auto &s) {
|
||||
s.authorization = authorization;
|
||||
s.status = "authorized"_n;
|
||||
});
|
||||
|
||||
// Проверяем статусы обоих сегментов
|
||||
auto s2c_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"));
|
||||
auto c2r_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"));
|
||||
|
||||
// Если оба сегмента авторизованы, авторизуем заявку
|
||||
if (s2c_segment.status == "authorized"_n && c2r_segment.status == "authorized"_n) {
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _soviet, [&](auto &o) {
|
||||
o.status = "authorized"_n;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Авторизация сегмента "contribute" для направления deliver_on_offer (authoffs2c).
|
||||
|
||||
@details Метод используется для подтверждения согласия совета на заявление на конвертацию в направлении OFFER → ORDER.
|
||||
Заявка становится авторизованной только когда оба сегмента (contribute и return) получили авторизацию.
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param request_hash Хэш встречной заявки на обмен (order)
|
||||
@param authorization Документ авторизации
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p _soviet
|
||||
*/
|
||||
[[eosio::action]] void marketplace::authoffs2c(eosio::name coopname, checksum256 request_hash, document2 authorization) {
|
||||
require_auth(_soviet);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.type == "order"_n, "Метод authoffs2c применим только к заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
|
||||
// Обновляем contribute сегмент
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.authorization = authorization;
|
||||
s.status = "authorized"_n;
|
||||
});
|
||||
|
||||
// Проверяем статусы обоих сегментов
|
||||
auto s2c_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"));
|
||||
auto c2r_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"));
|
||||
|
||||
// Если оба сегмента авторизованы, авторизуем заявку
|
||||
if (s2c_segment.status == "authorized"_n && c2r_segment.status == "authorized"_n) {
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _soviet, [&](auto &o) {
|
||||
o.status = "authorized"_n;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Отмена заявки с учетом комиссии за отмену.
|
||||
|
||||
@details Позволяет пользователю отменить родительскую или дочернюю заявку с учетом следующих правил:
|
||||
- Поставщик не может отменить заявку после поставки имущества (статусы supplied1+)
|
||||
- Заказчик может отменить с вычетом комиссии за отмену (cancellation_fee_amount)
|
||||
- При отмене возвращаются заблокированные единицы и средства
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя пользователя, инициирующего отмену
|
||||
@param request_hash Хэш заявки для отмены
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::cancel(eosio::name coopname, eosio::name username, checksum256 request_hash) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
// Проверяем права доступа на отмену заявки
|
||||
eosio::check(change.username == username, "Только владелец заявки может её отменить");
|
||||
|
||||
// Проверяем что заявка может быть отменена
|
||||
eosio::check(change.status != "completed"_n && change.status != "canceled"_n && change.status != "declined"_n,
|
||||
"Заявка уже завершена или отменена");
|
||||
|
||||
if (change.parent_id == 0) {
|
||||
marketplace::cancel_parent(coopname, username, request_hash);
|
||||
} else {
|
||||
marketplace::cancel_child(coopname, username, request_hash);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отмена родительской заявки.
|
||||
*/
|
||||
void marketplace::cancel_parent(eosio::name coopname, eosio::name username, checksum256 request_hash) {
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
// Родительская заявка может быть отменена только если нет заблокированных единиц
|
||||
eosio::check(change.blocked_units == 0, "Заявка не может быть отменена из-за наличия заблокированных единиц товара");
|
||||
|
||||
// Для родительского заказа разблокируем средства
|
||||
if (change.type == "order"_n && change.total_cost.amount > 0) {
|
||||
eosio::asset refund_amount = change.total_cost;
|
||||
|
||||
// Если заказчик отменяет заявку, вычитаем комиссию за отмену
|
||||
if (change.money_contributor == username && change.cancellation_fee_amount.amount > 0) {
|
||||
refund_amount -= change.cancellation_fee_amount;
|
||||
}
|
||||
|
||||
std::string memo = "Отмена родительской заявки по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(change.id);
|
||||
|
||||
if (refund_amount.amount > 0) {
|
||||
// Списываем средства с ЦПП маркетплейса
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, change.money_contributor, refund_amount, _marketplace_program, memo);
|
||||
// Начисляем средства на ЦПП Цифровой Кошелёк
|
||||
Wallet::add_available_funds(_marketplace, coopname, change.money_contributor, refund_amount, _wallet_program, memo);
|
||||
}
|
||||
|
||||
// Если была комиссия, списываем её и направляем в фонд членских взносов
|
||||
if (change.money_contributor == username && change.cancellation_fee_amount.amount > 0) {
|
||||
std::string fee_memo = "Комиссия за отмену родительской заявки по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(change.id);
|
||||
|
||||
// Списываем комиссию с ЦПП маркетплейса
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, change.money_contributor, change.cancellation_fee_amount, _marketplace_program, fee_memo);
|
||||
|
||||
// Направляем комиссию в фонд членских взносов
|
||||
Wallet::add_member_fee(_marketplace, coopname, username, _marketplace_program_id, change.cancellation_fee_amount, fee_memo);
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем сегменты родительской заявки
|
||||
marketplace::delete_segments_by_request(coopname, change.id);
|
||||
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Заявка не найдена для удаления");
|
||||
requests.erase(change_itr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отмена дочерней заявки с учетом комиссии за отмену.
|
||||
*/
|
||||
void marketplace::cancel_child(eosio::name coopname, eosio::name username, checksum256 request_hash) {
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
// Проверяем права на отмену в зависимости от типа заявки и статуса
|
||||
if (change.type == "offer"_n) {
|
||||
// Поставщик не может отменить заявку после поставки имущества
|
||||
eosio::check(change.status != "supplied1"_n && change.status != "supplied2"_n &&
|
||||
change.status != "delivered"_n && change.status != "received1"_n &&
|
||||
change.status != "received2"_n,
|
||||
"Поставщик не может отменить заявку после поставки имущества");
|
||||
}
|
||||
|
||||
// Возвращаем заблокированные единицы в родительскую заявку
|
||||
if (change.blocked_units > 0) {
|
||||
auto parent_itr = requests.find(parent_change.id);
|
||||
eosio::check(parent_itr != requests.end(), "Родительская заявка не найдена для обновления");
|
||||
requests.modify(parent_itr, _marketplace, [&](auto &e) {
|
||||
e.remaining_units += change.blocked_units;
|
||||
e.blocked_units -= change.blocked_units;
|
||||
e.base_cost = e.remaining_units * e.unit_cost;
|
||||
});
|
||||
}
|
||||
|
||||
// Разблокируем средства если это заказ
|
||||
if (change.type == "order"_n && change.total_cost.amount > 0) {
|
||||
eosio::asset refund_amount = change.total_cost;
|
||||
|
||||
// Если заказчик отменяет заявку, вычитаем комиссию за отмену
|
||||
if (change.money_contributor == username && change.cancellation_fee_amount.amount > 0) {
|
||||
refund_amount -= change.cancellation_fee_amount;
|
||||
}
|
||||
|
||||
std::string memo = "Отмена заявки по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(change.id);
|
||||
|
||||
if (refund_amount.amount > 0) {
|
||||
// Списываем средства с ЦПП маркетплейса
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, change.money_contributor, refund_amount, _marketplace_program, memo);
|
||||
// Начисляем средства на ЦПП Цифровой Кошелёк
|
||||
Wallet::add_available_funds(_marketplace, coopname, change.money_contributor, refund_amount, _wallet_program, memo);
|
||||
}
|
||||
|
||||
// Если была комиссия, списываем её и направляем в фонд членских взносов
|
||||
if (change.money_contributor == username && change.cancellation_fee_amount.amount > 0) {
|
||||
std::string fee_memo = "Комиссия за отмену заявки по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(change.id);
|
||||
|
||||
// Списываем комиссию с ЦПП маркетплейса
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, change.money_contributor, change.cancellation_fee_amount, _marketplace_program, fee_memo);
|
||||
|
||||
// Направляем комиссию в фонд членских взносов
|
||||
Wallet::add_member_fee(_marketplace, coopname, username, _marketplace_program_id, change.cancellation_fee_amount, fee_memo);
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем сегменты дочерней заявки
|
||||
marketplace::delete_segments_by_request(coopname, change.id);
|
||||
|
||||
// Проверяем равенство количества единиц для определения стратегии удаления
|
||||
auto updated_parent = requests.find(parent_change.id);
|
||||
bool units_equal = false;
|
||||
|
||||
if (updated_parent != requests.end()) {
|
||||
units_equal = (change.remaining_units == updated_parent->remaining_units);
|
||||
}
|
||||
|
||||
// Удаляем дочернюю заявку
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Заявка не найдена для удаления");
|
||||
requests.erase(change_itr);
|
||||
|
||||
// Если количество единиц равно - удаляем также родительскую заявку
|
||||
if (units_equal && updated_parent != requests.end()) {
|
||||
// Для родительского заказа тоже разблокируем средства если есть
|
||||
if (parent_change.type == "order"_n && parent_change.total_cost.amount > 0 && parent_change.parent_id == 0) {
|
||||
eosio::asset parent_refund_amount = parent_change.total_cost;
|
||||
|
||||
// Если заказчик отменяет заявку, вычитаем комиссию за отмену
|
||||
if (parent_change.money_contributor == username && parent_change.cancellation_fee_amount.amount > 0) {
|
||||
parent_refund_amount -= parent_change.cancellation_fee_amount;
|
||||
}
|
||||
|
||||
std::string parent_memo = "Отмена родительской заявки при отмене дочерней с равными единицами по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(parent_change.id);
|
||||
|
||||
if (parent_refund_amount.amount > 0) {
|
||||
// Списываем средства с ЦПП маркетплейса
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, parent_change.money_contributor, parent_refund_amount, _marketplace_program, parent_memo);
|
||||
// Начисляем средства на ЦПП Цифровой Кошелёк
|
||||
Wallet::add_available_funds(_marketplace, coopname, parent_change.money_contributor, parent_refund_amount, _wallet_program, parent_memo);
|
||||
}
|
||||
|
||||
// Если была комиссия, списываем её и направляем в фонд членских взносов
|
||||
if (parent_change.money_contributor == username && parent_change.cancellation_fee_amount.amount > 0) {
|
||||
std::string parent_fee_memo = "Комиссия за отмену родительской заявки при отмене дочерней по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(parent_change.id);
|
||||
|
||||
// Списываем комиссию с ЦПП маркетплейса
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, parent_change.money_contributor, parent_change.cancellation_fee_amount, _marketplace_program, parent_fee_memo);
|
||||
|
||||
// Направляем комиссию в фонд членских взносов
|
||||
Wallet::add_member_fee(_marketplace, coopname, username, _marketplace_program_id, parent_change.cancellation_fee_amount, parent_fee_memo);
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем сегменты родительской заявки
|
||||
marketplace::delete_segments_by_request(coopname, parent_change.id);
|
||||
|
||||
requests.erase(updated_parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Создать дочернюю заявку на денежный паевой взнос (в ответ на родительское предложение).
|
||||
*
|
||||
* Данный метод позволяет пользователю создать встречную заявку на денежный паевой взнос.
|
||||
*
|
||||
* @param coopname Имя кооператива
|
||||
* @param username Имя пользователя, инициирующего заявку
|
||||
* @param hash Хэш заявки (уникальный идентификатор)
|
||||
* @param parent_hash Хэш родительской заявки
|
||||
* @param units Количество частей (штук) товара или услуги
|
||||
* @param unit_cost Цена за единицу (штуку) товара или услуги
|
||||
* @param braname Имя кооперативного участка откуда будет забираться товар
|
||||
* @param statement Заявление на возврат паевого взноса имуществом
|
||||
* @param convert_in Заявление на конвертацию из кошелька в маркетплейс
|
||||
* @param meta Метаданные о заявке
|
||||
*
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::childorder(eosio::name coopname, eosio::name braname, eosio::name username, checksum256 hash, checksum256 parent_hash, uint64_t units, eosio::asset unit_cost, document2 statement, document2 convert_in, std::string meta) {
|
||||
require_auth(coopname);
|
||||
|
||||
// Проверяем, что заявка с таким хэшем не существует
|
||||
auto existing_request = get_request_by_hash(coopname, hash);
|
||||
eosio::check(!existing_request.has_value(), "Заявка с таким хэшем уже существует");
|
||||
|
||||
auto coop = get_cooperative_or_fail(coopname);
|
||||
eosio::check(unit_cost.symbol == coop.initial.symbol, "Неверный символ токена");
|
||||
eosio::check(units > 0, "Количество единиц в заявке должно быть больше нуля");
|
||||
eosio::check(unit_cost.amount >= 0, "Цена не может быть отрицательной");
|
||||
|
||||
// Проверяем существование кооперативного участка
|
||||
get_branch_or_fail(coopname, braname);
|
||||
|
||||
// Находим родительскую заявку по хэшу
|
||||
auto parent_change = get_request_by_hash_or_fail(coopname, parent_hash, "Родительская заявка не найдена");
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть предложением");
|
||||
|
||||
eosio::check(parent_change.unit_cost.amount == unit_cost.amount, "Торги запрещены. Стоимость единицы не совпадает с родительской заявкой");
|
||||
|
||||
//проводим проверку подписи документа
|
||||
verify_document_or_fail(statement);
|
||||
verify_document_or_fail(convert_in);
|
||||
|
||||
// Используем константу marketplace program_id
|
||||
auto program = get_program_or_fail(coopname, _marketplace_program_id);
|
||||
|
||||
// Проверяем, что пользователь является пайщиком кооператива
|
||||
get_participant_or_fail(coopname, username);
|
||||
|
||||
// Берем членский взнос и комиссию за отмену из родительского предложения
|
||||
eosio::asset membership_fee_amount = parent_change.membership_fee_amount;
|
||||
eosio::asset cancellation_fee_amount = parent_change.cancellation_fee_amount;
|
||||
eosio::asset base_cost = unit_cost * units;
|
||||
eosio::asset total_cost = base_cost + membership_fee_amount;
|
||||
|
||||
// Проверяем что комиссия за отмену не превышает общую стоимость
|
||||
eosio::check(cancellation_fee_amount <= total_cost, "Комиссия за отмену не может превышать общую стоимость заказа");
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
uint64_t request_id = get_global_id(_marketplace, "requests"_n);
|
||||
|
||||
std::string memo = "Начало поставки по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(request_id);
|
||||
|
||||
requests.emplace(_marketplace, [&](auto &i) {
|
||||
i.id = request_id;
|
||||
i.hash = hash;
|
||||
i.parent_id = parent_change.id;
|
||||
i.parent_hash = parent_hash;
|
||||
i.parent_username = parent_change.username;
|
||||
i.type = "order"_n;
|
||||
i.coopname = coopname;
|
||||
i.username = username;
|
||||
i.status = "active"_n;
|
||||
i.remaining_units = units;
|
||||
i.unit_cost = unit_cost;
|
||||
i.membership_fee_amount = membership_fee_amount;
|
||||
i.base_cost = base_cost;
|
||||
i.total_cost = total_cost;
|
||||
i.product_lifecycle_secs = parent_change.product_lifecycle_secs;
|
||||
i.warranty_period_secs = parent_change.warranty_period_secs;
|
||||
i.money_contributor = username;
|
||||
i.product_contributor = parent_change.username;
|
||||
i.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
i.cancellation_fee_amount = cancellation_fee_amount;
|
||||
i.braname = braname;
|
||||
});
|
||||
|
||||
// Создаем сегмент для дочерней заявки поставки из кооператива - заказчику
|
||||
marketplace::create_segment(coopname, request_id, marketplace::valid_segment("c2r"), username);
|
||||
|
||||
// Сохраняем заявление на конвертацию и заявление на возврат в contribute сегменте
|
||||
marketplace::update_segment_by_request_and_type(coopname, request_id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.convert_in = convert_in;
|
||||
s.statement = statement;
|
||||
s.status = "statement"_n;
|
||||
});
|
||||
|
||||
//Для блокировки средств необходимо их иметь на ЦПП, т.е. предварительно необходимо сконвертировать их с ЦПП кошелька
|
||||
// Списываем средства с ЦПП цифрового кошелька
|
||||
std::string convert_memo = "Конвертация средств из ЦПП 'Цифровой Кошелёк' в ЦПП 'Маркетплейс' для заказа №" + std::to_string(request_id);
|
||||
Wallet::sub_available_funds(_marketplace, coopname, username, total_cost, _wallet_program, convert_memo);
|
||||
|
||||
// Добавляем средства на ЦПП маркетплейса
|
||||
Wallet::add_available_funds(_marketplace, coopname, username, total_cost, _marketplace_program, convert_memo);
|
||||
|
||||
// Блокируем средства на программе маркетплейса
|
||||
Wallet::block_funds(_marketplace, coopname, username, total_cost, _marketplace_program, memo);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Завершение поставки в направлении deliver_on_offer (completeoff).
|
||||
|
||||
@details После истечения гарантийной задержки завершает поставку и переводит единицы из заблокированных в доставленные.
|
||||
|
||||
Процесс: OFFER (parent) → ORDER (child)
|
||||
- Завершение происходит после истечения warranty_delay
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя пользователя
|
||||
@param request_hash Хэш встречной заявки (order)
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::completeoff(eosio::name coopname, eosio::name username, checksum256 request_hash) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.type == "order"_n, "Метод completeoff применим только к заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
|
||||
eosio::check(change.status == "received2"_n, "Завершение возможно только в статусе received2");
|
||||
eosio::check(change.warranty_delay_until.sec_since_epoch() < eosio::current_time_point().sec_since_epoch(), "Время гарантийной задержки еще не истекло");
|
||||
|
||||
// Удаляем родительскую заявку (offer)
|
||||
auto parent_itr = requests.find(parent_change.id);
|
||||
requests.erase(parent_itr);
|
||||
|
||||
// Удаляем встречную заявку (order)
|
||||
auto change_itr = requests.find(change.id);
|
||||
requests.erase(change_itr);
|
||||
|
||||
// Обрабатываем членские взносы (увеличиваем счёт кооператива согласно ТЗ)
|
||||
if (change.membership_fee_amount.amount > 0) {
|
||||
std::string member_fee_memo = "Членский взнос по программе №" + std::to_string(_marketplace_program_id) + " с идентификатором: " + checksum256_to_hex(change.hash);
|
||||
Wallet::add_member_fee(_marketplace, coopname, change.product_contributor, _marketplace_program_id, change.membership_fee_amount, member_fee_memo);
|
||||
}
|
||||
// Удаляем все сегменты, связанные с заявкой
|
||||
marketplace::delete_segments_by_request(coopname, change.id);
|
||||
|
||||
std::string memo = "Возврат паевого взноса по программе №" + std::to_string(_marketplace_program_id) + " с идентификатором: " + checksum256_to_hex(change.hash) + "в ЦПП 'Цифровой Кошелёк'";
|
||||
|
||||
// Поставщику списываем средства в программе маркетплейса (base_cost согласно ТЗ)
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, change.product_contributor, change.base_cost, _marketplace_program, memo);
|
||||
|
||||
// Поставщику начисляем средства в ЦПП Цифровой Кошелек (base_cost согласно ТЗ)
|
||||
Wallet::add_available_funds(_marketplace, coopname, change.product_contributor, change.base_cost, _wallet_program, memo);
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Отклонение принятия заявки советом (declineacc).
|
||||
|
||||
@details Данный метод вызывается советом когда заявление на конвертацию, возврат или взнос отклоняется.
|
||||
При отклонении отменяется дочерняя заявка, а родительская отменяется только если количество единиц товара равно.
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param request_hash Хэш заявки, которая должна быть отменена
|
||||
@param reason Причина отклонения
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p _soviet
|
||||
*/
|
||||
[[eosio::action]] void marketplace::declineacc(eosio::name coopname, checksum256 request_hash, std::string reason) {
|
||||
require_auth(_soviet);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
|
||||
// Если заявка не найдена, ничего не делаем (возможно уже удалена)
|
||||
if (!change_opt.has_value()) {
|
||||
print("Заявка уже удалена или не найдена. Кооператив: ", coopname, ", причина: ", reason);
|
||||
return;
|
||||
}
|
||||
|
||||
auto change = change_opt.value();
|
||||
|
||||
// Логирование отклонения
|
||||
print("Отклонение заявления советом. Заявка ID: ", change.id, ", причина: ", reason);
|
||||
|
||||
// Если это дочерняя заявка, отменяем её и проверяем нужно ли отменять родительскую
|
||||
if (change.parent_id > 0) {
|
||||
decline_child_request(coopname, change);
|
||||
} else {
|
||||
// Если это родительская заявка, ищем все дочерние и отменяем их
|
||||
decline_parent_and_children(coopname, change);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отмена дочерней заявки при отклонении с проверкой равенства единиц
|
||||
*/
|
||||
void marketplace::decline_child_request(eosio::name coopname, const request& child_change) {
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
|
||||
// Проверяем, что заявка все еще существует
|
||||
auto child_itr = requests.find(child_change.id);
|
||||
if (child_itr == requests.end()) {
|
||||
return; // Заявка уже удалена
|
||||
}
|
||||
|
||||
// Разблокируем средства если это заказ
|
||||
if (child_change.type == "order"_n && child_change.total_cost.amount > 0) {
|
||||
std::string memo = "Отклонение заявления советом по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(child_change.id);
|
||||
|
||||
// Списываем средства с ЦПП маркетплейса
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, child_change.money_contributor, child_change.total_cost, _marketplace_program, memo);
|
||||
// Начисляем средства на ЦПП Цифровой Кошелёк
|
||||
Wallet::add_available_funds(_marketplace, coopname, child_change.money_contributor, child_change.total_cost, _wallet_program, memo);
|
||||
}
|
||||
|
||||
// Получаем родительскую заявку
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, child_change.parent_hash);
|
||||
bool should_cancel_parent = false;
|
||||
|
||||
if (parent_change_opt.has_value()) {
|
||||
auto parent_itr = requests.find(parent_change_opt.value().id);
|
||||
if (parent_itr != requests.end()) {
|
||||
// Возвращаем заблокированные единицы в родительскую заявку
|
||||
if (child_change.blocked_units > 0) {
|
||||
requests.modify(parent_itr, _marketplace, [&](auto &e) {
|
||||
e.remaining_units += child_change.blocked_units;
|
||||
e.blocked_units -= child_change.blocked_units;
|
||||
e.base_cost = e.remaining_units * e.unit_cost;
|
||||
});
|
||||
|
||||
// Получаем обновленную родительскую заявку для проверки единиц
|
||||
auto updated_parent = requests.find(parent_change_opt.value().id);
|
||||
|
||||
// Проверяем равенство количества единиц для определения стратегии удаления
|
||||
// Сравниваем remaining_units дочерней заявки с remaining_units обновленной родительской
|
||||
bool units_equal = (child_change.remaining_units == updated_parent->remaining_units);
|
||||
|
||||
if (units_equal) {
|
||||
// Если количество единиц равно - отмечаем для удаления родительской заявки
|
||||
should_cancel_parent = true;
|
||||
}
|
||||
} else {
|
||||
// Если заблокированных единиц нет, сравниваем remaining_units как есть
|
||||
bool units_equal = (child_change.remaining_units == parent_itr->remaining_units);
|
||||
if (units_equal) {
|
||||
should_cancel_parent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем сегменты дочерней заявки
|
||||
marketplace::delete_segments_by_request(coopname, child_change.id);
|
||||
|
||||
// Удаляем дочернюю заявку
|
||||
requests.erase(child_itr);
|
||||
|
||||
// Удаляем родительскую заявку если количество единиц равно
|
||||
if (should_cancel_parent && parent_change_opt.has_value()) {
|
||||
// Используем метод decline_parent_request для консистентности
|
||||
decline_parent_request(coopname, parent_change_opt.value());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отмена родительской заявки при отклонении
|
||||
*/
|
||||
void marketplace::decline_parent_request(eosio::name coopname, const request& parent_change) {
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
|
||||
// Проверяем, что заявка все еще существует
|
||||
auto parent_itr = requests.find(parent_change.id);
|
||||
if (parent_itr == requests.end()) {
|
||||
return; // Заявка уже удалена
|
||||
}
|
||||
|
||||
// Разблокируем средства если это родительский заказ
|
||||
if (parent_change.type == "order"_n && parent_change.total_cost.amount > 0 && parent_change.parent_id == 0) {
|
||||
std::string memo = "Отклонение заявления советом по программе №" + std::to_string(_marketplace_program_id) + " с идентификатором: " + checksum256_to_hex(parent_change.hash);
|
||||
|
||||
// Списываем средства с ЦПП маркетплейса
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, parent_change.username, parent_change.total_cost, _marketplace_program, memo);
|
||||
|
||||
// Начисляем средства на ЦПП Цифровой Кошелёк
|
||||
Wallet::add_available_funds(_marketplace, coopname, parent_change.username, parent_change.total_cost, _wallet_program, memo);
|
||||
}
|
||||
|
||||
// Удаляем родительскую заявку
|
||||
requests.erase(parent_itr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отмена родительской заявки и всех её дочерних заявок
|
||||
*/
|
||||
void marketplace::decline_parent_and_children(eosio::name coopname, const request& parent_change) {
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
|
||||
// Ищем все дочерние заявки и отменяем их
|
||||
auto parent_index = requests.get_index<"byparent"_n>();
|
||||
auto children_begin = parent_index.lower_bound(parent_change.id);
|
||||
auto children_end = parent_index.upper_bound(parent_change.id);
|
||||
|
||||
// Сначала отменяем всех детей
|
||||
std::vector<uint64_t> children_to_cancel;
|
||||
for (auto itr = children_begin; itr != children_end; ++itr) {
|
||||
children_to_cancel.push_back(itr->id);
|
||||
}
|
||||
|
||||
for (uint64_t child_id : children_to_cancel) {
|
||||
auto child_itr = requests.find(child_id);
|
||||
if (child_itr != requests.end()) {
|
||||
// Для дочерних заявок используем упрощенную логику без проверки единиц
|
||||
// так как мы отменяем родительскую заявку в любом случае
|
||||
decline_child_simple(coopname, *child_itr);
|
||||
}
|
||||
}
|
||||
|
||||
// Затем отменяем родительскую заявку
|
||||
decline_parent_request(coopname, parent_change);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Упрощенная отмена дочерней заявки без проверки единиц (используется при отмене родительской)
|
||||
*/
|
||||
void marketplace::decline_child_simple(eosio::name coopname, const request& child_change) {
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
|
||||
// Проверяем, что заявка все еще существует
|
||||
auto child_itr = requests.find(child_change.id);
|
||||
if (child_itr == requests.end()) {
|
||||
return; // Заявка уже удалена
|
||||
}
|
||||
|
||||
// Разблокируем средства если это заказ
|
||||
if (child_change.type == "order"_n && child_change.total_cost.amount > 0) {
|
||||
std::string memo = "Отклонение заявления советом по программе №" + std::to_string(_marketplace_program_id) + " с идентификатором: " + checksum256_to_hex(child_change.hash);
|
||||
|
||||
// Списываем средства с ЦПП маркетплейса
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, child_change.money_contributor, child_change.total_cost, _marketplace_program, memo);
|
||||
// Начисляем средства на ЦПП Цифровой Кошелёк
|
||||
Wallet::add_available_funds(_marketplace, coopname, child_change.money_contributor, child_change.total_cost, _wallet_program, memo);
|
||||
}
|
||||
|
||||
// Удаляем сегменты дочерней заявки
|
||||
marketplace::delete_segments_by_request(coopname, child_change.id);
|
||||
|
||||
// Удаляем дочернюю заявку
|
||||
requests.erase(child_itr);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Отклонение встречной заявки в направлении deliver_on_offer (declineoff).
|
||||
|
||||
@details Позволяет поставщику отклонить встречную заявку (child order) на поставку имущества.
|
||||
|
||||
Процесс: OFFER (parent) → ORDER (child)
|
||||
- Поставщик отклоняет встречную заявку
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя поставщика
|
||||
@param request_hash Хэш встречной заявки (order)
|
||||
@param meta Причина отклонения
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::declineoff(eosio::name coopname, eosio::name username, checksum256 request_hash, std::string meta) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.type == "order"_n, "Метод offerdecline применим только к заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
eosio::check(change.status == "active"_n, "Только активная заявка может быть отклонена");
|
||||
|
||||
// Если заявка была принята (blocked_units > 0), возвращаем единицы товара в родительскую заявку
|
||||
if (change.blocked_units > 0) {
|
||||
auto parent_itr = requests.find(parent_change.id);
|
||||
eosio::check(parent_itr != requests.end(), "Родительская заявка не найдена для обновления");
|
||||
requests.modify(parent_itr, _marketplace, [&](auto &p) {
|
||||
p.remaining_units += change.blocked_units;
|
||||
p.blocked_units -= change.blocked_units;
|
||||
p.base_cost = p.remaining_units * p.unit_cost;
|
||||
});
|
||||
|
||||
// Проверяем инварианты родительской заявки
|
||||
auto updated_parent = requests.find(parent_change.id);
|
||||
marketplace::check_units_invariant(*updated_parent, "offerdecline_parent_update");
|
||||
}
|
||||
|
||||
// Разблокируем средства заказчика
|
||||
if (change.total_cost.amount > 0) {
|
||||
std::string memo = "Отклонение встречной заявки поставщиком по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(change.id);
|
||||
|
||||
// Списываем средства с ЦПП маркетплейса
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, change.money_contributor, change.total_cost, _marketplace_program, memo);
|
||||
// Начисляем средства на ЦПП Цифровой Кошелёк
|
||||
Wallet::add_available_funds(_marketplace, coopname, change.money_contributor, change.total_cost, _wallet_program, memo);
|
||||
}
|
||||
|
||||
// Удаляем сегменты встречной заявки
|
||||
marketplace::delete_segments_by_request(coopname, change.id);
|
||||
|
||||
// Проверяем равенство количества единиц для определения стратегии удаления
|
||||
auto updated_parent = requests.find(parent_change.id);
|
||||
bool units_equal = false;
|
||||
|
||||
if (updated_parent != requests.end()) {
|
||||
units_equal = (change.remaining_units == updated_parent->remaining_units);
|
||||
}
|
||||
|
||||
// Удаляем встречную заявку
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для удаления");
|
||||
requests.erase(change_itr);
|
||||
|
||||
// Если количество единиц равно - удаляем также родительскую заявку
|
||||
if (units_equal && updated_parent != requests.end()) {
|
||||
requests.erase(updated_parent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Начало транспортировки имущества в направлении deliver_on_offer (deliveroff1).
|
||||
|
||||
@details Председатель или уполномоченный КУ передает имущество на транспортировку.
|
||||
|
||||
Процесс: OFFER (parent) → ORDER (child)
|
||||
- Этап 1 из 4: Передача имущества на транспортировку
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя председателя или уполномоченного КУ
|
||||
@param request_hash Хэш встречной заявки (order)
|
||||
@param transport_act_1 Акт передачи имущества на транспортировку
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::deliveroff1(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 transport_act_1) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "supplied2"_n, "Только поставка с подтверждением может быть отправлена на транспортировку");
|
||||
eosio::check(change.type == "order"_n, "Метод deliveroff1 применим только к заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
|
||||
auto branch = get_branch_or_fail(coopname, change.braname);
|
||||
|
||||
//Проверяем права доступа на КУ (председатель или доверенное лицо)
|
||||
eosio::check(branch.is_user_authorized(username), "Недостаточно прав доступа для передачи имущества на транспортировку");
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(transport_act_1, { username });
|
||||
|
||||
// Обновляем статус встречной заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o) {
|
||||
o.status = "delivered1"_n;
|
||||
o.supplied_units = o.blocked_units;
|
||||
});
|
||||
|
||||
// Сохраняем акт передачи на транспортировку в сегменте s2c
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.transport_act_1 = transport_act_1;
|
||||
s.coopactor = username; // Представитель кооператива, который передал имущество на транспортировку
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "deliveroff1_child_update");
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Прием имущества водителем для транспортировки в направлении deliver_on_offer (deliveroff2).
|
||||
|
||||
@details Водитель (пайщик) принимает имущество в транспортировку.
|
||||
|
||||
Процесс: OFFER (parent) → ORDER (child)
|
||||
- Этап 2 из 4: Прием имущества водителем
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя водителя (пайщик)
|
||||
@param request_hash Хэш встречной заявки (order)
|
||||
@param transport_act_2 Акт приема имущества водителем
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::deliveroff2(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 transport_act_2) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "delivered1"_n, "Прием в транспортировку возможен только в статусе delivered1");
|
||||
eosio::check(change.type == "order"_n, "Метод deliveroff2 применим только к заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
|
||||
// Проверяем, что пользователь является пайщиком кооператива
|
||||
get_participant_or_fail(coopname, username);
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(transport_act_2, { username });
|
||||
|
||||
// Обновляем статус встречной заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o) {
|
||||
o.status = "delivered2"_n;
|
||||
});
|
||||
|
||||
// Сохраняем акт приема водителем в сегменте s2c
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.transport_act_2 = transport_act_2;
|
||||
s.driver_username = username; // Водитель, который принял имущество
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "deliveroff2_child_update");
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Доставка имущества до КУ водителем в направлении deliver_on_offer (deliveroff3).
|
||||
|
||||
@details Водитель (пайщик) доставляет имущество до кооперативного участка получателя.
|
||||
|
||||
Процесс: OFFER (parent) → ORDER (child)
|
||||
- Этап 3 из 4: Доставка до КУ водителем
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя водителя (пайщик)
|
||||
@param request_hash Хэш встречной заявки (order)
|
||||
@param transport_act_3 Акт доставки до КУ
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::deliveroff3(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 transport_act_3) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "delivered2"_n, "Доставка до КУ возможна только в статусе delivered2");
|
||||
eosio::check(change.type == "order"_n, "Метод deliveroff3 применим только к заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
|
||||
// Проверяем, что пользователь является пайщиком кооператива
|
||||
get_participant_or_fail(coopname, username);
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(transport_act_3, { username });
|
||||
|
||||
// Обновляем статус встречной заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o) {
|
||||
o.status = "delivered3"_n;
|
||||
o.delivered_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
|
||||
// Сохраняем акт доставки до КУ в сегменте s2c
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.transport_act_3 = transport_act_3;
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "deliveroff3_child_update");
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Прием имущества председателем КУ в направлении deliver_on_offer (deliveroff4).
|
||||
|
||||
@details Председатель кооперативного участка принимает доставленное имущество.
|
||||
|
||||
Процесс: OFFER (parent) → ORDER (child)
|
||||
- Этап 4 из 4: Прием имущества председателем КУ
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя председателя КУ
|
||||
@param request_hash Хэш встречной заявки (order)
|
||||
@param transport_act_4 Акт приема имущества председателем КУ
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::deliveroff4(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 transport_act_4) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "delivered3"_n, "Прием имущества председателем возможен только в статусе delivered3");
|
||||
eosio::check(change.type == "order"_n, "Метод deliveroff4 применим только к заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
|
||||
auto branch = get_branch_or_fail(coopname, change.braname);
|
||||
|
||||
//Проверяем права доступа на КУ (председатель или доверенное лицо)
|
||||
eosio::check(branch.is_user_authorized(username), "Недостаточно прав доступа для приема имущества");
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(transport_act_4, { username });
|
||||
|
||||
// Обновляем статус встречной заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o) {
|
||||
o.status = "delivered4"_n;
|
||||
o.deadline_for_receipt = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch() + 3 * 24 * 60 * 60); // 3 дня на получение
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "deliveroff4_child_update");
|
||||
|
||||
// Сохраняем акт приема председателем КУ в сегменте s2c
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.transport_act_4 = transport_act_4;
|
||||
s.receive_from_driver_coopactor = username; // Председатель КУ, который принял имущество
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Создать родительскую заявку на имущественный паевой взнос.
|
||||
*
|
||||
* Данный метод позволяет пользователю создать родительскую заявку на имущественный паевой взнос.
|
||||
*
|
||||
* @param coopname Имя кооператива
|
||||
* @param username Имя пользователя, инициирующего заявку
|
||||
* @param hash Хэш заявки (уникальный идентификатор)
|
||||
* @param units Количество частей (штук) товара или услуги
|
||||
* @param unit_cost Цена за единицу (штуку) товара или услуги
|
||||
* @param product_lifecycle_secs Время жизни продукта, заявляемое поставщиком
|
||||
* @param warranty_period_secs Гарантийный срок в секундах
|
||||
* @param membership_fee_amount Сумма членского взноса
|
||||
* @param cancellation_fee_amount Сумма комиссии за отмену заявки
|
||||
* @param braname Имя кооперативного участка куда будет поставляться товар
|
||||
* @param meta Метаданные о заявке
|
||||
*
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::parentoffer(eosio::name coopname, eosio::name braname, eosio::name username, checksum256 hash, uint64_t units, eosio::asset unit_cost, uint32_t product_lifecycle_secs, uint32_t warranty_period_secs, eosio::asset membership_fee_amount, eosio::asset cancellation_fee_amount, std::string meta) {
|
||||
require_auth(coopname);
|
||||
|
||||
// Проверяем, что заявка с таким хэшем не существует
|
||||
auto existing_request = get_request_by_hash(coopname, hash);
|
||||
eosio::check(!existing_request.has_value(), "Заявка с таким хэшем уже существует");
|
||||
|
||||
// Проверяем, что символ токена совпадает с символом токена кооператива
|
||||
auto coop = get_cooperative_or_fail(coopname);
|
||||
eosio::check(unit_cost.symbol == coop.initial.symbol, "Неверный символ токена");
|
||||
eosio::check(membership_fee_amount.symbol == coop.initial.symbol, "Неверный символ токена для членского взноса");
|
||||
eosio::check(cancellation_fee_amount.symbol == coop.initial.symbol, "Неверный символ токена для комиссии отмены");
|
||||
eosio::check(units > 0, "Количество единиц в заявке должно быть больше нуля");
|
||||
eosio::check(unit_cost.amount >= 0, "Цена не может быть отрицательной");
|
||||
eosio::check(membership_fee_amount.amount >= 0, "Членский взнос не может быть отрицательным");
|
||||
eosio::check(cancellation_fee_amount.amount >= 0, "Комиссия за отмену не может быть отрицательной");
|
||||
|
||||
// Проверяем, что пользователь является пайщиком кооператива
|
||||
get_participant_or_fail(coopname, username);
|
||||
|
||||
// Проверяем существование кооперативного участка
|
||||
get_branch_or_fail(coopname, braname);
|
||||
|
||||
// Проверяем существование программы маркетплейса
|
||||
auto program = get_program_or_fail(coopname, _marketplace_program_id);
|
||||
|
||||
// Гарантийный срок возврата должен быть установлен для предложений
|
||||
eosio::check(product_lifecycle_secs > 0, "Гарантийный срок возврата для имущества должен быть установлен");
|
||||
eosio::check(warranty_period_secs > 0, "Гарантийный срок должен быть больше нуля");
|
||||
|
||||
// Для предложения рассчитываем общую стоимость включая членский взнос
|
||||
eosio::asset base_cost = unit_cost * units;
|
||||
eosio::asset total_cost = base_cost + membership_fee_amount;
|
||||
|
||||
// Проверяем что комиссия за отмену не превышает общую стоимость
|
||||
eosio::check(cancellation_fee_amount <= total_cost, "Комиссия за отмену не может превышать общую стоимость предложения");
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
uint64_t request_id = get_global_id(_marketplace, "requests"_n);
|
||||
requests.emplace(_marketplace, [&](auto &i) {
|
||||
i.id = request_id;
|
||||
i.hash = hash;
|
||||
i.type = "offer"_n;
|
||||
i.username = username;
|
||||
i.coopname = coopname;
|
||||
i.status = "active"_n;
|
||||
i.remaining_units = units;
|
||||
i.unit_cost = unit_cost;
|
||||
i.base_cost = base_cost;
|
||||
i.membership_fee_amount = membership_fee_amount;
|
||||
i.total_cost = total_cost;
|
||||
i.product_lifecycle_secs = product_lifecycle_secs;
|
||||
i.warranty_period_secs = warranty_period_secs;
|
||||
i.meta = meta;
|
||||
i.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
i.cancellation_fee_amount = cancellation_fee_amount;
|
||||
i.braname = braname;
|
||||
});
|
||||
|
||||
// Создаем сегмент для родительской заявки поставки от пайщика в кооператив
|
||||
marketplace::create_segment(coopname, request_id, marketplace::valid_segment("s2c"), username);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Подтверждение получения имущества председателем в направлении deliver_on_offer (recvcnfoff).
|
||||
|
||||
@details Председатель совета подтверждает получение имущества заказчиком и переводит статус на received2.
|
||||
|
||||
Процесс: OFFER (parent) → ORDER (child)
|
||||
- Председатель подтверждает получение имущества заказчиком
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя председателя
|
||||
@param request_hash Хэш встречной заявки (order)
|
||||
@param document Акт подтверждения получения
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::recvcnfoff(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 act2) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.type == "order"_n, "Метод offerrecievecnfrm применим только к заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
|
||||
eosio::check(change.status == "received1"_n, "Подтверждение получения возможно только в статусе received1");
|
||||
|
||||
eosio::check(username == change.money_contributor, "Недостаточно прав доступа для подтверждения получения");
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(act2, { username });
|
||||
|
||||
// Обновляем статус встречной заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &ch) {
|
||||
ch.status = "received2"_n;
|
||||
ch.received_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
ch.warranty_delay_until = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch() + change.warranty_period_secs);
|
||||
});
|
||||
|
||||
// Сохраняем акт получения в return сегменте
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"), [&](auto &s) {
|
||||
s.act2 = act2;
|
||||
s.status = "received2"_n;
|
||||
});
|
||||
|
||||
// Уменьшаем паевой фонд на сумму возвращаемой стоимости имущества заказчику
|
||||
Fund::sub_circulating_funds(_marketplace, coopname, change.total_cost, false);
|
||||
|
||||
std::string memo = "Возврат паевого взноса по программе №" + std::to_string(_marketplace_program_id) + " с идентификатором: " + checksum256_to_hex(change.hash);
|
||||
|
||||
// Списываем заблокированный баланс заказчика в ЦПП Маркетплейс (base_cost согласно ТЗ)
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, change.money_contributor, change.base_cost, _marketplace_program, memo);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Подпись акта получения имущества заказчиком в направлении deliver_on_offer (recvoff).
|
||||
|
||||
@details Заказчик подписывает акт получения имущества от поставщика.
|
||||
|
||||
Процесс: OFFER (parent) → ORDER (child)
|
||||
- Заказчик (автор child order) получает имущество от поставщика
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя заказчика
|
||||
@param request_hash Хэш встречной заявки (order)
|
||||
@param document Акт получения имущества
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::recvoff(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 act1) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.type == "order"_n, "Метод offerrecieve применим только к заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
|
||||
eosio::check(change.status == "delivered4"_n, "Имущество может быть получено только в статусе delivered4");
|
||||
eosio::check(change.deadline_for_receipt.sec_since_epoch() >= eosio::current_time_point().sec_since_epoch(), "Время на получение имущества истекло");
|
||||
|
||||
auto branch = get_branch_or_fail(coopname, change.braname);
|
||||
|
||||
//Проверяем права доступа на КУ (председатель или доверенное лицо)
|
||||
eosio::check(branch.is_user_authorized(username), "Недостаточно прав доступа для выдачи имущества");
|
||||
|
||||
// Проверяем подпись документа от председателя или доверенного
|
||||
verify_document_or_fail(act1, { username });
|
||||
|
||||
// Обновляем статус и устанавливаем гарантийную задержку
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
|
||||
requests.modify(change_itr, coopname, [&](auto &ch){
|
||||
ch.status = "received1"_n;
|
||||
});
|
||||
|
||||
// Сохраняем акт получения в return сегменте
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"), [&](auto &s) {
|
||||
s.act1 = act1;
|
||||
s.status = "received1"_n;
|
||||
s.coopactor = username; // Представитель кооператива, который выдал имущество
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Подтверждение поставки председателем в направлении deliver_on_offer (supplcnfoff).
|
||||
|
||||
@details Председатель совета подтверждает поставку имущества и переводит статус заявки на "доставлено".
|
||||
|
||||
Процесс: OFFER (parent) → ORDER (child)
|
||||
- Председатель подтверждает факт поставки имущества
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя председателя
|
||||
@param request_hash Хэш встречной заявки (order)
|
||||
@param document Акт подтверждения поставки
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::supplcnfoff(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 act2) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.type == "order"_n, "Метод offersupplycnfrm применим только к заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
|
||||
eosio::check(change.status == "supplied1"_n, "Подтверждение поставки возможно только в статусе supplied1");
|
||||
|
||||
auto branch = get_branch_or_fail(coopname, change.braname);
|
||||
|
||||
//Проверяем права доступа на КУ (председатель или доверенное лицо)
|
||||
eosio::check(branch.is_user_authorized(username), "Недостаточно прав доступа для приёма имущества");
|
||||
|
||||
// Проверяем подписи документа
|
||||
verify_document_or_fail(act2, { username, change.product_contributor });
|
||||
|
||||
// Обновляем статус встречной заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o) {
|
||||
o.status = "supplied2"_n;
|
||||
o.supplied_units = o.blocked_units;
|
||||
// delivered_at и deadline_for_receipt устанавливаются в delivercnfoff согласно ТЗ
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "offersupplycnfrm_child_update");
|
||||
|
||||
// Сохраняем акт подтверждения поставки в contribute сегменте
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.act2 = act2;
|
||||
s.status = "supplied2"_n;
|
||||
s.coopactor = username; // Представитель кооператива, который принял имущество
|
||||
});
|
||||
|
||||
// Пополняем паевой фонд
|
||||
Fund::add_circulating_funds(_marketplace, coopname, change.total_cost);
|
||||
|
||||
// Пополняем кошелёк поставщика (base_cost, а не total_cost согласно ТЗ)
|
||||
std::string memo = "Имущественный паевой взнос по заявке №" + checksum256_to_hex(change.hash);
|
||||
Wallet::add_available_funds(_marketplace, coopname, change.product_contributor, change.base_cost, _marketplace_program, memo);
|
||||
|
||||
// Блокируем средства на программе маркетплейса
|
||||
Wallet::block_funds(_marketplace, coopname, change.product_contributor, change.base_cost, _marketplace_program, memo);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Поставка имущества в направлении deliver_on_offer (supplyoff).
|
||||
|
||||
@details Поставщик поставляет имущество по встречной заявке и предоставляет акт поставки.
|
||||
|
||||
Процесс: OFFER (parent) → ORDER (child)
|
||||
- Поставщик (автор parent offer) поставляет имущество заказчику
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя поставщика
|
||||
@param request_hash Хэш встречной заявки (order)
|
||||
@param document Акт поставки имущества
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::supplyoff(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 act1) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "authorized"_n, "Только авторизованная заявка может быть поставлена");
|
||||
eosio::check(change.type == "order"_n, "Метод offersupply применим только к встречным заявкам типа order");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "offer"_n, "Родительская заявка должна быть типа offer");
|
||||
eosio::check(change.product_contributor == username, "Недостаточно прав доступа передачи имущества");
|
||||
|
||||
//проводим проверку подписи документа
|
||||
verify_document_or_fail(act1, { username });
|
||||
|
||||
// Обновляем статус встречной заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o) {
|
||||
o.status = "supplied1"_n;
|
||||
o.supplied_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "offersupply_child_update");
|
||||
|
||||
// Сохраняем акт поставки в contribute сегменте
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.act1 = act1;
|
||||
s.status = "supplied1"_n;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Подтверждение готовности выполнить заказ в направлении deliver_on_order (acceptord).
|
||||
|
||||
@details Данный метод позволяет поставщику, который получил заказ от заказчика, подтвердить свою готовность принять его и создать встречную заявку типа "offer". При этом поставщик подает два документа: заявление на взнос имуществом и заявление на конвертацию в цифровой кошелек.
|
||||
|
||||
Процесс: ORDER (parent) → OFFER (child)
|
||||
- Parent: денежный паевой взнос (заказчик)
|
||||
- Child: имущественный паевой взнос (поставщик)
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя поставщика, подтверждающего готовность выполнить заказ
|
||||
@param request_hash Хэш заявки, которую следует принять
|
||||
@param contribution_document Заявление на взнос имуществом
|
||||
@param conversion_document Заявление на конвертацию в цифровой кошелек
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::acceptord(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 contribution_document, document2 conversion_document) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "active"_n, "Только активная заявка может быть принята");
|
||||
eosio::check(change.type == "offer"_n, "Метод orderaccept применим только к встречным заявкам типа offer");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "order"_n, "Родительская заявка должна быть типа order");
|
||||
eosio::check(parent_change.username == username, "Недостаточно прав доступа");
|
||||
eosio::check(parent_change.remaining_units >= change.remaining_units, "Недостаточно объектов для поставки");
|
||||
|
||||
// Проверяем подписи документов
|
||||
verify_document_or_fail(contribution_document);
|
||||
verify_document_or_fail(conversion_document);
|
||||
|
||||
// Обновляем родительскую заявку (order)
|
||||
auto parent_itr = requests.find(parent_change.id);
|
||||
eosio::check(parent_itr != requests.end(), "Родительская заявка не найдена для обновления");
|
||||
requests.modify(parent_itr, _marketplace, [&](auto &i) {
|
||||
i.remaining_units -= change.remaining_units;
|
||||
i.base_cost = (parent_change.remaining_units - change.remaining_units) * parent_change.unit_cost;
|
||||
i.blocked_units += change.remaining_units;
|
||||
});
|
||||
|
||||
// Проверяем инварианты родительской заявки
|
||||
auto updated_parent = requests.find(parent_change.id);
|
||||
marketplace::check_units_invariant(*updated_parent, "orderaccept_parent_update");
|
||||
|
||||
// Обновляем встречную заявку (offer)
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o){
|
||||
o.status = "accepted"_n;
|
||||
o.blocked_units += change.remaining_units;
|
||||
o.remaining_units = 0;
|
||||
o.accepted_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "orderaccept_child_update");
|
||||
|
||||
// Сохраняем заявление на конвертацию в contribute сегменте
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.statement = conversion_document;
|
||||
s.status = "statement"_n;
|
||||
});
|
||||
|
||||
// Сохраняем заявление на возврат в return сегменте
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"), [&](auto &s) {
|
||||
s.statement = contribution_document;
|
||||
s.status = "statement"_n;
|
||||
});
|
||||
|
||||
// Извлекаем заявления из сегментов
|
||||
auto s2c_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"));
|
||||
auto c2r_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"));
|
||||
|
||||
// Используем хэш встречной заявки как идентификатор пакета решений
|
||||
checksum256 agenda_hash = change.hash;
|
||||
|
||||
// Отправляем заявление на взнос имуществом (из contribute сегмента) в совет
|
||||
Action::send<createagenda_interface>(
|
||||
_soviet,
|
||||
"createagenda"_n,
|
||||
_marketplace,
|
||||
coopname,
|
||||
username,
|
||||
get_valid_soviet_action("authords2c"_n),
|
||||
agenda_hash,
|
||||
_marketplace,
|
||||
Marketplace::get_valid_marketplace_action("authords2c"_n),
|
||||
"declineacc"_n,
|
||||
s2c_segment.statement,
|
||||
std::string("")
|
||||
);
|
||||
|
||||
// Отправляем заявление на конвертацию (из return сегмента) в совет
|
||||
Action::send<createagenda_interface>(
|
||||
_soviet,
|
||||
"createagenda"_n,
|
||||
_marketplace,
|
||||
coopname,
|
||||
username,
|
||||
get_valid_soviet_action("authordc2r"_n),
|
||||
agenda_hash,
|
||||
_marketplace,
|
||||
Marketplace::get_valid_marketplace_action("authordc2r"_n),
|
||||
"declineacc"_n,
|
||||
c2r_segment.statement,
|
||||
std::string("")
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Авторизация сегмента "c2r" для направления deliver_on_order (authordc2r).
|
||||
|
||||
@details Метод используется для подтверждения согласия совета на заявление на конвертацию в направлении ORDER → OFFER.
|
||||
Заявка становится авторизованной только когда оба сегмента (contribute и return) получили авторизацию.
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param request_hash Хэш встречной заявки на обмен (offer)
|
||||
@param authorization Документ авторизации
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p _soviet
|
||||
*/
|
||||
[[eosio::action]] void marketplace::authordc2r(eosio::name coopname, checksum256 request_hash, document2 authorization) {
|
||||
require_auth(_soviet);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.type == "offer"_n, "Метод authordc2r применим только к заявкам типа offer");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "order"_n, "Родительская заявка должна быть типа order");
|
||||
|
||||
// Обновляем return сегмент
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"), [&](auto &s) {
|
||||
s.authorization = authorization;
|
||||
s.status = "authorized"_n;
|
||||
});
|
||||
|
||||
// Проверяем статусы обоих сегментов
|
||||
auto s2c_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"));
|
||||
auto c2r_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"));
|
||||
|
||||
// Если оба сегмента авторизованы, авторизуем заявку
|
||||
if (s2c_segment.status == "authorized"_n && c2r_segment.status == "authorized"_n) {
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _soviet, [&](auto &o) {
|
||||
o.status = "authorized"_n;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Авторизация сегмента "s2c" для направления deliver_on_order (authords2c).
|
||||
|
||||
@details Метод используется для подтверждения согласия совета на заявление на взнос имуществом в направлении ORDER → OFFER.
|
||||
Заявка становится авторизованной только когда оба сегмента (contribute и return) получили авторизацию.
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param request_hash Хэш встречной заявки на обмен (offer)
|
||||
@param authorization Документ авторизации
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p _soviet
|
||||
*/
|
||||
[[eosio::action]] void marketplace::authords2c(eosio::name coopname, checksum256 request_hash, document2 authorization) {
|
||||
require_auth(_soviet);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.type == "offer"_n, "Метод authords2c применим только к заявкам типа offer");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "order"_n, "Родительская заявка должна быть типа order");
|
||||
|
||||
// Обновляем contribute сегмент
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.authorization = authorization;
|
||||
s.status = "authorized"_n;
|
||||
});
|
||||
|
||||
// Проверяем статусы обоих сегментов
|
||||
auto s2c_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"));
|
||||
auto c2r_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"));
|
||||
|
||||
// Если оба сегмента авторизованы, авторизуем заявку
|
||||
if (s2c_segment.status == "authorized"_n && c2r_segment.status == "authorized"_n) {
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _soviet, [&](auto &o) {
|
||||
o.status = "authorized"_n;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Создать дочернюю заявку на имущественный паевой взнос (в ответ на родительский заказ).
|
||||
*
|
||||
* Данный метод позволяет пользователю создать встречную заявку на имущественный паевой взнос.
|
||||
*
|
||||
* @param coopname Имя кооператива
|
||||
* @param username Имя пользователя, инициирующего заявку
|
||||
* @param hash Хэш заявки (уникальный идентификатор)
|
||||
* @param parent_hash Хэш родительской заявки
|
||||
* @param units Количество частей (штук) товара или услуги
|
||||
* @param unit_cost Цена за единицу (штуку) товара или услуги
|
||||
* @param product_lifecycle_secs Время жизни продукта, заявляемое поставщиком
|
||||
* @param braname Имя кооперативного участка куда будет поставляться товар
|
||||
* @param document Сопутствующий подписанный документ на взнос или возврат взноса
|
||||
* @param meta Метаданные о заявке
|
||||
*
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::childoffer(eosio::name coopname, eosio::name braname, eosio::name username, checksum256 hash, checksum256 parent_hash, uint64_t units, eosio::asset unit_cost, uint32_t product_lifecycle_secs, document2 document, std::string meta) {
|
||||
require_auth(coopname);
|
||||
|
||||
// Проверяем, что заявка с таким хэшем не существует
|
||||
auto existing_request = get_request_by_hash(coopname, hash);
|
||||
eosio::check(!existing_request.has_value(), "Заявка с таким хэшем уже существует");
|
||||
|
||||
cooperatives2_index coops(_registrator, _registrator.value);
|
||||
auto coop = coops.find(coopname.value);
|
||||
eosio::check(coop != coops.end() && coop -> is_coop(), "Кооператив не найден");
|
||||
eosio::check(unit_cost.symbol == coop -> initial.symbol, "Неверный символ токена");
|
||||
eosio::check(units > 0, "Количество единиц в заявке должно быть больше нуля");
|
||||
eosio::check(unit_cost.amount >= 0, "Цена не может быть отрицательной");
|
||||
|
||||
// Проверяем существование кооперативного участка
|
||||
get_branch_or_fail(coopname, braname);
|
||||
|
||||
// Находим родительскую заявку по хэшу
|
||||
auto parent_change = get_request_by_hash_or_fail(coopname, parent_hash, "Родительская заявка не найдена");
|
||||
eosio::check(parent_change.type == "order"_n, "Родительская заявка должна быть заказом");
|
||||
|
||||
eosio::check(parent_change.unit_cost.amount == unit_cost.amount, "Торги запрещены");
|
||||
eosio::check(product_lifecycle_secs > 0, "Гарантийный срок возврата для имущества должен быть установлен");
|
||||
|
||||
//проводим проверку подписи документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
|
||||
// Используем константу marketplace program_id
|
||||
auto program = get_program_or_fail(coopname, _marketplace_program_id);
|
||||
|
||||
participants_index participants(_soviet, coopname.value);
|
||||
auto participant = participants.find(username.value);
|
||||
eosio::check(participant != participants.end(), "Вы не являетесь членом указанного кооператива");
|
||||
|
||||
// Берем членский взнос и комиссию за отмену из родительского заказа
|
||||
eosio::asset membership_fee_amount = parent_change.membership_fee_amount;
|
||||
eosio::asset cancellation_fee_amount = parent_change.cancellation_fee_amount;
|
||||
eosio::asset base_cost = unit_cost * units;
|
||||
eosio::asset total_cost = base_cost + membership_fee_amount;
|
||||
|
||||
// Проверяем что комиссия за отмену не превышает общую стоимость
|
||||
eosio::check(cancellation_fee_amount <= total_cost, "Комиссия за отмену не может превышать общую стоимость заказа");
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
uint64_t request_id = get_global_id(_marketplace, "requests"_n);
|
||||
|
||||
requests.emplace(_marketplace, [&](auto &i) {
|
||||
i.id = request_id;
|
||||
i.hash = hash;
|
||||
i.parent_id = parent_change.id;
|
||||
i.parent_hash = parent_hash;
|
||||
i.parent_username = parent_change.username;
|
||||
i.type = "offer"_n;
|
||||
i.coopname = coopname;
|
||||
i.username = username;
|
||||
i.status = "active"_n;
|
||||
i.remaining_units = units;
|
||||
i.unit_cost = unit_cost;
|
||||
i.membership_fee_amount = membership_fee_amount;
|
||||
i.base_cost = base_cost;
|
||||
i.total_cost = total_cost;
|
||||
i.product_lifecycle_secs = product_lifecycle_secs;
|
||||
i.money_contributor = parent_change.username;
|
||||
i.product_contributor = username;
|
||||
i.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
i.cancellation_fee_amount = cancellation_fee_amount;
|
||||
i.braname = braname;
|
||||
});
|
||||
|
||||
// Создаем сегмент для дочерней заявки поставки от пайщика в кооператив
|
||||
marketplace::create_segment(coopname, request_id, marketplace::valid_segment("s2c"), username);
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Завершение процесса поставки в направлении deliver_on_order (completeord).
|
||||
|
||||
@details После истечения гарантийного срока и успешного получения товара заказчиком, процесс поставки завершается. Обновляются статусы, количество объектов, происходят финансовые операции.
|
||||
|
||||
Процесс: ORDER (parent) → OFFER (child)
|
||||
- Финализация денежного и имущественного паевых взносов
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя пользователя, инициирующего завершение
|
||||
@param request_hash Хэш встречной заявки (offer)
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::completeord(eosio::name coopname, eosio::name username, checksum256 request_hash) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.type == "offer"_n, "Метод completeord применим только к заявкам типа offer");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "order"_n, "Родительская заявка должна быть типа order");
|
||||
eosio::check(change.status == "recconfirmed"_n, "Завершение возможно только в статусе recconfirmed");
|
||||
|
||||
// Обновляем родительскую заявку (order)
|
||||
auto parent_itr = requests.find(parent_change.id);
|
||||
requests.erase(parent_itr);
|
||||
|
||||
// Обновляем встречную заявку (offer)
|
||||
auto change_itr = requests.find(change.id);
|
||||
requests.erase(change_itr);
|
||||
|
||||
auto program = get_program_or_fail(coopname, _marketplace_program_id);
|
||||
std::string memo = "Успешное завершение поставки имущества по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(change.id);
|
||||
|
||||
// Заказчику разблокируем баланс и списываем его
|
||||
Wallet::sub_blocked_funds(_marketplace, coopname, change.money_contributor, change.total_cost, _marketplace_program, memo);
|
||||
|
||||
// Поставщику разблокируем средства в программе
|
||||
Wallet::unblock_funds(_marketplace, coopname, change.product_contributor, change.base_cost, _marketplace_program, memo);
|
||||
|
||||
// Обрабатываем членские взносы
|
||||
if (change.membership_fee_amount.amount > 0) {
|
||||
// Сохраняем членский взнос в программе для дальнейшего списания
|
||||
Wallet::add_member_fee(_marketplace, coopname, change.money_contributor, _marketplace_program_id, change.membership_fee_amount, memo);
|
||||
}
|
||||
|
||||
// Извлекаем сегменты для завершения
|
||||
auto return_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("return"));
|
||||
auto contribution_segment = marketplace::get_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("contribute"));
|
||||
|
||||
// Удаляем сегменты встречной заявки
|
||||
marketplace::delete_segments_by_request(coopname, change.id);
|
||||
|
||||
// Удаляем встречную заявку
|
||||
requests.erase(change);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Отказ от заказа в направлении deliver_on_order (declineord).
|
||||
|
||||
@details Этот метод позволяет заказчику отклонить встречную заявку поставщика типа offer.
|
||||
|
||||
Процесс: ORDER (parent) → OFFER (child)
|
||||
- Заказчик отклоняет заявку поставщика
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя заказчика, отклоняющего заявку
|
||||
@param request_hash Хэш встречной заявки (offer), которую следует отклонить
|
||||
@param meta Дополнительные метаданные, связанные с отказом
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::declineord(eosio::name coopname, eosio::name username, checksum256 request_hash, std::string meta) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.type == "offer"_n, "Метод orderdecline применим только к заявкам типа offer");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "order"_n, "Родительская заявка должна быть типа order");
|
||||
eosio::check(change.status == "active"_n, "Только активная заявка может быть отклонена");
|
||||
|
||||
// Если заявка была принята (blocked_units > 0), возвращаем единицы товара в родительскую заявку
|
||||
if (change.blocked_units > 0) {
|
||||
auto parent_itr = requests.find(parent_change.id);
|
||||
eosio::check(parent_itr != requests.end(), "Родительская заявка не найдена для обновления");
|
||||
requests.modify(parent_itr, _marketplace, [&](auto &e) {
|
||||
e.remaining_units += change.blocked_units;
|
||||
e.blocked_units -= change.blocked_units;
|
||||
e.base_cost = e.remaining_units * e.unit_cost;
|
||||
});
|
||||
|
||||
// Проверяем инварианты после изменения
|
||||
auto updated_parent = requests.find(parent_change.id);
|
||||
marketplace::check_units_invariant(*updated_parent, "orderdecline_parent_update");
|
||||
}
|
||||
|
||||
// Обновляем встречную заявку
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, coopname, [&](auto &o){
|
||||
o.status = "declined"_n;
|
||||
o.declined_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
o.meta = meta;
|
||||
o.blocked_units = 0; // Обнуляем заблокированные единицы в дочерней заявке
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "orderdecline_child_update");
|
||||
|
||||
// Примечание: В процессе ORDER → OFFER разблокировка средств не требуется,
|
||||
// так как средства блокируются у заказчика при создании родительской заявки order,
|
||||
// а не при создании встречной заявки offer
|
||||
|
||||
// Проверяем равенство количества единиц для определения стратегии удаления
|
||||
auto updated_parent = requests.find(parent_change.id);
|
||||
bool units_equal = (change.remaining_units == updated_parent->remaining_units);
|
||||
|
||||
if (units_equal) {
|
||||
// Если количество единиц равно - удаляем обе заявки
|
||||
requests.erase(updated_parent);
|
||||
requests.erase(updated_change);
|
||||
} else {
|
||||
// Иначе удаляем только встречную заявку
|
||||
requests.erase(updated_change);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Доставка товара для направления deliver_on_order (deliverord).
|
||||
|
||||
@details Данный метод используется для фиксации доставки имущества от поставщика к заказчику.
|
||||
Встречная заявка должна быть предварительно в статусе "поставлено".
|
||||
|
||||
Процесс: ORDER (parent) → OFFER (child)
|
||||
- Parent: денежный паевой взнос (заказчик)
|
||||
- Child: имущественный паевой взнос (поставщик)
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя председателя совета
|
||||
@param request_hash Хэш встречной заявки (offer)
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::deliverord(eosio::name coopname, eosio::name username, checksum256 request_hash) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "supplied"_n, "Только поставленная заявка может быть доставлена");
|
||||
eosio::check(change.type == "offer"_n, "Метод orderdeliver применим только к заявкам типа offer");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "order"_n, "Родительская заявка должна быть типа order");
|
||||
|
||||
// Обновляем статус встречной заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o) {
|
||||
o.status = "delivered"_n;
|
||||
o.delivered_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
o.supplied_units = o.blocked_units;
|
||||
o.deadline_for_receipt = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch() + 3 * 24 * 60 * 60); // 3 дня на подтверждение
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "orderdeliver_update");
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Создать родительскую заявку на денежный паевой взнос.
|
||||
*
|
||||
* Данный метод позволяет пользователю создать родительскую заявку на денежный паевой взнос.
|
||||
*
|
||||
* @param coopname Имя кооператива
|
||||
* @param username Имя пользователя, инициирующего заявку
|
||||
* @param hash Хэш заявки (уникальный идентификатор)
|
||||
* @param units Количество частей (штук) товара или услуги
|
||||
* @param unit_cost Цена за единицу (штуку) товара или услуги
|
||||
* @param membership_fee_amount Сумма членского взноса
|
||||
* @param cancellation_fee_amount Сумма комиссии за отмену заявки
|
||||
* @param braname Имя кооперативного участка откуда будет забираться товар
|
||||
* @param convert_in Заявление на конвертацию из кошелька в маркетплейс
|
||||
* @param meta Метаданные о заявке
|
||||
*
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::parentorder(eosio::name coopname, eosio::name braname, eosio::name username, checksum256 hash, uint64_t units, eosio::asset unit_cost, eosio::asset membership_fee_amount, eosio::asset cancellation_fee_amount, document2 convert_in, std::string meta) {
|
||||
require_auth(coopname);
|
||||
|
||||
// Проверяем, что заявка с таким хэшем не существует
|
||||
auto existing_request = get_request_by_hash(coopname, hash);
|
||||
eosio::check(!existing_request.has_value(), "Заявка с таким хэшем уже существует");
|
||||
|
||||
cooperatives2_index coops(_registrator, _registrator.value);
|
||||
auto coop = coops.find(coopname.value);
|
||||
eosio::check(coop != coops.end() && coop -> is_coop(), "Кооператив не найден");
|
||||
eosio::check(unit_cost.symbol == coop -> initial.symbol, "Неверный символ токена");
|
||||
eosio::check(membership_fee_amount.symbol == coop -> initial.symbol, "Неверный символ токена для членского взноса");
|
||||
eosio::check(cancellation_fee_amount.symbol == coop -> initial.symbol, "Неверный символ токена для комиссии отмены");
|
||||
eosio::check(units > 0, "Количество единиц в заявке должно быть больше нуля");
|
||||
eosio::check(unit_cost.amount >= 0, "Цена не может быть отрицательной");
|
||||
eosio::check(membership_fee_amount.amount >= 0, "Членский взнос не может быть отрицательным");
|
||||
eosio::check(cancellation_fee_amount.amount >= 0, "Комиссия за отмену не может быть отрицательной");
|
||||
|
||||
// Проверяем существование кооперативного участка
|
||||
get_branch_or_fail(coopname, braname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
uint64_t request_id = get_global_id(_marketplace, "requests"_n);
|
||||
|
||||
participants_index participants(_soviet, coopname.value);
|
||||
auto participant = participants.find(username.value);
|
||||
eosio::check(participant != participants.end(), "Вы не являетесь членом указанного кооператива");
|
||||
|
||||
auto program = get_program_or_fail(coopname, _marketplace_program_id);
|
||||
|
||||
// Для заказа рассчитываем общую стоимость включая членский взнос
|
||||
eosio::asset base_cost = unit_cost * units;
|
||||
eosio::asset total_cost = base_cost + membership_fee_amount;
|
||||
|
||||
// Проверяем что комиссия за отмену не превышает общую стоимость
|
||||
eosio::check(cancellation_fee_amount <= total_cost, "Комиссия за отмену не может превышать общую стоимость заказа");
|
||||
|
||||
// Проверяем подпись документа на конвертацию
|
||||
verify_document_or_fail(convert_in);
|
||||
|
||||
requests.emplace(_marketplace, [&](auto &i) {
|
||||
i.id = request_id;
|
||||
i.hash = hash;
|
||||
i.type = "order"_n;
|
||||
i.username = username;
|
||||
i.coopname = coopname;
|
||||
i.status = "active"_n;
|
||||
i.remaining_units = units;
|
||||
i.unit_cost = unit_cost;
|
||||
i.base_cost = base_cost;
|
||||
i.total_cost = total_cost;
|
||||
i.membership_fee_amount = membership_fee_amount;
|
||||
i.product_lifecycle_secs = 0; // Не используется для заказов
|
||||
i.meta = meta;
|
||||
i.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
i.cancellation_fee_amount = cancellation_fee_amount;
|
||||
i.braname = braname;
|
||||
});
|
||||
|
||||
// Создаем сегмент для родительской заявки поставки из кооператива - заказчику
|
||||
marketplace::create_segment(coopname, request_id, marketplace::valid_segment("c2r"), username);
|
||||
|
||||
// Сохраняем заявление на конвертацию в contribute сегменте
|
||||
marketplace::update_segment_by_request_and_type(coopname, request_id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.convert_in = convert_in;
|
||||
s.status = "convertin"_n;
|
||||
});
|
||||
|
||||
// Списываем средства с ЦПП цифрового кошелька
|
||||
std::string convert_memo = "Конвертация средств из ЦПП 'Цифровой Кошелёк' в ЦПП 'Маркетплейс' для заказа №" + std::to_string(request_id);
|
||||
Wallet::sub_available_funds(_marketplace, coopname, username, total_cost, _wallet_program, convert_memo);
|
||||
|
||||
// Добавляем средства на ЦПП маркетплейса
|
||||
Wallet::add_available_funds(_marketplace, coopname, username, total_cost, _marketplace_program, convert_memo);
|
||||
|
||||
// Блокируем средства на программе маркетплейса
|
||||
std::string memo = "Создание заказа по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(request_id);
|
||||
Wallet::block_funds(_marketplace, coopname, username, total_cost, _marketplace_program, memo);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Подтверждение получения товара заказчиком для направления deliver_on_order (recvcnford).
|
||||
|
||||
@details Председатель совета подтверждает получение имущества заказчиком на основе предоставленного документа.
|
||||
Встречная заявка должна быть предварительно в статусе "получено".
|
||||
|
||||
Процесс: ORDER (parent) → OFFER (child)
|
||||
- Parent: денежный паевой взнос (заказчик)
|
||||
- Child: имущественный паевой взнос (поставщик)
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя председателя совета
|
||||
@param request_hash Хэш встречной заявки (offer)
|
||||
@param document Документ подтверждения получения
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::recvcnford(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "received1"_n, "Подтверждение получения возможно только после получения");
|
||||
eosio::check(change.type == "offer"_n, "Метод recvcnford применим только к заявкам типа offer");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "order"_n, "Родительская заявка должна быть типа order");
|
||||
|
||||
auto branch = get_branch_or_fail(coopname, change.braname);
|
||||
eosio::check(change.money_contributor == username, "Недостаточно прав доступа для получения имущества");
|
||||
|
||||
//проводим проверку подписи документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
// Уменьшаем паевой фонд на сумму возвращаемой стоимости имущества заказчику
|
||||
Fund::sub_circulating_funds(_marketplace, coopname, parent_change.total_cost, false);
|
||||
|
||||
// Обновляем статус встречной заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o) {
|
||||
o.status = "received2"_n;
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "orderrecconfirm_update");
|
||||
|
||||
// Обновляем return сегмент документом подтверждения
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"), [&](auto &s) {
|
||||
s.act2 = document;
|
||||
s.status = "received2"_n;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Получение товара для направления deliver_on_order (recvord).
|
||||
|
||||
@details Данный метод позволяет заказчику подтвердить получение имущества и предоставить акт получения.
|
||||
Встречная заявка должна быть предварительно в статусе "доставлено".
|
||||
|
||||
Процесс: ORDER (parent) → OFFER (child)
|
||||
- Parent: денежный паевой взнос (заказчик)
|
||||
- Child: имущественный паевой взнос (поставщик)
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя заказчика
|
||||
@param request_hash Хэш встречной заявки (offer)
|
||||
@param document Акт получения имущества
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::recvord(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "delivered"_n, "Только доставленная заявка может быть получена");
|
||||
eosio::check(change.type == "offer"_n, "Метод orderreceive применим только к заявкам типа offer");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "order"_n, "Родительская заявка должна быть типа order");
|
||||
|
||||
auto branch = get_branch_or_fail(coopname, change.braname);
|
||||
//Проверяем права доступа на КУ (председатель или доверенное лицо)
|
||||
eosio::check(branch.is_user_authorized(username), "Недостаточно прав доступа для передачи имущества");
|
||||
|
||||
//проводим проверку подписи документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
// Обновляем статус встречной заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o) {
|
||||
o.status = "received1"_n;
|
||||
o.received_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "orderreceive_update");
|
||||
|
||||
// Обновляем return сегмент документом получения
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("c2r"), [&](auto &s) {
|
||||
s.act1 = document;
|
||||
s.status = "received1"_n;
|
||||
s.coopactor = username; // Представитель кооператива, который выдал имущество
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Подтверждение поставки товара для направления deliver_on_order (supplcnford).
|
||||
|
||||
@details Председатель совета подтверждает поставку имущества поставщиком на основе предоставленного документа.
|
||||
Встречная заявка должна быть предварительно в статусе "поставлено".
|
||||
|
||||
Процесс: ORDER (parent) → OFFER (child)
|
||||
- Parent: денежный паевой взнос (заказчик)
|
||||
- Child: имущественный паевой взнос (поставщик)
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя председателя совета
|
||||
@param request_hash Хэш встречной заявки (offer)
|
||||
@param document Документ подтверждения поставки
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::supplcnford(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "supplied"_n, "Подтверждение поставки возможно только после поставки");
|
||||
eosio::check(change.type == "offer"_n, "Метод ordersupplyconfirm применим только к заявкам типа offer");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "order"_n, "Родительская заявка должна быть типа order");
|
||||
|
||||
auto branch = get_branch_or_fail(coopname, change.braname);
|
||||
|
||||
//Проверяем права доступа на КУ (председатель или доверенное лицо)
|
||||
eosio::check(branch.is_user_authorized(username), "Недостаточно прав доступа для приёма имущества");
|
||||
|
||||
//проводим проверку подписи документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
std::string memo = "Поставка имущества по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(change.id);
|
||||
|
||||
// Начисляем поставщику заблокированный баланс в цифровой кошелек
|
||||
Wallet::add_blocked_funds(_marketplace, coopname, change.product_contributor, change.base_cost, _marketplace_program, memo);
|
||||
|
||||
// Увеличиваем сумму циркуляции в системе на сумму взноса
|
||||
Fund::add_circulating_funds(_marketplace, coopname, parent_change.total_cost);
|
||||
|
||||
// Обновляем статус встречной заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o) {
|
||||
o.status = "supplied1"_n;
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "ordersupplyconfirm_update");
|
||||
|
||||
// Обновляем contribute сегмент документом подтверждения
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.act2 = document;
|
||||
s.status = "supplied1"_n;
|
||||
s.coopactor = username; // Представитель кооператива, который принял имущество
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Поставка товара для направления deliver_on_order (supplyord).
|
||||
|
||||
@details Данный метод позволяет поставщику зафиксировать поставку имущества.
|
||||
Встречная заявка должна быть предварительно авторизована советом.
|
||||
|
||||
Процесс: ORDER (parent) → OFFER (child)
|
||||
- Parent: денежный паевой взнос (заказчик)
|
||||
- Child: имущественный паевой взнос (поставщик)
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя поставщика, предоставляющего товар или услугу
|
||||
@param request_hash Хэш встречной заявки (offer)
|
||||
@param document Документ, подтверждающий поставку товара или услуги
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::supplyord(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "authorized"_n, "Только авторизованная заявка может быть поставлена");
|
||||
eosio::check(change.type == "offer"_n, "Метод ordersupply применим только к встречным заявкам типа offer");
|
||||
eosio::check(change.parent_id > 0, "У встречной заявки должна быть родительская заявка");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(parent_change.type == "order"_n, "Родительская заявка должна быть типа order");
|
||||
|
||||
eosio::check(change.product_contributor == username, "Недостаточно прав доступа передачи имущества");
|
||||
|
||||
//проводим проверку подписи документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
// Обновляем статус встречной заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Встречная заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &o) {
|
||||
o.status = "supplied1"_n;
|
||||
o.supplied_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
});
|
||||
|
||||
// Проверяем инварианты дочерней заявки
|
||||
auto updated_change = requests.find(change.id);
|
||||
marketplace::check_units_invariant(*updated_change, "ordersupply_update");
|
||||
|
||||
// Обновляем contribute сегмент документом поставки
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, marketplace::valid_segment("s2c"), [&](auto &s) {
|
||||
s.act1 = document;
|
||||
s.status = "supplied1"_n;
|
||||
});
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
[[eosio::action]] void marketplace::delivered(eosio::name coopname, eosio::name username, uint64_t exchange_id) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Заявка не найдена");
|
||||
eosio::check(change -> parent_id > 0, "Только продукт по встречной заявке может быть доставлен");
|
||||
|
||||
eosio::check(change -> status == "supplied2"_n, "Продукт может быть поставлен только по заявке в статусе supplied2");
|
||||
|
||||
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
|
||||
auto chairman = soviet.get_chairman();
|
||||
|
||||
eosio::check(username == chairman, "Недостаточно прав доступа для подтверждения доставки");
|
||||
|
||||
auto program = get_program_or_fail(coopname, change -> program_id);
|
||||
|
||||
exchange.modify(change, coopname, [&](auto &ch) {
|
||||
ch.status = "delivered"_n;
|
||||
ch.delivered_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
ch.deadline_for_receipt = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch() + change -> product_lifecycle_secs / 4);
|
||||
});
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
[[eosio::action]] void marketplace::dispute(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document){
|
||||
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
|
||||
eosio::check(change -> type == "order"_n, "Спор может быть открыт только по заявке на поставку");
|
||||
|
||||
eosio::check(change != exchange.end(), "Заявка не найдена");
|
||||
eosio::check(change -> username == username, "Только заказчик может открыть спор");
|
||||
|
||||
eosio::check(change -> is_warranty_return == false, "Нельзя открыть спор на спор");
|
||||
|
||||
auto parent_change = exchange.find(change -> parent_id);
|
||||
eosio::check(parent_change != exchange.end(), "Родительская заявка не найдена");
|
||||
|
||||
eosio::check(change -> status == "recieved2"_n, "Неверный статус для открытия спора");
|
||||
|
||||
exchange.modify(change, _marketplace, [&](auto &e){
|
||||
e.status = "disputed"_n;
|
||||
e.disputed_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
e.warranty_return_id = change -> id;
|
||||
});
|
||||
|
||||
uint64_t new_id = get_global_id(_marketplace, "exchange"_n);
|
||||
uint64_t new_parent_id = get_global_id(_marketplace, "exchange"_n);
|
||||
|
||||
auto cooperative = get_cooperative_or_fail(coopname);
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
//открытая заявка от заказчика на поставку имущества поставщику
|
||||
exchange.emplace(_marketplace, [&](auto &i) {
|
||||
i.id = new_parent_id;
|
||||
i.type = "offer"_n;
|
||||
i.program_id = change -> program_id;
|
||||
i.username = username;
|
||||
i.parent_username = ""_n;
|
||||
i.parent_id = 0;
|
||||
i.coopname = coopname;
|
||||
i.status = "published"_n;
|
||||
i.remain_units = change -> remain_units;
|
||||
i.unit_cost = change -> unit_cost;
|
||||
|
||||
i.supplier_amount = i.unit_cost * i.remain_units;
|
||||
i.total_cost = i.supplier_amount;
|
||||
|
||||
i.membership_fee = asset(0, cooperative.initial.symbol);
|
||||
i.cancellation_fee_amount = asset(0, cooperative.initial.symbol);
|
||||
|
||||
i.product_lifecycle_secs = change -> product_lifecycle_secs;
|
||||
i.data = change -> data;
|
||||
i.meta = change -> meta;
|
||||
i.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
|
||||
i.is_warranty_return = true;
|
||||
i.warranty_return_id = change -> id;
|
||||
});
|
||||
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_marketplace,
|
||||
"newid"_n,
|
||||
std::make_tuple(new_parent_id, "offer")
|
||||
).send();
|
||||
|
||||
//новая встречная заявка для будущего возврата поставщику
|
||||
exchange.emplace(_marketplace, [&](auto &i) {
|
||||
i.id = new_id;
|
||||
i.parent_id = new_parent_id;
|
||||
i.type = "order"_n;
|
||||
|
||||
i.program_id = parent_change -> program_id;
|
||||
i.coopname = coopname;
|
||||
|
||||
i.username = change -> parent_username;
|
||||
i.parent_username = change -> username;
|
||||
|
||||
i.status = "published"_n;
|
||||
|
||||
i.remain_units = change -> remain_units;
|
||||
i.unit_cost = change -> unit_cost;
|
||||
|
||||
i.supplier_amount = change -> supplier_amount;
|
||||
i.total_cost = change -> supplier_amount;;
|
||||
|
||||
i.membership_fee = asset(0, cooperative.initial.symbol); //размер членского взноса должен установить председатель в методе resolve
|
||||
i.cancellation_fee_amount = asset(0, cooperative.initial.symbol);
|
||||
|
||||
i.data = change -> data;
|
||||
i.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
|
||||
i.is_warranty_return = true;
|
||||
i.warranty_return_id = exchange_id;
|
||||
|
||||
i.contribute_product_statement = document;
|
||||
i.money_contributor = parent_change -> username;
|
||||
i.product_contributor = change -> username;
|
||||
i.product_lifecycle_secs = 0; //потому что гарантийного срока в споре нет
|
||||
|
||||
});
|
||||
|
||||
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_marketplace,
|
||||
"newid"_n,
|
||||
std::make_tuple(new_id, "order"_n)
|
||||
).send();
|
||||
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
# Система гарантийного возврата (диспут)
|
||||
|
||||
## Обзор
|
||||
|
||||
Новая система диспута работает через создание дополнительных сегментов в рамках существующей пары заявок, а не через создание новых заявок.
|
||||
|
||||
## Типы сегментов диспута
|
||||
|
||||
- **wreturn** - сегмент возврата товара от заказчика в кооператив
|
||||
- **wsupply** - сегмент выдачи товара из кооператива поставщику
|
||||
|
||||
## Процесс диспута
|
||||
|
||||
### 1. Открытие диспута (`dispute`)
|
||||
- Заказчик подает претензию с документом
|
||||
- Создаются два новых сегмента: `wreturn` и `wsupply`
|
||||
- Статус заявки меняется на `disputed`
|
||||
|
||||
### 2. Авторизация диспута (`wauthorize`)
|
||||
- Совет авторизует принятие товара от заказчика
|
||||
- Совет авторизует последующую выдачу товара поставщику
|
||||
- Статус заявки меняется на `wauthorized`
|
||||
|
||||
### 3. Возврат товара (`wreturn`)
|
||||
- Заказчик возвращает товар в кооператив
|
||||
- Председатель принимает товар и подписывает акт
|
||||
- Статус заявки меняется на `wreturned`
|
||||
|
||||
### 4. Предложение товара поставщику (`woffer`)
|
||||
- Кооператив предлагает поставщику забрать товар
|
||||
- Создается акт передачи
|
||||
- Статус заявки меняется на `woffered`
|
||||
|
||||
### 5. Принятие решения поставщиком (`waccept`)
|
||||
- Поставщик может принять товар (`accept=true`) или отказаться (`accept=false`)
|
||||
- При принятии: статус становится `wcompleted`
|
||||
- При отказе: статус становится `wdeclined`
|
||||
|
||||
## Статусы заявок диспута
|
||||
|
||||
- **disputed** - диспут открыт
|
||||
- **wauthorized** - диспут авторизован советом
|
||||
- **wreturned** - товар возвращен в кооператив
|
||||
- **woffered** - товар предложен поставщику
|
||||
- **wcompleted** - поставщик принял товар, диспут завершен
|
||||
- **wdeclined** - поставщик отказался от товара, диспут завершен
|
||||
|
||||
## Статусы сегментов
|
||||
|
||||
- **created** - сегмент создан
|
||||
- **statement** - документ/заявление предоставлено
|
||||
- **authorized** - авторизовано советом
|
||||
- **returned** - товар возвращен в кооператив
|
||||
- **offered** - товар предложен поставщику
|
||||
- **accepted** - поставщик принял товар
|
||||
- **declined** - поставщик отказался от товара
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Открытие гарантийного спора по заявке.
|
||||
|
||||
@details Заказчик может открыть спор после получения товара, если есть проблемы с качеством или соответствием. Спор создает специальные сегменты для возврата товара и получения компенсации.
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя заказчика, открывающего спор
|
||||
@param request_hash Хэш заявки, по которой открывается спор
|
||||
@param document Документ с описанием претензии
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
**/
|
||||
[[eosio::action]] void marketplace::dispute(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document){
|
||||
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.type == "order"_n, "Спор может быть открыт только по заявке на поставку");
|
||||
eosio::check(change.username == username, "Только заказчик может открыть спор");
|
||||
eosio::check(change.is_warranty_return == false, "Нельзя открыть спор на спор");
|
||||
|
||||
auto parent_change_opt = Marketplace::get_request_by_hash(coopname, change.parent_hash);
|
||||
eosio::check(parent_change_opt.has_value(), "Родительская заявка не найдена");
|
||||
auto parent_change = parent_change_opt.value();
|
||||
|
||||
eosio::check(change.status == "received2"_n, "Неверный статус для открытия спора");
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
// Обновляем статус заявки на "disputed"
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &e){
|
||||
e.status = "disputed"_n;
|
||||
e.disputed_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
e.is_warranty_return = true;
|
||||
});
|
||||
|
||||
// Создаем сегмент для возврата товара от заказчика в кооператив
|
||||
uint64_t wreturn_segment_id = marketplace::create_segment(coopname, change.id, "wreturn"_n);
|
||||
|
||||
// Создаем сегмент для выдачи товара из кооператива поставщику
|
||||
uint64_t wsupply_segment_id = marketplace::create_segment(coopname, change.id, "wsupply"_n);
|
||||
|
||||
// Сохраняем документ с претензией в wreturn сегменте
|
||||
marketplace::update_segment(coopname, wreturn_segment_id, [&](auto &s) {
|
||||
s.statement = document;
|
||||
s.status = "disputed"_n;
|
||||
});
|
||||
|
||||
// Сохраняем документ с претензией в wsupply сегменте
|
||||
marketplace::update_segment(coopname, wsupply_segment_id, [&](auto &s) {
|
||||
s.statement = document;
|
||||
s.status = "disputed"_n;
|
||||
});
|
||||
|
||||
// Используем хэш заявки как идентификатор пакета решений
|
||||
checksum256 agenda_hash = change.hash;
|
||||
|
||||
// Отправляем заявление на гарантийный возврат в совет
|
||||
Action::send<createagenda_interface>(
|
||||
_soviet,
|
||||
"createagenda"_n,
|
||||
_marketplace,
|
||||
coopname,
|
||||
username,
|
||||
get_valid_soviet_action("mpwreturn"_n),
|
||||
agenda_hash,
|
||||
_marketplace,
|
||||
Marketplace::get_valid_marketplace_action("wauthorize"_n),
|
||||
"wdecline"_n,
|
||||
document,
|
||||
std::string("")
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Принятие или отказ поставщика от товара в рамках гарантийного возврата
|
||||
|
||||
@details Поставщик может принять товар (accept=true) или отказаться от него (accept=false).
|
||||
При принятии товар передается поставщику и диспут завершается.
|
||||
При отказе товар остается у кооператива и диспут завершается.
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя поставщика
|
||||
@param request_hash Хэш встречной заявки с диспутом
|
||||
@param accept Принимает ли поставщик товар (true/false)
|
||||
@param document Документ с решением поставщика
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::waccept(eosio::name coopname, eosio::name username, checksum256 request_hash, bool accept, document2 document) {
|
||||
require_auth(coopname);
|
||||
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "woffered"_n, "Товар должен быть предложен поставщику");
|
||||
eosio::check(change.product_contributor == username, "Только поставщик может принять решение о товаре");
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
// ИСПРАВЛЕНИЕ: Возвращаем заблокированные единицы товара в родительскую заявку
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto parent_change = requests.find(change.parent_id);
|
||||
eosio::check(parent_change != requests.end(), "Родительская заявка не найдена");
|
||||
|
||||
if (change.blocked_units > 0) {
|
||||
requests.modify(parent_change, _marketplace, [&](auto &e) {
|
||||
e.remaining_units += change.blocked_units;
|
||||
e.blocked_units -= change.blocked_units;
|
||||
e.base_cost = e.remaining_units * e.unit_cost;
|
||||
});
|
||||
}
|
||||
|
||||
if (accept) {
|
||||
// Поставщик принимает товар
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, "wsupply"_n, [&](auto &s) {
|
||||
s.act2 = document;
|
||||
s.status = "accepted"_n;
|
||||
});
|
||||
|
||||
// Обновляем статус заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &ch) {
|
||||
ch.status = "wcompleted"_n;
|
||||
ch.blocked_units = 0; // Обнуляем заблокированные единицы
|
||||
});
|
||||
|
||||
} else {
|
||||
// Поставщик отказывается от товара
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, "wsupply"_n, [&](auto &s) {
|
||||
s.act2 = document;
|
||||
s.status = "declined"_n;
|
||||
});
|
||||
|
||||
// Обновляем статус заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &ch) {
|
||||
ch.status = "wdeclined"_n;
|
||||
ch.blocked_units = 0; // Обнуляем заблокированные единицы
|
||||
});
|
||||
}
|
||||
|
||||
print("Поставщик ", username, (accept ? " принял" : " отказался от"), " товара. Диспут завершен.");
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Авторизация гарантийного возврата советом
|
||||
|
||||
@details Совет авторизует принятие товара от заказчика и его последующую выдачу поставщику
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param request_hash Хэш встречной заявки с диспутом
|
||||
@param wreturn_decision_id Идентификатор решения по принятию товара
|
||||
@param wreturn_authorization Документ авторизации принятия товара
|
||||
@param wsupply_decision_id Идентификатор решения по выдаче товара поставщику
|
||||
@param wsupply_authorization Документ авторизации выдачи товара
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p _soviet
|
||||
*/
|
||||
[[eosio::action]] void marketplace::wauthorize(eosio::name coopname, checksum256 request_hash, uint64_t wreturn_decision_id, document2 wreturn_authorization, uint64_t wsupply_decision_id, document2 wsupply_authorization) {
|
||||
require_auth(_soviet);
|
||||
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "disputed"_n, "Заявка должна быть в статусе диспута");
|
||||
|
||||
// Обновляем wreturn сегмент
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, "wreturn"_n, [&](auto &s) {
|
||||
s.decision_id = wreturn_decision_id;
|
||||
s.authorization = wreturn_authorization;
|
||||
s.status = "authorized"_n;
|
||||
});
|
||||
|
||||
// Обновляем wsupply сегмент
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, "wsupply"_n, [&](auto &s) {
|
||||
s.decision_id = wsupply_decision_id;
|
||||
s.authorization = wsupply_authorization;
|
||||
s.status = "authorized"_n;
|
||||
});
|
||||
|
||||
// Обновляем статус заявки
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _soviet, [&](auto &o) {
|
||||
o.status = "wauthorized"_n;
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Предложение товара поставщику в рамках гарантийного возврата
|
||||
|
||||
@details Кооператив предлагает поставщику забрать товар, возвращенный заказчиком.
|
||||
Создается предложение с актом передачи.
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя председателя, предлагающего товар
|
||||
@param request_hash Хэш встречной заявки с диспутом
|
||||
@param document Акт передачи товара поставщику
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::woffer(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document) {
|
||||
require_auth(coopname);
|
||||
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "wreturned"_n, "Товар может быть предложен только после возврата в кооператив");
|
||||
|
||||
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
|
||||
auto chairman = soviet.get_chairman();
|
||||
|
||||
eosio::check(username == chairman, "Недостаточно прав доступа для предложения товара");
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
// Сохраняем акт передачи в wsupply сегменте
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, "wsupply"_n, [&](auto &s) {
|
||||
s.act1 = document;
|
||||
s.status = "offered"_n;
|
||||
});
|
||||
|
||||
// Обновляем статус заявки
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &ch) {
|
||||
ch.status = "woffered"_n;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Возврат товара от заказчика в кооператив
|
||||
|
||||
@details Заказчик возвращает товар в кооператив в рамках гарантийного возврата.
|
||||
Председатель принимает товар и подписывает акт приёма.
|
||||
|
||||
@param coopname Имя кооператива
|
||||
@param username Имя председателя, принимающего товар
|
||||
@param request_hash Хэш встречной заявки с диспутом
|
||||
@param document Акт приёма товара от заказчика
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void marketplace::wreturn(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index requests(_marketplace, coopname.value);
|
||||
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
|
||||
eosio::check(change_opt.has_value(), "Заявка не найдена");
|
||||
auto change = change_opt.value();
|
||||
|
||||
eosio::check(change.status == "wauthorized"_n, "Товар может быть возвращен только по авторизованному диспуту");
|
||||
|
||||
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
|
||||
auto chairman = soviet.get_chairman();
|
||||
|
||||
eosio::check(username == chairman, "Недостаточно прав доступа для принятия возврата");
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
// Сохраняем акт приёма в wreturn сегменте
|
||||
marketplace::update_segment_by_request_and_type(coopname, change.id, "wreturn"_n, [&](auto &s) {
|
||||
s.act1 = document;
|
||||
s.status = "returned"_n;
|
||||
});
|
||||
|
||||
// Обновляем статус заявки
|
||||
auto change_itr = requests.find(change.id);
|
||||
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
|
||||
requests.modify(change_itr, _marketplace, [&](auto &ch) {
|
||||
ch.status = "wreturned"_n;
|
||||
});
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Модерация товара на маркетплейсе.
|
||||
*
|
||||
* Данный метод предназначен для модерации товара перед его публикацией на маркетплейсе.
|
||||
* Метод может быть вызван только администратором маркетплейса.
|
||||
*
|
||||
* @param username Имя пользователя-администратора, который вызывает данный метод.
|
||||
* @param exchange_id Уникальный идентификатор товара, который нужно опубликовать после модерации.
|
||||
*
|
||||
* @note Авторизация требуется от аккаунта: @p username
|
||||
*/
|
||||
[[eosio::action]] void marketplace::moderate(eosio::name coopname, eosio::name username, uint64_t exchange_id, uint64_t cancellation_fee) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Объявление не найдено");
|
||||
|
||||
if (change -> status == "moderation"_n || change -> status == "prohibit"_n) {
|
||||
|
||||
eosio::check(cancellation_fee >= 0 && cancellation_fee < 100, "Комиссия отмены должна быть от 0 до 100 процентов");
|
||||
eosio::asset cancellation_fee_amount = change -> total_cost * cancellation_fee / 100;
|
||||
|
||||
exchange.modify(change, username, [&](auto &o){
|
||||
o.status = "published"_n;
|
||||
o.cancellation_fee = cancellation_fee;
|
||||
o.cancellation_fee_amount = cancellation_fee_amount;
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Создать заявку на имущественный паевой взнос.
|
||||
*
|
||||
* Данный метод позволяет пользователю создать заявку на имущественный паевой взнос в системе.
|
||||
*
|
||||
* @param params Параметры для создания заявки на имущественный паевой взнос.
|
||||
*
|
||||
* @note Авторизация требуется от аккаунта: @p params.username
|
||||
*/
|
||||
[[eosio::action]] void marketplace::offer (const exchange_params& params) {
|
||||
require_auth(params.coopname);
|
||||
|
||||
marketplace::create("offer"_n, params);
|
||||
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Создать заявку на денежный паевой взнос.
|
||||
*
|
||||
* Данный метод позволяет пользователю создать заявку на денежный паевой взнос в системе.
|
||||
*
|
||||
* @param params Параметры для создания заявки на денежный паевой взнос.
|
||||
*
|
||||
* @note Авторизация требуется от аккаунта: @p params.username
|
||||
*/
|
||||
[[eosio::action]] void marketplace::order (const exchange_params& params) {
|
||||
require_auth(params.coopname);
|
||||
|
||||
marketplace::create("order"_n, params);
|
||||
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Отказ в прохождении модерации заявки на товар.
|
||||
*
|
||||
* Этот метод предназначен для администраторов маркетплейса, чтобы отказать в публикации товара после его модерации.
|
||||
* При отказе администратор указывает причину отказа в параметре `meta`.
|
||||
*
|
||||
* @param username Имя пользователя-администратора, который вызывает данный метод.
|
||||
* @param exchange_id Уникальный идентификатор товара, публикацию которого нужно запретить.
|
||||
* @param meta Строковое описание или причина, по которой товар не прошел модерацию.
|
||||
*
|
||||
* @note Авторизация требуется от аккаунта: @p username
|
||||
*/
|
||||
[[eosio::action]] void marketplace::prohibit(eosio::name coopname, eosio::name username, uint64_t exchange_id, std::string meta) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Объявление не найдено");
|
||||
|
||||
exchange.modify(change, username, [&](auto &o){
|
||||
o.status = "prohibit"_n;
|
||||
o.meta = meta;
|
||||
});
|
||||
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Опубликовать товар на маркетплейсе.
|
||||
*
|
||||
* Этот метод позволяет владельцу товара опубликовать его на маркетплейсе. Для публикации товар должен находиться в статусе "unpublished".
|
||||
*
|
||||
* @param username Имя пользователя, являющегося владельцем заявки.
|
||||
* @param exchange_id Идентификатор заявки, которую следует опубликовать.
|
||||
*
|
||||
* @note Авторизация требуется от аккаунта: @p username
|
||||
*/
|
||||
[[eosio::action]] void marketplace::publish(eosio::name coopname, eosio::name username, uint64_t exchange_id) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
|
||||
staff_index staff(_soviet, coopname.value);
|
||||
auto persona = staff.find(username.value);
|
||||
|
||||
eosio::check(change != exchange.end(), "Объявление не найдено");
|
||||
eosio::check(change->username == username || (persona != staff.end() && persona -> has_right(_marketplace, "unpublish"_n)), "У вас нет права на публикацию данной заявки");
|
||||
eosio::check(change->status == "unpublished"_n || change->status == "prohibit"_n, "Неверный статус для публикации");
|
||||
|
||||
exchange.modify(change, username, [&](auto &o) {
|
||||
if (change->status == "unpublished"_n)
|
||||
o.status = "published"_n;
|
||||
else
|
||||
o.status = "moderation"_n;
|
||||
});
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Подпись акта получения имущества пайщиком
|
||||
**/
|
||||
|
||||
[[eosio::action]] void marketplace::recieve(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Заявка не найдена");
|
||||
eosio::check(change -> parent_id > 0, "Только продукт по встречной заявке может быть выдан");
|
||||
|
||||
eosio::check(change -> status == "delivered"_n, "Продукт может быть выдан только по заявке в статусе ожидания получения");
|
||||
|
||||
eosio::check(change -> deadline_for_receipt.sec_since_epoch() >= eosio::current_time_point().sec_since_epoch(), "Время на выдачу имущества истекло");
|
||||
|
||||
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
|
||||
auto chairman = soviet.get_chairman();
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
if (change -> type == "order"_n) { //если указанная заявка - это заказ продукта
|
||||
//то получение продукта может осуществить только пользователь из username
|
||||
eosio::check(change -> username == username, "Недостачно прав доступа для получения имущества");
|
||||
} else { //если указанная заявка - это поставка продукта
|
||||
|
||||
//то поставку может осуществить только пользователь username в этой заявке
|
||||
eosio::check(change -> parent_username == username, "Недостаточно прав доступа для получения имущества");
|
||||
};
|
||||
|
||||
//подписываем акт приёма-передачи кооперативу пайщиком
|
||||
exchange.modify(change, coopname, [&](auto &ch){
|
||||
ch.status = "recieved1"_n;
|
||||
ch.recieved_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
ch.warranty_delay_until = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch() + change -> product_lifecycle_secs / 4);
|
||||
ch.product_recieve_act = document;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
[[eosio::action]] void marketplace::recievecnfrm(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Заявка не найдена");
|
||||
eosio::check(change -> parent_id > 0, "Только продукт по встречной заявке может быть поставлен");
|
||||
|
||||
eosio::check(change -> status == "recieved1"_n, "Продукт может быть поставлен только по заявке в статусе recieved1");
|
||||
|
||||
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
|
||||
auto chairman = soviet.get_chairman();
|
||||
|
||||
eosio::check(username == chairman, "Недостачно прав доступа для подтверждения выдачи имущества");
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
exchange.modify(change, _marketplace, [&](auto &ch) {
|
||||
ch.status = "recieved2"_n;
|
||||
ch.product_recieve_act_validation = document;
|
||||
});
|
||||
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_soviet,
|
||||
"recieved"_n,
|
||||
std::make_tuple(coopname, exchange_id)
|
||||
).send();
|
||||
|
||||
//уменьшаем паевой фонд
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_fund,
|
||||
"subcirculate"_n,
|
||||
std::make_tuple(coopname, change -> total_cost, false)
|
||||
).send();
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
[[eosio::action]] void marketplace::supply(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Заявка не найдена");
|
||||
eosio::check(change -> parent_id > 0, "Только продукт по встречной заявке может быть поставлен");
|
||||
|
||||
eosio::check(change -> status == "authorized"_n, "Продукт может быть поставлен только по заявке в статусе authorized");
|
||||
|
||||
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
|
||||
auto chairman = soviet.get_chairman();
|
||||
|
||||
eosio::check(username == chairman, "Недостаточно прав доступа для подтверждения поставки");
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
exchange.modify(change, _marketplace, [&](auto &ch) {
|
||||
ch.supplied_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
ch.status = "supplied1"_n;
|
||||
ch.product_contribution_act_validation = document;
|
||||
});
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Отказ от предложения.
|
||||
**/
|
||||
|
||||
[[eosio::action]] void marketplace::supplycnfrm(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Заявка не найдена");
|
||||
eosio::check(change -> parent_id > 0, "Только продукт по встречной заявке может быть поставлен");
|
||||
|
||||
eosio::check(change -> status == "supplied1"_n, "Продукт может быть поставлен только по заявке в статусе supplied1");
|
||||
|
||||
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
|
||||
auto chairman = soviet.get_chairman();
|
||||
|
||||
eosio::check(change -> product_contributor == username, "Недостаточно прав доступа для совершения поставки");
|
||||
|
||||
// Проверяем подпись документа
|
||||
verify_document_or_fail(document);
|
||||
|
||||
//подписываем акт приёма-передачи кооперативу пайщиком
|
||||
exchange.modify(change, coopname, [&](auto &ch) {
|
||||
ch.status = "supplied2"_n;
|
||||
ch.product_contribution_act = document;
|
||||
});
|
||||
|
||||
std::string memo = "Приём имущественного паевого взноса по программе №" + std::to_string(change -> program_id) + " с ID: " + std::to_string(change -> id);
|
||||
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_soviet,
|
||||
"addbal"_n,
|
||||
std::make_tuple(coopname, change -> product_contributor, change -> program_id, change -> supplier_amount, memo)
|
||||
).send();
|
||||
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_soviet,
|
||||
"blockbal"_n,
|
||||
std::make_tuple(coopname, change -> product_contributor, change -> program_id, change -> supplier_amount, memo)
|
||||
).send();
|
||||
|
||||
//увеличиваем паевой фонд
|
||||
action(
|
||||
permission_level{ _marketplace, "active"_n},
|
||||
_fund,
|
||||
"addcirculate"_n,
|
||||
std::make_tuple(coopname, change -> total_cost)
|
||||
).send();
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Снять товар с публикации на маркетплейсе.
|
||||
*
|
||||
* Этот метод предназначен для снятия товара с публикации. Только владелец товара может снять его с публикации.
|
||||
*
|
||||
* @param username Имя пользователя, являющегося владельцем заявки.
|
||||
* @param exchange_id Идентификатор заявки, которую следует снять с публикации.
|
||||
*
|
||||
* @note Авторизация требуется от аккаунта: @p username
|
||||
*/
|
||||
[[eosio::action]] void marketplace::unpublish(eosio::name coopname, eosio::name username, uint64_t exchange_id) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Объявление не найдено");
|
||||
|
||||
staff_index staff(_soviet, coopname.value);
|
||||
auto persona = staff.find(username.value);
|
||||
eosio::check(change->username == username || (persona != staff.end() && persona -> has_right(_marketplace, "unpublish"_n)), "У вас нет права на снятие данной заявки");
|
||||
|
||||
exchange.modify(change, username, [&](auto &o){
|
||||
o.status = "unpublished"_n;
|
||||
});
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Отмена заявки и возврат токенов.
|
||||
|
||||
@details Позволяет пользователю отменить родительскую или дочернюю заявку, а также обеспечивает возврат токенов владельцу (если применимо). При отмене проверяется наличие заявки и её текущий статус. Если заявка является родительской, вызывается метод `cancel_parent`, иначе — `cancel_child`.
|
||||
|
||||
@param username Имя пользователя, инициировавшего отмену.
|
||||
@param exchange_id Идентификатор заявки для отмены.
|
||||
|
||||
@note Авторизация требуется от аккаунта: @p username
|
||||
*/
|
||||
[[eosio::action]] void marketplace::update(eosio::name coopname, eosio::name username, uint64_t exchange_id, uint64_t remain_units, eosio::asset unit_cost, std::string data, std::string meta) {
|
||||
require_auth(coopname);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Заявка на обмен не найдена");
|
||||
eosio::check(unit_cost.symbol == change -> unit_cost.symbol, "Неверный символ токен");
|
||||
eosio::check(change -> parent_id == 0, "Встречные заявки можно только отменять");
|
||||
eosio::check(change -> username == username, "У вас нет права на отмену данной заявки");
|
||||
eosio::check(remain_units >= 0, "Количество единиц товара должно быть положительным");
|
||||
|
||||
eosio::name status = "moderation"_n;
|
||||
|
||||
if (change -> data == data && change -> meta == meta)
|
||||
status = change -> status;
|
||||
|
||||
exchange.modify(change, _marketplace, [&](auto &i) {
|
||||
i.status = status;
|
||||
i.unit_cost = unit_cost;
|
||||
i.remain_units = remain_units;
|
||||
i.data = data;
|
||||
i.meta = meta;
|
||||
i.supplier_amount = remain_units * unit_cost;
|
||||
});
|
||||
}
|
||||
@@ -1,24 +1,19 @@
|
||||
eosio::asset minimum = deposit -> quantity == cooperative.registration ? cooperative.minimum : cooperative.org_minimum.value();
|
||||
eosio::asset initial = deposit -> quantity == cooperative.registration ? cooperative.initial : cooperative.org_initial.value();
|
||||
// eosio::asset minimum = deposit -> quantity == cooperative.registration ? cooperative.minimum : cooperative.org_minimum.value();
|
||||
// eosio::asset initial = deposit -> quantity == cooperative.registration ? cooperative.initial : cooperative.org_initial.value();
|
||||
|
||||
action(
|
||||
permission_level{ _gateway, "active"_n},
|
||||
_fund,
|
||||
"subcirculate"_n,
|
||||
std::make_tuple(coopname, minimum, true)
|
||||
).send();
|
||||
// Fund::sub_circulating_funds(_gateway, coopname, minimum, true);
|
||||
|
||||
//вычесть вступительную сумму из фонда
|
||||
action(
|
||||
permission_level{ _gateway, "active"_n},
|
||||
_fund,
|
||||
"subinitial"_n,
|
||||
std::make_tuple(coopname, initial)
|
||||
).send();
|
||||
// //вычесть вступительную сумму из фонда
|
||||
// action(
|
||||
// permission_level{ _gateway, "active"_n},
|
||||
// _fund,
|
||||
// "subinitial"_n,
|
||||
// std::make_tuple(coopname, initial)
|
||||
// ).send();
|
||||
|
||||
action (
|
||||
permission_level{ _gateway, "active"_n},
|
||||
_soviet,
|
||||
"cancelreg"_n,
|
||||
std::make_tuple(coopname, deposit -> username, memo)
|
||||
).send();
|
||||
// action (
|
||||
// permission_level{ _gateway, "active"_n},
|
||||
// _soviet,
|
||||
// "cancelreg"_n,
|
||||
// std::make_tuple(coopname, deposit -> username, memo)
|
||||
// ).send();
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* - устанавливаем дату вступления
|
||||
* - фиксируем принятый взнос в реестре взносов
|
||||
* 4. fund::addcirculate (добавляем минимальный взнос)
|
||||
* 5. fund::spreadamount? (опционально распределяем вступительный взнос по фондам)
|
||||
* 5. fund::addinitial (добавляем вступительный взнос в кошелек кооператива)
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
@@ -78,20 +78,10 @@
|
||||
});
|
||||
|
||||
|
||||
action(
|
||||
permission_level{ _registrator, "active"_n},
|
||||
_fund,
|
||||
"addcirculate"_n,
|
||||
std::make_tuple(coopname, minimum)
|
||||
).send();
|
||||
Fund::add_circulating_funds(_registrator, coopname, minimum);
|
||||
|
||||
if (spread_initial) {
|
||||
action(
|
||||
permission_level{ _registrator, "active"_n},
|
||||
_fund,
|
||||
"addinitial"_n,
|
||||
std::make_tuple(coopname, initial)
|
||||
).send();
|
||||
Fund::add_initial_funds(_registrator, coopname, initial);
|
||||
}
|
||||
|
||||
eosio::name braname = ""_n;
|
||||
|
||||
@@ -35,19 +35,9 @@ void registrator::confirmreg(eosio::name coopname, checksum256 registration_hash
|
||||
).send();
|
||||
|
||||
//добавляем
|
||||
action(
|
||||
permission_level{ _registrator, "active"_n},
|
||||
_fund,
|
||||
"addcirculate"_n,
|
||||
std::make_tuple(coopname, candidate -> minimum)
|
||||
).send();
|
||||
Fund::add_circulating_funds(_registrator, coopname, candidate -> minimum);
|
||||
|
||||
action(
|
||||
permission_level{ _registrator, "active"_n},
|
||||
_fund,
|
||||
"addinitial"_n,
|
||||
std::make_tuple(coopname, candidate -> initial)
|
||||
).send();
|
||||
Fund::add_initial_funds(_registrator, coopname, candidate -> initial);
|
||||
|
||||
// Увеличиваем счетчик активных пайщиков
|
||||
cooperatives2_index cooperatives(_registrator, _registrator.value);
|
||||
|
||||
@@ -50,9 +50,6 @@
|
||||
|
||||
#include "src/fund/fundwithdraw.cpp"
|
||||
|
||||
#include "src/marketplace/change.cpp"
|
||||
#include "src/marketplace/recieved.cpp"
|
||||
|
||||
#include "src/participant/addparticipant.cpp"
|
||||
#include "src/participant/block.cpp"
|
||||
#include "src/participant/cancelreg.cpp"
|
||||
|
||||
@@ -115,12 +115,6 @@ public:
|
||||
|
||||
[[eosio::action]] void cancelvote(eosio::name coopname, eosio::name member, uint64_t decision_id);
|
||||
|
||||
//marketplace.cpp
|
||||
[[eosio::action]] void change(eosio::name coopname, eosio::name parent_username, eosio::name username, uint64_t exchange_id, eosio::name money_contributor, eosio::name product_contributor);
|
||||
[[eosio::action]] void recieved (eosio::name coopname, uint64_t exchange_id);
|
||||
static void change_effect(eosio::name executer, eosio::name coopname, uint64_t decision_id, uint64_t batch_id);
|
||||
|
||||
|
||||
//programs.cpp
|
||||
[[eosio::action]] void createprog(eosio::name coopname, eosio::name username, eosio::name type, std::string title, std::string announce, std::string description, std::string preview, std::string images, eosio::name calculation_type, eosio::asset fixed_membership_contribution, uint64_t membership_percent_fee, bool is_can_coop_spend_share_contributions, std::string meta);
|
||||
[[eosio::action]] void editprog(eosio::name coopname, eosio::name username, uint64_t program_id, uint64_t draft_id, std::string title, std::string announce, std::string description, std::string preview, std::string images, std::string meta);
|
||||
|
||||
@@ -19,9 +19,7 @@ void soviet::exec(eosio::name executer, eosio::name coopname, uint64_t decision_
|
||||
eosio::check(decision != decisions.end(),"Решение не найдено в оперативной памяти");
|
||||
eosio::check(decision -> authorized == true, "Только авторизованное решение может быть исполнено");
|
||||
|
||||
if (decision -> type == _change_action){//операция взноса-возврата взноса в маркетплейсе
|
||||
soviet::change_effect(executer, coopname, decision->id, decision->batch_id);
|
||||
} else if (decision -> type == _withdraw_action){//операция возврата паевого взноса из кошелька (вывод)
|
||||
if (decision -> type == _withdraw_action){//операция возврата паевого взноса из кошелька (вывод)
|
||||
soviet::withdraw_effect(executer, coopname, decision->id, decision->batch_id);
|
||||
} else if (decision -> type == _afund_withdraw_action) {//операция использования фонда накопления
|
||||
soviet::subaccum_effect(executer, coopname, decision->id, decision->batch_id);
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
/**
|
||||
\ingroup public_actions
|
||||
\brief Обмен в кооперативе
|
||||
*
|
||||
* Этот метод вызывается контрактом marketplace при создании запроса на обмен в кооперативе. При успешном вызове формируется повестка со всеми сопровождающими документами, которая отправляется на голосование членам совета.
|
||||
*
|
||||
* @param coopname Имя кооператива
|
||||
* @param exchange_id Идентификатор обмена
|
||||
* @param orderer Участник, который вносит денежный паевый взнос
|
||||
* @param offerer Участник, который вносит имущественный паевый взнос
|
||||
* @param quantity Сумма взаимного зачёта
|
||||
*
|
||||
* @note Авторизация требуется от аккаунта: @p _marketplace
|
||||
*/
|
||||
void soviet::change(eosio::name coopname, eosio::name parent_username, eosio::name username, uint64_t exchange_id, eosio::name money_contributor, eosio::name product_contributor) {
|
||||
require_auth(_marketplace);
|
||||
|
||||
decisions_index decisions(_soviet, coopname.value);
|
||||
auto decision_id_1 = get_id(_soviet, coopname, "decisions"_n);
|
||||
auto decision_id_2 = get_id(_soviet, coopname, "decisions"_n);
|
||||
|
||||
changes_index changes(_soviet, coopname.value);
|
||||
auto batch_id = get_global_id(_soviet, "change"_n);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto change = exchange.find(exchange_id);
|
||||
eosio::check(change != exchange.end(), "Заявка не обнаружена");
|
||||
|
||||
changes.emplace(_marketplace, [&](auto &c) {
|
||||
c.id = batch_id;
|
||||
c.exchange_id = exchange_id;
|
||||
c.contribution_product_decision_id = decision_id_1;
|
||||
c.return_product_decision_id = decision_id_2;
|
||||
});
|
||||
|
||||
// Вызов для первого решения
|
||||
checksum256 hash = eosio::sha256((char*)&batch_id, sizeof(batch_id));
|
||||
|
||||
decisions.emplace(_soviet, [&](auto &d) {
|
||||
d.id = decision_id_1;
|
||||
d.type = _change_action;
|
||||
d.batch_id = batch_id;
|
||||
d.coopname = coopname;
|
||||
d.username = product_contributor;
|
||||
d.statement = change -> contribute_product_statement;
|
||||
d.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
d.expired_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch() + _decision_expiration);
|
||||
d.hash = hash;
|
||||
});
|
||||
|
||||
decisions.emplace(_soviet, [&](auto &d){
|
||||
d.id = decision_id_2;
|
||||
d.type = _change_action;
|
||||
d.batch_id = batch_id;
|
||||
d.coopname = coopname;
|
||||
d.username = money_contributor;
|
||||
d.statement = change -> return_product_statement;
|
||||
d.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
|
||||
d.expired_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch() + _decision_expiration);
|
||||
d.hash = hash;
|
||||
});
|
||||
|
||||
Action::send<newsubmitted_interface>(
|
||||
_soviet,
|
||||
"newsubmitted"_n,
|
||||
_soviet,
|
||||
coopname,
|
||||
product_contributor,
|
||||
_product_contribution_action,
|
||||
hash,
|
||||
change -> contribute_product_statement
|
||||
);
|
||||
|
||||
// Вызов для второго решения
|
||||
|
||||
Action::send<newsubmitted_interface>(
|
||||
_soviet,
|
||||
"newsubmitted"_n,
|
||||
_soviet,
|
||||
coopname,
|
||||
money_contributor,
|
||||
_product_return_action,
|
||||
hash,
|
||||
change -> return_product_statement
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
void soviet::change_effect(eosio::name executer, eosio::name coopname, uint64_t decision_id, uint64_t batch_id) {
|
||||
decisions_index decisions(_soviet, coopname.value);
|
||||
auto decision = decisions.find(decision_id);
|
||||
|
||||
eosio::check(decision != decisions.end(), "Решение не найдено");
|
||||
|
||||
changes_index changes(_soviet, coopname.value);
|
||||
auto change = changes.find(batch_id);
|
||||
eosio::check(change != changes.end(), "Объект обмена не найден");
|
||||
|
||||
auto contribution_product_decision = decisions.find(change -> contribution_product_decision_id);
|
||||
auto return_product_decision = decisions.find(change -> return_product_decision_id);
|
||||
|
||||
eosio::check(contribution_product_decision != decisions.end(), "Черновик решения о приёме имущества не найден");
|
||||
eosio::check(return_product_decision != decisions.end(), "Черновик решения о возврате взноса не найден");
|
||||
|
||||
if (contribution_product_decision -> authorized == true && return_product_decision -> authorized == true){
|
||||
// если принимается второе решение, то эффект должен оповестить контракт кооплейса
|
||||
|
||||
action(
|
||||
permission_level{ _soviet, "active"_n},
|
||||
_marketplace,
|
||||
"authorize"_n,
|
||||
std::make_tuple(coopname, change -> exchange_id, contribution_product_decision -> id, contribution_product_decision -> authorization, return_product_decision -> id, return_product_decision -> authorization)
|
||||
).send();
|
||||
|
||||
decisions.erase(contribution_product_decision);
|
||||
decisions.erase(return_product_decision);
|
||||
changes.erase(change);
|
||||
|
||||
} else {
|
||||
print("Ожидаем получение второго решения");
|
||||
// если принимается первое решение, то эффекта при обмене не должно быть
|
||||
// просто ждём второе решение
|
||||
}
|
||||
};
|
||||
@@ -1,80 +0,0 @@
|
||||
// удостоверить акт приёма-передачи от кооператива
|
||||
// это должно срабатывать на выдаче имущества заказчику для формирования закрывающего списка документов
|
||||
void soviet::recieved (eosio::name coopname, uint64_t exchange_id) {
|
||||
require_auth(_marketplace);
|
||||
|
||||
requests_index exchange(_marketplace, coopname.value);
|
||||
auto request = exchange.find(exchange_id);
|
||||
eosio::check(request != exchange.end(), "Заявка не обнаружена");
|
||||
eosio::check(request -> parent_id > 0, "Только встречная заявка может быть обработана");
|
||||
|
||||
checksum256 hash = eosio::sha256((char*)&request -> id, sizeof(request -> id));
|
||||
|
||||
Action::send<newdecision_interface>(
|
||||
_soviet,
|
||||
"newdecision"_n,
|
||||
_soviet,
|
||||
coopname,
|
||||
request -> money_contributor,
|
||||
_product_return_action,
|
||||
hash,
|
||||
request -> return_product_authorization
|
||||
);
|
||||
|
||||
Action::send<newresolved_interface>(
|
||||
_soviet,
|
||||
"newresolved"_n,
|
||||
_soviet,
|
||||
coopname,
|
||||
request -> money_contributor,
|
||||
_product_return_action,
|
||||
hash,
|
||||
request -> return_product_statement
|
||||
);
|
||||
|
||||
Action::send<newact_interface>(
|
||||
_soviet,
|
||||
"newact"_n,
|
||||
_soviet,
|
||||
coopname,
|
||||
request -> money_contributor,
|
||||
_product_return_action,
|
||||
hash,
|
||||
request -> product_recieve_act_validation
|
||||
);
|
||||
|
||||
Action::send<newdecision_interface>(
|
||||
_soviet,
|
||||
"newdecision"_n,
|
||||
_soviet,
|
||||
coopname,
|
||||
request -> product_contributor,
|
||||
_product_contribution_action,
|
||||
hash,
|
||||
request -> contribution_product_authorization
|
||||
);
|
||||
|
||||
Action::send<newresolved_interface>(
|
||||
_soviet,
|
||||
"newresolved"_n,
|
||||
_soviet,
|
||||
coopname,
|
||||
request -> product_contributor,
|
||||
_product_contribution_action,
|
||||
hash,
|
||||
request -> contribute_product_statement
|
||||
);
|
||||
|
||||
Action::send<newact_interface>(
|
||||
_soviet,
|
||||
"newact"_n,
|
||||
_soviet,
|
||||
coopname,
|
||||
request -> product_contributor,
|
||||
_product_contribution_action,
|
||||
hash,
|
||||
request -> product_contribution_act_validation
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user