common capital tests
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/lib/**",
|
||||
"${workspaceFolder}/lib/eosiolib/contracts/**",
|
||||
"${workspaceFolder}/lib/eosiolib/core/**"
|
||||
],
|
||||
"defines": [
|
||||
"_DEBUG",
|
||||
"UNICODE",
|
||||
"_UNICODE",
|
||||
"int128_t=__int128_t",
|
||||
"uint128_t=__uint128_t"
|
||||
],
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++20"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
{
|
||||
// Enable the ESlint flat config support
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": true,
|
||||
// Auto fix
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
// "source.fixAll": "explicit",
|
||||
// "source.organizeImports": "never"
|
||||
},
|
||||
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||
"eslint.rules.customizations": [
|
||||
{
|
||||
"rule": "style/*",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-indent",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-spacing",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-spaces",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-order",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-dangle",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-newline",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*quotes",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*semi",
|
||||
"severity": "off"
|
||||
}
|
||||
],
|
||||
// Enable eslint for all supported languages
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml"
|
||||
],
|
||||
// "[cpp]": {
|
||||
// "editor.defaultFormatter": "ms-vscode.cpptools",
|
||||
// },
|
||||
// "C_Cpp.vcFormat.indent.withinParentheses": "indent",
|
||||
"C_Cpp.errorSquiggles": "disabled"
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import EosApi from 'eosjs-api'
|
||||
// @ts-expect-error заменить на antelope
|
||||
import ecc from 'eosjs-ecc'
|
||||
import config from '../configs'
|
||||
import { consoleIt } from '../tests/shared/consoleIt'
|
||||
|
||||
import type { Contract, Feature, Keys, Network } from '../types'
|
||||
|
||||
@@ -987,4 +988,35 @@ export default class Blockchain {
|
||||
|
||||
console.log('Программа установлена: ', params)
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет транзакцию и автоматически выводит консоль логи через consoleIt
|
||||
* @param actions - массив действий для транзакции
|
||||
* @param options - опции транзакции (по умолчанию стандартные)
|
||||
* @param options.blocksBehind - количество блоков позади для подтверждения
|
||||
* @param options.expireSeconds - время жизни транзакции в секундах
|
||||
* @returns результат транзакции
|
||||
*/
|
||||
async transactWithLogs(
|
||||
actions: any[],
|
||||
options: { blocksBehind?: number, expireSeconds?: number } = {},
|
||||
): Promise<any> {
|
||||
await this.update_pass_instance()
|
||||
|
||||
const defaultOptions = {
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
...options,
|
||||
}
|
||||
|
||||
const result = await this.api.transact(
|
||||
{ actions },
|
||||
defaultOptions,
|
||||
)
|
||||
|
||||
// Автоматически выводим консоль логи
|
||||
consoleIt(result)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -173,6 +173,7 @@ export async function commitToResult(
|
||||
commitHash,
|
||||
commit: blockchainCommit,
|
||||
transactionId: approveCommitResult.transaction_id,
|
||||
transaction: approveCommitResult,
|
||||
finalProject,
|
||||
finalContributor,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
|
||||
/**
|
||||
* Получает кошелек капитализации пользователя
|
||||
* @param blockchain экземпляр блокчейна
|
||||
* @param coopname название кооператива
|
||||
* @param username имя пользователя
|
||||
* @returns кошелек капитализации или null если не найден
|
||||
*/
|
||||
export async function getCapitalWallet(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
username: string,
|
||||
) {
|
||||
const capitalWallets = await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'capwallets',
|
||||
100,
|
||||
username,
|
||||
username,
|
||||
2, // by_username index
|
||||
)
|
||||
|
||||
return capitalWallets[0] || null
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
|
||||
|
||||
export async function getProject(blockchain: Blockchain, coopname: string, project_hash: string): Promise<CapitalContract.Tables.Projects.IProject> {
|
||||
const rows = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
CapitalContract.Tables.Projects.tableName,
|
||||
1000,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))
|
||||
|
||||
return rows[0]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
|
||||
/**
|
||||
* Получает кошелек проекта пользователя
|
||||
* @param blockchain экземпляр блокчейна
|
||||
* @param coopname название кооператива
|
||||
* @param projectHash хеш проекта
|
||||
* @param username имя пользователя
|
||||
* @returns кошелек проекта или null если не найден
|
||||
*/
|
||||
export async function getProjectWallet(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
projectHash: string,
|
||||
username: string,
|
||||
) {
|
||||
const projectWallets = await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projwallets',
|
||||
100,
|
||||
username,
|
||||
username,
|
||||
3, // by_username index
|
||||
)
|
||||
|
||||
// Ищем кошелек для конкретного проекта
|
||||
return projectWallets.find((wallet: any) => wallet.project_hash === projectHash) || null
|
||||
}
|
||||
@@ -127,8 +127,8 @@ export async function investInProject(
|
||||
console.log('▶ Кошелек программы:', finalProgramWallet)
|
||||
|
||||
// Проверка изменения балансов
|
||||
expect(parseFloat(finalUserWallet.blocked)).toBe(parseFloat(prevUserWallet.blocked) + parseFloat(investAmount))
|
||||
expect(parseFloat(finalProgramWallet.blocked)).toBe(parseFloat(prevProgramWallet.blocked) + parseFloat(investAmount))
|
||||
expect(parseFloat(finalUserWallet.blocked)).toBeCloseTo(parseFloat(prevUserWallet.blocked) + parseFloat(investAmount), 1)
|
||||
expect(parseFloat(finalProgramWallet.blocked)).toBeCloseTo(parseFloat(prevProgramWallet.blocked) + parseFloat(investAmount), 1)
|
||||
|
||||
console.log(`\n✅ Инвестирование на ${investAmount} завершено успешно!`)
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { getSegment } from './getSegment'
|
||||
|
||||
export async function processAddContributor(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
project_hash: string,
|
||||
username: string,
|
||||
) {
|
||||
// Состояние сегмента до добавления (может не существовать)
|
||||
let segmentBefore: any = null
|
||||
try {
|
||||
segmentBefore = await getSegment(blockchain, coopname, project_hash, username)
|
||||
}
|
||||
catch (error) {
|
||||
// Сегмент может не существовать - это нормально
|
||||
}
|
||||
|
||||
const data: CapitalContract.Actions.AddContributor.IAddContributor = {
|
||||
coopname,
|
||||
project_hash,
|
||||
username,
|
||||
}
|
||||
|
||||
// Выполняем добавление участника в проект
|
||||
const txAdd = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.AddContributor.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blocksBehind: 3, expireSeconds: 30 },
|
||||
)
|
||||
getTotalRamUsage(txAdd)
|
||||
expect(txAdd.transaction_id).toBeDefined()
|
||||
|
||||
// Состояние сегмента после добавления
|
||||
const segmentAfter = await getSegment(blockchain, coopname, project_hash, username)
|
||||
|
||||
return {
|
||||
transactionId: txAdd.transaction_id,
|
||||
segmentBefore,
|
||||
segmentAfter,
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,11 @@ import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { generateRandomSHA256 } from '../../utils/randomHash'
|
||||
import { fakeDocument } from '../shared/fakeDocument'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getCoopProgramWallet, getUserProgramWallet } from '../wallet/walletUtils'
|
||||
import { getSegment } from './getSegment'
|
||||
import { getProjectWallet } from './getProjectWallet'
|
||||
import { capitalProgramId, walletProgramId } from './consts'
|
||||
import { getProject } from './getProject'
|
||||
|
||||
/**
|
||||
* Функция для конвертации сегмента участника
|
||||
@@ -32,6 +36,20 @@ export async function processConvertSegment(
|
||||
const segmentBefore = await getSegment(blockchain, coopname, projectHash, username)
|
||||
console.log('📊 Сегмент до конвертации:', segmentBefore)
|
||||
|
||||
// Получаем кошельки до конвертации
|
||||
const mainWalletBefore = await getCoopProgramWallet(blockchain, coopname, walletProgramId)
|
||||
const capitalWalletBefore = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
|
||||
const userCapitalWalletBefore = await getUserProgramWallet(blockchain, coopname, username, capitalProgramId)
|
||||
const projectWalletBefore = await getProjectWallet(blockchain, coopname, projectHash, username)
|
||||
const projectBefore = await getProject(blockchain, coopname, projectHash)
|
||||
|
||||
console.log('💰 Кошельки до конвертации:')
|
||||
console.log(' ▶ Главный кошелек программы:', mainWalletBefore)
|
||||
console.log(' ▶ Глобальный кошелек программы капитализации:', capitalWalletBefore)
|
||||
console.log(' ▶ Кошелек пользователя в программе капитализации:', userCapitalWalletBefore)
|
||||
console.log(' ▶ Кошелек проекта:', projectWalletBefore)
|
||||
console.log(' ▶ Проект до конвертации: ', projectBefore)
|
||||
|
||||
// 1. Конвертируем сегмент
|
||||
const convertSegmentData: CapitalContract.Actions.ConvertSegment.IConvertSegment = {
|
||||
coopname,
|
||||
@@ -70,16 +88,71 @@ export async function processConvertSegment(
|
||||
expect(convertResult.transaction_id).toBeDefined()
|
||||
console.log('✅ Сегмент конвертирован:', convertHash)
|
||||
|
||||
// Получаем сегмент после конвертации
|
||||
// Получаем сегмент после конвертации (должен быть удален)
|
||||
const segmentAfter = await getSegment(blockchain, coopname, projectHash, username)
|
||||
|
||||
// Проверяем изменение статуса сегмента на COMPLETED
|
||||
expect(segmentAfter.status).toBe('completed')
|
||||
console.log('🔍 Статус сегмента после конвертации:', segmentAfter.status)
|
||||
// Получаем кошельки после конвертации
|
||||
const mainWalletAfter = await getCoopProgramWallet(blockchain, coopname, walletProgramId)
|
||||
const capitalWalletAfter = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
|
||||
const userCapitalWalletAfter = await getUserProgramWallet(blockchain, coopname, username, capitalProgramId)
|
||||
const projectWalletAfter = await getProjectWallet(blockchain, coopname, projectHash, username)
|
||||
|
||||
console.log('💰 Кошельки после конвертации:')
|
||||
console.log(' ▶ Главный кошелек программы:', mainWalletAfter)
|
||||
console.log(' ▶ Глобальный кошелек программы капитализации:', capitalWalletAfter)
|
||||
console.log(' ▶ Кошелек пользователя в программе капитализации:', userCapitalWalletAfter)
|
||||
console.log(' ▶ Кошелек проекта:', projectWalletAfter)
|
||||
|
||||
// Проверяем что сегмент удален после конвертации
|
||||
expect(segmentAfter).toBeUndefined()
|
||||
console.log('🔍 Сегмент после конвертации: ', segmentAfter)
|
||||
|
||||
// Проверяем изменения в кошельках
|
||||
console.log('\n🔍 Проверка изменений в кошельках:')
|
||||
|
||||
// Проверяем главный кошелек программы (wallet_amount)
|
||||
if (walletAmount !== '0.0000 RUB') {
|
||||
const walletAmountValue = parseFloat(walletAmount.split(' ')[0])
|
||||
const beforeAvailable = mainWalletBefore ? parseFloat(mainWalletBefore.available.split(' ')[0]) : 0
|
||||
const afterAvailable = mainWalletAfter ? parseFloat(mainWalletAfter.available.split(' ')[0]) : 0
|
||||
const expectedIncrease = beforeAvailable + walletAmountValue
|
||||
console.log(`✅ Главный кошелек программы: ${beforeAvailable} → ${afterAvailable} (+${walletAmountValue})`)
|
||||
expect(afterAvailable).toBeCloseTo(expectedIncrease, 1)
|
||||
}
|
||||
|
||||
// Проверяем глобальный кошелек программы капитализации (capital_amount)
|
||||
if (capitalAmount !== '0.0000 RUB') {
|
||||
const capitalAmountValue = parseFloat(capitalAmount.split(' ')[0])
|
||||
const beforeBlocked = capitalWalletBefore ? parseFloat(capitalWalletBefore.blocked.split(' ')[0]) : 0
|
||||
const afterBlocked = capitalWalletAfter ? parseFloat(capitalWalletAfter.blocked.split(' ')[0]) : 0
|
||||
const expectedIncrease = beforeBlocked + capitalAmountValue
|
||||
console.log(`✅ Глобальный кошелек программы капитализации: ${beforeBlocked} → ${afterBlocked} (+${capitalAmountValue})`)
|
||||
expect(afterBlocked).toBeCloseTo(expectedIncrease, 1)
|
||||
}
|
||||
|
||||
// Проверяем кошелек пользователя в программе капитализации (capital_amount)
|
||||
if (capitalAmount !== '0.0000 RUB') {
|
||||
const capitalAmountValue = parseFloat(capitalAmount.split(' ')[0])
|
||||
const beforeBlocked = userCapitalWalletBefore ? parseFloat(userCapitalWalletBefore.blocked.split(' ')[0]) : 0
|
||||
const afterBlocked = userCapitalWalletAfter ? parseFloat(userCapitalWalletAfter.blocked.split(' ')[0]) : 0
|
||||
const expectedIncrease = beforeBlocked + capitalAmountValue
|
||||
console.log(`✅ Кошелек пользователя в программе капитализации: ${beforeBlocked} → ${afterBlocked} (+${capitalAmountValue})`)
|
||||
expect(afterBlocked).toBeCloseTo(expectedIncrease, 1)
|
||||
}
|
||||
|
||||
// Проверяем кошелек проекта (project_amount)
|
||||
if (projectAmount !== '0.0000 RUB' && projectBefore.parent_hash.includes('0000000000')) {
|
||||
const projectAmountValue = parseFloat(projectAmount.split(' ')[0])
|
||||
const beforeShares = projectWalletBefore ? parseFloat(projectWalletBefore.shares.split(' ')[0]) : 0
|
||||
const afterShares = projectWalletAfter ? parseFloat(projectWalletAfter.shares.split(' ')[0]) : 0
|
||||
const expectedIncrease = beforeShares + projectAmountValue
|
||||
console.log(`✅ Кошелек проекта: ${beforeShares} → ${afterShares} (+${projectAmountValue})`)
|
||||
expect(afterShares).toBeCloseTo(expectedIncrease, 1)
|
||||
}
|
||||
|
||||
console.log('\n📊 Результаты после конвертации сегмента:')
|
||||
console.log('▶ Сегмент до:', segmentBefore)
|
||||
console.log('▶ Сегмент после:', segmentAfter)
|
||||
console.log('▶ Сегмент после удалён: ', segmentAfter)
|
||||
|
||||
console.log(`\n✅ Процесс конвертации сегмента завершен успешно!`)
|
||||
|
||||
@@ -87,6 +160,14 @@ export async function processConvertSegment(
|
||||
convertHash,
|
||||
segmentBefore,
|
||||
segmentAfter,
|
||||
mainWalletBefore,
|
||||
mainWalletAfter,
|
||||
capitalWalletBefore,
|
||||
capitalWalletAfter,
|
||||
userCapitalWalletBefore,
|
||||
userCapitalWalletAfter,
|
||||
projectWalletBefore,
|
||||
projectWalletAfter,
|
||||
transactionId: convertResult.transaction_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
|
||||
export async function processDeleteProject(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
project_hash: string,
|
||||
) {
|
||||
// Состояние проекта до удаления
|
||||
const projectBefore = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
const data: CapitalContract.Actions.DeleteProject.IDeleteProject = {
|
||||
coopname,
|
||||
project_hash,
|
||||
}
|
||||
|
||||
// Выполняем удаление проекта
|
||||
const txDelete = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.DeleteProject.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blocksBehind: 3, expireSeconds: 30 },
|
||||
)
|
||||
getTotalRamUsage(txDelete)
|
||||
expect(txDelete.transaction_id).toBeDefined()
|
||||
|
||||
// Проверяем что проект удален
|
||||
const projectsAfter = await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
)
|
||||
|
||||
return {
|
||||
transactionId: txDelete.transaction_id,
|
||||
projectBefore,
|
||||
projectAfter: projectsAfter[0] || null, // null если удален
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { getCapitalWallet } from './getCapitalWallet'
|
||||
|
||||
export async function processFundProgram(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
amount: string,
|
||||
memo: string = 'Тестовое финансирование программы',
|
||||
) {
|
||||
console.log(`\n🔹 Начало финансирования программы капитализации: сумма=${amount}, memo=${memo}`)
|
||||
|
||||
// Получение данных ДО финансирования
|
||||
const programWalletBefore = await getCapitalWallet(blockchain, coopname, coopname)
|
||||
|
||||
console.log('📊 Состояние программы ДО финансирования:')
|
||||
console.log('▶ Кошелек программы:', programWalletBefore)
|
||||
|
||||
// Финансирование программы капитализации
|
||||
const fundData: CapitalContract.Actions.FundProgram.IFundProgram = {
|
||||
coopname,
|
||||
amount,
|
||||
memo,
|
||||
}
|
||||
|
||||
const fundResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.FundProgram.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: fundData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(fundResult)
|
||||
expect(fundResult.transaction_id).toBeDefined()
|
||||
|
||||
// Получение данных ПОСЛЕ финансирования
|
||||
const programWalletAfter = await getCapitalWallet(blockchain, coopname, coopname)
|
||||
|
||||
console.log('📊 Состояние программы ПОСЛЕ финансирования:')
|
||||
console.log('▶ Кошелек программы:', programWalletAfter)
|
||||
|
||||
console.log(`\n✅ Финансирование программы капитализации завершено успешно!`)
|
||||
|
||||
return {
|
||||
transactionId: fundResult.transaction_id,
|
||||
transaction: fundResult,
|
||||
programWalletBefore,
|
||||
programWalletAfter,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { getProjectWallet } from './getProjectWallet'
|
||||
|
||||
export async function processFundProject(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
projectHash: string,
|
||||
amount: string,
|
||||
memo: string = 'Тестовое финансирование проекта',
|
||||
) {
|
||||
console.log(`\n🔹 Начало финансирования проекта: проект=${projectHash}, сумма=${amount}, memo=${memo}`)
|
||||
|
||||
// Получение данных ДО финансирования
|
||||
const projectWalletBefore = await getProjectWallet(blockchain, coopname, projectHash, coopname)
|
||||
|
||||
console.log('📊 Состояние проекта ДО финансирования:')
|
||||
console.log('▶ Кошелек проекта:', projectWalletBefore)
|
||||
|
||||
// Финансирование проекта
|
||||
const fundData: CapitalContract.Actions.FundProject.IFundProject = {
|
||||
coopname,
|
||||
project_hash: projectHash,
|
||||
amount,
|
||||
memo,
|
||||
}
|
||||
|
||||
const fundResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.FundProject.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: fundData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(fundResult)
|
||||
expect(fundResult.transaction_id).toBeDefined()
|
||||
|
||||
// Получение данных ПОСЛЕ финансирования
|
||||
const projectWalletAfter = await getProjectWallet(blockchain, coopname, projectHash, coopname)
|
||||
|
||||
console.log('📊 Состояние проекта ПОСЛЕ финансирования:')
|
||||
console.log('▶ Кошелек проекта:', projectWalletAfter)
|
||||
|
||||
console.log(`\n✅ Финансирование проекта завершено успешно!`)
|
||||
|
||||
return {
|
||||
transactionId: fundResult.transaction_id,
|
||||
transaction: fundResult,
|
||||
projectWalletBefore,
|
||||
projectWalletAfter,
|
||||
}
|
||||
}
|
||||
@@ -14,22 +14,14 @@ export async function processMasterApprove(
|
||||
commit_hash: commitHash,
|
||||
}
|
||||
|
||||
const result = await blockchain.api.transact(
|
||||
const result = await blockchain.transactWithLogs([
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.CommitApprove.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.CommitApprove.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
])
|
||||
|
||||
getTotalRamUsage(result)
|
||||
expect(result.transaction_id).toBeDefined()
|
||||
|
||||
@@ -2,6 +2,7 @@ import { expect } from 'vitest'
|
||||
import { CapitalContract, SovietContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { generateRandomSHA256 } from '../../utils/randomHash'
|
||||
import { setDocumentSignatures } from '../../utils/setDocumentSignatures'
|
||||
import { processLastDecision } from '../soviet/processLastDecision'
|
||||
import { fakeDocument } from '../shared/fakeDocument'
|
||||
import type Blockchain from '../../blockchain'
|
||||
@@ -34,7 +35,8 @@ export async function processPushResult(
|
||||
const segmentBefore = await getSegment(blockchain, coopname, projectHash, username)
|
||||
console.log('📊 Сегмент до внесения результата:', segmentBefore)
|
||||
|
||||
fakeDocument.signatures[0].signer = username
|
||||
// Устанавливаем подписи документа
|
||||
const documentWithSignatures = setDocumentSignatures([username])
|
||||
|
||||
// 1. Вносим результат
|
||||
const pushResultData: CapitalContract.Actions.PushResult.IPushResult = {
|
||||
@@ -44,7 +46,7 @@ export async function processPushResult(
|
||||
result_hash: resultHash,
|
||||
contribution_amount: contributionAmount,
|
||||
debt_amount: debtAmount,
|
||||
statement: fakeDocument,
|
||||
statement: documentWithSignatures,
|
||||
debt_hashes: debtHashes,
|
||||
}
|
||||
|
||||
@@ -95,12 +97,13 @@ export async function processPushResult(
|
||||
await processLastDecision(blockchain, coopname)
|
||||
console.log('✅ Решение совета принято')
|
||||
|
||||
console.log('🔍 Подписываем акт №1 вкладчиком', documentWithSignatures)
|
||||
// 4. Подписываем акт №1 вкладчиком
|
||||
const signAct1Data: CapitalContract.Actions.SignAct1.ISignAct1 = {
|
||||
coopname,
|
||||
username,
|
||||
result_hash: resultHash,
|
||||
act: fakeDocument,
|
||||
act: documentWithSignatures,
|
||||
}
|
||||
|
||||
console.log(`\n✅ Подписание акта №1 вкладчиком ${username}`)
|
||||
@@ -125,18 +128,18 @@ export async function processPushResult(
|
||||
expect(signAct1Result.transaction_id).toBeDefined()
|
||||
console.log('✅ Акт №1 подписан вкладчиком')
|
||||
|
||||
fakeDocument.signatures[1] = fakeDocument.signatures[0]
|
||||
fakeDocument.signatures[1].signer = 'ant'
|
||||
// Создаем документ с подписями вкладчика и председателя
|
||||
const documentWithChairmanSignature = setDocumentSignatures([username, 'ant'])
|
||||
|
||||
// 5. Подписываем акт №2 председателем
|
||||
const signAct2Data: CapitalContract.Actions.SignAct2.ISignAct2 = {
|
||||
coopname,
|
||||
result_hash: resultHash,
|
||||
act: fakeDocument,
|
||||
username: 'ant',
|
||||
act: documentWithChairmanSignature,
|
||||
chairman: 'ant',
|
||||
}
|
||||
|
||||
console.log(`\n✅ Подписание акта №2 председателем`)
|
||||
console.log(`\n✅ Подписание акта №2 председателем`, documentWithChairmanSignature)
|
||||
const signAct2Result = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { getCapitalWallet } from './getCapitalWallet'
|
||||
|
||||
export async function processRefreshProg(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
username: string,
|
||||
) {
|
||||
console.log(`\n🔹 Начало обновления CRPS пайщика в программе капитализации: пользователь=${username}`)
|
||||
|
||||
// Получение данных ДО обновления
|
||||
const programWalletBefore = await getCapitalWallet(blockchain, coopname, username)
|
||||
|
||||
console.log('📊 Состояние пайщика ДО обновления CRPS:')
|
||||
console.log('▶ Кошелек пайщика:', programWalletBefore)
|
||||
|
||||
// Обновление CRPS пайщика в программе капитализации
|
||||
const refreshData: CapitalContract.Actions.RefreshProgram.IRefreshProgram = {
|
||||
coopname,
|
||||
username,
|
||||
}
|
||||
|
||||
const refreshResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.RefreshProgram.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: refreshData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(refreshResult)
|
||||
expect(refreshResult.transaction_id).toBeDefined()
|
||||
|
||||
// Получение данных ПОСЛЕ обновления
|
||||
const programWalletAfter = await getCapitalWallet(blockchain, coopname, username)
|
||||
|
||||
console.log('📊 Состояние пайщика ПОСЛЕ обновления CRPS:')
|
||||
console.log('▶ Кошелек пайщика:', programWalletAfter)
|
||||
|
||||
console.log(`\n✅ Обновление CRPS пайщика в программе капитализации завершено успешно!`)
|
||||
|
||||
return {
|
||||
transactionId: refreshResult.transaction_id,
|
||||
transaction: refreshResult,
|
||||
programWalletBefore,
|
||||
programWalletAfter,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { getProjectWallet } from './getProjectWallet'
|
||||
|
||||
export async function processRefreshProj(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
projectHash: string,
|
||||
username: string,
|
||||
) {
|
||||
console.log(`\n🔹 Начало обновления CRPS пайщика в проекте: проект=${projectHash}, пользователь=${username}`)
|
||||
|
||||
// Получение данных ДО обновления
|
||||
const projectWalletBefore = await getProjectWallet(blockchain, coopname, projectHash, username)
|
||||
|
||||
console.log('📊 Состояние кошелька проекта ДО обновления CRPS:')
|
||||
console.log('▶ Кошелек проекта:', projectWalletBefore)
|
||||
|
||||
// Обновление CRPS пайщика в проекте
|
||||
const refreshData: CapitalContract.Actions.RefreshProject.IRefreshProject = {
|
||||
coopname,
|
||||
project_hash: projectHash,
|
||||
username,
|
||||
}
|
||||
|
||||
const refreshResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.RefreshProject.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: refreshData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(refreshResult)
|
||||
expect(refreshResult.transaction_id).toBeDefined()
|
||||
|
||||
// Получение данных ПОСЛЕ обновления
|
||||
const projectWalletAfter = await getProjectWallet(blockchain, coopname, projectHash, username)
|
||||
|
||||
console.log('📊 Состояние кошелька проекта ПОСЛЕ обновления CRPS:')
|
||||
console.log('▶ Кошелек проекта:', projectWalletAfter)
|
||||
|
||||
console.log(`\n✅ Обновление CRPS пайщика в проекте завершено успешно!`)
|
||||
|
||||
return {
|
||||
transactionId: refreshResult.transaction_id,
|
||||
transaction: refreshResult,
|
||||
projectWalletBefore,
|
||||
projectWalletAfter,
|
||||
}
|
||||
}
|
||||
@@ -28,21 +28,15 @@ export async function refreshSegment(
|
||||
username,
|
||||
}
|
||||
|
||||
const refreshResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.RefreshSegment.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: refreshSegmentData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
const refreshResult = await blockchain.transactWithLogs(
|
||||
[
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.RefreshSegment.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: refreshSegmentData,
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
getTotalRamUsage(refreshResult)
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
interface ActionTrace {
|
||||
act?: {
|
||||
account: string
|
||||
name: string
|
||||
authorization?: { actor: string }[]
|
||||
}
|
||||
console?: string
|
||||
inline_traces?: ActionTrace[]
|
||||
}
|
||||
|
||||
interface TransactionResult {
|
||||
processed?: {
|
||||
action_traces?: ActionTrace[]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Извлекает и печатает все консоль логи из результата транзакции
|
||||
* @param transaction - результат транзакции blockchain.api.transact
|
||||
*/
|
||||
export function consoleIt(transaction: TransactionResult): void {
|
||||
if (!transaction?.processed?.action_traces) {
|
||||
console.log('❌ Нет данных о транзакции или action traces')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('\n📋 КОНСОЛЬ ЛОГИ ТРАНЗАКЦИИ:')
|
||||
console.log('='.repeat(50))
|
||||
|
||||
let hasConsoleLogs = false
|
||||
|
||||
function processActionTrace(trace: ActionTrace, depth = 0): void {
|
||||
const indent = ' '.repeat(depth)
|
||||
|
||||
// Получаем информацию о действии
|
||||
const account = trace.act?.account || 'unknown'
|
||||
const action = trace.act?.name || 'unknown'
|
||||
const actor = trace.act?.authorization?.[0]?.actor || 'unknown'
|
||||
|
||||
// Печатаем заголовок действия
|
||||
console.log(`${indent}🔧 ${account}:${action} (actor: ${actor})`)
|
||||
|
||||
// Если есть консоль логи, печатаем их
|
||||
if (trace.console) {
|
||||
hasConsoleLogs = true
|
||||
const logs = trace.console.split('\n').filter(log => log.trim())
|
||||
|
||||
if (logs.length > 0) {
|
||||
console.log(`${indent}📝 Console output:`)
|
||||
logs.forEach((log) => {
|
||||
console.log(`${indent} ${log}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log(`${indent} (нет консоль логов)`)
|
||||
}
|
||||
|
||||
// Обрабатываем inline traces рекурсивно
|
||||
if (trace.inline_traces?.length) {
|
||||
console.log(`${indent} ↳ Inline actions:`)
|
||||
trace.inline_traces.forEach((inlineTrace) => {
|
||||
processActionTrace(inlineTrace, depth + 1)
|
||||
})
|
||||
}
|
||||
|
||||
if (depth === 0) {
|
||||
console.log('') // Пустая строка между действиями верхнего уровня
|
||||
}
|
||||
}
|
||||
|
||||
// Обрабатываем все action traces
|
||||
transaction.processed.action_traces.forEach((trace, index) => {
|
||||
console.log(`\n📍 Action ${index + 1}:`)
|
||||
processActionTrace(trace, 0)
|
||||
})
|
||||
|
||||
if (!hasConsoleLogs) {
|
||||
console.log('ℹ️ В транзакции не найдено консоль логов')
|
||||
}
|
||||
|
||||
console.log('='.repeat(50))
|
||||
console.log('🏁 Конец логов транзакции\n')
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from './combinedKeys'
|
||||
export * from './consoleIt'
|
||||
export * from './fakeDocument'
|
||||
export * from './fakeVote'
|
||||
export * from './formatDateToISOString'
|
||||
@@ -17,6 +17,11 @@ export async function getUserProgramWallet(blockchain: any, coopname: string, us
|
||||
return wallets.find((el: any) => el.program_id === program_id)
|
||||
}
|
||||
|
||||
export async function getUserProgramWalletAmount(blockchain: any, coopname: string, username: string, program_id: number) {
|
||||
const wallet = await getUserProgramWallet(blockchain, coopname, username, program_id)
|
||||
return wallet ? `${(parseFloat(wallet.available) + parseFloat(wallet.blocked)).toFixed(4)} RUB` : '0.0000 RUB'
|
||||
}
|
||||
|
||||
export async function getCoopProgramWallet(blockchain: any, coopname: string, program_id: number) {
|
||||
const program = await blockchain.getTableRows(
|
||||
SovietContract.contractName.production,
|
||||
@@ -76,5 +81,5 @@ export function compareTokenAmounts(prevAmount: string, currentAmount: string, e
|
||||
const current = parseFloat(currentAmount.split(' ')[0])
|
||||
const expected = prev + expectedIncrease
|
||||
|
||||
expect(current).toBe(expected)
|
||||
expect(current).toBeCloseTo(expected, 4)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { fakeDocument } from '../tests/shared/fakeDocument'
|
||||
|
||||
/**
|
||||
* Устанавливает подписи документа на основе массива имен аккаунтов
|
||||
* @param usernames массив имен аккаунтов для подписи документа
|
||||
* @returns копия fakeDocument с установленными подписями
|
||||
*/
|
||||
export function setDocumentSignatures(usernames: string[]) {
|
||||
const document = JSON.parse(JSON.stringify(fakeDocument))
|
||||
|
||||
// Очищаем существующие подписи
|
||||
document.signatures = []
|
||||
|
||||
// Создаем подписи для каждого пользователя
|
||||
usernames.forEach((username, index) => {
|
||||
const signature = {
|
||||
id: index + 1,
|
||||
signed_hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
|
||||
signer: username,
|
||||
public_key: 'EOS5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzxrLg3J',
|
||||
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
|
||||
signed_at: '2025-05-14T12:22:26',
|
||||
meta: '{}',
|
||||
}
|
||||
document.signatures.push(signature)
|
||||
})
|
||||
|
||||
return document
|
||||
}
|
||||
@@ -29,7 +29,6 @@ void capital::createdebt(name coopname, name username, checksum256 project_hash,
|
||||
|
||||
// Проверяем что участник существует в проекте
|
||||
auto exist_contributor = Capital::Contributors::get_active_contributor_with_appendix_or_fail(coopname, project_hash, username);
|
||||
eosio::check(exist_contributor.has_value(), "Договор УХД с пайщиком не найден");
|
||||
|
||||
// Проверяем что сегмент существует и обновлен
|
||||
auto exist_segment = Capital::Segments::get_segment(coopname, project_hash, username);
|
||||
|
||||
@@ -21,6 +21,10 @@ void capital::debtpaycnfrm(name coopname, checksum256 debt_hash) {
|
||||
// Обновляем статус долга на PAID
|
||||
Capital::Debts::update_debt_status(coopname, debt_hash, Capital::Debts::Status::PAID, _gateway);
|
||||
|
||||
// Увеличиваем значение счёта выданных ссуд
|
||||
auto memo = Capital::Memo::get_debt_memo(exist_debt.username);
|
||||
Ledger::add(_capital, coopname, Ledger::accounts::LONG_TERM_LOANS, exist_debt.amount, memo);
|
||||
|
||||
// Увеличиваем долг contributor (теперь долг активен и должен быть погашен через внесение результата)
|
||||
Capital::Contributors::increase_debt_amount(coopname, exist_debt.username, exist_debt.amount);
|
||||
};
|
||||
+2
-1
@@ -12,7 +12,8 @@
|
||||
* @note Авторизация требуется от аккаунта: @p _soviet или @p _gateway
|
||||
*/
|
||||
void capital::fundprog(eosio::name coopname, asset amount, std::string memo) {
|
||||
auto payer = check_auth_and_get_payer_or_fail({_soviet, _gateway});
|
||||
require_auth(coopname);
|
||||
// auto payer = check_auth_and_get_payer_or_fail({_soviet, _gateway});
|
||||
|
||||
Wallet::validate_asset(amount);
|
||||
|
||||
|
||||
+2
-1
@@ -15,7 +15,8 @@
|
||||
* @note Авторизация требуется от аккаунта: @p _soviet или @p _gateway
|
||||
*/
|
||||
void capital::fundproj(eosio::name coopname, checksum256 project_hash, asset amount, std::string memo) {
|
||||
auto payer = check_auth_and_get_payer_or_fail({ _soviet, _gateway });
|
||||
require_auth(coopname);
|
||||
// auto payer = check_auth_and_get_payer_or_fail({ _soviet, _gateway });
|
||||
Wallet::validate_asset(amount);
|
||||
|
||||
auto exist_project = Capital::Projects::get_project(coopname, project_hash);
|
||||
|
||||
+2
-2
@@ -9,10 +9,10 @@
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_capital_actions
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p username
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void capital::refreshproj(name coopname, checksum256 project_hash, name username) {
|
||||
require_auth(username);
|
||||
require_auth(coopname);
|
||||
|
||||
// Проверяем существование проекта
|
||||
auto project = Capital::Projects::get_project_or_fail(coopname, project_hash);
|
||||
|
||||
@@ -34,7 +34,10 @@ void capital::approvecmmt(eosio::name coopname, eosio::name master, checksum256
|
||||
Capital::Core::increment_authors_crps_in_project(coopname, commit.project_hash,
|
||||
commit.amounts.authors_base_pool,
|
||||
commit.amounts.authors_bonus_pool);
|
||||
|
||||
|
||||
// Распределяем премии вкладчиков между всеми вкладчиками проекта через CRPS
|
||||
Capital::Core::increment_contributors_crps_in_project(coopname, commit.project_hash, commit.amounts.contributors_bonus_pool);
|
||||
|
||||
// Удаляем коммит после обработки
|
||||
Capital::Commits::delete_commit(coopname, commit_hash);
|
||||
};
|
||||
@@ -13,15 +13,21 @@
|
||||
* @note Авторизация требуется от аккаунта: @p username
|
||||
*/
|
||||
void capital::addcontrib(eosio::name coopname, checksum256 project_hash, eosio::name username) {
|
||||
require_auth(username);
|
||||
require_auth(coopname);
|
||||
|
||||
// Проверяем существование проекта
|
||||
auto project = Capital::Projects::get_project_or_fail(coopname, project_hash);
|
||||
|
||||
auto check_appendix = Capital::Contributors::get_active_contributor_with_appendix_or_fail(coopname, project_hash, username);
|
||||
|
||||
// Проверяем наличие сегмента
|
||||
auto exist_segment = Capital::Segments::get_segment(coopname, project_hash, username);
|
||||
|
||||
// Добавляем вкладчика через CRPS систему
|
||||
// Функция сама выполнит все необходимые проверки:
|
||||
// - наличие активного договора УХД
|
||||
// - наличие подписанного приложения к проекту
|
||||
// - положительный баланс в программе капитализации
|
||||
if (exist_segment.has_value()) {
|
||||
// Проверяем что пользователь не зарегистрирован в проекте как вкладчик
|
||||
eosio::check(!exist_segment -> is_contributor, "Вкладчик уже зарегистрирован в проекте");
|
||||
}
|
||||
|
||||
// Добавляем вкладчика
|
||||
Capital::Core::upsert_contributor_segment(coopname, project_hash, username);
|
||||
}
|
||||
+15
-4
@@ -2,8 +2,10 @@
|
||||
* @brief Удаляет проект
|
||||
* Удаляет проект из системы кооператива:
|
||||
* - Проверяет существование проекта
|
||||
* - Валидирует что проект в статусе voting
|
||||
* - Удаляет проект и все связанные с ним данные
|
||||
* - Валидирует что проект в статусе completed
|
||||
* - Проверяет что все сегменты сконвертированы (удалены)
|
||||
* - Проверяет что в проектном кошельке 0 total_shares
|
||||
* - Удаляет проект
|
||||
* @param coopname Наименование кооператива
|
||||
* @param project_hash Хеш проекта для удаления
|
||||
* @ingroup public_actions
|
||||
@@ -15,7 +17,16 @@ void capital::delproject(name coopname, checksum256 project_hash) {
|
||||
require_auth(coopname);
|
||||
|
||||
auto project = Capital::Projects::get_project_or_fail(coopname, project_hash);
|
||||
eosio::check(project.status == Capital::Projects::Status::VOTING, "Проект должен быть в статусе 'voting'");
|
||||
eosio::check(project.status == Capital::Projects::Status::COMPLETED, "Проект должен быть в статусе 'completed'");
|
||||
|
||||
//TODO: delete project with check that we are delete everything related to it
|
||||
// Проверяем что все сегменты сконвертированы (удалены)
|
||||
eosio::check(!Capital::Segments::has_project_segments(coopname, project_hash),
|
||||
"Не все сегменты сконвертированы. Сначала конвертируйте все сегменты");
|
||||
|
||||
// Проверяем что в проектном кошельке 0 total_shares
|
||||
eosio::check(project.membership.total_shares.amount == 0,
|
||||
"В проектном кошельке есть доли. Сначала выведите все доли");
|
||||
|
||||
// Удаляем проект
|
||||
Capital::Projects::delete_project(coopname, project_hash);
|
||||
}
|
||||
+36
-39
@@ -2,21 +2,21 @@
|
||||
* @defgroup public_capital_project_deletion_process Удалить проект
|
||||
* @ingroup public_capital_processes
|
||||
*
|
||||
* Процесс полного удаления проекта и всех связанных данных из блокчейна
|
||||
* в случаях, когда хранение данных более не целесообразно.
|
||||
* Процесс удаления проекта из блокчейна после завершения всех финансовых операций
|
||||
* и конвертации вкладов участников.
|
||||
*
|
||||
* ## Назначение процесса
|
||||
*
|
||||
* Удаление проекта обеспечивает:
|
||||
* - Освобождение ресурсов блокчейна от неактуальных данных
|
||||
* - Удаление проектов, не предполагавших получение членских взносов
|
||||
* - Очистку системы от завершенных экспериментальных проектов
|
||||
* - Удаление проектов, все вклады в которых сконвертированы
|
||||
* - Очистку системы от завершенных проектов без активных участников
|
||||
* - Оптимизацию использования оперативной памяти блокчейна
|
||||
*
|
||||
* @pre
|
||||
* - Все вклады участников должны быть сконвертированы
|
||||
* - Нет неразрешенных финансовых обязательств
|
||||
* - Проект изначально не предполагал получение членских взносов
|
||||
* - Проект должен быть в статусе COMPLETED
|
||||
* - Все сегменты участников должны быть сконвертированы (удалены)
|
||||
* - В проектном кошельке должно быть 0 total_shares
|
||||
*
|
||||
* \htmlonly
|
||||
* <div class="mermaid">
|
||||
@@ -24,17 +24,21 @@
|
||||
* participant К as Кооператив
|
||||
* participant ПК as Контракт Capital
|
||||
* participant П as Проект
|
||||
* participant С as Сегменты
|
||||
*
|
||||
* К->>ПК: 1. Инициация удаления проекта
|
||||
* Note over ПК: Проверка статуса проекта
|
||||
* ПК->>П: 2. Проверка готовности к удалению
|
||||
* alt Проект готов к удалению
|
||||
* П->>ПК: 3. Подтверждение готовности
|
||||
* ПК->>ПК: 4. Валидация конвертации вкладов
|
||||
* ПК->>ПК: 5. Удаление связанных данных
|
||||
* ПК->>П: 6. Удаление записи проекта
|
||||
* else Проект не готов
|
||||
* ПК->>К: Ошибка: проект не готов к удалению
|
||||
* ПК->>П: 2. Проверка статуса COMPLETED
|
||||
* ПК->>С: 3. Проверка наличия сегментов
|
||||
* alt Сегменты отсутствуют
|
||||
* ПК->>П: 4. Проверка total_shares = 0
|
||||
* alt total_shares = 0
|
||||
* ПК->>П: 5. Удаление проекта
|
||||
* else total_shares > 0
|
||||
* ПК->>К: Ошибка: есть доли в кошельке
|
||||
* end
|
||||
* else Сегменты есть
|
||||
* ПК->>К: Ошибка: не все сегменты сконвертированы
|
||||
* end
|
||||
* </div>
|
||||
* \endhtmlonly
|
||||
@@ -52,42 +56,35 @@
|
||||
* ### Шаг 2: Проверка статуса проекта
|
||||
* <b>Действие:</b> Валидация состояния
|
||||
*
|
||||
* Система проверяет, что проект находится в корректном статусе
|
||||
* для удаления. В текущей реализации требуется статус VOTING,
|
||||
* но в продакшене логично требовать CLOSED статус.
|
||||
* Система проверяет, что проект находится в статусе COMPLETED.
|
||||
* Только завершенные проекты могут быть удалены.
|
||||
*
|
||||
* @post Подтвержден корректный статус проекта
|
||||
* @post Подтвержден статус COMPLETED проекта
|
||||
*
|
||||
* ### Шаг 3: Валидация конвертации вкладов
|
||||
* <b>Действие:</b> Проверка завершенности финансовых операций
|
||||
* ### Шаг 3: Проверка конвертации сегментов
|
||||
* <b>Действие:</b> Валидация завершенности конвертации
|
||||
*
|
||||
* Перед удалением система должна проверить, что:
|
||||
* - Все сегменты участников сконвертированы
|
||||
* - Нет неразрешенных финансовых обязательств
|
||||
* - Все средства переведены в целевые кошельки или проекты
|
||||
* Система проверяет, что все сегменты участников сконвертированы
|
||||
* и удалены из системы. Если сегменты остались, удаление невозможно.
|
||||
*
|
||||
* @post Проверка завершенности всех финансовых операций
|
||||
* @post Подтверждено отсутствие сегментов проекта
|
||||
*
|
||||
* ### Шаг 4: Удаление связанных данных
|
||||
* <b>Действие:</b> Очистка всех таблиц проекта
|
||||
* ### Шаг 4: Проверка проектного кошелька
|
||||
* <b>Действие:</b> Валидация финансового состояния
|
||||
*
|
||||
* Система должна удалить все данные, связанные с проектом:
|
||||
* - Записи участников и их сегменты
|
||||
* - Данные голосования и результаты
|
||||
* - Инвестиции и финансовые операции
|
||||
* - Коммиты и трудозатраты
|
||||
* - Расходы и ссуды
|
||||
* Система проверяет, что в проектном кошельке нет активных долей
|
||||
* (total_shares = 0). Если доли остались, их необходимо вывести.
|
||||
*
|
||||
* @post Удаление всех связанных записей из таблиц блокчейна
|
||||
* @post Подтверждено отсутствие долей в проектном кошельке
|
||||
*
|
||||
* ### Шаг 5: Удаление записи проекта
|
||||
* ### Шаг 5: Удаление проекта
|
||||
* <b>Действие:</b> Финальная очистка
|
||||
*
|
||||
* После удаления всех связанных данных система удаляет
|
||||
* основную запись проекта из таблицы проектов.
|
||||
* После всех проверок система удаляет запись проекта из таблицы проектов.
|
||||
*
|
||||
* @post Полное удаление проекта из блокчейна
|
||||
* @post Освобождение ресурсов оперативной памяти
|
||||
*
|
||||
* @warning Текущая реализация не завершена и находится в разработке.
|
||||
* @note Проект может быть удален только после полной конвертации всех вкладов
|
||||
* участников и вывода всех долей из проектного кошелька.
|
||||
*/
|
||||
+31
-58
@@ -33,70 +33,44 @@ void capital::convertsegm(eosio::name coopname, eosio::name username,
|
||||
"Сегмент пайщика не найден");
|
||||
|
||||
// Проверяем статус сегмента - результат должен быть внесён после pushrslt
|
||||
eosio::check(segment.status == Capital::Segments::Status::CONTRIBUTED,
|
||||
"Результат не внесён. Сначала внесите результат через pushrslt");
|
||||
eosio::check(segment.status == Capital::Segments::Status::ACCEPTED, "Результат не внесён. Сначала внесите результат через pushrslt");
|
||||
|
||||
// Проверяем актуальность сегмента (включая синхронизацию с инвестициями)
|
||||
Capital::Segments::check_segment_is_updated(coopname, project_hash, username,
|
||||
"Сегмент не обновлен. Выполните rfrshsegment перед конвертацией");
|
||||
Capital::Segments::check_segment_is_updated(coopname, project_hash, username, "Сегмент не обновлен. Выполните rfrshsegment перед конвертацией");
|
||||
|
||||
// === ФАЗА 1: БАЗОВЫЕ ПРОВЕРКИ ===
|
||||
Wallet::validate_asset(wallet_amount);
|
||||
Wallet::validate_asset(capital_amount);
|
||||
Wallet::validate_asset(project_amount);
|
||||
|
||||
// Проверяем, что у пользователя есть что конвертировать
|
||||
eosio::check(segment.total_segment_cost.amount > 0,
|
||||
"У участника нет средств для конвертации (общая стоимость сегмента равна нулю)");
|
||||
print("wallet_amount: ", wallet_amount.to_string());
|
||||
print("capital_amount: ", capital_amount.to_string());
|
||||
print("project_amount: ", project_amount.to_string());
|
||||
|
||||
// === ФАЗА 2: РАСЧЕТ ДОСТУПНЫХ СУММ ===
|
||||
// Доступная сумма для конвертации в кошелек
|
||||
eosio::asset available_for_wallet = segment.provisional_amount - segment.debt_amount;
|
||||
print("available_for_wallet: ", available_for_wallet.to_string());
|
||||
eosio::check(wallet_amount <= available_for_wallet, "Сумма конвертации в кошелек превышает доступную и обеспеченную суммы. Для конвертации доступна обеспеченная сумма за вычетом суммы выданных ссуд (provisional_amount - debt_amount)");
|
||||
|
||||
// Проверяем что долг уже был погашен после pushrslt
|
||||
eosio::check(segment.debt_settled >= segment.debt_amount,
|
||||
"Долг не был погашен. Сначала внесите результат через pushrslt");
|
||||
// Доступная сумма для конвертации в проект (все базовые суммы всех ролей за вычетом выданных ссуд)
|
||||
eosio::asset available_for_project = segment.creator_base + segment.author_base + segment.coordinator_base + segment.investor_base + segment.property_base - segment.debt_amount;
|
||||
print("available_for_project: ", available_for_project.to_string());
|
||||
eosio::check(project_amount <= available_for_project, "Сумма конвертации в проект превышает себестоимость вкладов за вычетом суммы выданных ссуд");
|
||||
|
||||
// Суммы доступные для конвертации в проект (все базовые суммы всех ролей)
|
||||
eosio::asset available_for_project = segment.creator_base + segment.author_base +
|
||||
segment.coordinator_base + segment.investor_base + segment.property_base;
|
||||
|
||||
// Суммы доступные для конвертации в кошелек (только creator_base + author_base + coordinator_base)
|
||||
// уменьшенные на погашенный долг, но не больше provisional_amount
|
||||
eosio::asset base_for_wallet = segment.creator_base + segment.author_base + segment.coordinator_base;
|
||||
eosio::asset wallet_base_after_debt = base_for_wallet - segment.debt_amount;
|
||||
|
||||
// Долг не может превышать базовые суммы создателя/автора/координатора
|
||||
eosio::check(wallet_base_after_debt.amount >= 0,
|
||||
"Долг превышает базовые суммы создателя/автора/координатора");
|
||||
|
||||
eosio::asset available_for_wallet = (wallet_base_after_debt < segment.provisional_amount) ?
|
||||
wallet_base_after_debt : segment.provisional_amount;
|
||||
|
||||
// Все взносы должны конвертироваться в капитализацию, если не идут в проект или кошелек
|
||||
// Вычитаем погашенный долг из общей стоимости сегмента
|
||||
eosio::asset total_segment_available = segment.total_segment_cost - segment.debt_amount;
|
||||
|
||||
// === ФАЗА 3: ПРОВЕРКИ ОГРАНИЧЕНИЙ ПО НАПРАВЛЕНИЯМ ===
|
||||
|
||||
// Проверка 3.1: Ограничения конвертации в проект
|
||||
eosio::check(project_amount <= available_for_project,
|
||||
"Сумма конвертации в проект превышает доступные базовые суммы всех ролей (creator_base + author_base + coordinator_base + investor_base + property_base)");
|
||||
|
||||
// Проверка 3.2: Ограничения конвертации в кошелек
|
||||
eosio::check(wallet_amount <= available_for_wallet,
|
||||
"Сумма конвертации в кошелек превышает доступные суммы (только creator_base + author_base + coordinator_base и не больше provisional_amount)");
|
||||
|
||||
// Проверка 3.3: Ограничения конвертации в капитализацию
|
||||
// Доступная сумма для конвертации в программу
|
||||
eosio::asset available_for_program = segment.total_segment_cost - segment.debt_amount;
|
||||
print("available_for_program: ", available_for_program.to_string());
|
||||
|
||||
// Проверяем что общая сумма конвертации равна доступной сумме для конвертации в программу
|
||||
eosio::asset total_convert = wallet_amount + capital_amount + project_amount;
|
||||
eosio::check(capital_amount == (total_segment_available - wallet_amount - project_amount),
|
||||
"Сумма конвертации в капитал должна равняться: total_segment_cost - wallet_amount - project_amount");
|
||||
|
||||
// === ФАЗА 4: ФИНАЛЬНАЯ ПРОВЕРКА ===
|
||||
// Проверяем что общая сумма конвертации соответствует всем взносам сегмента
|
||||
eosio::check(total_convert == total_segment_available,
|
||||
"Общая сумма конвертации должна равняться общей стоимости сегмента (total_segment_cost)");
|
||||
|
||||
Wallet::sub_blocked_funds(_capital, coopname, username, total_convert - project_amount, _source_program,
|
||||
Capital::Memo::get_convert_segment_to_wallet_memo(convert_hash));
|
||||
print("total_convert: ", total_convert.to_string());
|
||||
eosio::check(total_convert == available_for_program, "Общая сумма конвертации должна равняться сумме всех себестоимостей и премий за вычетом суммы выданных ссуд");
|
||||
|
||||
eosio::check(capital_amount <= available_for_program, "Сумма конвертации в программу превышает сумму себестоимости и премий за вычетом суммы выданных ссуд");
|
||||
eosio::check(capital_amount == (available_for_program - wallet_amount - project_amount), "В программу должно быть сконвертировано всё доступное, что не конвертируется в проект или кошелёк");
|
||||
|
||||
// Списываем средства с кошелька генерации, которые уходят из проекта
|
||||
Wallet::sub_blocked_funds(_capital, coopname, username, total_convert - project_amount, _source_program, Capital::Memo::get_convert_segment_to_wallet_memo(convert_hash));
|
||||
|
||||
// Выполняем операции с балансами
|
||||
if (wallet_amount.amount > 0) {
|
||||
@@ -114,6 +88,7 @@ void capital::convertsegm(eosio::name coopname, eosio::name username,
|
||||
if (project_amount.amount > 0) {
|
||||
// Определяем целевой проект для конвертации
|
||||
auto current_project = Capital::Projects::get_project_or_fail(coopname, project_hash);
|
||||
|
||||
checksum256 target_project_hash = project_hash;
|
||||
checksum256 empty_hash = checksum256();
|
||||
|
||||
@@ -127,19 +102,17 @@ void capital::convertsegm(eosio::name coopname, eosio::name username,
|
||||
eosio::check(target_project.can_convert_to_project,
|
||||
"Конвертация в кошелек проекта запрещена для данного проекта");
|
||||
|
||||
auto check_appendix = Capital::Contributors::get_active_contributor_with_appendix_or_fail(coopname, target_project_hash, username);
|
||||
|
||||
// Создаем или обновляем кошелек целевого проекта с долями участника
|
||||
Capital::Wallets::upsert_project_wallet(coopname, target_project_hash, username, project_amount);
|
||||
|
||||
// Добавляем сконвертированные средства в глобальный пул программных инвестиций
|
||||
Capital::Core::add_program_investment_funds(coopname, project_amount);
|
||||
|
||||
Capital::Projects::add_project_membership_shares(coopname, target_project_hash, project_amount);
|
||||
Capital::Projects::add_project_converted_funds(coopname, target_project_hash, project_amount);
|
||||
}
|
||||
|
||||
// Обновляем сегмент (включая конвертированные суммы)
|
||||
Capital::Segments::update_segment_conversion(coopname, project_hash, username,
|
||||
wallet_amount, capital_amount, project_amount);
|
||||
// Удаляем сегмент после конвертации
|
||||
Capital::Segments::remove_segment(coopname, project_hash, username);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
void capital::approverslt(eosio::name coopname, checksum256 result_hash, document2 approved_statement){
|
||||
require_auth(coopname);
|
||||
require_auth(_soviet);
|
||||
|
||||
// Проверяем заявление
|
||||
verify_document_or_fail(approved_statement);
|
||||
@@ -26,7 +26,7 @@ void capital::approverslt(eosio::name coopname, checksum256 result_hash, documen
|
||||
eosio::check(exist_result->status == Capital::Results::Status::CREATED, "Неверный статус результата");
|
||||
|
||||
// Устанавливаем одобренное заявление
|
||||
Capital::Results::set_result_approved_statement(coopname, result_hash, approved_statement, coopname);
|
||||
Capital::Results::set_result_approved_statement(coopname, result_hash, approved_statement);
|
||||
|
||||
// Обновляем статус
|
||||
Capital::Results::update_result_status(coopname, result_hash, Capital::Results::Status::APPROVED);
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* @brief Отклоняет одобрение результата участника
|
||||
* Отклоняет одобрение результата участника и возвращает статус сегмента:
|
||||
* - Проверяет подлинность документа причины отклонения
|
||||
* - Валидирует статус результата (должен быть created)
|
||||
* - Удаляет объект результата и возвращает статус сегмента в ready
|
||||
* @param coopname Наименование кооператива
|
||||
* @param result_hash Хеш результата для отклонения одобрения
|
||||
* @param decline_reason Документ причины отклонения
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_capital_actions
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
void capital::declineapprv(eosio::name coopname, checksum256 result_hash, document2 decline_reason) {
|
||||
require_auth(coopname);
|
||||
|
||||
// Проверяем документ причины отклонения
|
||||
verify_document_or_fail(decline_reason);
|
||||
|
||||
// Получаем результат и проверяем статус
|
||||
auto exist_result = Capital::Results::get_result(coopname, result_hash);
|
||||
eosio::check(exist_result.has_value(), "Результат не найден");
|
||||
eosio::check(exist_result->status == Capital::Results::Status::CREATED, "Неверный статус результата");
|
||||
|
||||
// Удаляем объект результата и возвращаем статус сегмента в GENERATION
|
||||
Capital::Results::delete_result_and_reset_segment(coopname, exist_result->project_hash, exist_result->username);
|
||||
|
||||
// Возвращаем статус сегмента в GENERATION
|
||||
Capital::Segments::update_segment_status(coopname, exist_result->project_hash, exist_result->username, Capital::Segments::Status::GENERATION);
|
||||
};
|
||||
@@ -6,25 +6,22 @@
|
||||
* - Удаляет объект результата и возвращает статус сегмента в ready
|
||||
* @param coopname Наименование кооператива
|
||||
* @param result_hash Хеш результата для отклонения
|
||||
* @param decision Документ решения совета
|
||||
* @param reason Причина отклонения
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_capital_actions
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p _soviet
|
||||
*/
|
||||
void capital::declrslt(eosio::name coopname, checksum256 result_hash, document2 decision) {
|
||||
void capital::declrslt(eosio::name coopname, checksum256 result_hash, std::string reason) {
|
||||
require_auth(_soviet);
|
||||
|
||||
// Проверяем документ решения
|
||||
verify_document_or_fail(decision);
|
||||
|
||||
// Получаем результат и проверяем статус
|
||||
auto exist_result = Capital::Results::get_result(coopname, result_hash);
|
||||
eosio::check(exist_result.has_value(), "Объект результата не найден");
|
||||
eosio::check(exist_result->status == Capital::Results::Status::APPROVED, "Неверный статус результата");
|
||||
eosio::check(exist_result->status == Capital::Results::Status::APPROVED || exist_result -> status == Capital::Results::Status::CREATED, "Неверный статус результата");
|
||||
|
||||
// Удаляем объект результата и возвращаем статус сегмента в GENERATION
|
||||
Capital::Results::delete_result_and_reset_segment(coopname, exist_result->project_hash, exist_result->username);
|
||||
// Удаляем объект результата
|
||||
Capital::Results::delete_result(coopname, exist_result->project_hash, exist_result->username);
|
||||
|
||||
// Возвращаем статус сегмента в GENERATION
|
||||
Capital::Segments::update_segment_status(coopname, exist_result->project_hash, exist_result->username, Capital::Segments::Status::GENERATION);
|
||||
|
||||
@@ -113,11 +113,13 @@ void capital::pushrslt(name coopname, name username, checksum256 project_hash, c
|
||||
eosio::check(debt_hashes.empty(), "Если нет суммы долга, вектор хэшей долгов должен быть пустым");
|
||||
}
|
||||
|
||||
// Обновляем сегмент после принятия результата и пересчитываем доли - объединенная операция
|
||||
// для избежания двойного обновления одной записи
|
||||
Capital::Segments::update_segment_after_result_contribution_with_shares(coopname, project_hash, username,
|
||||
// Обновляем сегмент
|
||||
Capital::Segments::update_segment_after_result_contribution(coopname, project_hash, username,
|
||||
debt_amount);
|
||||
|
||||
// Обновляем накопительные показатели контрибьютора на основе его ролей в сегменте
|
||||
Capital::Contributors::update_contributor_ratings_from_segment(coopname, segment);
|
||||
|
||||
// Отправляем результат на одобрение председателем
|
||||
Capital::Results::send_result_for_approval(coopname, username, result_hash, statement);
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
void capital::signact2(eosio::name coopname, eosio::name username, checksum256 result_hash, document2 act) {
|
||||
void capital::signact2(eosio::name coopname, eosio::name chairman, checksum256 result_hash, document2 act) {
|
||||
require_auth(coopname);
|
||||
|
||||
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
|
||||
auto chairman = soviet.get_chairman();
|
||||
auto real_chairman = soviet.get_chairman();
|
||||
|
||||
eosio::check(username == chairman,
|
||||
eosio::check(real_chairman == chairman,
|
||||
"Только председатель может принять имущество");
|
||||
|
||||
// Проверяем результат и права
|
||||
@@ -31,12 +31,12 @@ void capital::signact2(eosio::name coopname, eosio::name username, checksum256 r
|
||||
eosio::check(result->status == Capital::Results::Status::ACT1, "Неверный статус. Первый акт должен быть подписан");
|
||||
|
||||
// Проверяем документ
|
||||
verify_document_or_fail(act, { result->username, username });
|
||||
verify_document_or_fail(act, { result->username, real_chairman });
|
||||
|
||||
// Устанавливаем второй акт
|
||||
Capital::Results::set_result_act2(coopname, result_hash, act);
|
||||
|
||||
auto contributor = Capital::Contributors::get_active_contributor_or_fail(coopname, username);
|
||||
auto contributor = Capital::Contributors::get_active_contributor_or_fail(coopname, result -> username);
|
||||
auto memo = Capital::Memo::get_push_result_memo(contributor -> id);
|
||||
|
||||
// Начисляем заблокированные средства в кошелек программы генерации
|
||||
|
||||
@@ -90,7 +90,6 @@
|
||||
#include "app/result_submission/convert_segment/convertsegm.cpp"
|
||||
#include "app/result_submission/push_result/approverslt.cpp"
|
||||
#include "app/result_submission/push_result/authrslt.cpp"
|
||||
#include "app/result_submission/push_result/declineapprv.cpp"
|
||||
#include "app/result_submission/push_result/declrslt.cpp"
|
||||
#include "app/result_submission/push_result/pushrslt.cpp"
|
||||
#include "app/result_submission/push_result/signact1.cpp"
|
||||
|
||||
@@ -153,16 +153,13 @@ public:
|
||||
void approverslt(eosio::name coopname, checksum256 result_hash, document2 approved_statement);
|
||||
|
||||
[[eosio::action]]
|
||||
void declineapprv(eosio::name coopname, checksum256 result_hash, document2 decline_reason);
|
||||
|
||||
[[eosio::action]]
|
||||
void declrslt(eosio::name coopname, checksum256 result_hash, document2 decision);
|
||||
void declrslt(eosio::name coopname, checksum256 result_hash, std::string reason);
|
||||
|
||||
[[eosio::action]]
|
||||
void signact1(eosio::name coopname, eosio::name username, checksum256 result_hash, document2 act);
|
||||
|
||||
[[eosio::action]]
|
||||
void signact2(eosio::name coopname, eosio::name username, checksum256 result_hash, document2 act);
|
||||
void signact2(eosio::name coopname, eosio::name chairman, checksum256 result_hash, document2 act);
|
||||
|
||||
// конвертация
|
||||
|
||||
|
||||
@@ -71,23 +71,23 @@ namespace Capital::Core {
|
||||
segments.modify(segment_it, _capital, [&](auto &s) {
|
||||
if (s.is_author) {
|
||||
// Обновляем базовые авторские награды
|
||||
int64_t pending_base_reward = 1 *
|
||||
(project.crps.author_base_cumulative_reward_per_share - s.last_author_base_reward_per_share);
|
||||
|
||||
if (pending_base_reward > 0) {
|
||||
eosio::asset base_reward_asset = eosio::asset(pending_base_reward, _root_govern_symbol);
|
||||
s.author_base += base_reward_asset;
|
||||
s.last_author_base_reward_per_share = project.crps.author_base_cumulative_reward_per_share;
|
||||
|
||||
double base_delta = project.crps.author_base_cumulative_reward_per_share - s.last_author_base_reward_per_share;
|
||||
if (base_delta > 0.0) {
|
||||
int64_t pending_base_reward = static_cast<int64_t>(base_delta);
|
||||
if (pending_base_reward > 0) {
|
||||
s.author_base += eosio::asset(pending_base_reward, _root_govern_symbol);
|
||||
s.last_author_base_reward_per_share = project.crps.author_base_cumulative_reward_per_share;
|
||||
}
|
||||
}
|
||||
|
||||
// Обновляем бонусные авторские награды
|
||||
int64_t pending_bonus_reward = 1 *
|
||||
(project.crps.author_bonus_cumulative_reward_per_share - s.last_author_bonus_reward_per_share);
|
||||
|
||||
if (pending_bonus_reward > 0) {
|
||||
s.author_bonus += eosio::asset(pending_bonus_reward, _root_govern_symbol);
|
||||
s.last_author_bonus_reward_per_share = project.crps.author_bonus_cumulative_reward_per_share;
|
||||
double bonus_delta = project.crps.author_bonus_cumulative_reward_per_share - s.last_author_bonus_reward_per_share;
|
||||
if (bonus_delta > 0.0) {
|
||||
int64_t pending_bonus_reward = static_cast<int64_t>(bonus_delta);
|
||||
if (pending_bonus_reward > 0) {
|
||||
s.author_bonus += eosio::asset(pending_bonus_reward, _root_govern_symbol);
|
||||
s.last_author_bonus_reward_per_share = project.crps.author_bonus_cumulative_reward_per_share;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -106,13 +106,13 @@ namespace Capital::Core {
|
||||
if (p.counts.total_authors > 0) {
|
||||
// Обновляем базовые авторские награды
|
||||
if (base_reward.amount > 0) {
|
||||
int64_t base_delta = base_reward.amount / p.counts.total_authors;
|
||||
double base_delta = static_cast<double>(base_reward.amount) / static_cast<double>(p.counts.total_authors);
|
||||
p.crps.author_base_cumulative_reward_per_share += base_delta;
|
||||
}
|
||||
|
||||
// Обновляем бонусные авторские награды
|
||||
if (bonus_reward.amount > 0) {
|
||||
int64_t bonus_delta = bonus_reward.amount / p.counts.total_authors;
|
||||
double bonus_delta = static_cast<double>(bonus_reward.amount) / static_cast<double>(p.counts.total_authors);
|
||||
p.crps.author_bonus_cumulative_reward_per_share += bonus_delta;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,24 @@ namespace Capital::Core {
|
||||
/**
|
||||
* @brief Обновляет CRPS поля в проекте для вкладчиков при добавлении наград
|
||||
*/
|
||||
void update_contributor_crps(eosio::name coopname, const checksum256 &project_hash, const eosio::asset &reward_amount) {
|
||||
void increment_contributors_crps_in_project(eosio::name coopname, const checksum256 &project_hash, const eosio::asset &reward_amount) {
|
||||
Capital::project_index projects(_capital, coopname.value);
|
||||
auto project = projects.find(Capital::Projects::get_project_or_fail(coopname, project_hash).id);
|
||||
|
||||
print("DEBUG increment_contributors_crps: reward_amount=", reward_amount);
|
||||
print(" total_shares=", project->crps.total_capital_contributors_shares);
|
||||
print(" old_crps=", project->crps.contributor_cumulative_reward_per_share);
|
||||
|
||||
projects.modify(project, _capital, [&](auto &p) {
|
||||
// Проверяем что есть зарегистрированные доли для распределения
|
||||
if (p.crps.total_capital_contributors_shares.amount > 0) {
|
||||
// Рассчитываем дельту reward per share (награда на долю в базовых единицах)
|
||||
int64_t delta = reward_amount.amount / p.crps.total_capital_contributors_shares.amount;
|
||||
p.crps.contributor_cumulative_reward_per_share += delta;
|
||||
// Простой расчет награды на долю
|
||||
double reward_per_share = static_cast<double>(reward_amount.amount) / static_cast<double>(p.crps.total_capital_contributors_shares.amount);
|
||||
p.crps.contributor_cumulative_reward_per_share += reward_per_share;
|
||||
print(" reward_per_share=", reward_per_share);
|
||||
print(" new_crps=", p.crps.contributor_cumulative_reward_per_share);
|
||||
} else {
|
||||
print(" NO SHARES REGISTERED!");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -51,6 +60,7 @@ void upsert_contributor_segment(eosio::name coopname, const checksum256 &project
|
||||
});
|
||||
|
||||
// Увеличиваем счетчики вкладчиков
|
||||
print("DEBUG upsert_contributor: new contributor ", username, " shares=", user_shares);
|
||||
Capital::Projects::increment_total_contributors(coopname, project_hash);
|
||||
Capital::Projects::increment_total_contributor_shares(coopname, project_hash, user_shares);
|
||||
} else {
|
||||
@@ -81,6 +91,7 @@ void upsert_contributor_segment(eosio::name coopname, const checksum256 &project
|
||||
|
||||
if (became_contributor) {
|
||||
// Увеличиваем счетчик зарегистрированных вкладчиков
|
||||
print("DEBUG upsert_contributor: existing user ", username, " became contributor, shares=", user_shares);
|
||||
Capital::Projects::increment_total_contributors(coopname, project_hash);
|
||||
// Увеличиваем счетчик долей для нового вкладчика
|
||||
Capital::Projects::increment_total_contributor_shares(coopname, project_hash, user_shares);
|
||||
@@ -96,44 +107,39 @@ void upsert_contributor_segment(eosio::name coopname, const checksum256 &project
|
||||
auto segment_opt = Segments::get_segment(coopname, project_hash, username);
|
||||
|
||||
if (!segment_opt.has_value()) {
|
||||
print("DEBUG: segment not found for ", username);
|
||||
return; // Сегмент не найден
|
||||
}
|
||||
|
||||
auto segment_it = segments.find(segment_opt->id);
|
||||
auto project = Capital::Projects::get_project_or_fail(coopname, project_hash);
|
||||
|
||||
print("DEBUG refresh_contributor_segment: user=", username);
|
||||
print(" shares=", segment_opt->capital_contributor_shares);
|
||||
print(" project_crps=", project.crps.contributor_cumulative_reward_per_share);
|
||||
print(" last_crps=", segment_opt->last_contributor_reward_per_share);
|
||||
|
||||
segments.modify(segment_it, _capital, [&](auto &s) {
|
||||
// Обновляем награды вкладчика
|
||||
// Обновляем награды вкладчика через CRPS алгоритм
|
||||
if (s.capital_contributor_shares.amount > 0) {
|
||||
int64_t pending_contributor_reward = s.capital_contributor_shares.amount *
|
||||
(project.crps.contributor_cumulative_reward_per_share - s.last_contributor_reward_per_share);
|
||||
// Разность наград на долю
|
||||
double reward_per_share_delta = project.crps.contributor_cumulative_reward_per_share - s.last_contributor_reward_per_share;
|
||||
print(" delta=", reward_per_share_delta);
|
||||
|
||||
if (pending_contributor_reward > 0) {
|
||||
s.contributor_bonus += eosio::asset(pending_contributor_reward, _root_govern_symbol);
|
||||
s.last_contributor_reward_per_share = project.crps.contributor_cumulative_reward_per_share;
|
||||
if (reward_per_share_delta > 0.0) {
|
||||
// Простой расчет награды участника
|
||||
double pending_reward_double = static_cast<double>(s.capital_contributor_shares.amount) * reward_per_share_delta;
|
||||
int64_t pending_contributor_reward = static_cast<int64_t>(pending_reward_double);
|
||||
print(" pending_reward=", pending_contributor_reward);
|
||||
|
||||
if (pending_contributor_reward > 0) {
|
||||
s.contributor_bonus += eosio::asset(pending_contributor_reward, _root_govern_symbol);
|
||||
s.last_contributor_reward_per_share = project.crps.contributor_cumulative_reward_per_share;
|
||||
print(" new_bonus=", s.contributor_bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Обновление capital_contributor_shares на основе текущего баланса
|
||||
eosio::asset capital_balance = Capital::Core::get_capital_program_user_share_balance(coopname, username);
|
||||
|
||||
if (s.capital_contributor_shares != capital_balance) {
|
||||
// Корректируем общие доли в проекте
|
||||
Capital::project_index projects(_capital, coopname.value);
|
||||
auto project_it = projects.find(project.id);
|
||||
projects.modify(project_it, _capital, [&](auto &p) {
|
||||
p.crps.total_capital_contributors_shares = p.crps.total_capital_contributors_shares - s.capital_contributor_shares + capital_balance;
|
||||
});
|
||||
|
||||
// Если становится новым вкладчиком, инициализируем CRPS и устанавливаем флаг
|
||||
if (!s.is_contributor && capital_balance.amount > 0) {
|
||||
s.is_contributor = true;
|
||||
s.last_contributor_reward_per_share = project.crps.contributor_cumulative_reward_per_share;
|
||||
// Увеличиваем счетчик зарегистрированных вкладчиков
|
||||
Capital::Projects::increment_total_contributors(coopname, project_hash);
|
||||
}
|
||||
|
||||
s.capital_contributor_shares = capital_balance;
|
||||
} else {
|
||||
print(" no shares!");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Capital::Core {
|
||||
if (is_segment_updated) {
|
||||
return; // Сегмент обновлен
|
||||
}
|
||||
|
||||
print("before refresh_segment");
|
||||
// Обновляем награды для каждой роли отдельно
|
||||
refresh_author_segment(coopname, project_hash, username);
|
||||
refresh_coordinator_segment(coopname, project_hash, username);
|
||||
@@ -148,12 +148,16 @@ namespace Capital::Core {
|
||||
}
|
||||
|
||||
project_wallets.modify(wallet_it, _capital, [&](auto &w) {
|
||||
// Рассчитываем накопленные награды от членских взносов на основе долей в кошельке проекта
|
||||
int64_t pending_membership_reward = w.shares.amount *
|
||||
(project.membership.cumulative_reward_per_share - w.last_membership_reward_per_share);
|
||||
// Рассчитываем накопленные награды от членских взносов
|
||||
double membership_delta = project.membership.cumulative_reward_per_share - w.last_membership_reward_per_share;
|
||||
|
||||
if (pending_membership_reward > 0) {
|
||||
w.membership_available += eosio::asset(pending_membership_reward, _root_govern_symbol);
|
||||
if (membership_delta > 0.0) {
|
||||
double pending_reward_double = static_cast<double>(w.shares.amount) * membership_delta;
|
||||
int64_t pending_membership_reward = static_cast<int64_t>(pending_reward_double);
|
||||
|
||||
if (pending_membership_reward > 0) {
|
||||
w.membership_available += eosio::asset(pending_membership_reward, _root_govern_symbol);
|
||||
}
|
||||
}
|
||||
|
||||
w.last_membership_reward_per_share = project.membership.cumulative_reward_per_share;
|
||||
|
||||
@@ -14,15 +14,10 @@ namespace Capital::Core {
|
||||
void increment_authors_crps_in_project(eosio::name coopname, const checksum256 &project_hash,
|
||||
const eosio::asset &base_reward, const eosio::asset &bonus_reward);
|
||||
|
||||
/**
|
||||
* @brief Обновляет CRPS поля в проекте для координаторов при добавлении наград
|
||||
*/
|
||||
void update_coordinator_crps(eosio::name coopname, const checksum256 &project_hash, const eosio::asset &reward_amount);
|
||||
|
||||
/**
|
||||
* @brief Обновляет CRPS поля в проекте для вкладчиков при добавлении наград
|
||||
*/
|
||||
void update_contributor_crps(eosio::name coopname, const checksum256 &project_hash, const eosio::asset &reward_amount);
|
||||
void increment_contributors_crps_in_project(eosio::name coopname, const checksum256 &project_hash, const eosio::asset &reward_amount);
|
||||
|
||||
/**
|
||||
* @brief Обновляет сегмент участника - пересчитывает накопленные награды на основе CRPS
|
||||
|
||||
@@ -17,8 +17,8 @@ namespace Capital::Core {
|
||||
state.program_membership_available += amount;
|
||||
|
||||
if (total_shares.amount > 0) {
|
||||
// Рассчитываем награду на долю в базовых единицах
|
||||
int64_t delta = amount.amount / total_shares.amount;
|
||||
// Рассчитываем награду на долю
|
||||
double delta = static_cast<double>(amount.amount) / static_cast<double>(total_shares.amount);
|
||||
state.program_membership_cumulative_reward_per_share += delta;
|
||||
}
|
||||
|
||||
@@ -40,14 +40,19 @@ namespace Capital::Core {
|
||||
auto state = Capital::get_global_state(coopname);
|
||||
|
||||
// Считаем дельту CRPS
|
||||
int64_t current_crps = state.program_membership_cumulative_reward_per_share;
|
||||
int64_t last_crps = capital_wallet_opt.has_value() ? capital_wallet_opt->last_program_crps : 0;
|
||||
int64_t delta = current_crps - last_crps;
|
||||
double current_crps = state.program_membership_cumulative_reward_per_share;
|
||||
double last_crps = capital_wallet_opt.has_value() ? capital_wallet_opt->last_program_crps : 0.0;
|
||||
double delta = current_crps - last_crps;
|
||||
|
||||
if (delta > 0) {
|
||||
// Начисляем вознаграждение
|
||||
eosio::asset reward_amount = eosio::asset(share_balance.amount * delta, _root_govern_symbol);
|
||||
Capital::Wallets::upsert_capital_wallet(coopname, username, current_crps, reward_amount);
|
||||
if (delta > 0.0) {
|
||||
// Рассчитываем вознаграждение
|
||||
double pending_reward_double = static_cast<double>(share_balance.amount) * delta;
|
||||
int64_t pending_reward = static_cast<int64_t>(pending_reward_double);
|
||||
|
||||
if (pending_reward > 0) {
|
||||
eosio::asset reward_amount = eosio::asset(pending_reward, _root_govern_symbol);
|
||||
Capital::Wallets::upsert_capital_wallet(coopname, username, current_crps, reward_amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -234,7 +234,8 @@ namespace Capital::Core::Generation {
|
||||
|
||||
// Используем total_shares для расчета распределения на основе долей
|
||||
if (project -> membership.total_shares.amount > 0) {
|
||||
int64_t delta = membership_current_fund.amount / project->membership.total_shares.amount;
|
||||
double delta = static_cast<double>(membership_current_fund.amount) /
|
||||
static_cast<double>(project->membership.total_shares.amount);
|
||||
p.membership.cumulative_reward_per_share += delta;
|
||||
};
|
||||
});
|
||||
|
||||
@@ -8,16 +8,22 @@ namespace Capital {
|
||||
|
||||
/**
|
||||
* @brief Структура для данных CRPS (Cumulative Reward Per Share) распределения наград
|
||||
*
|
||||
* CRPS алгоритм с использованием double для точности:
|
||||
* 1. При добавлении награды: crps += награда / количество_долей
|
||||
* 2. При расчете награды: pending = доли_участника * (current_crps - last_crps)
|
||||
*
|
||||
* Все CRPS поля используют double для высокой точности без проблем с overflow
|
||||
*/
|
||||
struct crps_data {
|
||||
// Общее количество долей вкладчиков
|
||||
eosio::asset total_capital_contributors_shares = asset(0, _root_govern_symbol); ///< Общее количество долей вкладчиков в проекте
|
||||
// Общее количество долей вкладчиков (изменяется только при регистрации/обновлении долей участников)
|
||||
eosio::asset total_capital_contributors_shares = asset(0, _root_govern_symbol); ///< Сумма всех capital_contributor_shares зарегистрированных вкладчиков
|
||||
|
||||
// Накопительные награды на долю
|
||||
int64_t author_base_cumulative_reward_per_share = 0; ///< Накопительная базовая награда на авторскую долю
|
||||
int64_t author_bonus_cumulative_reward_per_share = 0; ///< Накопительная бонусная награда на авторскую долю
|
||||
int64_t coordinator_cumulative_reward_per_share = 0; ///< Накопительная награда на координаторскую долю
|
||||
int64_t contributor_cumulative_reward_per_share = 0; ///< Накопительная награда на вкладчическую долю
|
||||
// Накопительные награды на долю (double для точности)
|
||||
double author_base_cumulative_reward_per_share = 0.0; ///< Накопительная базовая награда на авторскую долю
|
||||
double author_bonus_cumulative_reward_per_share = 0.0; ///< Накопительная бонусная награда на авторскую долю
|
||||
double coordinator_cumulative_reward_per_share = 0.0; ///< Накопительная награда на координаторскую долю (НЕ ИСПОЛЬЗУЕТСЯ)
|
||||
double contributor_cumulative_reward_per_share = 0.0; ///< Накопительная награда на вкладчическую долю
|
||||
};
|
||||
|
||||
} // namespace Capital
|
||||
@@ -209,4 +209,6 @@ inline void create_debt_agenda(
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace Capital::Debts
|
||||
@@ -31,7 +31,7 @@ namespace Capital {
|
||||
asset program_membership_funded = asset(0, _root_govern_symbol); ///< Общая сумма членских взносов по программе
|
||||
asset program_membership_available = asset(0, _root_govern_symbol); ///< Доступная сумма членских взносов по программе
|
||||
asset program_membership_distributed = asset(0, _root_govern_symbol); ///< Распределенная сумма членских взносов по программе
|
||||
int64_t program_membership_cumulative_reward_per_share; ///< Накопительное вознаграждение на долю в членских взносах
|
||||
double program_membership_cumulative_reward_per_share; ///< Накопительное вознаграждение на долю в членских взносах
|
||||
|
||||
config config; ///< Управляемая конфигурация контракта
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ using namespace eosio;
|
||||
|
||||
struct membership_crps {
|
||||
|
||||
int64_t cumulative_reward_per_share = 0; ///< Сумма накопленных членских взносов на одну долю вкладчика проекта
|
||||
double cumulative_reward_per_share = 0.0; ///< Сумма накопленных членских взносов на одну долю
|
||||
eosio::asset total_shares = asset(0, _root_govern_symbol); ///< Общая сумма долей всех участников в проекте (только сконвертированные в кошельки проекта)
|
||||
|
||||
eosio::asset funded = asset(0, _root_govern_symbol); ///< Общее количество поступивших членских взносов
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
namespace Capital::Memo {
|
||||
inline std::string get_debt_memo(eosio::name username) {
|
||||
return "Выдача ссуды для " + username.to_string();
|
||||
}
|
||||
|
||||
inline std::string get_invest_memo(uint64_t contributor_id) {
|
||||
return "Зачёт части целевого паевого взноса по программе 'Цифровой Кошелёк' в качестве паевого взноса по договору УХД с ID: " + std::to_string(contributor_id);
|
||||
|
||||
@@ -668,5 +668,19 @@ namespace Capital::Projects {
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Удаляет проект
|
||||
* @param coopname Имя кооператива
|
||||
* @param project_hash Хэш проекта
|
||||
*/
|
||||
inline void delete_project(eosio::name coopname, const checksum256 &project_hash) {
|
||||
auto exist_project = get_project_or_fail(coopname, project_hash);
|
||||
|
||||
project_index projects(_capital, coopname.value);
|
||||
auto project_itr = projects.find(exist_project.id);
|
||||
|
||||
projects.erase(project_itr);
|
||||
}
|
||||
|
||||
}// namespace Project
|
||||
@@ -135,9 +135,9 @@ inline void create_result_for_participant(eosio::name coopname, const checksum25
|
||||
|
||||
|
||||
/**
|
||||
* @brief Удаляет объект результата и возвращает статус сегмента в ready
|
||||
* @brief Удаляет объект результата
|
||||
*/
|
||||
inline void delete_result_and_reset_segment(eosio::name coopname, const checksum256 &project_hash,
|
||||
inline void delete_result(eosio::name coopname, const checksum256 &project_hash,
|
||||
eosio::name username) {
|
||||
// Удаляем объект результата
|
||||
auto result_opt = get_result_by_project_and_username(coopname, project_hash, username);
|
||||
@@ -149,19 +149,6 @@ inline void delete_result_and_reset_segment(eosio::name coopname, const checksum
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Удаляет объект результата после успешного принятия
|
||||
*/
|
||||
inline void delete_result(eosio::name coopname, const checksum256 &project_hash,
|
||||
eosio::name username) {
|
||||
auto result_opt = get_result_by_project_and_username(coopname, project_hash, username);
|
||||
if (result_opt.has_value()) {
|
||||
result_index results(_capital, coopname.value);
|
||||
auto result_itr = results.find(result_opt->id);
|
||||
results.erase(result_itr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обновляет статус результата
|
||||
*/
|
||||
@@ -195,14 +182,14 @@ inline void set_result_authorization(eosio::name coopname, const checksum256 &re
|
||||
/**
|
||||
* @brief Устанавливает одобренное заявление результата
|
||||
*/
|
||||
inline void set_result_approved_statement(eosio::name coopname, const checksum256 &result_hash, const document2 &approved_statement, eosio::name payer) {
|
||||
inline void set_result_approved_statement(eosio::name coopname, const checksum256 &result_hash, const document2 &approved_statement) {
|
||||
auto exist_result = get_result(coopname, result_hash);
|
||||
eosio::check(exist_result.has_value(), "Объект результата не найден");
|
||||
|
||||
result_index results(_capital, coopname.value);
|
||||
auto result = results.find(exist_result->id);
|
||||
|
||||
results.modify(result, payer, [&](auto &r){
|
||||
|
||||
results.modify(result, _capital, [&](auto &r){
|
||||
r.statement = approved_statement;
|
||||
});
|
||||
}
|
||||
@@ -217,7 +204,7 @@ inline void set_result_act1(eosio::name coopname, const checksum256 &result_hash
|
||||
result_index results(_capital, coopname.value);
|
||||
auto result = results.find(exist_result->id);
|
||||
|
||||
results.modify(result, _capital, [&](auto &r){
|
||||
results.modify(result, coopname, [&](auto &r){
|
||||
r.act = act;
|
||||
});
|
||||
}
|
||||
@@ -232,7 +219,7 @@ inline void set_result_act2(eosio::name coopname, const checksum256 &result_hash
|
||||
result_index results(_capital, coopname.value);
|
||||
auto result = results.find(exist_result->id);
|
||||
|
||||
results.modify(result, _capital, [&](auto &r){
|
||||
results.modify(result, coopname, [&](auto &r){
|
||||
r.act = act;
|
||||
});
|
||||
}
|
||||
@@ -241,18 +228,36 @@ inline void set_result_act2(eosio::name coopname, const checksum256 &result_hash
|
||||
* @brief Отправляет результат на рассмотрение в совет
|
||||
*/
|
||||
inline void send_result_to_soviet(eosio::name coopname, eosio::name username, const checksum256 &result_hash, const document2 &approved_statement) {
|
||||
::Soviet::create_agenda(
|
||||
_capital,
|
||||
coopname,
|
||||
username,
|
||||
Names::SovietActions::CREATE_RESULT,
|
||||
result_hash,
|
||||
_capital,
|
||||
Names::Capital::AUTHORIZE_RESULT,
|
||||
Names::Capital::DECLINE_RESULT,
|
||||
approved_statement,
|
||||
std::string("")
|
||||
);
|
||||
::Soviet::create_agenda(
|
||||
_capital,
|
||||
coopname,
|
||||
username,
|
||||
Names::SovietActions::CREATE_RESULT,
|
||||
result_hash,
|
||||
_capital,
|
||||
Names::Capital::AUTHORIZE_RESULT,
|
||||
Names::Capital::DECLINE_RESULT,
|
||||
approved_statement,
|
||||
std::string("")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отправляет результат на одобрение председателем
|
||||
*/
|
||||
inline void send_result_for_approval(eosio::name coopname, eosio::name username, const checksum256 &result_hash, const document2 &statement) {
|
||||
::Soviet::create_approval(
|
||||
_capital,
|
||||
coopname,
|
||||
username,
|
||||
statement,
|
||||
Names::Capital::CREATE_RESULT,
|
||||
result_hash,
|
||||
_capital,
|
||||
Names::Capital::APPROVE_RESULT,
|
||||
Names::Capital::DECLINE_RESULT,
|
||||
std::string("")
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace Results
|
||||
|
||||
@@ -69,15 +69,13 @@ namespace Capital::Segments {
|
||||
eosio::asset property_base = asset(0, _root_govern_symbol); ///< Стоимость внесенного имущества участника
|
||||
|
||||
// CRPS поля для масштабируемого распределения наград
|
||||
int64_t last_author_base_reward_per_share = 0; ///< Последняя зафиксированная базовая награда на долю для авторов
|
||||
int64_t last_author_bonus_reward_per_share = 0; ///< Последняя зафиксированная бонусная награда на долю для авторов
|
||||
int64_t last_coordinator_reward_per_share = 0; ///< Последняя зафиксированная награда на долю для координаторов
|
||||
int64_t last_contributor_reward_per_share = 0; ///< Последняя зафиксированная награда на долю для вкладчиков
|
||||
double last_author_base_reward_per_share = 0.0; ///< Последняя зафиксированная базовая награда на долю для авторов
|
||||
double last_author_bonus_reward_per_share = 0.0; ///< Последняя зафиксированная бонусная награда на долю для авторов
|
||||
double last_contributor_reward_per_share = 0.0; ///< Последняя зафиксированная награда на долю для вкладчиков
|
||||
|
||||
// Доли в программе и проекте
|
||||
eosio::asset capital_contributor_shares = asset(0, _root_govern_symbol); ///< Количество долей вкладчика в программе капитализации
|
||||
eosio::asset project_contributor_shares = asset(0, _root_govern_symbol); ///< Текущая доля участника в проекте (базовые_вклады - конвертации - ссуды)
|
||||
|
||||
|
||||
// Последняя известная сумма инвестиций в проекте для расчета provisional_amount
|
||||
eosio::asset last_known_invest_pool = asset(0, _root_govern_symbol); ///< Последняя известная сумма инвестиций в проекте
|
||||
|
||||
@@ -271,31 +269,28 @@ inline bool is_voting_participant(eosio::name coopname, const checksum256 &proje
|
||||
* @return true если сегмент обновлен
|
||||
*/
|
||||
inline bool is_segment_updated(eosio::name coopname, const checksum256 &project_hash, eosio::name username) {
|
||||
auto segment_opt = get_segment(coopname, project_hash, username);
|
||||
if (!segment_opt.has_value()) {
|
||||
return false; // Сегмент не найден
|
||||
}
|
||||
auto segment = get_segment_or_fail(coopname, project_hash, username, "Сегмент пайщика не найден");
|
||||
|
||||
auto project = Capital::Projects::get_project_or_fail(coopname, project_hash);
|
||||
|
||||
// Проверяем актуальность CRPS для каждой роли
|
||||
bool author_updated = (!segment_opt->is_author) ||
|
||||
(segment_opt->last_author_base_reward_per_share == project.crps.author_base_cumulative_reward_per_share &&
|
||||
segment_opt->last_author_bonus_reward_per_share == project.crps.author_bonus_cumulative_reward_per_share);
|
||||
bool author_updated = (!segment.is_author) ||
|
||||
(segment.last_author_base_reward_per_share == project.crps.author_base_cumulative_reward_per_share &&
|
||||
segment.last_author_bonus_reward_per_share == project.crps.author_bonus_cumulative_reward_per_share);
|
||||
|
||||
// Координаторы используют пропорциональное распределение на основе coordinators_investment_pool
|
||||
bool coordinator_updated = (!segment_opt->is_coordinator) ||
|
||||
(segment_opt->last_known_coordinators_investment_pool == project.fact.coordinators_investment_pool);
|
||||
bool coordinator_updated = (!segment.is_coordinator) ||
|
||||
(segment.last_known_coordinators_investment_pool == project.fact.coordinators_investment_pool);
|
||||
|
||||
bool contributor_updated = (!segment_opt->is_contributor) ||
|
||||
(segment_opt->last_contributor_reward_per_share == project.crps.contributor_cumulative_reward_per_share);
|
||||
bool contributor_updated = (!segment.is_contributor) ||
|
||||
(segment.last_contributor_reward_per_share == project.crps.contributor_cumulative_reward_per_share);
|
||||
|
||||
// Проверяем актуальность инвестиционного пула для расчета provisional_amount (нужно всем ролям)
|
||||
bool invest_pool_updated = (segment_opt->last_known_invest_pool == project.fact.invest_pool);
|
||||
bool invest_pool_updated = (segment.last_known_invest_pool == project.fact.invest_pool);
|
||||
|
||||
// Проверяем актуальность базового пула создателей для корректного расчета использования инвестиций (нужно только инвесторам)
|
||||
bool creators_base_pool_updated = (!segment_opt->is_investor) ||
|
||||
(segment_opt->last_known_creators_base_pool == project.fact.creators_base_pool);
|
||||
bool creators_base_pool_updated = (!segment.is_investor) ||
|
||||
(segment.last_known_creators_base_pool == project.fact.creators_base_pool);
|
||||
|
||||
return author_updated && coordinator_updated && contributor_updated && invest_pool_updated && creators_base_pool_updated;
|
||||
}
|
||||
@@ -427,7 +422,7 @@ inline void update_segment_conversion(eosio::name coopname, const checksum256 &p
|
||||
* @brief Объединенная функция: обновляет сегмент после принятия результата и пересчитывает доли участника
|
||||
* Оптимизированная версия для избежания двойного обновления одной записи
|
||||
*/
|
||||
inline void update_segment_after_result_contribution_with_shares(eosio::name coopname, const checksum256 &project_hash,
|
||||
inline void update_segment_after_result_contribution(eosio::name coopname, const checksum256 &project_hash,
|
||||
eosio::name username,
|
||||
eosio::asset debt_settled_amount = asset(0, _root_govern_symbol)) {
|
||||
segments_index segments(_capital, coopname.value);
|
||||
@@ -445,18 +440,6 @@ inline void update_segment_after_result_contribution_with_shares(eosio::name coo
|
||||
if (debt_settled_amount.amount > 0) {
|
||||
s.debt_settled += debt_settled_amount;
|
||||
}
|
||||
|
||||
// Пересчитываем доли участника: базовые_вклады - конвертации - ссуды
|
||||
eosio::asset base_contributions = s.creator_base + s.author_base + s.coordinator_base + s.property_base;
|
||||
//TODO: тут кажется это не нужно т.к. суммы равны нулю - проверить
|
||||
eosio::asset conversions = s.converted_to_wallet + s.converted_to_capital;
|
||||
|
||||
s.project_contributor_shares = base_contributions - conversions - s.debt_amount;
|
||||
|
||||
// Доли не могут быть отрицательными
|
||||
if (s.project_contributor_shares.amount < 0) {
|
||||
s.project_contributor_shares = asset(0, _root_govern_symbol);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -515,7 +498,38 @@ inline void decrease_debt_amount(eosio::name coopname, uint64_t segment_id, eosi
|
||||
s.debt_amount -= amount;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Удаляет сегмент участника
|
||||
* @param coopname Имя кооператива
|
||||
* @param project_hash Хэш проекта
|
||||
* @param username Имя пользователя
|
||||
*/
|
||||
inline void remove_segment(eosio::name coopname, const checksum256 &project_hash, eosio::name username) {
|
||||
segments_index segments(_capital, coopname.value);
|
||||
auto idx = segments.get_index<"byprojuser"_n>();
|
||||
auto key = combine_checksum_ids(project_hash, username);
|
||||
auto segment_itr = idx.find(key);
|
||||
|
||||
eosio::check(segment_itr != idx.end(), "Сегмент участника не найден");
|
||||
|
||||
idx.erase(segment_itr);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Проверяет наличие сегментов в проекте
|
||||
* @param coopname Имя кооператива
|
||||
* @param project_hash Хэш проекта
|
||||
* @return true если есть сегменты, false если нет
|
||||
*/
|
||||
inline bool has_project_segments(eosio::name coopname, const checksum256 &project_hash) {
|
||||
segments_index segments(_capital, coopname.value);
|
||||
auto idx = segments.get_index<"byproject"_n>();
|
||||
auto it = idx.lower_bound(project_hash);
|
||||
|
||||
return it != idx.end() && it->project_hash == project_hash;
|
||||
}
|
||||
|
||||
} // namespace Capital::Segments
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Capital {
|
||||
uint64_t id; ///< ID кошелька (внутренний ключ)
|
||||
eosio::name coopname; ///< Имя кооператива
|
||||
eosio::name username; ///< Имя пользователя
|
||||
int64_t last_program_crps = 0; ///< Последнее значение программной CRPS для членских взносов
|
||||
double last_program_crps = 0.0; ///< Последнее значение программной CRPS для членских взносов
|
||||
eosio::asset capital_available = asset(0, _root_govern_symbol); ///< Доступные средства от членских взносов для вывода
|
||||
|
||||
uint64_t primary_key() const { return id; } ///< Первичный ключ (1)
|
||||
@@ -38,7 +38,7 @@ namespace Capital {
|
||||
checksum256 project_hash; ///< Хэш проекта
|
||||
eosio::name username; ///< Имя пользователя
|
||||
eosio::asset shares = asset(0, _root_govern_symbol); ///< Доли участника в проекте для получения членских взносов
|
||||
int64_t last_membership_reward_per_share = 0; ///< Последнее значение CRPS для членских взносов
|
||||
double last_membership_reward_per_share = 0.0; ///< Последнее значение CRPS для членских взносов
|
||||
eosio::asset membership_available = asset(0, _root_govern_symbol); ///< Доступные средства от членских взносов для вывода
|
||||
|
||||
uint64_t primary_key() const { return id; } ///< Первичный ключ (1)
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace Names {
|
||||
// Коллбэки для результатов
|
||||
constexpr eosio::name AUTHORIZE_RESULT = "authrslt"_n;
|
||||
constexpr eosio::name DECLINE_RESULT = "declrslt"_n;
|
||||
constexpr eosio::name APPROVE_RESULT = "approverslt"_n;
|
||||
|
||||
// Коллбэки для платежей
|
||||
constexpr eosio::name CONFIRM_EXPENSE_PAYMENT = "exppaycnfrm"_n;
|
||||
@@ -110,5 +111,6 @@ namespace Names {
|
||||
constexpr eosio::name CREATE_WITHDRAW_1 = "createwthd1"_n; // акцепт возврата из задания
|
||||
constexpr eosio::name CREATE_WITHDRAW_2 = "createwthd2"_n; // акцепт возврата из проекта
|
||||
constexpr eosio::name CREATE_WITHDRAW_3 = "createwthd3"_n; // акцепт возврата из программы
|
||||
constexpr eosio::name CREATE_RESULT = "createresult"_n; // акцепт результата
|
||||
}
|
||||
}
|
||||
@@ -86,9 +86,9 @@ public:
|
||||
}
|
||||
|
||||
static void validate_asset(const eosio::asset& amount) {
|
||||
check(amount.symbol == _root_govern_symbol, "Invalid token symbol");
|
||||
check(amount.is_valid(), "Invalid asset");
|
||||
check(amount.amount > 0, "Amount must be positive");
|
||||
check(amount.symbol == _root_govern_symbol, "Неверный символ токена");
|
||||
check(amount.is_valid(), "Неверный актив");
|
||||
check(amount.amount >= 0, "Сумма должна быть неотрицательной");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -58,7 +58,6 @@ export interface IApprovecmmt {
|
||||
coopname: IName
|
||||
master: IName
|
||||
commit_hash: IChecksum256
|
||||
empty_document: IDocument2
|
||||
}
|
||||
|
||||
export interface IApprovedebt {
|
||||
@@ -395,12 +394,6 @@ export interface IDebtpaydcln {
|
||||
reason: string
|
||||
}
|
||||
|
||||
export interface IDeclineapprv {
|
||||
coopname: IName
|
||||
result_hash: IChecksum256
|
||||
decline_reason: IDocument2
|
||||
}
|
||||
|
||||
export interface IDeclinecmmt {
|
||||
coopname: IName
|
||||
master: IName
|
||||
@@ -447,7 +440,7 @@ export interface IDeclpinv {
|
||||
export interface IDeclrslt {
|
||||
coopname: IName
|
||||
result_hash: IChecksum256
|
||||
decision: IDocument2
|
||||
reason: string
|
||||
}
|
||||
|
||||
export interface IDelproject {
|
||||
@@ -704,6 +697,7 @@ export interface IPushrslt {
|
||||
contribution_amount: IAsset
|
||||
debt_amount: IAsset
|
||||
statement: IDocument2
|
||||
debt_hashes: IChecksum256[]
|
||||
}
|
||||
|
||||
export interface IRefreshprog {
|
||||
@@ -843,7 +837,7 @@ export interface ISignact1 {
|
||||
|
||||
export interface ISignact2 {
|
||||
coopname: IName
|
||||
username: IName
|
||||
chairman: IName
|
||||
result_hash: IChecksum256
|
||||
act: IDocument2
|
||||
}
|
||||
|
||||
@@ -394,12 +394,6 @@ export interface IDebtpaydcln {
|
||||
reason: string
|
||||
}
|
||||
|
||||
export interface IDeclineapprv {
|
||||
coopname: IName
|
||||
result_hash: IChecksum256
|
||||
decline_reason: IDocument2
|
||||
}
|
||||
|
||||
export interface IDeclinecmmt {
|
||||
coopname: IName
|
||||
master: IName
|
||||
@@ -446,7 +440,7 @@ export interface IDeclpinv {
|
||||
export interface IDeclrslt {
|
||||
coopname: IName
|
||||
result_hash: IChecksum256
|
||||
decision: IDocument2
|
||||
reason: string
|
||||
}
|
||||
|
||||
export interface IDelproject {
|
||||
@@ -843,7 +837,7 @@ export interface ISignact1 {
|
||||
|
||||
export interface ISignact2 {
|
||||
coopname: IName
|
||||
username: IName
|
||||
chairman: IName
|
||||
result_hash: IChecksum256
|
||||
act: IDocument2
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user