feat(capital/segments): approved_rate_per_hour в сегменте + createcmmt через approved-ставку

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

PRD-D2: «approved_rate_per_hour хранится в сегменте; createcmmt использует
approved-ставку». До этого коммита createcmmt брал ставку из глобального
contributors.rate_per_hour — мастер компонента не мог дифференцировать.

Изменения:
- segments.hpp: поля approved_rate_per_hour, approved_hours_per_day (по умолчанию 0
  — fallback на глобальную ставку из contributors)
- segments.hpp::set_approved_rate(coopname, project_hash, username, rate, hours)
  — helper будет вызван из approverole (Story 1.7)
- createcmmt.cpp: если segment.approved_rate_per_hour.amount > 0 — используется
  approved; иначе contributor->rate_per_hour. Меняется и creator_base, и
  delta_amounts (через calculate_fact_generation_amounts)
- editcontrib (без изменения) — теперь не влияет на approved-ставки на
  сегментах: контракт берёт сегментные значения вперёд глобальных

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
coopops
2026-05-18 17:00:43 +00:00
parent 7ca2695121
commit 1f486ce48a
2 changed files with 43 additions and 4 deletions
@@ -47,11 +47,20 @@ void capital::createcmmt(eosio::name coopname, eosio::name username, checksum256
eosio::check(creator_hours > 0, "Только положительная сумма часов создателя");
eosio::check(creator_hours <= 1000000, "Превышено максимальное количество часов (1 млн)");
// Если на сегменте уже утверждена мастером approved-ставка (Story 1.7/1.8) — берём её,
// иначе fallback на глобальную rate_per_hour из contributors. Это позволяет мастеру
// компонента дифференцировать ставку по проекту без изменения договора УХД.
auto segment_opt = Capital::Segments::get_segment(coopname, project_hash, username);
eosio::asset effective_rate = contributor->rate_per_hour;
if (segment_opt.has_value() && segment_opt->approved_rate_per_hour.amount > 0) {
effective_rate = segment_opt->approved_rate_per_hour;
}
// считаем сумму фактических затрат создателя на основе ставки в час и потраченного времени
eosio::asset creator_base = contributor -> rate_per_hour * creator_hours;
eosio::asset creator_base = effective_rate * creator_hours;
// Вычисляем фактическое изменение сумм генерации
auto delta_amounts = Capital::Core::Generation::calculate_fact_generation_amounts(contributor -> rate_per_hour, creator_hours);
auto delta_amounts = Capital::Core::Generation::calculate_fact_generation_amounts(effective_rate, creator_hours);
// Создаем коммит без отправки на аппрув председателю
// Одобрение осуществляется мастером проекта через approvecmmt
@@ -112,7 +112,13 @@ namespace Capital::Segments {
eosio::asset available_for_program = asset(0, _root_govern_symbol); ///< Доступная сумма для конвертации в программу (intellectual_cost - debt_amount)
eosio::asset available_for_wallet = asset(0, _root_govern_symbol); ///< Доступная сумма для конвертации в кошелек (provisional_amount - debt_amount)
double share_percent = 0.0; ///< Доля участника в результате (intellectual_cost / fact.total * 100)
// Допуски L2 — утверждаются мастером компонента/сегмента через approverole (Story 1.7).
// Используются в createcmmt вместо глобального contributors.rate_per_hour, если ≠ 0.
// 0 (значение по умолчанию) означает «approved-ставка не выставлена, fallback на contributors».
eosio::asset approved_rate_per_hour = asset(0, _root_govern_symbol); ///< Утверждённая мастером ставка часа на этом сегменте
uint64_t approved_hours_per_day = 0; ///< Утверждённое мастером количество часов в день на этом сегменте
uint64_t primary_key() const { return id; } ///< Первичный ключ (1)
checksum256 by_project_hash() const { return project_hash; } ///< Индекс по хэшу проекта (2)
@@ -156,6 +162,30 @@ namespace Capital::Segments {
return segment(*segment_itr);
}
/**
* @brief Фиксирует утверждённую мастером ставку часа и норматив часов в день на сегменте.
* Вызывается из action approverole (Story 1.7). createcmmt далее берёт эту
* ставку для расчёта generation_amounts вместо глобальной из contributors.
*/
inline void set_approved_rate(
eosio::name coopname,
const checksum256 &project_hash,
eosio::name username,
const eosio::asset &rate_per_hour,
uint64_t hours_per_day
) {
segments_index segments(_capital, coopname.value);
auto idx = segments.get_index<"byprojuser"_n>();
auto rkey = combine_checksum_ids(project_hash, username);
auto it = idx.find(rkey);
eosio::check(it != idx.end(), "Сегмент участника на этом проекте не найден");
idx.modify(it, coopname, [&](auto &s) {
s.approved_rate_per_hour = rate_per_hour;
s.approved_hours_per_day = hours_per_day;
});
}
/**
* @brief Подсчитывает количество авторов в проекте.