feat(capital/state): program_expense_pool + topupprogexp

Story 1.6 эпика «Благорост» (blago issue 51F-18).

Программные расходы (Story 1.5) списываются из дедикейтнутого пула, а не из
аллокаций проектов. До этого коммита поля для такого пула в global_state не было.

Изменения:
- global_state.program_expense_pool — доступная сумма программы под расходы
- global_state.program_expense_reserved — зарезервированная под approved/authorized
  программные расходы (Story 1.5: createprogexp резервирует, declprogexp возвращает,
  exppaycnfrm списывает окончательно)
- State::topup_program_expense_pool / reserve_program_expense /
  release_program_expense / consume_program_expense — utility-функции для Story 1.5
- topupprogexp(coopname, amount) action — председатель переводит amount из
  global_available_invest_pool в program_expense_pool; деньги остаются на
  ledger2-кошельке BLAGOROST_FUND, меняется только разбивка по назначению

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
coopops
2026-05-18 16:55:44 +00:00
parent 67c5b96a18
commit d02c0ce66f
4 changed files with 104 additions and 2 deletions
@@ -0,0 +1,21 @@
/**
* @brief Пополняет пул программных расходов из общего инвестиционного пула программы.
*
* Программные расходы — это целевые списания «Благорост», не аллоцированные ни в один проект
* (хозяйственные расходы программы, оплата инфраструктуры программы и т.п.). Прежде чем
* создавать запрос на программный расход, председатель пополняет дедикейтнутый пул
* @c program_expense_pool из общего @c global_available_invest_pool — деньги остаются
* на том же ledger2-кошельке @c BLAGOROST_FUND, меняется только разбивка по назначению.
*
* @param coopname Кооператив (scope state)
* @param amount Сумма пополнения (символ строго совпадает с программой)
* @ingroup public_actions
* @ingroup public_capital_actions
*
* @note Авторизация требуется от аккаунта: @p coopname (председатель).
*/
void capital::topupprogexp(name coopname, eosio::asset amount) {
require_auth(coopname);
Capital::State::topup_program_expense_pool(coopname, amount);
}
@@ -95,6 +95,9 @@
#include "app/debt_managment/create_debt/declinedebt.cpp"
#include "app/debt_managment/settle_debt/settledebt.cpp"
// Управление программными расходами
#include "app/expense_managment/program_expenses/topupprogexp.cpp"
// Управление расходами
#include "app/expense_managment/create_expense/approveexpns.cpp"
#include "app/expense_managment/create_expense/capauthexpns.cpp"
@@ -322,4 +322,7 @@ public:
[[eosio::action]]
void settledebt(name coopname, name username, eosio::asset amount, document2 statement);
[[eosio::action]]
void topupprogexp(name coopname, eosio::asset amount);
};
@@ -39,8 +39,11 @@ namespace Capital {
asset program_membership_distributed = asset(0, _root_govern_symbol); ///< Распределенная сумма членских взносов по программе
double program_membership_cumulative_reward_per_share; ///< Накопительное вознаграждение на долю в членских взносах
asset program_expense_pool = asset(0, _root_govern_symbol); ///< Доступная сумма программы под целевые расходы (без аллокации в проекты)
asset program_expense_reserved = asset(0, _root_govern_symbol); ///< Сумма, зарезервированная под approved/authorized программные расходы
config config; ///< Управляемая конфигурация контракта
uint64_t primary_key() const { return coopname.value; } ///< Первичный ключ (1)
};
@@ -78,7 +81,7 @@ inline global_state get_global_state(name coopname) {
/**
* @brief Проверяет что контракт инициализирован.
*
*
* @param coopname Имя кооператива.
*/
inline void is_initialized(name coopname) {
@@ -86,6 +89,78 @@ inline void is_initialized(name coopname) {
auto itr = global_state_inst.find(coopname.value);
eosio::check(itr != global_state_inst.end(), "Контракт не инициализирован");
}
/**
* @brief Пополняет пул программных расходов на @p amount из общего пула инвестиций программы.
* Вызывается из action topupprogexp (председатель).
*/
inline void topup_program_expense_pool(eosio::name coopname, const asset &amount) {
global_state_table global_state_inst(_capital, _capital.value);
auto itr = global_state_inst.find(coopname.value);
eosio::check(itr != global_state_inst.end(), "Контракт не инициализирован");
eosio::check(amount.is_valid() && amount.amount > 0, "Сумма пополнения должна быть положительной");
eosio::check(itr->global_available_invest_pool >= amount,
"Недостаточно свободных инвестиций программы для пополнения пула расходов");
global_state_inst.modify(itr, _capital, [&](auto &s) {
s.global_available_invest_pool -= amount;
s.program_expense_pool += amount;
});
}
/**
* @brief Резервирует средства под approved/authorized программный расход:
* program_expense_pool -= amount, program_expense_reserved += amount.
*/
inline void reserve_program_expense(eosio::name coopname, const asset &amount) {
global_state_table global_state_inst(_capital, _capital.value);
auto itr = global_state_inst.find(coopname.value);
eosio::check(itr != global_state_inst.end(), "Контракт не инициализирован");
eosio::check(amount.is_valid() && amount.amount > 0, "Сумма резерва должна быть положительной");
eosio::check(itr->program_expense_pool >= amount,
"Недостаточно средств в пуле программных расходов");
global_state_inst.modify(itr, _capital, [&](auto &s) {
s.program_expense_pool -= amount;
s.program_expense_reserved += amount;
});
}
/**
* @brief Освобождает зарезервированное при отклонении программного расхода:
* program_expense_reserved -= amount, program_expense_pool += amount.
*/
inline void release_program_expense(eosio::name coopname, const asset &amount) {
global_state_table global_state_inst(_capital, _capital.value);
auto itr = global_state_inst.find(coopname.value);
eosio::check(itr != global_state_inst.end(), "Контракт не инициализирован");
eosio::check(amount.is_valid() && amount.amount > 0, "Сумма освобождения должна быть положительной");
eosio::check(itr->program_expense_reserved >= amount,
"Зарезервированных программных расходов меньше указанной суммы");
global_state_inst.modify(itr, _capital, [&](auto &s) {
s.program_expense_reserved -= amount;
s.program_expense_pool += amount;
});
}
/**
* @brief Окончательное списание программного расхода после exppaycnfrm:
* program_expense_reserved -= amount. Деньги уже ушли gateway,
* ledger2-проводка PAY_EXPENSE сделана в action подтверждения.
*/
inline void consume_program_expense(eosio::name coopname, const asset &amount) {
global_state_table global_state_inst(_capital, _capital.value);
auto itr = global_state_inst.find(coopname.value);
eosio::check(itr != global_state_inst.end(), "Контракт не инициализирован");
eosio::check(amount.is_valid() && amount.amount > 0, "Сумма списания должна быть положительной");
eosio::check(itr->program_expense_reserved >= amount,
"Зарезервированных программных расходов меньше указанной суммы");
global_state_inst.modify(itr, _capital, [&](auto &s) {
s.program_expense_reserved -= amount;
});
}
}// namespace State
}// namespace Capital