Merge branch 'testnet'

This commit is contained in:
Alex Ant
2025-04-29 16:21:40 +05:00
1176 changed files with 78891 additions and 17959 deletions
Vendored
BIN
View File
Binary file not shown.
+150
View File
@@ -0,0 +1,150 @@
---
description:
globs: components/controller/**
alwaysApply: false
---
# NestJS Controller и Чистая архитектура
## Основные принципы
1. Чистая архитектура - главный архитектурный подход. Пиши код с комментариями.
2. Типы IName, IChecksum256, ITimePointSec - это просто строки.
3. Домен должен быть полностью изолирован от инфраструктурных деталей.
4. Направление зависимостей - внутрь (к ядру, домену).
## Структура проекта
- `domain/` - доменный слой (бизнес-логика, независимая от инфраструктуры)
- `infrastructure/` - инфраструктурный слой (адаптеры к внешним системам)
- `modules/` - слой приложения (DTO, резолверы, сервисы)
## Слои и их взаимодействие
1. **Домен**:
- Содержит чистые доменные интерфейсы и реализованные на них сущности
- НЕ должен зависеть от `cooptypes` (инфраструктурный контракт)
- Использует собственные доменные типы (например, Date вместо строковых timestamp)
- Интерфейсы портов описывают взаимодействие с внешними системами
2. **Инфраструктура**:
- Содержит адаптеры к внешним системам (блокчейн, БД и т.д.)
- Осуществляет преобразование между доменными и инфраструктурными типами
- Общая логика преобразования выносится в утилитарные классы (например, `DomainToBlockchainUtils`)
3. **Модули (приложение)**:
- DTO имплементируют доменные интерфейсы напрямую
- Резолвер вызывает сервис, который принимает DTO
- Сервис передает объекты DTO в интерактор домена
## Поток данных
1. Резолвер принимает входные данные и передает их в сервис
2. Сервис вызывает интерактор домена, передавая ему объекты DTO (имплементирующие доменный интерфейс)
3. Интерактор выполняет бизнес-логику и взаимодействует с портами домена
4. Адаптеры (инфраструктурный слой) преобразуют доменные объекты в формат внешних систем (JSON для хранения мета-данных, ISO для дат и т.д.)
5. Адаптеры возвращают результаты в домен, домен возвращает данные в сервис, который возвращает их в резолвер
## Правила преобразования данных
1. Преобразование DTO → доменный объект: не требуется, если DTO имплементирует доменный интерфейс
2. Преобразование доменный объект → инфраструктурный тип: происходит в адаптерах
3. Преобразование документов, дат и других сложных объектов: используются утилитарные классы уровня инфраструктуры
## Работа с типами данных
1. **Даты**:
- В доменных интерфейсах используется тип `Date`
- В DTO используется тип `Date` с декораторами `@IsDate()` и `@Type(() => Date)`
- В инфраструктурном слое происходит преобразование `Date` → `string` (ISO формат)
```typescript
// Доменный интерфейс
interface MeetDomainInterface {
open_at: Date;
}
// DTO
@Field(() => Date)
@IsDate()
@Type(() => Date)
open_at!: Date;
// Адаптер
const blockchainData = {
open_at: domainToBlockchainUtils.convertDateToBlockchainFormat(data.open_at)
};
```
2. **Документы**:
- В доменных интерфейсах используется собственный тип `SignedDocumentDomainInterface<T>`
- В DTO используется соответствующий DTO-класс (например, `SignedDigitalDocumentInputDTO`)
- В инфраструктурном слое происходит преобразование между форматами (meta-поля в JSON и т.д.)
## Типовые ошибки
1. **Нарушение изоляции домена**: домен должен быть изолирован от инфраструктуры. Не используйте импорты из `cooptypes` в доменных интерфейсах.
```typescript
// Неправильно
import { MeetContract } from 'cooptypes';
export type VoteDomainInterface = MeetContract.Actions.Vote.IInput;
// Правильно
export interface VoteDomainInterface {
coopname: string;
hash: string;
// ... доменные поля
}
```
2. **Преобразование в неправильном слое**: преобразование доменных объектов в инфраструктурные типы должно происходить в адаптерах, а не в доменном слое или сервисе.
```typescript
// Неправильно (в доменном интеракторе)
async vote(data: VoteDomainInterface) {
const blockchainData = { ...data, meta: JSON.stringify(data.meta) };
// ...
}
// Правильно (в адаптере)
async vote(data: VoteDomainInterface) {
const blockchainData = this.convertToBlockchainFormat(data);
// ...
}
```
3. **Дублирование логики преобразования**: общая логика преобразования должна быть вынесена в утилитарные классы.
```typescript
// Неправильно (дублирование в разных адаптерах)
// В MeetAdapter
private convertDoc(doc) { return { ...doc, meta: JSON.stringify(doc.meta) }; }
// В BranchAdapter
private convertDoc(doc) { return { ...doc, meta: JSON.stringify(doc.meta) }; }
// Правильно (общий утилитарный класс)
// DomainToBlockchainUtils
convertSignedDocumentToBlockchainFormat(doc) {
return { ...doc, meta: JSON.stringify(doc.meta) };
}
```
## Примеры
```typescript
// Доменный интерфейс - чистый, без зависимостей от инфраструктуры
export interface VoteOnAnnualGeneralMeetInputDomainInterface {
coopname: string;
hash: string;
member: string;
ballot: VoteItemInputDomainInterface[];
}
// DTO имплементирует доменный интерфейс напрямую
@InputType('VoteOnAnnualGeneralMeetInput')
export class VoteOnAnnualGeneralMeetInputDTO implements VoteOnAnnualGeneralMeetInputDomainInterface {
@Field(() => String)
@IsString()
coopname!: string;
// ... остальные поля
}
// Адаптер преобразует доменный объект в инфраструктурный тип
async vote(data: VoteOnAnnualGeneralMeetInputDomainInterface): Promise<TransactionResult> {
// Преобразуем доменный объект в инфраструктурный тип
const blockchainData: MeetContract.Actions.Vote.IInput = {
coopname: data.coopname,
hash: data.hash,
// ... преобразование других полей
};
// ... отправка данных во внешнюю систему
}
```
@@ -0,0 +1,41 @@
---
description:
globs:
alwaysApply: false
---
Бэкенд реализован здесь components/controller, sdk здесь components/sdk.
Пример создания Query к API через SDK:
``` ts
async function loadBranches(data: IGetBranchesInput): Promise<IBranch[]> {
const { [Queries.Branches.GetBranches.name]: output } = await client.Query(
Queries.Branches.GetBranches.query,
{
variables: {
data
}
}
);
return output;
}
```
Такие запросы обычно хранятся в папке Entities и вызываются из его store.
Пример мутации:
``` ts
async function selectBranch(data: ISelectBranchInput): Promise<boolean>{
const {[Mutations.Branches.SelectBranch.name]: result} = await client.Mutation(Mutations.Branches.SelectBranch.mutation, {variables: {
data
}})
return result
}
```
Мутации обычно храним в папке features.
Как можешь обратить внимание, все мутации и запросы строятся по одному шаблону. Вся необходимая информация уже есть в SDK.
+12
View File
@@ -0,0 +1,12 @@
---
description:
globs:
alwaysApply: true
---
# DESKTOP
- Пишем код фронтенда в архитектуре Feature Sliced Design (FSD).
- Стиль шаблонов PUG на composite API.
- Всегда создаём index.ts файлы для vue компонент.
- Создавай индексные файлы для vue через export {default as NAME} from './where', а на уровне выше, если это необходимо, делай export * from './ui'
+59
View File
@@ -0,0 +1,59 @@
---
description:
globs: components/sdk/**
alwaysApply: false
---
# Руководство по работе с GraphQL Zeus в SDK
## Основной процесс
1. **Анализ DTO бэкенда**:
- Изучите структуру DTO классов (`@ObjectType`) в бэкенде
- Обратите внимание на имена типов в декораторах `@ObjectType('ИмяТипа')`
- Отметьте поля, связи и вложенные объекты
2. **Создание селекторов**:
- Для каждого DTO создайте соответствующий селектор с именем `raw<ИмяТипа>Selector`
- Селектор - это объект, где ключи соответствуют полям DTO, а значения - `true`
- Для вложенных объектов используйте вложенные селекторы: `field: nestedSelector`
- Для списков используйте один селектор без массива: `items: itemSelector`
3. **Валидация селекторов**:
- Для каждого селектора создайте проверку типа:
```typescript
const _validate: MakeAllFieldsRequired<ValueTypes['ТочноеИмяГрафКьЭлТипа']> = rawSelector
```
- Имя типа должно точно совпадать с именем в декораторе `@ObjectType`
4. **Экспорт селекторов**:
- Создайте финальный селектор с помощью функции `Selector`:
```typescript
export const typeSelector = Selector('ТочноеИмяГрафКьЭлТипа')(rawTypeSelector)
```
- Экспортируйте сырой селектор для переиспользования
- Экспортируйте тип модели: `export type modelType = ModelTypes['ТочноеИмяГрафКьЭлТипа']`
5. **Создание запросов/мутаций**:
- Используйте селекторы в запросах и мутациях:
```typescript
export const query = Selector('Query')({
queryName: [{ data: $('data', 'ТочноеИмяВходногоТипа!') }, exportedSelector]
})
```
- Для параметров используйте оператор `$` с точным именем входного типа
- Создайте интерфейс входных данных:
```typescript
export interface IInput {
data: ModelTypes['ТочноеИмяВходногоТипа']
}
```
## Особенности работы
- **Документы**: всегда сохраняйте структуру `{ hash, signatures, rawDocument }`
- **Сложные DTO**: разбивайте на атомарные селекторы и комбинируйте их
- **Типы в Zeus**: часто отличаются от имен классов в бэкенде, всегда проверяйте в `schema.gql`
- **Массивы**: Zeus автоматически обрабатывает массивы, не используйте `[selector]`
Это руководство поможет правильно структурировать работу с SDK и избежать типичных ошибок при работе с Zeus.
+3
View File
@@ -2,3 +2,6 @@ node_modules/
lerna-debug.log
components/controller/graph.png
blockchain-data/
scripts/changelog-prompt.md
scripts/changelog-release.md
scripts/release-info.md
+5 -3
View File
@@ -4,11 +4,13 @@
"editor.formatOnSave": true,
"editor.tabCompletion": "onlySnippets",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint":"explicit"
},
"eslint.run": "onType",
"eslint.validate": ["javascript", "typescript", "vue"],
// "typescript.tsdk": "node_modules/typescript/lib",
"i18n-ally.localesPaths": ["src/i18n"],
"typescript.format.enable": true,
"typescript.format.indentSwitchCase": false,
"typescript.validate.enable": true,
"typescript.validate.enable": true
}
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.
+1
View File
@@ -11,6 +11,7 @@
"scripts": {
"enter": "./scripts/enter.sh",
"cli": "esno src/index.ts",
"deploy": "esno src/index.ts deploy",
"cleos": "esno src/index.ts cleos",
"unlock": "esno src/index.ts unlock",
"boot": "esno src/index.ts boot",
+37 -65
View File
@@ -4,6 +4,7 @@ import { JsSignatureProvider } from 'eosjs/dist/eosjs-jssig'
import { Api, JsonRpc, Serialize } from 'eosjs'
import {
DraftContract,
GatewayContract,
RegistratorContract,
SovietContract,
SystemContract,
@@ -513,8 +514,8 @@ export default class Blockchain {
console.log('Новый кооператив пре-иниализирован: ', params)
}
// TODO change registerOrganization to registerCooperative
async registerOrganization(
// TODO change registerCooperative to registerCooperative
async registerCooperative(
params: RegistratorContract.Actions.RegisterCooperative.IRegisterCooperative,
) {
await this.update_pass_instance()
@@ -573,7 +574,7 @@ export default class Blockchain {
)
}
async registerAccount2(
async createAccount(
params: RegistratorContract.Actions.CreateAccount.ICreateAccount,
) {
await this.update_pass_instance()
@@ -586,7 +587,7 @@ export default class Blockchain {
name: RegistratorContract.Actions.CreateAccount.actionName,
authorization: [
{
actor: params.registrator,
actor: params.coopname,
permission: 'active',
},
],
@@ -618,7 +619,7 @@ export default class Blockchain {
name: RegistratorContract.Actions.RegisterUser.actionName,
authorization: [
{
actor: params.registrator,
actor: params.coopname,
permission: 'active',
},
],
@@ -800,36 +801,39 @@ export default class Blockchain {
async exec(params: SovietContract.Actions.Decisions.Exec.IExec) {
await this.update_pass_instance()
await this.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Decisions.Exec.actionName,
authorization: [
{
actor: params.executer,
permission: 'active',
try {
await this.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Decisions.Exec.actionName,
authorization: [
{
actor: params.executer,
permission: 'active',
},
],
data: {
...params,
},
],
data: {
...params,
},
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
}
catch (e) {
console.dir(e, { depth: null })
}
console.log('Решение исполнено: ', params)
}
async joinCoop(
params: RegistratorContract.Actions.JoinCooperative.IJoinCooperative,
async ConfirmPayment(
params: GatewayContract.Actions.CompleteIncome.ICompeteIncome,
) {
await this.update_pass_instance()
@@ -837,11 +841,11 @@ export default class Blockchain {
{
actions: [
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.JoinCooperative.actionName,
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteIncome.actionName,
authorization: [
{
actor: params.registrator,
actor: params.coopname,
permission: 'active',
},
],
@@ -985,36 +989,4 @@ export default class Blockchain {
console.log('Программа установлена: ', params)
}
async makeCoagreement(
params: SovietContract.Actions.Agreements.MakeCoagreement.IMakeCoagreement,
) {
await this.update_pass_instance()
await this.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Agreements.MakeCoagreement.actionName,
authorization: [
{
actor: params.administrator,
permission: 'active',
},
],
data: {
...params,
},
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
console.log('Программа установлена: ', params)
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ max-block-net-usage-threshold-bytes = 1024
verbose-http-errors = true
chain-state-history = true
trace-history = true
state-history-endpoint = 0.0.0.0:8080
state-history-endpoint = 0.0.0.0:8070
resource-monitor-space-threshold = 99
resource-monitor-not-shutdown-on-threshold-exceeded = true
wasm-runtime = eos-vm
+15
View File
@@ -75,4 +75,19 @@ export default [
path: path.resolve(process.cwd(), '../contracts/build/contracts/capital'),
target: 'capital',
},
{
name: 'wallet',
path: path.resolve(process.cwd(), '../contracts/build/contracts/wallet'),
target: 'wallet',
},
{
name: 'loan',
path: path.resolve(process.cwd(), '../contracts/build/contracts/loan'),
target: 'loan',
},
{
name: 'meet',
path: path.resolve(process.cwd(), '../contracts/build/contracts/meet'),
target: 'meet',
},
]
+12
View File
@@ -87,6 +87,18 @@ export default {
name: 'capital',
code_permissions_to: ['capital'],
},
{
name: 'loan',
code_permissions_to: ['loan'],
},
{
name: 'meet',
code_permissions_to: ['meet'],
},
{
name: 'wallet',
code_permissions_to: ['wallet'],
},
{
name: 'soviet',
code_permissions_to: ['soviet'],
+25 -71
View File
@@ -6,6 +6,7 @@ import type { Account, Contract, Keys } from '../types'
import config, { GOVERN_SYMBOL, SYMBOL } from '../configs'
import Blockchain from '../blockchain'
import { sendPostToCoopbackWithSecret, sleep } from '../utils'
import { generateRandomSHA256 } from '../utils/randomHash'
const test_hash
= '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777'
@@ -46,48 +47,6 @@ export class CooperativeClass {
})
}
async createAgreements(coopname: string) {
// console.log('создаём кооперативное соглашение/положение по кошельку')
await this.blockchain.makeCoagreement({
coopname,
administrator: coopname,
type: 'wallet',
draft_id: TCooperative.Registry.WalletAgreement.registry_id,
program_id: 1,
})
await this.blockchain.makeCoagreement({
coopname,
administrator: coopname,
type: 'signature',
draft_id: TCooperative.Registry.RegulationElectronicSignature.registry_id,
program_id: 0,
})
await this.blockchain.makeCoagreement({
coopname,
administrator: coopname,
type: 'user',
draft_id: TCooperative.Registry.UserAgreement.registry_id,
program_id: 0,
})
await this.blockchain.makeCoagreement({
coopname,
administrator: coopname,
type: 'privacy',
draft_id: TCooperative.Registry.PrivacyPolicy.registry_id,
program_id: 0,
})
await this.blockchain.makeCoagreement({
coopname,
administrator: coopname,
type: 'coopenomics',
draft_id: TCooperative.Registry.CoopenomicsAgreement.registry_id,
program_id: 0,
})
}
async createCooperative(username: string, keys?: Keys) {
const account = await this.blockchain.generateKeypair(
username,
@@ -96,8 +55,7 @@ export class CooperativeClass {
)
console.log('Регистрируем аккаунт')
await this.blockchain.registerAccount2({
registrator: config.provider,
await this.blockchain.createAccount({
coopname: config.provider,
referer: '',
username: account.username,
@@ -106,40 +64,22 @@ export class CooperativeClass {
})
console.log('Регистрируем аккаунт как пользователя')
const registration_hash = generateRandomSHA256()
await this.blockchain.registerUser({
registrator: config.provider,
coopname: config.provider,
braname: '',
username: account.username,
type: 'organization',
})
console.log('Переводим аккаунт в организации')
await this.blockchain.registerOrganization({
username: account.username,
coopname: account.username,
params: {
is_cooperative: true,
coop_type: 'conscoop',
announce: 'Тестовый кооператив',
description: 'Тестовое описание',
initial: `100.0000 ${config.token.govern_symbol}`,
minimum: `300.0000 ${config.token.govern_symbol}`,
org_initial: `1000.0000 ${config.token.govern_symbol}`,
org_minimum: `3000.0000 ${config.token.govern_symbol}`,
},
document,
statement: document,
registration_hash,
})
console.log('Отправляем заявление на вступление')
await this.blockchain.joinCoop({
registrator: config.provider_chairman,
await this.blockchain.ConfirmPayment({
coopname: config.provider,
username: account.username,
document,
braname: '',
income_hash: registration_hash,
})
console.log('Голосуем по решению в провайдере')
@@ -182,9 +122,23 @@ export class CooperativeClass {
},
})
console.log('создаём программы и соглашения новому кооперативу')
// await this.createPrograms(username)
await this.createAgreements(username)
console.log('Переводим аккаунт в кооператив')
await this.blockchain.registerCooperative({
username: account.username,
coopname: account.username,
params: {
is_cooperative: true,
coop_type: 'conscoop',
announce: 'Тестовый кооператив',
description: 'Тестовое описание',
initial: `100.0000 ${config.token.govern_symbol}`,
minimum: `300.0000 ${config.token.govern_symbol}`,
org_initial: `1000.0000 ${config.token.govern_symbol}`,
org_minimum: `3000.0000 ${config.token.govern_symbol}`,
},
document,
})
console.log('Переводим инициализационные токены')
await this.blockchain.transfer({ from: 'eosio', to: username, quantity: `100.0000 ${SYMBOL}`, memo: '' })
-1
View File
@@ -316,7 +316,6 @@ export async function startInfra() {
const cooperative = new CooperativeClass(blockchain)
await cooperative.createAgreements(config.provider)
await cooperative.createPrograms(config.provider)
console.log(`Арендуем ресурсы провайдеру`)
+3 -4
View File
@@ -28,7 +28,7 @@ export class CooperativeClass {
this.blockchain = blockchain
}
async createParticipant(username: string, keys?: Keys) {
async addUser(username: string, keys?: Keys) {
const account = await this.blockchain.generateKeypair(
username,
keys,
@@ -37,7 +37,6 @@ export class CooperativeClass {
console.log('Регистрируем аккаунт')
const data: RegistratorContract.Actions.AddUser.IAddUser = {
registrator: config.provider,
coopname: config.provider,
referer: '',
username: account.username,
@@ -115,12 +114,12 @@ export class CooperativeClass {
}
}
export async function createParticipant(username: string) {
export async function addUser(username: string) {
// инициализируем инстанс с ключами
const blockchain = new Blockchain(config.network, config.private_keys)
const cooperative = new CooperativeClass(blockchain)
await cooperative.createParticipant(username, {
await cooperative.addUser(username, {
// eslint-disable-next-line node/prefer-global/process
privateKey: process.env.EOSIO_PRV_KEY!,
// eslint-disable-next-line node/prefer-global/process
+63 -39
View File
@@ -4,7 +4,7 @@ import Blockchain from '../blockchain'
import config from '../configs'
import { getTotalRamUsage, globalRamStats } from '../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../utils/randomHash'
import { createParticipant } from '../init/participant'
import { addUser } from '../init/participant'
import { generateRandomUsername } from '../utils/randomUsername'
import { sleep } from '../utils'
import { processDecision } from './soviet/processDecision'
@@ -55,19 +55,19 @@ beforeAll(async () => {
investor1 = generateRandomUsername()
console.log('investor1: ', investor1)
await createParticipant(investor1)
await addUser(investor1)
tester1 = generateRandomUsername()
console.log('tester1: ', tester1)
await createParticipant(tester1)
await addUser(tester1)
tester2 = generateRandomUsername()
console.log('tester2: ', tester2)
await createParticipant(tester2)
await addUser(tester2)
tester3 = generateRandomUsername()
console.log('tester3: ', tester3)
await createParticipant(tester3)
await addUser(tester3)
// const { stdout } = await execa('esno', [CLI_PATH, 'boot'], { stdio: 'inherit' })
// expect(stdout).toContain('Boot process completed')
@@ -397,6 +397,8 @@ describe('тест контракта CAPITAL', () => {
application: 'voskhod',
project_hash: project1.project_hash,
result_hash: generateRandomSHA256(),
assignee: 'ant',
assignment: 'Задание #1: тут описание',
}
result1 = data
@@ -477,55 +479,56 @@ describe('тест контракта CAPITAL', () => {
const { finalResult, commitHash } = await commitToResult(blockchain, 'voskhod', result1.result_hash, result1.project_hash, tester1, tester1CommitHours)
commits.push(commitHash)
expect(finalResult.creators_amount).toBe('10000.0000 RUB')
expect(finalResult.creators_base).toBe('10000.0000 RUB')
expect(finalResult.creators_bonus).toBe('3820.0000 RUB')
expect(finalResult.authors_bonus).toBe('16180.0000 RUB')
expect(finalResult.generated_amount).toBe('30000.0000 RUB')
expect(finalResult.generated).toBe('30000.0000 RUB')
expect(finalResult.capitalists_bonus).toBe('48540.0000 RUB')
expect(finalResult.total_amount).toBe('78540.0000 RUB')
expect(finalResult.total).toBe('78540.0000 RUB')
})
it('добавить коммит создателя tester2 на 10 часов по 1000 RUB', async () => {
const { finalResult, commitHash } = await commitToResult(blockchain, 'voskhod', result1.result_hash, result1.project_hash, tester2, tester2CommitHours)
commits.push(commitHash)
expect(finalResult.creators_amount).toBe('20000.0000 RUB')
expect(finalResult.creators_base).toBe('20000.0000 RUB')
expect(finalResult.creators_bonus).toBe('7640.0000 RUB')
expect(finalResult.authors_bonus).toBe('32360.0000 RUB')
expect(finalResult.generated_amount).toBe('60000.0000 RUB')
expect(finalResult.generated).toBe('60000.0000 RUB')
expect(finalResult.capitalists_bonus).toBe('97080.0000 RUB')
expect(finalResult.total_amount).toBe('157080.0000 RUB')
expect(finalResult.total).toBe('157080.0000 RUB')
})
it('пишем заявление на возврат в кошелёк от tester1 на 10000 RUB', async () => {
const withdrawAmount = `${(tester1CommitHours * parseFloat(ratePerHour)).toFixed(4)} RUB`
console.log('commits: ', commits)
const { withdrawHash, transactionId } = await withdrawContribution(
blockchain,
'voskhod',
tester1,
result1.result_hash,
result1.project_hash,
[commits[0]],
withdrawAmount,
fakeDocument,
)
})
// TODO: заменить на получение ссуды, а возврат перенести ниже
// it('пишем заявление на возврат в кошелёк от tester1 на 10000 RUB', async () => {
// const withdrawAmount = `${(tester1CommitHours * parseFloat(ratePerHour)).toFixed(4)} RUB`
// console.log('commits: ', commits)
// const { withdrawHash, transactionId } = await withdrawContribution(
// blockchain,
// 'voskhod',
// tester1,
// result1.result_hash,
// result1.project_hash,
// [commits[0]],
// withdrawAmount,
// fakeDocument,
// )
// })
it('пишем заявление на возврат в кошелёк от tester2 на 10000 RUB', async () => {
const withdrawAmount = `${(tester2CommitHours * parseFloat(ratePerHour)).toFixed(4)} RUB`
// it('пишем заявление на возврат в кошелёк от tester2 на 10000 RUB', async () => {
// const withdrawAmount = `${(tester2CommitHours * parseFloat(ratePerHour)).toFixed(4)} RUB`
const { withdrawHash, transactionId } = await withdrawContribution(
blockchain,
'voskhod',
tester2,
result1.result_hash,
result1.project_hash,
[commits[1]],
withdrawAmount,
fakeDocument,
)
})
// const { withdrawHash, transactionId } = await withdrawContribution(
// blockchain,
// 'voskhod',
// tester2,
// result1.result_hash,
// result1.project_hash,
// [commits[1]],
// withdrawAmount,
// fakeDocument,
// )
// })
it('регистрируем расход на 1000 RUB', async () => {
const expenseAmount = '1000.0000 RUB'
@@ -605,15 +608,24 @@ describe('тест контракта CAPITAL', () => {
expect(blockchainResult.result_hash).toBe(result1.result_hash)
expect(blockchainResult.project_hash).toBe(project1.project_hash)
expect(blockchainResult.coopname).toBe('voskhod')
expect(blockchainResult.status).toBe('opened')
expect(blockchainResult.status).toBe('closed')
expect(blockchainResult.creators_amount_remain).toBe(blockchainResult.creators_amount)
expect(blockchainResult.creators_bonus_remain).toBe(blockchainResult.creators_bonus)
expect(blockchainResult.authors_bonus_remain).toBe(blockchainResult.authors_bonus)
expect(blockchainResult.capitalists_bonus_remain).toBe(blockchainResult.capitalists_bonus)
console.log('Результат после старта приёма: ', blockchainResult)
})
it('обновляем капитал первого создателя в результате', async () => {
const claim_hash = generateRandomSHA256()
const data: CapitalContract.Actions.CreateClaim.ICreateClaim = {
coopname: 'voskhod',
application: 'voskhod',
result_hash: result1.result_hash,
username: tester1,
claim_hash,
}
const result = await blockchain.api.transact(
@@ -656,11 +668,14 @@ describe('тест контракта CAPITAL', () => {
})
it('обновляем капитал второго создателя в результате', async () => {
const claim_hash = generateRandomSHA256()
const data: CapitalContract.Actions.CreateClaim.ICreateClaim = {
coopname: 'voskhod',
application: 'voskhod',
result_hash: result1.result_hash,
username: tester2,
claim_hash,
}
const result = await blockchain.api.transact(
@@ -703,11 +718,14 @@ describe('тест контракта CAPITAL', () => {
})
it('повторно обновляем капитал второго создателя в результате и ожидаем ошибку', async () => {
const claim_hash = generateRandomSHA256()
const data: CapitalContract.Actions.CreateClaim.ICreateClaim = {
coopname: 'voskhod',
application: 'voskhod',
result_hash: result1.result_hash,
username: tester2,
claim_hash,
}
try {
@@ -742,11 +760,14 @@ describe('тест контракта CAPITAL', () => {
})
it('обновляем капитал инвестора в результате', async () => {
const claim_hash = generateRandomSHA256()
const data: CapitalContract.Actions.CreateClaim.ICreateClaim = {
coopname: 'voskhod',
application: 'voskhod',
result_hash: result1.result_hash,
username: investor1,
claim_hash,
}
const result = await blockchain.api.transact(
@@ -789,11 +810,14 @@ describe('тест контракта CAPITAL', () => {
})
it('повторно обновляем капитал инвестора в результате и ожидаем ошибку', async () => {
const claim_hash = generateRandomSHA256()
const data: CapitalContract.Actions.CreateClaim.ICreateClaim = {
coopname: 'voskhod',
application: 'voskhod',
result_hash: result1.result_hash,
username: investor1,
claim_hash,
}
try {
@@ -28,7 +28,7 @@ export async function commitToResult(
resultHash,
2,
'sha256',
))[0] || { spend: '0.0000 RUB', commits_count: 0 }
))[0] || { spended: '0.0000 RUB', commits_count: 0 }
const prevProject = (await blockchain.getTableRows(
CapitalContract.contractName.production,
@@ -39,7 +39,7 @@ export async function commitToResult(
projectHash,
3,
'sha256',
))[0] || { spend: '0.0000 RUB' }
))[0] || { spended: '0.0000 RUB' }
const prevContributor = (await blockchain.getTableRows(
CapitalContract.contractName.production,
@@ -62,10 +62,9 @@ export async function commitToResult(
coopname,
application: coopname,
result_hash: resultHash,
creator,
commit_hash: commitHash,
contribution_statement: fakeDocument,
contributed_hours: spendHours,
username: creator,
}
console.log(`\n🚀 Отправка транзакции CreateCommit для результата ${resultHash}`)
@@ -104,16 +103,14 @@ export async function commitToResult(
console.log('🔍 Коммит после создания:', blockchainCommit)
expect(blockchainCommit).toBeDefined()
expect(blockchainCommit.commit_hash).toBe(commitHash)
expect(blockchainCommit.spend).toBe(totalSpended)
expect(blockchainCommit.spended).toBe(totalSpended)
expect(blockchainCommit.status).toBe('created')
// Утверждение коммита
const approveCommitData: CapitalContract.Actions.ApproveCommit.IApproveCommit = {
const approveCommitData: SovietContract.Actions.Approves.ConfirmApprove.IConfirmApprove = {
coopname,
application: coopname,
commit_hash: commitHash,
approver: 'ant',
approved_statement: fakeDocument,
approval_hash: commitHash,
approved_document: fakeDocument,
}
console.log(`\n✅ Подтверждение коммита ${commitHash}`)
@@ -121,8 +118,8 @@ export async function commitToResult(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.ApproveCommit.actionName,
account: SovietContract.contractName.production,
name: SovietContract.Actions.Approves.ConfirmApprove.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: approveCommitData,
},
@@ -149,86 +146,84 @@ export async function commitToResult(
'sha256',
))[0]
console.log('🔍 Коммит после утверждения:', blockchainCommit)
expect(blockchainCommit).toBeDefined()
expect(blockchainCommit.commit_hash).toBe(commitHash)
expect(blockchainCommit.status).toBe('approved')
// Коммит удаляем после утверждения
expect(blockchainCommit).toBeUndefined()
// Получение всех решений и выполнение последнего
const decisions = await blockchain.getTableRows(
SovietContract.contractName.production,
coopname,
'decisions',
1000,
)
const lastDecision = decisions[decisions.length - 1]
// // Получение всех решений и выполнение последнего
// const decisions = await blockchain.getTableRows(
// SovietContract.contractName.production,
// coopname,
// 'decisions',
// 1000,
// )
// const lastDecision = decisions[decisions.length - 1]
console.log(`\n📜 Выполнение последнего решения: ${lastDecision.id}`)
await processDecision(blockchain, lastDecision.id)
// console.log(`\n📜 Выполнение последнего решения: ${lastDecision.id}`)
// await processDecision(blockchain, lastDecision.id)
{
// Утверждение коммита
const act1Data: CapitalContract.Actions.SetAct1.ISetAct1 = {
coopname,
application: coopname,
username: creator,
commit_hash: commitHash,
act: fakeDocument,
}
// {
// // Утверждение коммита
// const act1Data: CapitalContract.Actions.SetAct1.ISetAct1 = {
// coopname,
// application: coopname,
// username: creator,
// commit_hash: commitHash,
// act: fakeDocument,
// }
console.log(`\n✅ Установка акта1 по коммиту ${commitHash}`)
const act1Result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.SetAct1.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: act1Data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
// console.log(`\n✅ Установка акта1 по коммиту ${commitHash}`)
// const act1Result = await blockchain.api.transact(
// {
// actions: [
// {
// account: CapitalContract.contractName.production,
// name: CapitalContract.Actions.SetAct1.actionName,
// authorization: [{ actor: coopname, permission: 'active' }],
// data: act1Data,
// },
// ],
// },
// {
// blocksBehind: 3,
// expireSeconds: 30,
// },
// )
getTotalRamUsage(act1Result)
expect(act1Result.transaction_id).toBeDefined()
}
// getTotalRamUsage(act1Result)
// expect(act1Result.transaction_id).toBeDefined()
// }
{
// Утверждение коммита
const act2Data: CapitalContract.Actions.SetAct2.ISetAct2 = {
coopname,
application: coopname,
username: creator,
commit_hash: commitHash,
act: fakeDocument,
}
// {
// // Утверждение коммита
// const act2Data: CapitalContract.Actions.SetAct2.ISetAct2 = {
// coopname,
// application: coopname,
// username: creator,
// commit_hash: commitHash,
// act: fakeDocument,
// }
console.log(`\n✅ Установка акта2 по коммиту ${commitHash}`)
const act2Result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.SetAct2.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: act2Data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
// console.log(`\n✅ Установка акта2 по коммиту ${commitHash}`)
// const act2Result = await blockchain.api.transact(
// {
// actions: [
// {
// account: CapitalContract.contractName.production,
// name: CapitalContract.Actions.SetAct2.actionName,
// authorization: [{ actor: coopname, permission: 'active' }],
// data: act2Data,
// },
// ],
// },
// {
// blocksBehind: 3,
// expireSeconds: 30,
// },
// )
getTotalRamUsage(act2Result)
expect(act2Result.transaction_id).toBeDefined()
}
// getTotalRamUsage(act2Result)
// expect(act2Result.transaction_id).toBeDefined()
// }
// Проверка результата после утверждения коммита
const finalResult = (await blockchain.getTableRows(
@@ -269,8 +264,8 @@ export async function commitToResult(
console.log('▶ Проект:', finalProject)
console.log('▶ Контрибьютор:', finalContributor)
// expect(parseFloat(finalResult.spend)).toBe(parseFloat(prevResult.spend) + parseFloat(totalSpended))
// expect(parseFloat(finalProject.spend)).toBe(parseFloat(prevProject.spend))
// expect(parseFloat(finalResult.spended)).toBe(parseFloat(prevResult.spended) + parseFloat(totalSpended))
// expect(parseFloat(finalProject.spended)).toBe(parseFloat(prevProject.spended))
// Проверка, что у контрибьютора увеличились contributed_hours и available
// expect(parseFloat(finalContributor.contributed_hours)).toBe(parseFloat(prevContributor.contributed_hours) + spendHours)
@@ -174,26 +174,24 @@ export async function registerExpense(
const blockchainWithdraw = (await blockchain.getTableRows(
GatewayContract.contractName.production,
coopname,
GatewayContract.Tables.Withdraws.tableName,
GatewayContract.Tables.Outcomes.tableName,
1,
expenseHash,
expenseHash,
3,
2,
'sha256',
))[0]
console.log('🔍 Вывод средств:', blockchainWithdraw)
expect(blockchainWithdraw).toBeDefined()
expect(blockchainWithdraw.withdraw_hash).toBe(expenseHash)
expect(blockchainWithdraw.outcome_hash).toBe(expenseHash)
expect(blockchainWithdraw.quantity).toBe(expenseAmount)
expect(blockchainWithdraw.status).toBe('authorized')
expect(blockchainWithdraw.status).toBe('pending')
// Завершение вывода
const completeWithdrawData: GatewayContract.Actions.CompleteWithdraw.ICompleteWithdraw = {
const completeWithdrawData: GatewayContract.Actions.CompleteOutcome.ICompleteOutcome = {
coopname,
application: '',
memo: '',
withdraw_hash: expenseHash,
outcome_hash: expenseHash,
}
console.log(`\n✅ Завершение вывода ${expenseHash}`)
@@ -202,7 +200,7 @@ export async function registerExpense(
actions: [
{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteWithdraw.actionName,
name: GatewayContract.Actions.CompleteOutcome.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: completeWithdrawData,
},
@@ -0,0 +1,57 @@
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { GatewayContract, RegistratorContract, SovietContract } from 'cooptypes'
import { sha256 } from 'eosjs/dist/eosjs-key-conversions'
import Blockchain from '../blockchain'
import config from '../configs'
import { getTotalRamUsage, globalRamStats } from '../utils/getTotalRamUsage'
import { addUser } from '../init/participant'
import { generateRandomUsername } from '../utils/randomUsername'
import { generateRandomSHA256 } from '../utils/randomHash'
import { signWalletAgreement } from './wallet/signWalletAgreement'
import { depositToWallet } from './wallet/depositToWallet'
import { processDecision } from './soviet/processDecision'
import { getParticipant } from './registrator/getParticipant'
import { registerUser } from './registrator/registerUser'
const blockchain = new Blockchain(config.network, config.private_keys)
let tester1: string
const walletProgramStates1: any[] = []
const fakeDocument = {
hash: '157192B276DA23CC84AB078FC8755C051C5F0430BF4802E55718221E6B76C777',
public_key: 'PUB_K1_5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzzEtUA4',
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
meta: '{}',
}
beforeAll(async () => {
await blockchain.update_pass_instance()
}, 500_000)
afterAll(() => {
console.log('\n📊 **RAM USAGE SUMMARY** 📊')
let totalGlobalRam = 0
for (const [key, ramUsed] of Object.entries(globalRamStats)) {
console.log(` ${key} = ${(ramUsed / 1024).toFixed(2)} kb`)
totalGlobalRam += ramUsed
}
console.log(`\n💾 **TOTAL RAM USED IN TESTS**: ${(totalGlobalRam / 1024).toFixed(2)} kb\n`)
})
describe('тест Registrator', () => {
it('регистируем пайщика 1', async () => {
const coopname = 'voskhod'
const tester1 = generateRandomUsername()
await registerUser(blockchain, coopname, tester1)
})
it('регистируем пайщика 2', async () => {
const coopname = 'voskhod'
const tester2 = generateRandomUsername()
await registerUser(blockchain, coopname, tester2)
})
})
@@ -0,0 +1,19 @@
import { SovietContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
export async function getParticipant(
blockchain: Blockchain,
coopname: string,
username: string,
) {
const participant = (await blockchain.getTableRows(
SovietContract.contractName.production,
'voskhod',
'participants',
1,
username,
username,
))[0]
return participant
}
@@ -0,0 +1,123 @@
import { GatewayContract, RegistratorContract, SovietContract } from 'cooptypes'
import { expect } from 'vitest'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { processDecision } from '../soviet/processDecision'
import { getParticipant } from '../registrator/getParticipant'
import { fakeDocument } from '../soviet/fakeDocument'
import { compareTokenAmounts, getCoopWallet } from '../wallet/walletUtils'
export async function registerUser(blockchain: any, coopname: string, username: string) {
const registration_hash = generateRandomSHA256()
const account = await blockchain.generateKeypair(username, undefined, 'Аккаунт кооператива')
// 🔹 Получаем балансы до регистрации
const prevCoopWallet = await getCoopWallet(blockchain, coopname)
const prevAvailable = prevCoopWallet?.circulating_account?.available || '0.0000 RUB'
const prevInitial = prevCoopWallet?.initial_account?.available || '0.0000 RUB'
console.log('📦 CoopWallet BEFORE:', prevCoopWallet)
// 🔹 1. Создание аккаунта
{
const data: RegistratorContract.Actions.CreateAccount.ICreateAccount = {
coopname,
referer: '',
username,
public_key: account.publicKey,
meta: '',
}
const result = await blockchain.api.transact({
actions: [{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.CreateAccount.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
}],
}, { blocksBehind: 3, expireSeconds: 30 })
getTotalRamUsage(result)
console.log('✅ Аккаунт создан')
}
// 🔹 2. Регистрация пайщика
{
const data: RegistratorContract.Actions.RegisterUser.IRegisterUser = {
coopname,
braname: '',
username,
type: 'individual',
statement: fakeDocument,
registration_hash,
}
const result = await blockchain.api.transact({
actions: [{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.RegisterUser.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
}],
}, { blocksBehind: 3, expireSeconds: 30 })
getTotalRamUsage(result)
console.log('✅ Пайщик зарегистрирован')
}
// 🔹 3. Подтверждение прихода
{
const data: GatewayContract.Actions.CompleteIncome.ICompleteIncome = {
coopname,
income_hash: registration_hash,
}
const result = await blockchain.api.transact({
actions: [{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteIncome.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
}],
}, { blocksBehind: 3, expireSeconds: 30 })
getTotalRamUsage(result)
console.log('✅ Приход подтвержден')
}
// 🔹 4. Исполнение решения
const decisions = await blockchain.getTableRows(
SovietContract.contractName.production,
coopname,
'decisions',
1000,
)
const lastDecision = decisions.at(-1)
console.log('📜 Последнее решение:', lastDecision)
await processDecision(blockchain, lastDecision.id)
console.log('✅ Решение выполнено')
// 🔹 5. Проверка участника
const participant = await getParticipant(blockchain, coopname, username)
console.log('🙋 Участник:', participant)
expect(participant).toBeDefined()
expect(participant.has_vote).toBe(1)
expect(participant.status).toBe('accepted')
expect(participant.username).toBe(username)
// 🔹 6. Проверка балансов после регистрации
const coopWalletAfter = await getCoopWallet(blockchain, coopname)
const newAvailable = coopWalletAfter?.circulating_account?.available || '0.0000 RUB'
const newInitial = coopWalletAfter?.initial_account?.available || '0.0000 RUB'
console.log('💰 CoopWallet AFTER:', coopWalletAfter)
const minAmount = parseFloat(participant.minimum_amount.split(' ')[0])
const initAmount = parseFloat(participant.initial_amount.split(' ')[0])
compareTokenAmounts(prevAvailable, newAvailable, minAmount)
compareTokenAmounts(prevInitial, newInitial, initAmount)
return participant
}
+2 -2
View File
@@ -2,7 +2,7 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import Blockchain from '../blockchain'
import config from '../configs'
import { getTotalRamUsage, globalRamStats } from '../utils/getTotalRamUsage'
import { createParticipant } from '../init/participant'
import { addUser } from '../init/participant'
import { generateRandomUsername } from '../utils/randomUsername'
import { signWalletAgreement } from './wallet/signWalletAgreement'
import { depositToWallet } from './wallet/depositToWallet'
@@ -23,7 +23,7 @@ beforeAll(async () => {
tester1 = generateRandomUsername()
console.log('tester1: ', tester1)
await createParticipant(tester1)
await addUser(tester1)
}, 500_000)
afterAll(() => {
@@ -1,17 +1,17 @@
import { randomInt } from 'node:crypto'
import { GatewayContract } from 'cooptypes'
import { GatewayContract, WalletContract } from 'cooptypes'
import { expect } from 'vitest'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { compareTokenAmounts, getCoopProgramWallet, getCoopWallet, getDeposit, getUserProgramWallet } from './walletUtils'
export async function depositToWallet(blockchain: any, coopname: string, username: string, amount: number) {
const depositId = randomInt(100000)
const data: GatewayContract.Actions.CreateDeposit.ICreateDeposit = {
const data: WalletContract.Actions.CreateDeposit.ICreateDeposit = {
coopname,
username,
deposit_id: depositId,
type: 'deposit',
deposit_hash: generateRandomSHA256(),
quantity: `${amount.toFixed(4)} RUB`,
}
@@ -19,9 +19,9 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
{
actions: [
{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CreateDeposit.actionName,
authorization: [{ actor: username, permission: 'active' }],
account: WalletContract.contractName.production,
name: WalletContract.Actions.CreateDeposit.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
@@ -35,16 +35,15 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
const deposit = await getDeposit(blockchain, coopname, depositId)
const deposit = await getDeposit(blockchain, coopname, data.deposit_hash)
expect(deposit).toEqual(expect.objectContaining({
id: depositId,
expect(deposit).toMatchObject({
deposit_hash: data.deposit_hash,
username,
coopname,
type: 'deposit',
quantity: `${amount.toFixed(4)} RUB`,
status: 'pending',
}))
})
const prevUserWallet = await getUserProgramWallet(blockchain, coopname, username, 1)
const prevUserWalletAvailable = prevUserWallet?.available || '0.0000 RUB'
@@ -52,11 +51,9 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
const prevCoopWallet = await getCoopWallet(blockchain, coopname)
const prevCoopWalletAvailable = prevCoopWallet?.circulating_account?.available || '0.0000 RUB'
const data2: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit = {
const data2: GatewayContract.Actions.CompleteIncome.ICompleteIncome = {
coopname,
admin: coopname,
deposit_id: depositId,
memo: '',
income_hash: data.deposit_hash,
}
const result2 = await blockchain.api.transact(
@@ -64,7 +61,7 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
actions: [
{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteDeposit.actionName,
name: GatewayContract.Actions.CompleteIncome.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: data2,
},
@@ -77,8 +74,9 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
)
getTotalRamUsage(result2)
const deposit2 = await getDeposit(blockchain, coopname, depositId)
expect(deposit2.status).toBe('completed')
const deposit2 = await getDeposit(blockchain, coopname, data.deposit_hash)
expect(deposit2).toBeUndefined()
const program = await getCoopProgramWallet(blockchain, coopname, 1)
@@ -1,5 +1,6 @@
import { FundContract, GatewayContract, SovietContract } from 'cooptypes'
import { FundContract, SovietContract, WalletContract } from 'cooptypes'
import { expect } from 'vitest'
import type Blockchain from '../../blockchain'
export async function getUserProgramWallet(blockchain: any, coopname: string, username: string, program_id: number) {
const wallets = await blockchain.getTableRows(
@@ -24,7 +25,7 @@ export async function getCoopProgramWallet(blockchain: any, coopname: string, pr
program_id.toString(),
program_id.toString(),
)
console.log('PROGRAMS:', program)
return program[0]
}
@@ -37,14 +38,16 @@ export async function getCoopWallet(blockchain: any, coopname: string) {
))[0]
}
export async function getDeposit(blockchain: any, coopname: string, deposit_id: number) {
export async function getDeposit(blockchain: Blockchain, coopname: string, deposit_hash: string) {
return (await blockchain.getTableRows(
GatewayContract.contractName.production,
WalletContract.contractName.production,
coopname,
'deposits',
WalletContract.Tables.Deposits.tableName,
1,
deposit_id.toString(),
deposit_id.toString(),
deposit_hash,
deposit_hash,
2,
'sha256',
))[0]
}
+3
View File
@@ -30,6 +30,9 @@ macro(add_contract_build name)
endmacro()
# Добавляем все контракты
add_contract_build(meet)
add_contract_build(loan)
add_contract_build(wallet)
add_contract_build(capital)
add_contract_build(branch)
add_contract_build(contributor)
+6 -127
View File
@@ -1,18 +1,15 @@
#include "branch.hpp"
#include "src/addtrusted.cpp"
#include "src/createbranch.cpp"
#include "src/deletebranch.cpp"
#include "src/deltrusted.cpp"
#include "src/editbranch.cpp"
using namespace eosio;
[[eosio::action]] void branch::migrate() {
require_auth(_branch);
branchstat_index stat(_branch, _branch.value);
auto st = stat.find("nzpzufzhcfab"_n.value);
if (st != stat.end()){
stat.modify(st, _branch, [&](auto &s){
s.count = 0;
});
}
}
@@ -21,121 +18,3 @@ using namespace eosio;
require_auth(_system);
};
[[eosio::action]] void branch::addtrusted(eosio::name coopname, eosio::name braname, eosio::name trusted) {
check_auth_or_fail(_branch, coopname, coopname, "addtrusted"_n);
branch_index branches(_branch, coopname.value);
auto branch = branches.find(braname.value);
eosio::check(branch != branches.end(), "Кооперативный участок не найден");
auto trusted_account = get_account_or_fail(trusted);
eosio::check(trusted_account.type == "individual"_n, "Только физическое лицо может быть назначено доверенным кооперативного участка");
branches.modify(branch, coopname, [&](auto &b) {
eosio::check(std::find(b.trusted.begin(), b.trusted.end(), trusted) == b.trusted.end(),
"Доверенный уже добавлен в кооперативный участок");
eosio::check(b.trusted.size() < 3, "Не больше трех доверенных на одном кооперативном участке");
b.trusted.push_back(trusted);
});
}
[[eosio::action]] void branch::deltrusted(eosio::name coopname, eosio::name braname, eosio::name trusted) {
check_auth_or_fail(_branch, coopname, coopname, "deltrusted"_n);
branch_index branches(_branch, coopname.value);
auto branch = branches.find(braname.value);
eosio::check(branch != branches.end(), "Кооперативный участок не найден");
branches.modify(branch, coopname, [&](auto &b) {
auto it = std::find(b.trusted.begin(), b.trusted.end(), trusted);
eosio::check(it != b.trusted.end(), "Доверенный не найден в кооперативном участке");
b.trusted.erase(it);
});
}
[[eosio::action]] void branch::createbranch(eosio::name coopname, eosio::name braname, eosio::name trustee) {
check_auth_or_fail(_branch, coopname, coopname, "createbranch"_n);
branch_index branches(_branch, coopname.value);
auto coop = get_cooperative_or_fail(coopname);
auto authorizer_account = get_account_or_fail(trustee);
eosio::check(authorizer_account.type == "individual"_n, "Только физическое лицо может быть назначено председателем кооперативного участка");
branches.emplace(coopname, [&](auto &row) {
row.braname = braname;
row.trustee = trustee;
});
action(
permission_level{ _branch, "active"_n},
_registrator,
"createbranch"_n,
std::make_tuple(coopname, braname)
).send();
uint64_t branch_count = add_branch_count(coopname);
if (!coop.is_branched && branch_count >= 3) { //регистрируем переход на кооперативные участки
action(
permission_level{ _branch, "active"_n},
_registrator,
"enabranches"_n,
std::make_tuple(coopname)
).send();
}
//TODO create subfunds and subwallets
}
[[eosio::action]] void branch::editbranch(eosio::name coopname, eosio::name braname, eosio::name trustee) {
check_auth_or_fail(_branch, coopname, coopname, "editbranch"_n);
branch_index branches(_branch, coopname.value);
auto branch = branches.find(braname.value);
eosio::check(branch != branches.end(), "Кооперативный участок не найден");
auto authorizer_account = get_account_or_fail(trustee);
eosio::check(authorizer_account.type == "individual"_n, "Только физическое лицо может быть назначено председателем кооперативного участка");
branches.modify(branch, coopname, [&](auto &b) {
b.trustee = trustee;
});
}
[[eosio::action]] void branch::deletebranch(eosio::name coopname, eosio::name braname) {
check_auth_or_fail(_branch, coopname, coopname, "deletebranch"_n);
branch_index branches(_branch, coopname.value);
auto branch = branches.find(braname.value);
auto coop = get_cooperative_or_fail(coopname);
eosio::check(branch != branches.end(), "Кооперативный участок не найден");
branches.erase(branch);
// отключаем участников кооператива от кооперативного участка
action(
permission_level{ _branch, "active"_n},
_soviet,
"deletebranch"_n,
std::make_tuple(coopname, braname)
).send();
uint64_t new_count = sub_branch_count(coopname);
if (coop.is_branched && new_count < 3) { //отключаем систему КУ, если их меньше 3
action(
permission_level{ _branch, "active"_n},
_registrator,
"disbranches"_n,
std::make_tuple(coopname)
).send();
}
};
@@ -0,0 +1,17 @@
[[eosio::action]] void branch::addtrusted(eosio::name coopname, eosio::name braname, eosio::name trusted) {
check_auth_or_fail(_branch, coopname, coopname, "addtrusted"_n);
branch_index branches(_branch, coopname.value);
auto branch = branches.find(braname.value);
eosio::check(branch != branches.end(), "Кооперативный участок не найден");
auto trusted_account = get_account_or_fail(trusted);
eosio::check(trusted_account.type == "individual"_n, "Только физическое лицо может быть назначено доверенным кооперативного участка");
branches.modify(branch, coopname, [&](auto &b) {
eosio::check(std::find(b.trusted.begin(), b.trusted.end(), trusted) == b.trusted.end(),
"Доверенный уже добавлен в кооперативный участок");
eosio::check(b.trusted.size() < 3, "Не больше трех доверенных на одном кооперативном участке");
b.trusted.push_back(trusted);
});
}
@@ -0,0 +1,36 @@
[[eosio::action]] void branch::createbranch(eosio::name coopname, eosio::name braname, eosio::name trustee) {
check_auth_or_fail(_branch, coopname, coopname, "createbranch"_n);
branch_index branches(_branch, coopname.value);
auto coop = get_cooperative_or_fail(coopname);
auto authorizer_account = get_account_or_fail(trustee);
eosio::check(authorizer_account.type == "individual"_n, "Только физическое лицо может быть назначено председателем кооперативного участка");
branches.emplace(coopname, [&](auto &row) {
row.braname = braname;
row.trustee = trustee;
});
action(
permission_level{ _branch, "active"_n},
_registrator,
"createbranch"_n,
std::make_tuple(coopname, braname)
).send();
uint64_t branch_count = add_branch_count(coopname);
if (!coop.is_branched && branch_count >= 3) { //регистрируем переход на кооперативные участки
action(
permission_level{ _branch, "active"_n},
_registrator,
"enabranches"_n,
std::make_tuple(coopname)
).send();
}
//TODO create subfunds and subwallets
}
@@ -0,0 +1,30 @@
[[eosio::action]] void branch::deletebranch(eosio::name coopname, eosio::name braname) {
check_auth_or_fail(_branch, coopname, coopname, "deletebranch"_n);
branch_index branches(_branch, coopname.value);
auto branch = branches.find(braname.value);
auto coop = get_cooperative_or_fail(coopname);
eosio::check(branch != branches.end(), "Кооперативный участок не найден");
branches.erase(branch);
// отключаем участников кооператива от кооперативного участка
action(
permission_level{ _branch, "active"_n},
_soviet,
"deletebranch"_n,
std::make_tuple(coopname, braname)
).send();
uint64_t new_count = sub_branch_count(coopname);
if (coop.is_branched && new_count < 3) { //отключаем систему КУ, если их меньше 3
action(
permission_level{ _branch, "active"_n},
_registrator,
"disbranches"_n,
std::make_tuple(coopname)
).send();
}
};
@@ -0,0 +1,13 @@
[[eosio::action]] void branch::deltrusted(eosio::name coopname, eosio::name braname, eosio::name trusted) {
check_auth_or_fail(_branch, coopname, coopname, "deltrusted"_n);
branch_index branches(_branch, coopname.value);
auto branch = branches.find(braname.value);
eosio::check(branch != branches.end(), "Кооперативный участок не найден");
branches.modify(branch, coopname, [&](auto &b) {
auto it = std::find(b.trusted.begin(), b.trusted.end(), trusted);
eosio::check(it != b.trusted.end(), "Доверенный не найден в кооперативном участке");
b.trusted.erase(it);
});
}
@@ -0,0 +1,16 @@
[[eosio::action]] void branch::editbranch(eosio::name coopname, eosio::name braname, eosio::name trustee) {
check_auth_or_fail(_branch, coopname, coopname, "editbranch"_n);
branch_index branches(_branch, coopname.value);
auto branch = branches.find(braname.value);
eosio::check(branch != branches.end(), "Кооперативный участок не найден");
auto authorizer_account = get_account_or_fail(trustee);
eosio::check(authorizer_account.type == "individual"_n, "Только физическое лицо может быть назначено председателем кооперативного участка");
branches.modify(branch, coopname, [&](auto &b) {
b.trustee = trustee;
});
}
+97 -99
View File
@@ -1,17 +1,33 @@
// capital.cpp
#include "capital.hpp"
#include "src/result/createresult.cpp"
#include "src/result/startdistrbn.cpp"
#include "src/assignment/createassign.cpp"
#include "src/assignment/startdistrbn.cpp"
#include "src/investment/createinvest.cpp"
#include "src/investment/approveinvst.cpp"
#include "src/investment/capauthinvst.cpp"
#include "src/claim/approveclaim.cpp"
#include "src/claim/createclaim.cpp"
#include "src/claim/claimnow.cpp"
#include "src/claim/capauthclaim.cpp"
#include "src/commit/createcmmt.cpp"
#include "src/commit/approvecmmt.cpp"
#include "src/commit/declinecmmt.cpp"
#include "src/commit/delcmmt.cpp"
#include "src/debt/createdebt.cpp"
#include "src/debt/approvedebt.cpp"
#include "src/debt/debtauthcnfr.cpp"
#include "src/debt/debtpaycnfrm.cpp"
#include "src/debt/debtpaydcln.cpp"
#include "src/debt/declinedebt.cpp"
#include "src/debt/settledebt.cpp"
#include "src/result/approverslt.cpp"
#include "src/result/updaterslt.cpp"
#include "src/result/pushrslt.cpp"
#include "src/result/authrslt.cpp"
#include "src/result/setact1.cpp"
#include "src/result/setact2.cpp"
#include "src/registration/capregcontr.cpp"
#include "src/registration/approvereg.cpp"
@@ -22,13 +38,6 @@
#include "src/managment/addauthor.cpp"
#include "src/managment/allocate.cpp"
#include "src/managment/diallocate.cpp"
#include "src/managment/wthdrcallbck.cpp"
#include "src/commit/createcmmt.cpp"
#include "src/commit/approvecmmt.cpp"
#include "src/commit/capauthcmmt.cpp"
#include "src/commit/setact1.cpp"
#include "src/commit/setact2.cpp"
#include "src/withdraw_from_result/createwthd1.cpp"
@@ -48,8 +57,11 @@
#include "src/expense/approveexpns.cpp"
#include "src/expense/capauthexpns.cpp"
// #include "src/expense/capdeclexpns.cpp"
#include "src/expense/createexpnse.cpp"
#include "src/expense/expense_withdraw_callback.cpp"
#include "src/expense/exppaycnfrm.cpp"
#include "src/fundproj/fundproj.cpp"
#include "src/fundproj/refreshproj.cpp"
@@ -59,18 +71,6 @@
#include <optional>
// TODO: внедрить коррекцию долей при выходе пайщика из проекта / программы
// void capital::reduce_shares(name coopname, checksum256 project_hash, name username, asset amount) {
// require_auth(_capital);
// contributor_index contributors(_capital, coopname.value);
// auto idx = contributors.get_index<"byusername"_n>();
// auto itr = idx.find(username.value);
// eosio::check(itr != idx.end(), "Contributor not found");
// }
void capital::validate_project_hierarchy_depth(eosio::name coopname, checksum256 project_hash) {
uint8_t level = 0;
@@ -148,7 +148,7 @@ eosio::asset capital::get_amount_for_withdraw_from_commit(eosio::name coopname,
c.status = "withdrawed"_n;
});
return itr -> spend;
return itr -> spended;
}
@@ -256,21 +256,6 @@ std::optional<progwallet> capital::get_capital_wallet(eosio::name coopname, eosi
}
std::optional<result_author> capital::get_result_author(eosio::name coopname, eosio::name username, const checksum256 &result_hash) {
result_authors_index result_authors(_capital, coopname.value);
auto result_author_index = result_authors.get_index<"byresauthor"_n>();
uint128_t combined_id = combine_checksum_ids(result_hash, username);
auto result_author_itr = result_author_index.find(combined_id);
if (result_author_itr == result_author_index.end()) {
return std::nullopt;
}
return *result_author_itr;
}
std::optional<author> capital::get_author(eosio::name coopname, eosio::name username, const checksum256 &project_hash) {
authors_index authors(_capital, coopname.value);
auto project_author_index = authors.get_index<"byprojauthor"_n>();
@@ -285,36 +270,49 @@ std::optional<author> capital::get_author(eosio::name coopname, eosio::name user
return *author_itr;
}
std::optional<creator> capital::get_creator(eosio::name coopname, eosio::name username, const checksum256 &result_hash) {
std::optional<creator> capital::get_creator(eosio::name coopname, eosio::name username, const checksum256 &assignment_hash) {
creators_index creators(_capital, coopname.value);
auto result_creator_index = creators.get_index<"byresultcrtr"_n>();
auto assignment_creator_index = creators.get_index<"byassigncrtr"_n>();
uint128_t combined_id = combine_checksum_ids(result_hash, username);
auto creator_itr = result_creator_index.find(combined_id);
uint128_t combined_id = combine_checksum_ids(assignment_hash, username);
auto creator_itr = assignment_creator_index.find(combined_id);
if (creator_itr == result_creator_index.end()) {
if (creator_itr == assignment_creator_index.end()) {
return std::nullopt;
}
return *creator_itr;
}
std::optional<result> capital::get_result(eosio::name coopname, const checksum256 &result_hash) {
result_index results(_capital, coopname.value);
auto result_hash_index = results.get_index<"byhash"_n>();
std::optional<assignment> capital::get_assignment(eosio::name coopname, const checksum256 &assignment_hash) {
assignment_index assignments(_capital, coopname.value);
auto assignment_hash_index = assignments.get_index<"byhash"_n>();
auto result_itr = result_hash_index.find(result_hash);
if (result_itr == result_hash_index.end()) {
auto assignment_itr = assignment_hash_index.find(assignment_hash);
if (assignment_itr == assignment_hash_index.end()) {
return std::nullopt;
}
return *result_itr;
return *assignment_itr;
}
result capital::get_result_or_fail(eosio::name coopname, const checksum256 &result_hash, const char* msg) {
auto maybe_result = get_result(coopname, result_hash);
eosio::check(maybe_result.has_value(), msg);
return *maybe_result;
std::optional<debt> capital::get_debt(eosio::name coopname, const checksum256 &debt_hash) {
debts_index debts(_capital, coopname.value);
auto hash_index = debts.get_index<"bydebthash"_n>();
auto itr = hash_index.find(debt_hash);
if (itr == hash_index.end()) {
return std::nullopt;
}
return *itr;
}
assignment capital::get_assignment_or_fail(eosio::name coopname, const checksum256 &assignment_hash, const char* msg) {
auto maybe_assignment = get_assignment(coopname, assignment_hash);
eosio::check(maybe_assignment.has_value(), msg);
return *maybe_assignment;
}
@@ -386,10 +384,21 @@ std::optional<capital_tables::project_withdraw> capital::get_project_withdraw(eo
}
std::optional<claim> capital::get_claim(eosio::name coopname, const checksum256 &result_hash, eosio::name username) {
claim_index claims(_capital, coopname.value);
auto idx = claims.get_index<"byresuser"_n>();
auto rkey = combine_checksum_ids(result_hash, username);
std::optional<result> capital::get_result(eosio::name coopname, const checksum256 &result_hash) {
result_index results(_capital, coopname.value);
auto idx = results.get_index<"byhash"_n>();
auto it = idx.find(result_hash);
if (it == idx.end()) {
return std::nullopt;
}
return *it;
}
std::optional<result> capital::get_result_by_assignment_and_username(eosio::name coopname, const checksum256 &assignment_hash, eosio::name username) {
result_index results(_capital, coopname.value);
auto idx = results.get_index<"byresuser"_n>();
auto rkey = combine_checksum_ids(assignment_hash, username);
auto it = idx.find(rkey);
if (it == idx.end()) {
@@ -412,17 +421,17 @@ std::optional<convert> capital::get_convert(eosio::name coopname, const checksum
}
claim capital::get_claim_or_fail(eosio::name coopname, const checksum256 &result_hash, eosio::name username, const char* msg) {
auto c = get_claim(coopname, result_hash, username);
result capital::get_result_by_assignment_and_username_or_fail(eosio::name coopname, const checksum256 &assignment_hash, eosio::name username, const char* msg) {
auto c = get_result_by_assignment_and_username(coopname, assignment_hash, username);
eosio::check(c.has_value(), msg);
return *c;
}
std::optional<resactor> capital::get_resactor(eosio::name coopname, const checksum256 &result_hash, eosio::name username) {
resactor_index ractors(_capital, coopname.value);
auto idx = ractors.get_index<"byresuser"_n>();
auto rkey = combine_checksum_ids(result_hash, username);
std::optional<creauthor> capital::get_creauthor(eosio::name coopname, const checksum256 &assignment_hash, eosio::name username) {
creauthor_index creathors(_capital, coopname.value);
auto idx = creathors.get_index<"byresuser"_n>();
auto rkey = combine_checksum_ids(assignment_hash, username);
auto it = idx.find(rkey);
if (it == idx.end()) {
@@ -431,10 +440,10 @@ std::optional<resactor> capital::get_resactor(eosio::name coopname, const checks
return *it;
}
resactor capital::get_resactor_or_fail(eosio::name coopname, const checksum256 &result_hash, eosio::name username, const char* msg) {
auto maybe_resactor = get_resactor(coopname, result_hash, username);
eosio::check(maybe_resactor.has_value(), msg);
return *maybe_resactor;
creauthor capital::get_creauthor_or_fail(eosio::name coopname, const checksum256 &assignment_hash, eosio::name username, const char* msg) {
auto maybe_creauthor = get_creauthor(coopname, assignment_hash, username);
eosio::check(maybe_creauthor.has_value(), msg);
return *maybe_creauthor;
}
@@ -466,40 +475,29 @@ global_state capital::get_global_state(name coopname) {
}
void capital::ensure_contributor(name coopname, name username) {
// Получаем глобальное состояние для установки reward_per_share_last
auto gs = get_global_state(coopname);
}
//----------------------------------------------------------------------------
// calculcate_capital_amounts: для расчёта премий по формулам:
// - creators_bonus = spend * 0.382
// - authors_bonus = spend * 1.618
// - generated = spend + creators_bonus + authors_bonus
// - creators_bonus = spended * 0.382
// - authors_bonus = spended * 0.618
// - generated = spended + creators_bonus + authors_bonus
// - capitalists_bonus = generated * 1.618
// - total = generated + capitalists_bonus
//----------------------------------------------------------------------------
bonus_result capital::calculcate_capital_amounts(int64_t spend_amount) {
bonus_result br{};
bonus_result capital::calculcate_capital_amounts(const eosio::asset& spended) {
bonus_result br;
// Преобразуем spend_amount в double для дальнейших расчетов
double spend = double(spend_amount);
// 1) creators_bonus = spend_amount * 0.382
br.creators_bonus = int64_t(spend * 0.382);
// 2) authors_bonus = spend_amount * 1.618
br.authors_bonus = int64_t(spend * 1.618);
// 3) generated = spend + creators_bonus + authors_bonus
br.generated = int64_t(spend + br.creators_bonus + br.authors_bonus);
// 4) capitalists_bonus = generated * 1.618
br.capitalists_bonus = int64_t(double(br.generated) * 1.618);
// 5) total = generated + capitalists_bonus
br.total = int64_t(br.generated + br.capitalists_bonus);
double amount = static_cast<double>(spended.amount);
eosio::symbol sym = spended.symbol;
br.creator_base = spended;
br.author_base = spended;
br.creators_bonus = eosio::asset(int64_t(amount * 0.382), sym);
br.authors_bonus = eosio::asset(int64_t(amount * 0.618), sym);
br.generated = eosio::asset(br.creator_base.amount + br.creators_bonus.amount + br.authors_bonus.amount + br.author_base.amount, sym);
br.capitalists_bonus = eosio::asset( int64_t(static_cast<double>(br.generated.amount) * 1.618), sym);
br.total = eosio::asset(br.generated.amount + br.capitalists_bonus.amount, sym);
return br;
}
+70 -44
View File
@@ -28,24 +28,21 @@ public:
void init(name coopname, name initiator);
[[eosio::action]]
void createresult(name coopname, name application, checksum256 project_hash, checksum256 result_hash);
void createassign(name coopname, name application, checksum256 project_hash, checksum256 assignment_hash, eosio::name assignee, std::string description);
//Возврат из задания
[[eosio::action]]
void createwthd1(eosio::name coopname, eosio::name application, eosio::name username, checksum256 assignment_hash, checksum256 withdraw_hash, asset amount, document return_statement);
[[eosio::action]]
void wthdrcallbck(eosio::name coopname, eosio::name callback_type, checksum256 withdraw_hash);
//Возврат из результата
[[eosio::action]]
void createwthd1(eosio::name coopname, eosio::name application, eosio::name username, checksum256 result_hash, checksum256 withdraw_hash, asset amount, document return_statement);
[[eosio::action]]
void capauthwthd1(eosio::name coopname, uint64_t withdraw_id, document authorization);
void capauthwthd1(eosio::name coopname, checksum256 withdraw_hash, document authorization);
// Возврат из проекта
[[eosio::action]]
void createwthd2(name coopname, name application, name username, checksum256 project_hash, checksum256 withdraw_hash, asset amount, document return_statement);
[[eosio::action]]
void capauthwthd2(eosio::name coopname, uint64_t withdraw_id, document authorization);
void capauthwthd2(eosio::name coopname, checksum256 withdraw_hash, document authorization);
[[eosio::action]]
void approvewthd2(name coopname, name application, name approver, checksum256 withdraw_hash, document approved_return_statement);
@@ -55,12 +52,11 @@ public:
void createwthd3(name coopname, name application, name username, checksum256 project_hash, checksum256 withdraw_hash, asset amount, document return_statement);
[[eosio::action]]
void capauthwthd3(eosio::name coopname, uint64_t withdraw_id, document authorization);
void capauthwthd3(eosio::name coopname, checksum256 withdraw_hash, document authorization);
[[eosio::action]]
void approvewthd3(name coopname, name application, name approver, checksum256 withdraw_hash, document approved_return_statement);
[[eosio::action]]
void createproj (
checksum256 project_hash,
@@ -74,23 +70,24 @@ public:
std::string subject
);
// claims
// results
[[eosio::action]]
void createclaim(
void updaterslt(
eosio::name coopname,
eosio::name application,
eosio::name username,
checksum256 assignment_hash,
checksum256 result_hash
);
[[eosio::action]]
void claimnow(name coopname, name application, name username, checksum256 result_hash, document statement);
void pushrslt(name coopname, name application, checksum256 result_hash, document statement);
[[eosio::action]]
void capauthclaim(eosio::name coopname, uint64_t claim_id, document decision);
void authrslt(eosio::name coopname, checksum256 result_hash, document decision);
[[eosio::action]]
void approveclaim(eosio::name coopname, eosio::name application, eosio::name approver, eosio::name username, checksum256 result_hash, document approved_statement);
void approverslt(eosio::name coopname, eosio::name application, eosio::name approver, checksum256 result_hash, document approved_statement);
// конвертация
[[eosio::action]]
@@ -101,28 +98,54 @@ public:
eosio::name coopname,
eosio::name application,
eosio::name username,
checksum256 result_hash,
checksum256 assignment_hash,
checksum256 convert_hash,
document convert_statement
);
[[eosio::action]]
void startdistrbn(name coopname, name application, checksum256 result_hash);
void startdistrbn(name coopname, name application, checksum256 assignment_hash);
[[eosio::action]]
void addauthor(name coopname, name application, checksum256 project_hash, name author, uint64_t shares);
// Коммиты
[[eosio::action]]
void approvecmmt(eosio::name coopname, eosio::name application, eosio::name approver, checksum256 commit_hash, document approved_statement);
void createcmmt(eosio::name coopname, eosio::name application, eosio::name username, checksum256 assignment_hash, checksum256 commit_hash, uint64_t contributed_hours);
[[eosio::action]]
void createcmmt(eosio::name coopname, eosio::name application, eosio::name creator, checksum256 result_hash, checksum256 commit_hash, uint64_t contributed_hours, document contribution_statement);
void approvecmmt(eosio::name coopname, checksum256 commit_hash, document empty_document);
[[eosio::action]]
void declinecmmt(eosio::name coopname, checksum256 commit_hash, std::string reason);
[[eosio::action]]
void delcmmt(eosio::name coopname, eosio::name application, eosio::name approver, checksum256 commit_hash);
// Долги
[[eosio::action]]
void createdebt(name coopname, name username, checksum256 assignment_hash, checksum256 debt_hash, asset amount, time_point_sec repaid_at, document statement);
[[eosio::action]]
void approvedebt(eosio::name coopname, checksum256 debt_hash, document approved_statement);
[[eosio::action]]
void debtauthcnfr(eosio::name coopname, checksum256 debt_hash, document decision);
[[eosio::action]]
void debtpaycnfrm(name coopname, checksum256 debt_hash);
[[eosio::action]]
void debtpaydcln(name coopname, checksum256 debt_hash, std::string reason);
[[eosio::action]]
void declinedebt(name coopname, checksum256 debt_hash, std::string reason);
[[eosio::action]]
void settledebt(name coopname);
[[eosio::action]]
void setact1(eosio::name coopname, eosio::name application, eosio::name username, checksum256 commit_hash, document act);
[[eosio::action]]
void setact2(eosio::name coopname, eosio::name application, eosio::name username, checksum256 commit_hash, document act);
[[eosio::action]]
void capauthcmmt(eosio::name coopname, uint64_t commit_id, document authorization);
// Регистрация
[[eosio::action]]
@@ -137,30 +160,34 @@ public:
void createinvest(name coopname, name application, name username, checksum256 project_hash, checksum256 invest_hash, asset amount, document statement);
[[eosio::action]]
void capauthinvst(eosio::name coopname, uint64_t invest_id, document authorization);
void capauthinvst(eosio::name coopname, checksum256 invest_hash, document authorization);
[[eosio::action]]
void approveinvst(name coopname, name application, name approver, checksum256 invest_hash, document approved_statement);
[[eosio::action]]
void allocate(eosio::name coopname, eosio::name application, checksum256 project_hash, checksum256 result_hash, eosio::asset amount);
void allocate(eosio::name coopname, eosio::name application, checksum256 project_hash, checksum256 assignment_hash, eosio::asset amount);
[[eosio::action]]
void diallocate(eosio::name coopname, eosio::name application, checksum256 project_hash, checksum256 result_hash, eosio::asset amount);
void diallocate(eosio::name coopname, eosio::name application, checksum256 project_hash, checksum256 assignment_hash, eosio::asset amount);
[[eosio::action]]
void approvewthd1(name coopname, name application, name approver, checksum256 withdraw_hash, document approved_return_statement);
// Расходы
[[eosio::action]]
void createexpnse(eosio::name coopname, eosio::name application, checksum256 expense_hash, checksum256 result_hash, name creator, uint64_t fund_id, asset amount, std::string description, document statement);
void createexpnse(eosio::name coopname, eosio::name application, checksum256 expense_hash, checksum256 assignment_hash, name creator, uint64_t fund_id, asset amount, std::string description, document statement);
[[eosio::action]]
void approveexpns(name coopname, name application, name approver, checksum256 expense_hash, document approved_statement);
[[eosio::action]]
void capauthexpns(eosio::name coopname, uint64_t expense_id, document authorization);
void capauthexpns(eosio::name coopname, checksum256 expense_hash, document authorization);
[[eosio::action]]
void exppaycnfrm(eosio::name coopname, checksum256 expense_hash);
// Членские взносы
[[eosio::action]] void fundproj(eosio::name coopname, checksum256 project_hash, asset amount, std::string memo);
[[eosio::action]] void refreshproj(name coopname, name application, checksum256 project_hash, name username);
@@ -191,14 +218,14 @@ private:
* @return Текущее глобальное состояние.
*/
global_state get_global_state(name coopname);
void ensure_contributor(name coopname, name username);
static bonus_result calculcate_capital_amounts(int64_t spend_amount);
static bonus_result calculcate_capital_amounts(const eosio::asset& spended);
std::optional<author> get_author(eosio::name coopname, eosio::name username, const checksum256 &project_hash);
std::optional<creator> get_creator(eosio::name coopname, eosio::name username, const checksum256 &result_hash);
std::optional<result> get_result(eosio::name coopname, const checksum256 &result_hash);
std::optional<creator> get_creator(eosio::name coopname, eosio::name username, const checksum256 &assignment_hash);
std::optional<assignment> get_assignment(eosio::name coopname, const checksum256 &assignment_hash);
std::optional<debt> get_debt(eosio::name coopname, const checksum256 &debt_hash);
std::optional<project> get_project(eosio::name coopname, const checksum256 &project_hash);
std::optional<project> get_master_project(eosio::name coopname);
@@ -208,14 +235,17 @@ private:
int64_t get_capital_user_share_balance(eosio::name coopname, eosio::name username);
std::optional<claim> get_claim(eosio::name coopname, const checksum256 &result_hash, eosio::name username);
std::optional<result> get_result_by_assignment_and_username(eosio::name coopname, const checksum256 &assignment_hash, eosio::name username);
std::optional<result> get_result(eosio::name coopname, const checksum256 &result_hash);
std::optional<convert> get_convert(eosio::name coopname, const checksum256 &hash);
std::optional<result_author> get_result_author(eosio::name coopname, eosio::name username, const checksum256 &result_hash);
std::optional<commit> get_commit(eosio::name coopname, const checksum256 &hash);
std::optional<invest> get_invest(eosio::name coopname, const checksum256 &invest_hash);
std::optional<capital_tables::result_withdraw> get_result_withdraw(eosio::name coopname, const checksum256 &hash);
std::optional<capital_tables::project_withdraw> get_project_withdraw(eosio::name coopname, const checksum256 &hash);
std::optional<capital_tables::program_withdraw> get_program_withdraw(eosio::name coopname, const checksum256 &hash);
std::optional<expense> get_expense(eosio::name coopname, const checksum256 &hash);
@@ -231,17 +261,13 @@ private:
int64_t get_capital_program_share_balance(eosio::name coopname);
std::optional<capital_tables::program_withdraw> get_program_withdraw(eosio::name coopname, const checksum256 &hash);
uint64_t count_authors_by_project(eosio::name coopname, const checksum256 &project_hash);
eosio::asset get_amount_for_withdraw_from_commit(eosio::name coopname, eosio::name username, const checksum256 &hash);
void expense_withdraw_callback(name coopname, checksum256 withdraw_hash);
resactor get_resactor_or_fail(eosio::name coopname, const checksum256 &result_hash, eosio::name username, const char* msg);
std::optional<resactor> get_resactor(eosio::name coopname, const checksum256 &result_hash, eosio::name username);
claim get_claim_or_fail(eosio::name coopname, const checksum256 &result_hash, eosio::name username, const char* msg);
result get_result_or_fail(eosio::name coopname, const checksum256 &result_hash, const char* msg);
creauthor get_creauthor_or_fail(eosio::name coopname, const checksum256 &assignment_hash, eosio::name username, const char* msg);
std::optional<creauthor> get_creauthor(eosio::name coopname, const checksum256 &assignment_hash, eosio::name username);
result get_result_by_assignment_and_username_or_fail(eosio::name coopname, const checksum256 &assignment_hash, eosio::name username, const char* msg);
assignment get_assignment_or_fail(eosio::name coopname, const checksum256 &assignment_hash, const char* msg);
};
@@ -6,50 +6,53 @@
using namespace eosio;
struct bonus_result {
int64_t creators_bonus;
int64_t authors_bonus;
int64_t generated;
int64_t capitalists_bonus;
int64_t total;
asset author_base;
asset creator_base;
asset creators_bonus;
asset authors_bonus;
asset generated;
asset capitalists_bonus;
asset total;
};
// resactors.hpp (или в capital.hpp)
struct [[eosio::table, eosio::contract(CAPITAL)]] resactor {
// creauthors.hpp (или в capital.hpp)
struct [[eosio::table, eosio::contract(CAPITAL)]] creauthor {
uint64_t id;
checksum256 project_hash; // С каким результатом связана запись
checksum256 result_hash; // С каким результатом связана запись
eosio::name username; // Чей это учёт
checksum256 assignment_hash; // С каким результатом связана запись
eosio::name username; //
eosio::asset provisional_amount = asset(0, _root_govern_symbol);
eosio::asset debt_amount = asset(0, _root_govern_symbol);
eosio::asset spended = asset(0, _root_govern_symbol);
eosio::asset available = asset(0, _root_govern_symbol);
eosio::asset for_convert = asset(0, _root_govern_symbol);
eosio::asset spend = asset(0, _root_govern_symbol);
// Сколько пользователь имеет «авторских долей» в этом результате
uint64_t authors_shares = 0;
uint64_t author_shares = 0;
// Сколько пользователь имеет «создательских долей» в creators_bonus
uint64_t creators_bonus_shares = 0;
uint64_t creator_bonus_shares = 0;
// Сколько часов вложено в результат
// Сколько часов вложено в задание
uint64_t contributed_hours = 0;
uint64_t primary_key() const { return id; }
checksum256 by_result_hash() const { return result_hash; } ///< Индекс по хэшу результата
checksum256 by_assignment_hash() const { return assignment_hash; } ///< Индекс по хэшу задания
checksum256 by_project_hash() const { return project_hash; } ///< Индекс по хэшу проекта
// Индекс по (result_hash + username)
// Индекс по (assignment_hash + username)
uint128_t by_resuser() const {
return combine_checksum_ids(result_hash, username);
return combine_checksum_ids(assignment_hash, username);
}
};
typedef eosio::multi_index<
"resactors"_n, resactor,
indexed_by<"byproject"_n, const_mem_fun<resactor, checksum256, &resactor::by_project_hash>>,
indexed_by<"byresult"_n, const_mem_fun<resactor, checksum256, &resactor::by_result_hash>>,
indexed_by<"byresuser"_n, const_mem_fun<resactor, uint128_t, &resactor::by_resuser>>
> resactor_index;
"creauthors"_n, creauthor,
indexed_by<"byproject"_n, const_mem_fun<creauthor, checksum256, &creauthor::by_project_hash>>,
indexed_by<"byassignment"_n, const_mem_fun<creauthor, checksum256, &creauthor::by_assignment_hash>>,
indexed_by<"byresuser"_n, const_mem_fun<creauthor, uint128_t, &creauthor::by_resuser>>
> creauthor_index;
@@ -61,7 +64,7 @@ struct [[eosio::table, eosio::contract(CAPITAL)]] expense {
name status = "created"_n; ///< Статус расхода (created | approved | authorized)
checksum256 project_hash; ///< Хэш проекта, связанного с расходом.
checksum256 result_hash; ///< Хэш результата, связанного с расходом.
checksum256 assignment_hash; ///< Хэш задания, связанного с расходом.
checksum256 expense_hash; ///< Хэш расхода.
uint64_t fund_id; ///< Идентификатор фонда списания (expfunds в контакте fund)
eosio::asset amount; ///< Сумма расхода.
@@ -71,12 +74,12 @@ struct [[eosio::table, eosio::contract(CAPITAL)]] expense {
document approved_statement; ///< принятая записка председателем или доверенным
document authorization; ///< утвержденная записка советом
time_point_sec spend_at = current_time_point(); ///< Дата и время создания расхода.
time_point_sec spended_at = current_time_point(); ///< Дата и время создания расхода.
uint64_t primary_key() const { return id; } ///< Основной ключ.
uint64_t by_username() const { return username.value; } ///< По имени пользователя.
checksum256 by_expense_hash() const { return expense_hash; } ///< Индекс по хэшу задачи.
checksum256 by_result_hash() const { return result_hash; } ///< Индекс по хэшу результата.
checksum256 by_assignment_hash() const { return assignment_hash; } ///< Индекс по хэшу задания.
checksum256 by_project_hash() const { return project_hash; } ///< Индекс по хэшу проекта.
};
@@ -85,7 +88,7 @@ struct [[eosio::table, eosio::contract(CAPITAL)]] expense {
"expenses"_n, expense,
indexed_by<"byusername"_n, const_mem_fun<expense, uint64_t, &expense::by_username>>,
indexed_by<"byhash"_n, const_mem_fun<expense, checksum256, &expense::by_expense_hash>>,
indexed_by<"byresulthash"_n, const_mem_fun<expense, checksum256, &expense::by_result_hash>>,
indexed_by<"byassignment"_n, const_mem_fun<expense, checksum256, &expense::by_assignment_hash>>,
indexed_by<"byprojhash"_n, const_mem_fun<expense, checksum256, &expense::by_project_hash>>
> expense_index;
@@ -101,22 +104,24 @@ struct [[eosio::table, eosio::contract(CAPITAL)]] commit {
name username; ///< Имя пользователя, совершившего действие.
name status; ///< Статус коммита (created | approved | authorized | act1 | act2 )
checksum256 project_hash; ///< Хэш проекта, связанного с действием.
checksum256 result_hash; ///< Хэш результата, связанного с действием.
checksum256 assignment_hash; ///< Хэш задания, связанного с действием.
checksum256 commit_hash; ///< Хэш действия.
uint64_t contributed_hours; ///< Сумма временных затрат, связанная с действием.
eosio::asset spend; ///< Сумма затрат, связанная с действием.
eosio::asset rate_per_hour = asset(0, _root_govern_symbol); ///< Стоимость часа
eosio::asset spended = asset(0, _root_govern_symbol); ///< Сумма затрат, связанная с действием.
eosio::asset generated = asset(0, _root_govern_symbol); ///< Сумма генерации, связанная с действием.
eosio::asset creators_bonus = asset(0, _root_govern_symbol); ///< Сумма затрат, связанная с действием.
eosio::asset authors_bonus = asset(0, _root_govern_symbol);///< Сумма затрат, связанная с действием.
eosio::asset capitalists_bonus = asset(0, _root_govern_symbol); ///< Сумма затрат, связанная с действием.
eosio::asset total = asset(0, _root_govern_symbol); ///< Суммарная стоимость коммита на будущем приёме задания с учетом капитализации и генерации
document contribution_statement; ///< Техническое задание (спецификация) как приложение к договору УХД
document approved_statement; ///< Одобрение председателя или доверенного лица
document authorization; ///< Решение совета
document act1; ///< акт приема-передачи (1)
document act2; ///< акт приема-передачи (2)
std::string decline_comment;
time_point_sec created_at; ///< Дата и время создания действия.
uint64_t primary_key() const { return id; } ///< Основной ключ.
uint64_t by_username() const { return username.value; } ///< По имени пользователя.
checksum256 by_commit_hash() const { return commit_hash; } ///< Индекс по хэшу задачи.
checksum256 by_result_hash() const { return result_hash; } ///< Индекс по хэшу результата.
checksum256 by_assignment_hash() const { return assignment_hash; } ///< Индекс по хэшу задания.
checksum256 by_project_hash() const { return project_hash; } ///< Индекс по хэшу проекта.
};
@@ -124,7 +129,7 @@ typedef eosio::multi_index<
"commits"_n, commit,
indexed_by<"byusername"_n, const_mem_fun<commit, uint64_t, &commit::by_username>>,
indexed_by<"byhash"_n, const_mem_fun<commit, checksum256, &commit::by_commit_hash>>,
indexed_by<"byresulthash"_n, const_mem_fun<commit, checksum256, &commit::by_result_hash>>,
indexed_by<"byassignment"_n, const_mem_fun<commit, checksum256, &commit::by_assignment_hash>>,
indexed_by<"byprojhash"_n, const_mem_fun<commit, checksum256, &commit::by_project_hash>>
> commit_index;
@@ -183,12 +188,13 @@ struct [[eosio::table, eosio::contract(CAPITAL)]] contributor {
eosio::asset rate_per_hour = asset(0, _root_govern_symbol);
eosio::asset spend = asset(0, _root_govern_symbol);
eosio::asset spended = asset(0, _root_govern_symbol);
eosio::asset debt_amount = asset(0, _root_govern_symbol);
eosio::asset withdrawed = asset(0, _root_govern_symbol);
eosio::asset converted = asset(0, _root_govern_symbol);
eosio::asset expensed = asset(0, _root_govern_symbol);
eosio::asset returned = asset(0, _root_govern_symbol);
eosio::asset claimed = asset(0, _root_govern_symbol);
eosio::asset share_balance = asset(0, _root_govern_symbol); ///< Баланс долей пайщика
eosio::asset pending_rewards = asset(0, _root_govern_symbol); ///< Накопленные награды
@@ -227,19 +233,27 @@ struct [[eosio::table, eosio::contract(CAPITAL)]] project {
uint64_t authors_count;
uint64_t authors_shares;
uint64_t commits_count;
std::vector<uint64_t> expense_funds = {4};
eosio::asset target = asset(0, _root_govern_symbol);
eosio::asset invested = asset(0, _root_govern_symbol);
eosio::asset available = asset(0, _root_govern_symbol);
eosio::asset allocated = asset(0, _root_govern_symbol);
eosio::asset creators_base = asset(0, _root_govern_symbol);
eosio::asset creators_bonus = asset(0, _root_govern_symbol);
eosio::asset authors_bonus = asset(0, _root_govern_symbol);
eosio::asset capitalists_bonus = asset(0, _root_govern_symbol);
eosio::asset total = asset(0, _root_govern_symbol); // стоимость проекта с учетом генерации и капитализации
eosio::asset expensed = asset(0, _root_govern_symbol);
eosio::asset spend = asset(0, _root_govern_symbol);
eosio::asset spended = asset(0, _root_govern_symbol);
eosio::asset generated = asset(0, _root_govern_symbol);
eosio::asset converted = asset(0, _root_govern_symbol);
eosio::asset claimed = asset(0, _root_govern_symbol);
eosio::asset withdrawed = asset(0, _root_govern_symbol);
double parent_distribution_ratio = 1;
int64_t membership_cumulative_reward_per_share = 0;
@@ -261,59 +275,62 @@ typedef eosio::multi_index<"projects"_n, project,
> project_index;
struct [[eosio::table, eosio::contract(CAPITAL)]] result {
struct [[eosio::table, eosio::contract(CAPITAL)]] assignment {
uint64_t id;
checksum256 result_hash;
checksum256 assignment_hash;
checksum256 project_hash;
eosio::name status = "created"_n; ///< created
eosio::name status = "opened"_n; ///< opened | closed
eosio::name coopname;
eosio::name assignee;
std::string description;
uint64_t authors_shares;
uint64_t total_creators_bonus_shares;
uint64_t authors_count;
uint64_t commits_count;
time_point_sec created_at = current_time_point();
time_point_sec expired_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch() + 365 * 86400);
uint64_t authors_shares;
uint64_t total_creators_bonus_shares;
uint64_t authors_count;
uint64_t commits_count;
eosio::asset allocated = asset(0, _root_govern_symbol); ///< аллоцированные на создание задания средства
eosio::asset available = asset(0, _root_govern_symbol); ///< зарезерированные на создание задания средства
eosio::asset spended = asset(0, _root_govern_symbol); ///< фактически потраченные ресурсы на создание задание в виде времени (паевые взносы-возвраты)
eosio::asset generated = asset(0, _root_govern_symbol); ///< стоимость РИД с учётом премий авторов и создателей
eosio::asset expensed = asset(0, _root_govern_symbol); ///< фактически потраченные на создание задания средства в виде расходов (подписки, прочее)
eosio::asset withdrawed = asset(0, _root_govern_symbol); ///< фактически возвращенные средства из задания
eosio::asset allocated = asset(0, _root_govern_symbol); ///< аллоцированные на создание результата средства
eosio::asset available = asset(0, _root_govern_symbol); ///< зарезерированные на создание результата средства
eosio::asset spend = asset(0, _root_govern_symbol); ///< фактически потраченные ресурсы на создание результат в виде времени (паевые взносы-возвраты)
eosio::asset expensed = asset(0, _root_govern_symbol); ///< фактически потраченные на создание результата средства в виде расходов (подписки, прочее)
eosio::asset withdrawed = asset(0, _root_govern_symbol); ///< фактически возвращенные средства из результата
eosio::asset creators_base = asset(0, _root_govern_symbol); ///< себестоимость РИД
eosio::asset creators_amount = asset(0, _root_govern_symbol); ///< себестоимость РИД
eosio::asset generated_amount = asset(0, _root_govern_symbol); ///< стоимость РИД с учётом премий авторов и создателей
eosio::asset creators_bonus = asset(0, _root_govern_symbol); ///< премии создателей - 0.382 от себестоимости (creators_amount)
eosio::asset authors_bonus = asset(0, _root_govern_symbol); ///< премии авторов - 1.618 от себестоимости (creators_amount)
eosio::asset creators_bonus = asset(0, _root_govern_symbol); ///< премии создателей - 0.382 от себестоимости (creators_base)
eosio::asset authors_bonus = asset(0, _root_govern_symbol); ///< премии авторов - 1.618 от себестоимости (creators_base)
eosio::asset capitalists_bonus = asset(0, _root_govern_symbol); ///< премии пайщиков кооператива - 1.618 от generated_amount
eosio::asset total_amount = asset(0, _root_govern_symbol); ///< Капитализация РИД (стоимость РИД в generated_amount + capitalists_bonus)
eosio::asset total = asset(0, _root_govern_symbol); ///< Стоимость РИД с учетом генерации и капитализации (стоимость РИД в spended + authors_bonus + creators_bonus + capitalists_bonus)
eosio::asset authors_bonus_remain = asset(0, _root_govern_symbol); ///< сумма остатка для выплаты авторам
eosio::asset creators_amount_remain = asset(0, _root_govern_symbol); ///< сумма остатка для выплаты авторам
eosio::asset creators_base_remain = asset(0, _root_govern_symbol); ///< сумма остатка для выплаты авторам
eosio::asset creators_bonus_remain = asset(0, _root_govern_symbol); ///< сумма остатка для выплаты авторам
eosio::asset capitalists_bonus_remain = asset(0, _root_govern_symbol); ///< сумма остатка для выплаты пайщикам
uint64_t primary_key() const { return id; } ///< Основной ключ.
checksum256 by_hash() const { return result_hash; } ///< Индекс по хэшу результата.
checksum256 by_hash() const { return assignment_hash; } ///< Индекс по хэшу задания.
checksum256 by_project_hash() const { return project_hash; } ///< Индекс по хэшу идеи
};
typedef eosio::multi_index<"results"_n, result,
indexed_by<"byhash"_n, const_mem_fun<result, checksum256, &result::by_hash>>,
indexed_by<"byprojecthash"_n, const_mem_fun<result, checksum256, &result::by_project_hash>>
> result_index;
typedef eosio::multi_index<"assignments"_n, assignment,
indexed_by<"byhash"_n, const_mem_fun<assignment, checksum256, &assignment::by_hash>>,
indexed_by<"byprojecthash"_n, const_mem_fun<assignment, checksum256, &assignment::by_project_hash>>
> assignment_index;
struct [[eosio::table, eosio::contract(CAPITAL)]] claim {
struct [[eosio::table, eosio::contract(CAPITAL)]] result {
uint64_t id;
checksum256 project_hash;
checksum256 assignment_hash;
checksum256 result_hash;
eosio::name coopname;
@@ -323,39 +340,50 @@ struct [[eosio::table, eosio::contract(CAPITAL)]] claim {
eosio::name status = "created"_n; ///< created | statement | decision | act1 | act2 | completed
time_point_sec created_at = current_time_point();
eosio::asset author_amount = asset(0, _root_govern_symbol);
eosio::asset creator_amount = asset(0, _root_govern_symbol);
eosio::asset capitalist_amount = asset(0, _root_govern_symbol);
eosio::asset creator_base_amount = asset(0, _root_govern_symbol);
eosio::asset debt_amount = asset(0, _root_govern_symbol);
eosio::asset creator_bonus_amount = asset(0, _root_govern_symbol);
eosio::asset author_bonus_amount = asset(0, _root_govern_symbol);
eosio::asset generation_amount = asset(0, _root_govern_symbol);
eosio::asset capitalist_bonus_amount = asset(0, _root_govern_symbol);
eosio::asset total_amount = asset(0, _root_govern_symbol);
eosio::asset available_for_return = asset(0, _root_govern_symbol);
eosio::asset available_for_convert = asset(0, _root_govern_symbol);
document claim_statement; ///< Заявление
document result_statement; ///< Заявление
document approved_statement; ///< Принятое заявление
document authorization; ///< Решение
document authorization; ///< Решение совета
document act1; ///< Акт1
document act2; ///< Акт2
uint64_t primary_key() const { return id; } ///< Основной ключ.
uint64_t by_username() const { return username.value; } ///< Индекс по владельцу
checksum256 by_result_hash() const { return result_hash; } ///< Индекс по хэшу
checksum256 by_hash() const { return result_hash; } ///< Индекс по хэшу проекта
checksum256 by_assignment_hash() const { return assignment_hash; } ///< Индекс по хэшу
checksum256 by_project_hash() const { return project_hash; } ///< Индекс по хэшу проекта
uint128_t by_result_user() const {
return combine_checksum_ids(result_hash, username);
uint128_t by_assignment_user() const {
return combine_checksum_ids(assignment_hash, username);
}
};
typedef eosio::multi_index<"claims"_n, claim,
indexed_by<"byusername"_n, const_mem_fun<claim, uint64_t, &claim::by_username>>,
indexed_by<"byresult"_n, const_mem_fun<claim, checksum256, &claim::by_result_hash>>,
indexed_by<"byprojecthash"_n, const_mem_fun<claim, checksum256, &claim::by_project_hash>>,
indexed_by<"byresuser"_n, const_mem_fun<claim, uint128_t, &claim::by_result_user>>
> claim_index;
typedef eosio::multi_index<"results"_n, result,
indexed_by<"byusername"_n, const_mem_fun<result, uint64_t, &result::by_username>>,
indexed_by<"byhash"_n, const_mem_fun<result, checksum256, &result::by_hash>>,
indexed_by<"byassignment"_n, const_mem_fun<result, checksum256, &result::by_assignment_hash>>,
indexed_by<"byprojecthash"_n, const_mem_fun<result, checksum256, &result::by_project_hash>>,
indexed_by<"byresuser"_n, const_mem_fun<result, uint128_t, &result::by_assignment_user>>
> result_index;
struct [[eosio::table, eosio::contract(CAPITAL)]] convert {
uint64_t id;
checksum256 project_hash;
checksum256 result_hash;
checksum256 assignment_hash;
checksum256 convert_hash;
eosio::name coopname;
@@ -372,18 +400,18 @@ struct [[eosio::table, eosio::contract(CAPITAL)]] convert {
uint64_t primary_key() const { return id; } ///< Основной ключ.
uint64_t by_username() const { return username.value; } ///< Индекс по владельцу
checksum256 by_convert_hash() const { return convert_hash; } ///< Индекс по хэшу
checksum256 by_result_hash() const { return result_hash; } ///< Индекс по хэшу
checksum256 by_assignment_hash() const { return assignment_hash; } ///< Индекс по хэшу
checksum256 by_project_hash() const { return project_hash; } ///< Индекс по хэшу проекта
uint128_t by_result_user() const {
return combine_checksum_ids(result_hash, username);
uint128_t by_assignment_user() const {
return combine_checksum_ids(assignment_hash, username);
}
};
typedef eosio::multi_index<"converts"_n, convert,
indexed_by<"byusername"_n, const_mem_fun<convert, uint64_t, &convert::by_username>>,
indexed_by<"byhash"_n, const_mem_fun<convert, checksum256, &convert::by_convert_hash>>,
indexed_by<"byresult"_n, const_mem_fun<convert, checksum256, &convert::by_result_hash>>,
indexed_by<"byassignment"_n, const_mem_fun<convert, checksum256, &convert::by_assignment_hash>>,
indexed_by<"byprojecthash"_n, const_mem_fun<convert, checksum256, &convert::by_project_hash>>
> convert_index;
@@ -410,60 +438,32 @@ struct [[eosio::table, eosio::contract(CAPITAL)]] author {
> authors_index;
/**
* @brief Таблица для учёта авторства на результатах
* Принимает в себя копии объектов авторов из проекта. И используется для распределения премий по конкретному результату.
*/
struct [[eosio::table, eosio::contract(CAPITAL)]] result_author {
uint64_t id;
checksum256 project_hash;
checksum256 result_hash;
eosio::name username;
uint64_t shares;
uint64_t primary_key() const { return id; } ///< Основной ключ
uint64_t by_username() const { return username.value; } ///< Индекс по имени пользователя
checksum256 by_project_hash() const { return project_hash; } ///< Индекс по хэшу идеи
uint128_t by_result_author() const {
return combine_checksum_ids(project_hash, username);
}
};
typedef eosio::multi_index<"resauthors"_n, result_author,
indexed_by<"byusername"_n, const_mem_fun<result_author, uint64_t, &result_author::by_username>>,
indexed_by<"byprojecthash"_n, const_mem_fun<result_author, checksum256, &result_author::by_project_hash>>,
indexed_by<"byresauthor"_n, const_mem_fun<result_author, uint128_t, &result_author::by_result_author>>
> result_authors_index;
struct [[eosio::table, eosio::contract(CAPITAL)]] creator {
uint64_t id; ///< id и primary_key
checksum256 project_hash; ///< Хэш идеи
checksum256 result_hash; ///< Хэш результата интеллектуальной деятельности
checksum256 assignment_hash; ///< Хэш задания интеллектуальной деятельности
eosio::name username; ///< Имя пользователя
eosio::asset spend = asset(0, _root_govern_symbol); ///< Стоимость использованных ресурсов
eosio::asset spended = asset(0, _root_govern_symbol); ///< Стоимость использованных ресурсов
uint64_t primary_key() const { return id; }
checksum256 by_result_hash() const { return result_hash; }
checksum256 by_assignment_hash() const { return assignment_hash; }
checksum256 by_project_hash() const { return project_hash; }
uint128_t by_result_creator() const {
return combine_checksum_ids(result_hash, username);
uint128_t by_assignment_creator() const {
return combine_checksum_ids(assignment_hash, username);
}
uint64_t by_username() const { return username.value; }
};
typedef eosio::multi_index<"creators"_n, creator,
indexed_by<"byresulthash"_n, const_mem_fun<creator, checksum256, &creator::by_result_hash>>,
indexed_by<"byassignment"_n, const_mem_fun<creator, checksum256, &creator::by_assignment_hash>>,
indexed_by<"byprojecthash"_n, const_mem_fun<creator, checksum256, &creator::by_project_hash>>,
indexed_by<"byusername"_n, const_mem_fun<creator, uint64_t, &creator::by_username>>,
indexed_by<"byresultcrtr"_n, const_mem_fun<creator, uint128_t, &creator::by_result_creator>>
indexed_by<"byassigncrtr"_n, const_mem_fun<creator, uint128_t, &creator::by_assignment_creator>>
> creators_index;
/**
@@ -513,7 +513,7 @@ namespace capital_tables {
uint64_t id; ///< Уникальный ID запроса на возврат.
name coopname; ///< Имя аккаунта кооператива
checksum256 project_hash; ///< Хэш проекта
checksum256 result_hash; ///< Хэш результата
checksum256 assignment_hash; ///< Хэш задания
checksum256 withdraw_hash; ///< Уникальный внешний ключ
name username; ///< Имя аккаунта участника, запрашивающего возврат.
name status = "created"_n; ///< Статус взноса-возврата (created | approved | )
@@ -539,7 +539,7 @@ namespace capital_tables {
indexed_by<"byhash"_n, const_mem_fun<result_withdraw, checksum256, &result_withdraw::by_hash>>,
indexed_by<"byusername"_n, const_mem_fun<result_withdraw, uint64_t, &result_withdraw::by_account>>,
indexed_by<"bycreated"_n, const_mem_fun<result_withdraw, uint64_t, &result_withdraw::by_created>>
> result_withdraws_index; ///< Таблица для хранения запросов на возврат из результата.
> result_withdraws_index; ///< Таблица для хранения запросов на возврат из задания.
struct [[eosio::table, eosio::contract(CAPITAL)]] project_withdraw {
@@ -0,0 +1,50 @@
void capital::createassign(name coopname, name application, checksum256 project_hash, checksum256 assignment_hash, eosio::name assignee, std::string description) {
require_auth(coopname);
auto project = get_project(coopname, project_hash);
eosio::check(project.has_value(), "проект не найден");
// Проверяем, существует ли уже assignment
assignment_index assignments(_capital, coopname.value);
auto assignment = get_assignment(coopname, assignment_hash);
eosio::check(!assignment.has_value(), "Объект задания уже существует");
get_participant_or_fail(coopname, assignee);
// Создаём assignment
assignments.emplace(coopname, [&](auto &n) {
n.id = get_global_id_in_scope(_capital, coopname, "assignments"_n);
n.coopname = coopname;
n.assignee = assignee;
n.description = description;
n.project_hash = project_hash;
n.assignment_hash = assignment_hash;
n.authors_shares = project -> authors_shares;
n.authors_count = project -> authors_count;
});
authors_index authors(_capital, coopname.value);
auto authors_hash_index = authors.get_index<"byprojecthash"_n>();
// Перебираем всех авторов с данным project_hash
auto author_itr = authors_hash_index.lower_bound(project_hash);
uint64_t authors_count = 0;
creauthor_index creathors(_capital, coopname.value);
// Копируем запись автора идеи в assignment_authors
while(author_itr != authors_hash_index.end() && author_itr->project_hash == project_hash) {
creathors.emplace(coopname, [&](auto &ra){
ra.id = creathors.available_primary_key();
ra.assignment_hash = assignment_hash;
ra.project_hash = project_hash;
ra.username = author_itr->username;
ra.author_shares = author_itr -> shares;
});
authors_count++;
author_itr++;
}
eosio::check(authors_count > 0, "Нельзя создать задание без установленных авторов в проекте");
}
@@ -0,0 +1,22 @@
void capital::startdistrbn(name coopname, name application, checksum256 assignment_hash) {
check_auth_or_fail(_capital, coopname, application, "start"_n);
auto assignment = get_assignment(coopname, assignment_hash);
eosio::check(assignment.has_value(), "Задание не найдено");
eosio::check(assignment -> status == "opened"_n, "Только задание в статусе opened может быть запущен в распределение");
auto project = get_project(coopname, assignment -> project_hash);
eosio::check(project.has_value(), "проект не найден");
assignment_index assignments(_capital, coopname.value);
auto assignment_for_modify = assignments.find(assignment -> id);
//заносим доступные средства к распределению
assignments.modify(assignment_for_modify, coopname, [&](auto& row) {
row.status = "closed"_n;
row.authors_bonus_remain = row.authors_bonus;
row.creators_base_remain = row.creators_base;
row.creators_bonus_remain = row.creators_bonus;
row.capitalists_bonus_remain = row.capitalists_bonus;
});
};
@@ -1,25 +0,0 @@
void capital::approveclaim(eosio::name coopname, eosio::name application, eosio::name approver, eosio::name username, checksum256 result_hash, document approved_statement){
check_auth_or_fail(_capital, coopname, application, "approveclaim"_n);
verify_document_or_fail(approved_statement);
auto exist_claim = get_claim(coopname, result_hash, username);
eosio::check(exist_claim.has_value(), "Клайм пользователя для результата не существует");
auto result = get_result(coopname, result_hash);
eosio::check(result.has_value(), "Результат не найден");
claim_index claims(_capital, coopname.value);
auto claim = claims.find(exist_claim -> id);
claims.modify(claim, coopname, [&](auto &a) {
a.status = "approved"_n;
a.approved_statement = approved_statement;
});
//отправляем в совет
action(permission_level{ _capital, "active"_n}, _soviet, "createagenda"_n,
std::make_tuple(coopname, claim -> username, _capital, _capital_claim_authorize_action, claim -> id, claim -> claim_statement, std::string("")))
.send();
};
@@ -1,39 +0,0 @@
void capital::capauthclaim(eosio::name coopname, uint64_t claim_id, document decision) {
require_auth(_soviet);
claim_index claims(_capital, coopname.value);
auto claim = claims.find(claim_id);
eosio::check(claim != claims.end(), "Объект запроса доли не найден");
// Проверяем статус
eosio::check(claim -> status == "statement"_n, "Неверный статус");
auto exist = get_project(coopname, claim -> project_hash);
eosio::check(exist.has_value(),"Проект не найден");
project_index projects(_capital, coopname.value);
auto project = projects.find(exist -> id);
auto contributor = get_active_contributor_or_fail(coopname, claim -> project_hash, claim -> username);
contributor_index contributors(_capital, coopname.value);
auto contributor_for_modify = contributors.find(contributor -> id);
contributors.modify(contributor_for_modify, coopname, [&](auto &c){
c.claimed += claim -> total_amount;
c.share_balance += claim -> total_amount;
});
projects.modify(project, coopname, [&](auto &p) {
p.claimed += claim -> total_amount;
p.total_share_balance += claim -> total_amount;
});
std::string memo = "Зачёт части целевого паевого взноса по договору УХД с ID: " + std::to_string(contributor -> id) + " в качестве паевого взноса по программе 'Цифровой Кошелёк' с ID: " + std::to_string(claim_id);
//Увеличиваем баланс средств в капитализации
Wallet::add_blocked_funds(_capital, coopname, claim -> username, claim -> total_amount, _capital_program, memo);
//Удаляем объект claim за ненадобностью
claims.erase(claim);
};
@@ -1,22 +0,0 @@
void capital::claimnow(name coopname, name application, name username, checksum256 result_hash, document statement) {
check_auth_or_fail(_capital, coopname, application, "claimnow"_n);
// проверяем заявление
verify_document_or_fail(statement);
// извлекаем клайм
auto claim = get_claim(coopname, result_hash, username);
eosio::check(claim.has_value(), "Объект запроса доли не найден");
eosio::check(claim->status == "pending"_n, "Нельзя изменить статус клайма");
eosio::check(claim -> username == username, "Неверно указано имя пользователя владельца результата");
// обновляем клайм, добавляя заявление
claim_index claims(_capital, coopname.value);
auto claim_for_modify = claims.find(claim->id);
claims.modify(claim_for_modify, coopname, [&](auto &c){
c.status = "statement"_n;
c.claim_statement = statement;
});
}
@@ -1,131 +0,0 @@
void capital::createclaim(
eosio::name coopname,
eosio::name application,
eosio::name username,
checksum256 result_hash
) {
// Авторизация
check_auth_or_fail(_capital, coopname, application, "createclaim"_n);
// Получаем результат (или кидаем ошибку)
auto exist_result = get_result_or_fail(coopname, result_hash, "Результат не найден");
eosio::check(exist_result.status == "opened"_n, "Распределение стоимости результата еще не начато или уже завершено");
// Проверяем, нет ли уже такого клайма
auto existing_claim = get_claim(coopname, result_hash, username);
eosio::check(!existing_claim.has_value(), "Клайм уже существует");
// Находим запись в таблице results
result_index results(_capital, coopname.value);
auto result = results.find(exist_result.id);
// Получаем контрибьютора
auto exist_contributor = get_active_contributor_or_fail(coopname, exist_result.project_hash, username);
contributor_index contributors(_capital, coopname.value);
auto contributor = contributors.find(exist_contributor->id);
// Попробуем найти resactor
auto optional_resactor = get_resactor(coopname, result_hash, username);
//=== (A) Авторская часть ===
uint64_t author_amount = 0;
{
// Если resactor есть, считаем долю.
if (optional_resactor.has_value()) {
auto resactor = *optional_resactor;
// Исходная сумма (просто authors_bonus.amount)
uint64_t authors_total = result -> authors_bonus.amount;
// Доля пользователя
uint64_t user_auth_shares = resactor.authors_shares;
// Всего долей авторов
uint64_t total_auth_shares = result -> authors_shares;
if (authors_total > 0 && total_auth_shares > 0 && user_auth_shares > 0) {
uint128_t tmp = (uint128_t)authors_total * (uint128_t)user_auth_shares;
author_amount = (uint64_t)(tmp / (uint128_t)total_auth_shares);
print(result->authors_bonus_remain.amount, ", ", author_amount);
eosio::check(result->authors_bonus_remain.amount >= author_amount,
"Недостаточно средств для авторов");
}
}
}
//=== (B) Создательская часть бонусов ===
uint64_t creator_amount = 0;
{
if (optional_resactor.has_value()) {
auto resactor = *optional_resactor;
uint64_t creators_bonus_total = result->creators_bonus.amount; // вся сумма создателей
uint64_t user_creator_bonus_shares = resactor.creators_bonus_shares;
uint64_t total_creators_bonus_shares= result->total_creators_bonus_shares;
if (creators_bonus_total > 0 && total_creators_bonus_shares > 0 && user_creator_bonus_shares > 0) {
uint128_t tmp = (uint128_t)creators_bonus_total * (uint128_t)user_creator_bonus_shares;
creator_amount = (uint64_t)(tmp / (uint128_t)total_creators_bonus_shares);
eosio::check(result->creators_bonus_remain.amount >= creator_amount, "Недостаточно средств для создателей");
}
}
}
//=== (C) Капиталисты ===
// здесь resactor НЕ обязателен, доли берём из глобальных методов
uint64_t capitalist_amount = 0;
{
// Полная сумма для капиталистов
uint64_t capitals_total = result->capitalists_bonus.amount;
// Пользовательские доли берем из кошелька программы
uint64_t user_cap_shares = get_capital_user_share_balance(coopname, username);
// Общее число долей капиталистов во всей программе
uint64_t total_cap_shares = get_capital_program_share_balance(coopname);
if (capitals_total > 0 && total_cap_shares > 0 && user_cap_shares > 0) {
uint128_t tmp = (uint128_t)capitals_total * (uint128_t)user_cap_shares;
capitalist_amount = (uint64_t)(tmp / (uint128_t)total_cap_shares);
eosio::check(result->capitalists_bonus_remain.amount >= capitalist_amount,
"Недостаточно средств для пайщиков");
}
}
//=== Складываем всё ===
uint64_t total_claim_amount = author_amount + creator_amount + capitalist_amount;
eosio::asset total_amount(total_claim_amount, _root_govern_symbol);
//=== Создаём запись в claims ===
claim_index claims(_capital, coopname.value);
uint64_t claim_id = get_global_id_in_scope(_capital, coopname, "claims"_n);
claims.emplace(coopname, [&](auto &n) {
n.id = claim_id;
n.username = username;
n.result_hash = result_hash;
n.coopname = coopname;
n.status = "created"_n;
n.author_amount = asset(author_amount, _root_govern_symbol);
n.creator_amount = asset(creator_amount, _root_govern_symbol);
n.capitalist_amount = asset(capitalist_amount, _root_govern_symbol);
n.total_amount = total_amount;
});
//=== Уменьшаем остаток bonus_remain ===
results.modify(result, coopname, [&](auto &r){
// авторы
if (author_amount > 0) {
r.authors_bonus_remain -= eosio::asset(author_amount, _root_govern_symbol);
}
// создатели
if (creator_amount > 0) {
r.creators_bonus_remain -= eosio::asset(creator_amount, _root_govern_symbol);
}
// капиталисты
if (capitalist_amount > 0) {
r.capitalists_bonus_remain -= eosio::asset(capitalist_amount, _root_govern_symbol);
}
});
}
@@ -1,27 +1,85 @@
void capital::approvecmmt(eosio::name coopname, eosio::name application, eosio::name approver, checksum256 commit_hash, document approved_statement){
check_auth_or_fail(_capital, coopname, application, "approvecmmt"_n);
verify_document_or_fail(approved_statement);
void capital::approvecmmt(eosio::name coopname, checksum256 commit_hash, document empty_document) {
require_auth(_soviet);
auto exist_commit = get_commit(coopname, commit_hash);
eosio::check(exist_commit.has_value(), "Действие с указанным хэшем не существует");
auto result = get_result(coopname, exist_commit -> result_hash);
eosio::check(result.has_value(), "Результат не найден");
eosio::check(result -> status == "created"_n, "Нельзя добавить действие в уже закрытый результат");
eosio::check(exist_commit.has_value(), "Коммит не найден");
commit_index commits(_capital, coopname.value);
auto commit = commits.find(exist_commit -> id);
commits.modify(commit, coopname, [&](auto &a) {
a.status = "approved"_n;
a.approved_statement = approved_statement;
auto exist_assignment = get_assignment(coopname, commit -> assignment_hash);
eosio::check(exist_assignment.has_value(), "Задание не найдено");
eosio::check(exist_assignment -> status == "opened"_n, "Нельзя добавить коммит в уже закрытое задание");
// Обновляем assignment
assignment_index assignments(_capital, coopname.value);
auto assignment = assignments.find(exist_assignment -> id);
assignments.modify(assignment, _capital, [&](auto &row) {
row.spended += commit -> spended;
row.creators_base += commit -> spended;
row.generated += commit -> generated;
row.creators_bonus += commit -> creators_bonus;
row.authors_bonus += commit -> authors_bonus;
row.capitalists_bonus += commit -> capitalists_bonus;
row.total += commit -> total;
row.total_creators_bonus_shares += commit-> spended.amount;
row.commits_count++;
});
// Обновляем проект
auto exist_project = get_project(coopname, commit -> project_hash);
eosio::check(exist_project.has_value(),"Проект не найден");
project_index projects(_capital, coopname.value);
auto project = projects.find(exist_project -> id);
//отправляем в совет
action(permission_level{ _capital, "active"_n}, _soviet, "createagenda"_n,
std::make_tuple(coopname, commit -> username, _capital, _capital_commit_authorize_action, commit -> id, commit -> contribution_statement, std::string("")))
.send();
projects.modify(project, _capital, [&](auto &p) {
p.spended += commit -> spended;
p.generated += commit -> generated;
p.creators_base += commit -> spended;
p.creators_bonus += commit -> creators_bonus;
p.authors_bonus += commit -> authors_bonus;
p.capitalists_bonus += commit -> capitalists_bonus;
p.total += commit -> total;
p.commits_count++;
});
// Обновляем актора и проект
auto exist_contributor = get_active_contributor_or_fail(coopname, commit -> project_hash, commit -> username);
contributor_index contributors(_capital, coopname.value);
auto contributor = contributors.find(exist_contributor -> id);
contributors.modify(contributor, coopname, [&](auto &c){
c.spended += commit->spended;
c.contributed_hours += commit->contributed_hours;
});
// Обновляем запись в creauthors (создательские доли)
auto exist_creauthor = get_creauthor(coopname, commit->assignment_hash, commit->username);
creauthor_index creathors(_capital, coopname.value);
if (!exist_creauthor.has_value()) {
creathors.emplace(_capital, [&](auto &ra){
ra.id = creathors.available_primary_key();
ra.project_hash = commit->project_hash;
ra.assignment_hash = commit->assignment_hash;
ra.username = commit->username;
ra.spended = commit->spended;
ra.contributed_hours = commit->contributed_hours;
// сумма, которая доступна для получения ссуды и используется в качества залога
ra.provisional_amount = commit->spended;
ra.creator_bonus_shares = commit->spended.amount;
});
} else {
auto creauthor = creathors.find(exist_creauthor->id);
creathors.modify(creauthor, _capital, [&](auto &ra) {
ra.spended += commit->spended;
ra.contributed_hours += commit->contributed_hours;
ra.provisional_amount += commit->spended;
ra.creator_bonus_shares += commit->spended.amount;
});
}
commits.erase(commit);
};
@@ -1,16 +0,0 @@
void capital::capauthcmmt(eosio::name coopname, uint64_t commit_id, document authorization) {
require_auth(_soviet);
commit_index commits(_capital, coopname.value);
auto commit = commits.find(commit_id);
eosio::check(commit != commits.end(), "Коммит не найден");
auto contributor = get_active_contributor_or_fail(coopname, commit->project_hash, commit->username);
//TODO: change to coopname
commits.modify(commit, _soviet, [&](auto &n) {
n.status = "authorized"_n;
n.authorization = authorization;
});
};
@@ -1,35 +1,63 @@
void capital::createcmmt(eosio::name coopname, eosio::name application, eosio::name creator, checksum256 result_hash, checksum256 commit_hash, uint64_t contributed_hours, document contribution_statement){
check_auth_or_fail(_capital, coopname, application, "createcmmt"_n);
void capital::createcmmt(eosio::name coopname, eosio::name application, eosio::name username, checksum256 assignment_hash, checksum256 commit_hash, uint64_t contributed_hours){
require_auth(coopname);
verify_document_or_fail(contribution_statement);
auto assignment = get_assignment(coopname, assignment_hash);
eosio::check(assignment.has_value(), "Задание не найдено");
auto result = get_result(coopname, result_hash);
eosio::check(result.has_value(), "Результат не найден");
auto contributor = get_active_contributor_or_fail(coopname, result -> project_hash, creator);
auto contributor = get_active_contributor_or_fail(coopname, assignment -> project_hash, username);
auto exist_commit = get_commit(coopname, commit_hash);
eosio::check(!exist_commit.has_value(), "Действие с указанным хэшем уже существует");
eosio::check(contributed_hours > 0, "Только положительная сумма");
eosio::check(contributed_hours > 0, "Только положительная сумма часов");
eosio::asset spend = contributor -> rate_per_hour * contributed_hours;
// считаем сумму фактических затрат создателя на основе ставки в час и потраченного времени
eosio::asset spended = contributor -> rate_per_hour * contributed_hours;
// Вычисляем премии на основе суммы фактических затрат
auto amounts = capital::calculcate_capital_amounts(spended);
commit_index commits(_capital, coopname.value);
auto commit_id = get_global_id_in_scope(_capital, coopname, "commits"_n);
//TODO: надо смотреть надо ли здесь добавлять creator_base и author_base
commits.emplace(coopname, [&](auto &a) {
a.id = get_global_id_in_scope(_capital, coopname, "commits"_n);
a.id = commit_id;
a.status = "created"_n;
a.coopname = coopname;
a.application = application;
a.username = creator;
a.project_hash = result -> project_hash;
a.result_hash = result_hash;
a.username = username;
a.project_hash = assignment -> project_hash;
a.assignment_hash = assignment_hash;
a.commit_hash = commit_hash;
a.contributed_hours = contributed_hours;
a.spend = spend;
a.rate_per_hour = contributor -> rate_per_hour;
a.spended = spended;
a.generated = amounts.generated;
a.creators_bonus = amounts.creators_bonus;
a.authors_bonus = amounts.authors_bonus;
a.capitalists_bonus = amounts.capitalists_bonus;
a.total = amounts.total;
a.created_at = current_time_point();
a.contribution_statement = contribution_statement;
});
auto empty_doc = document{};
//отправить на approve председателю
Action::send<createapprv_interface>(
_soviet,
"createapprv"_n,
_capital,
coopname,
username,
empty_doc,
commit_hash,
_capital,
"approvecmmt"_n,
"declinecmmt"_n,
std::string("")
);
}
@@ -0,0 +1,14 @@
void capital::declinecmmt(eosio::name coopname, checksum256 commit_hash, std::string reason) {
require_auth(_soviet);
auto exist_commit = get_commit(coopname, commit_hash);
eosio::check(exist_commit.has_value(), "Коммит не найден");
commit_index commits(_capital, coopname.value);
auto commit = commits.find(exist_commit -> id);
commits.modify(commit, coopname, [&](auto &c) {
c.status = "declined"_n;
c.decline_comment = reason;
});
}
@@ -0,0 +1,14 @@
void capital::delcmmt(eosio::name coopname, eosio::name application, eosio::name approver, checksum256 commit_hash) {
require_auth(coopname);
auto exist_commit = get_commit(coopname, commit_hash);
eosio::check(exist_commit.has_value(), "Коммит с указанным хэшем не существует");
commit_index commits(_capital, coopname.value);
auto commit = commits.find(exist_commit->id);
eosio::check(commit != commits.end(), "Коммит не найден");
eosio::check(commit->status == "declined"_n, "Удалять можно только отклонённые коммиты");
commits.erase(commit);
}
@@ -1,124 +0,0 @@
/**
* @brief Принятие второго акта (act2). Убираем любой CRPS, просто плюсуем бонусы.
*/
void capital::setact2(
eosio::name coopname,
eosio::name application,
eosio::name username,
checksum256 commit_hash,
document act
) {
check_auth_or_fail(_capital, coopname, application, "setact2"_n);
verify_document_or_fail(act);
// Ищем коммит
auto exist_commit = get_commit(coopname, commit_hash);
eosio::check(exist_commit.has_value(), "Коммит не найден");
eosio::check(exist_commit -> username == username, "Неверно указано имя пользователя владельца результата");
eosio::check(exist_commit -> status == "act1"_n, "Неверный статус для поставки акта");
// Обновляем коммит
commit_index commits(_capital, coopname.value);
auto commit = commits.find(exist_commit->id);
commits.modify(commit, coopname, [&](auto &c) {
c.status = "act2"_n;
c.act2 = act;
});
// Ищем результат и проект
auto exist_result = get_result_or_fail(coopname, commit->result_hash, "Результат не найден");
auto exist_project = get_project(coopname, exist_result.project_hash);
eosio::check(exist_project.has_value(), "Проект не найден");
// Ищем вкладчика
auto exist_contributor = get_active_contributor_or_fail(coopname, exist_result.project_hash, commit->username);
contributor_index contributors(_capital, coopname.value);
auto contributor = contributors.find(exist_contributor->id);
// Распределяем spend на available / for_convert
// convert_percent — целое число от 0 до HUNDR_PERCENTS
int64_t for_convert_amount = (commit->spend.amount * contributor->convert_percent) / HUNDR_PERCENTS;
eosio::asset for_convert(for_convert_amount, commit->spend.symbol);
eosio::asset available = commit->spend - for_convert;
// Вычисляем bonus
auto br = capital::calculcate_capital_amounts(commit->spend.amount);
eosio::asset creators_bonus (br.creators_bonus, commit->spend.symbol);
eosio::asset authors_bonus (br.authors_bonus, commit->spend.symbol);
eosio::asset generated (br.generated, commit->spend.symbol);
eosio::asset capitalists_bonus (br.capitalists_bonus, commit->spend.symbol);
eosio::asset total (br.total, commit->spend.symbol);
// Обновляем result
result_index results(_capital, coopname.value);
auto result = results.find(exist_result.id);
eosio::check(result->available >= commit->spend, "Недостаточно средств в результате для приёма коммита");
results.modify(result, coopname, [&](auto &row) {
row.available -= commit->spend;
row.spend += commit->spend;
row.creators_amount += commit->spend;
row.total_creators_bonus_shares += commit->spend.amount;
row.creators_bonus += creators_bonus;
row.authors_bonus += authors_bonus;
row.generated_amount += generated;
row.capitalists_bonus += capitalists_bonus;
row.total_amount += total;
row.commits_count++;
});
// Обновляем проект
project_index projects(_capital, coopname.value);
auto project = projects.find(exist_project->id);
// Обновляем актора и проект
contributors.modify(contributor, coopname, [&](auto &c){
c.spend += commit->spend;
c.share_balance += commit->spend;
c.contributed_hours += commit->contributed_hours;
});
projects.modify(project, coopname, [&](auto &p) {
p.generated += generated;
p.spend += commit->spend;
p.available += commit->spend;
p.total_share_balance += commit->spend;
});
// Обновляем запись в resactors (создательские доли)
auto exist_resactor = get_resactor(coopname, commit->result_hash, commit->username);
resactor_index ractors(_capital, coopname.value);
if (!exist_resactor.has_value()) {
ractors.emplace(coopname, [&](auto &ra){
ra.id = ractors.available_primary_key();
ra.project_hash = commit->project_hash;
ra.result_hash = commit->result_hash;
ra.username = commit->username;
ra.creators_bonus_shares = commit->spend.amount;
ra.spend = commit->spend;
ra.for_convert = for_convert;
ra.available = available;
ra.contributed_hours = commit->contributed_hours;
});
} else {
auto resactor = ractors.find(exist_resactor->id);
ractors.modify(resactor, coopname, [&](auto &ra) {
ra.creators_bonus_shares += commit -> spend.amount;
ra.for_convert += for_convert;
ra.available += available;
ra.spend += commit->spend;
ra.contributed_hours += commit->contributed_hours;
});
}
// 10) Добавляем заблокированные средства на договор УХД
std::string memo = "Приём паевого взноса по договору УХД с contributor_id: " + std::to_string(contributor->id);
Wallet::add_blocked_funds(_capital, coopname, contributor->username, commit->spend, _source_program, memo);
}
@@ -4,13 +4,13 @@ void capital::approvecnvrt(eosio::name coopname, eosio::name application, eosio:
verify_document_or_fail(approved_statement);
auto exist_convert = get_convert(coopname, convert_hash);
eosio::check(exist_convert.has_value(), "Конвертация пользователя для результата не найдена");
eosio::check(exist_convert.has_value(), "Конвертация пользователя для задананиеа не найдена");
convert_index converts(_capital, coopname.value);
auto convert = converts.find(exist_convert -> id);
auto result = get_result(coopname, convert -> result_hash);
eosio::check(result.has_value(), "Результат не найден");
auto assignment = get_assignment(coopname, convert -> assignment_hash);
eosio::check(assignment.has_value(), "Задание не найдено");
// добавляем доли актору и отмечаем статистику сконвертированных сумм
auto contributor = get_active_contributor_or_fail(coopname, convert -> project_hash, convert -> username);
@@ -36,7 +36,7 @@ void capital::approvecnvrt(eosio::name coopname, eosio::name application, eosio:
std::string memo = "Зачёт части целевого паевого взноса по договору УХД с ID: " + std::to_string(contributor -> id) + " в качестве паевого взноса по программе 'Капитализация' с ID: " + std::to_string(convert -> id);
//Списываем баланс средств с УХД только при наличии convert_amount в claim
//Списываем баланс средств с УХД только при наличии convert_amount в result
Wallet::sub_blocked_funds(_capital, coopname, convert -> username, convert -> convert_amount, _source_program, memo);
//Увеличиваем баланс средств в капитализации
@@ -1,36 +1,40 @@
/**
* @brief
*
*/
void capital::createcnvrt(
eosio::name coopname,
eosio::name application,
eosio::name username,
checksum256 result_hash,
checksum256 assignment_hash,
checksum256 convert_hash,
document convert_statement
) {
// Авторизация
check_auth_or_fail(_capital, coopname, application, "createcnvrt"_n);
require_auth(coopname);
// Получаем результат (или кидаем ошибку)
auto exist_result = get_result_or_fail(coopname, result_hash, "Результат не найден");
eosio::check(exist_result.status == "opened"_n || exist_result.status == "created"_n, "Распределение стоимости результата еще не начато или уже завершено");
// Получаем задание (или кидаем ошибку)
auto exist_assignment = get_assignment_or_fail(coopname, assignment_hash, "Задание не найдено");
eosio::check(exist_assignment.status == "closed"_n, "Распределение стоимости задания еще не начато");
// Проверяем, нет ли уже такой конвертации
auto existing_convert = get_convert(coopname, convert_hash);
eosio::check(!existing_convert.has_value(), "Объект конвертации с указанным хэшем уже существует");
// Находим запись в таблице results
result_index results(_capital, coopname.value);
auto result = results.find(exist_result.id);
// Находим запись в таблице assignments
assignment_index assignments(_capital, coopname.value);
auto assignment = assignments.find(exist_assignment.id);
// Получаем контрибьютора
auto exist_contributor = get_active_contributor_or_fail(coopname, exist_result.project_hash, username);
auto exist_contributor = get_active_contributor_or_fail(coopname, exist_assignment.project_hash, username);
contributor_index contributors(_capital, coopname.value);
auto contributor = contributors.find(exist_contributor->id);
// Попробуем найти resactor
auto exist_resactor = get_resactor_or_fail(coopname, result_hash, username, "Объект актора в результате не найден");
// Находим creauthor
auto exist_creauthor = get_creauthor_or_fail(coopname, assignment_hash, username, "Объект актора в результате не найден");
//=== Сумма к конвертации ===
asset convert_amount = exist_resactor.available + exist_resactor.for_convert;
asset convert_amount = exist_creauthor.available + exist_creauthor.for_convert;
eosio::check(convert_amount.amount > 0, "Нет доступных средств для конвертации");
//=== Создаём запись в converts ===
@@ -40,8 +44,8 @@ void capital::createcnvrt(
converts.emplace(coopname, [&](auto &n) {
n.id = convert_id;
n.username = username;
n.project_hash = exist_result.project_hash;
n.result_hash = result_hash;
n.project_hash = exist_assignment.project_hash;
n.assignment_hash = assignment_hash;
n.convert_hash = convert_hash;
n.coopname = coopname;
n.status = "created"_n;
@@ -49,18 +53,18 @@ void capital::createcnvrt(
n.convert_statement = convert_statement;
});
eosio::check(result -> creators_amount_remain >= convert_amount, "Недостаточно средств в result.creators_amount_remain для конвертации");
eosio::check(assignment -> creators_base_remain >= convert_amount, "Недостаточно средств в assignment.creators_base_remain для конвертации");
results.modify(result, coopname, [&](auto &r) {
r.creators_amount_remain -= convert_amount;
assignments.modify(assignment, coopname, [&](auto &r) {
r.creators_base_remain -= convert_amount;
});
resactor_index ractors(_capital, coopname.value);
creauthor_index creathors(_capital, coopname.value);
auto resactor = ractors.find(exist_resactor.id);
auto creauthor = creathors.find(exist_creauthor.id);
//обнуляем доступные средства к выводу или конвертации
ractors.modify(resactor, coopname, [&](auto &ra) {
creathors.modify(creauthor, coopname, [&](auto &ra) {
ra.for_convert = asset(0, _root_govern_symbol);
ra.available = asset(0, _root_govern_symbol);
});
@@ -0,0 +1,32 @@
void capital::approvedebt(eosio::name coopname, checksum256 debt_hash, document approved_statement)
{
require_auth(_soviet);
debts_index debts(_capital, coopname.value);
auto exist_debt = get_debt(coopname, debt_hash);
eosio::check(exist_debt.has_value(), "Долг не найден");
auto debt = debts.find(exist_debt -> id);
debts.modify(debt, coopname, [&](auto& d) {
d.status = "approved"_n;
d.approved_statement = approved_statement;
});
//отправляем в совет
Action::send<createagenda_interface>(
_soviet,
"createagenda"_n,
_capital,
coopname,
debt -> username,
get_valid_soviet_action("createdebt"_n),
debt_hash,
_capital,
"debtauthcnfr"_n,
"declinedebt"_n,
debt -> statement,
std::string("")
);
}
@@ -0,0 +1,75 @@
void capital::createdebt(name coopname, name username, checksum256 assignment_hash, checksum256 debt_hash, asset amount, time_point_sec repaid_at, document statement) {
require_auth(coopname);
verify_document_or_fail(statement);
Wallet::validate_asset(amount);
auto exist_assignment = get_assignment(coopname, assignment_hash);
eosio::check(exist_assignment.has_value(), "Задание не найдено");
assignment_index assignments(_capital, coopname.value);
auto assignment = assignments.find(exist_assignment -> id);
eosio::check(assignment -> status == "opened"_n, "Только результаты в статусе opened могут быть основанием для выдачи ссуды");
auto exist_contributor = get_active_contributor_or_fail(coopname, assignment -> project_hash, username);
eosio::check(exist_contributor.has_value(), "Договор УХД с пайщиком не найден");
contributor_index contributors(_capital, coopname.value);
auto contributor = contributors.find(exist_contributor -> id);
contributors.modify(contributor, coopname, [&](auto &c){
c.debt_amount += amount;
});
auto exist_creauthor = get_creauthor(coopname, assignment_hash, username);
eosio::check(exist_creauthor.has_value(), "Резактор не найден");
creauthor_index creauthors(_capital, coopname.value);
auto creauthor = creauthors.find(exist_creauthor->id);
eosio::check(creauthor -> provisional_amount >= amount, "Недостаточно доступных средств для получения ссуды");
creauthors.modify(creauthor, coopname, [&](auto &ra) {
ra.debt_amount += amount;
ra.provisional_amount -= amount;
});
auto exist_debt = get_debt(coopname, debt_hash);
eosio::check(!exist_debt.has_value(), "Ссуда с указанным hash уже существует");
debts_index debts(_capital, coopname.value);
auto debt_id = get_global_id_in_scope(_soviet, coopname, "debts"_n);
debts.emplace(coopname, [&](auto &d){
d.id = debt_id;
d.coopname = coopname;
d.username = username;
d.debt_hash = debt_hash;
d.assignment_hash = assignment_hash;
d.project_hash = assignment -> project_hash;
d.amount = amount;
d.statement = statement;
d.repaid_at = repaid_at;
});
// Отправляем в совет approve-запрос
action(
permission_level{_capital, "active"_n}, // кто вызывает
_soviet,
"createapprv"_n,
std::make_tuple(
coopname,
username,
statement,
debt_hash, // внешний ID
_capital, // callback_contract (текущий контракт)
"approvedebt"_n, // callback_action_approve
"declinedebt"_n, // callback_action_decline
std::string("")
)
).send();
}
@@ -0,0 +1,43 @@
//действие вызывается советом как коллбэк при положительном решении по вопросу выдачи ссуд
//вызывает контракт шлюза для регистрации исходящего платежа
void capital::debtauthcnfr(eosio::name coopname, checksum256 debt_hash, document decision) {
require_auth(_soviet);
auto exist_debt = get_debt(coopname, debt_hash);
eosio::check(exist_debt.has_value(), "Долг не найден");
debts_index debts(_capital, coopname.value);
auto debt = debts.find(exist_debt -> id);
debts.modify(debt, _capital, [&](auto &d){
d.status = "authorized"_n;
d.authorization = decision;
});
//Создаём объект долга в контракте loan
Action::send<createdebt_interface>(
_loan,
"createdebt"_n,
_capital,
coopname,
debt -> username,
debt -> debt_hash,
debt -> repaid_at,
debt -> amount
);
// создаём объект исходящего платежа в gateway с коллбэком после обработки
Action::send<createoutpay_interface>(
_gateway,
"createoutpay"_n,
_capital,
coopname,
debt -> username,
debt -> debt_hash,
debt -> amount,
_capital,
"debtpaycnfrm"_n,
"dclnauthdebt"_n
);
};
@@ -0,0 +1,11 @@
void capital::debtpaycnfrm(name coopname, checksum256 debt_hash) {
require_auth(_gateway);
auto exist_debt = get_debt(coopname, debt_hash);
eosio::check(exist_debt.has_value(), "Долг не найден");
debts_index debts(_capital, coopname.value);
auto debt = debts.find(exist_debt -> id);
debts.erase(debt);
};
@@ -0,0 +1,22 @@
void capital::debtpaydcln(name coopname, checksum256 debt_hash, std::string reason) {
require_auth(_gateway);
auto exist_debt = get_debt(coopname, debt_hash);
eosio::check(exist_debt.has_value(), "Долг не найден");
debts_index debts(_capital, coopname.value);
auto debt = debts.find(exist_debt -> id);
//Удаляем объект долга в контракте loan
Action::send<settledebt_interface>(
_loan,
"settledebt"_n,
_capital,
coopname,
debt -> username,
debt -> debt_hash,
debt -> amount
);
debts.erase(debt);
};
@@ -0,0 +1,38 @@
void capital::declinedebt(name coopname, checksum256 debt_hash, std::string reason) {
//вызывается при отклонении советом или председателем из контракта совета
require_auth(_gateway);
auto exist_debt = get_debt(coopname, debt_hash);
eosio::check(exist_debt.has_value(), "Долг не найден");
debts_index debts(_capital, coopname.value);
auto debt = debts.find(exist_debt -> id);
auto exist_contributor = get_active_contributor_or_fail(coopname, debt -> project_hash, debt -> username);
eosio::check(exist_contributor.has_value(), "Договор УХД с пайщиком не найден");
contributor_index contributors(_capital, coopname.value);
auto contributor = contributors.find(exist_contributor -> id);
eosio::check(contributor -> debt_amount >= debt -> amount, "Возникла какая-то чудовищная ошибка: у пайщика недостаточно средств в долговом кошельке для того, чтобы принять возврат долга. Такого вообще не должно было быть, но если произошло, пожалуйста, срочно обратитесь в поддержку.");
contributors.modify(contributor, coopname, [&](auto &c) {
c.debt_amount -= debt -> amount;
});
auto exist_creauthor = get_creauthor(coopname, debt -> assignment_hash, debt -> username);
eosio::check(exist_creauthor.has_value(), "Резактор не найден");
creauthor_index creauthors(_capital, coopname.value);
auto creauthor = creauthors.find(exist_creauthor->id);
eosio::check(creauthor -> provisional_amount >= debt -> amount, "Недостаточно доступных средств для получения ссуды");
eosio::check(creauthor -> debt_amount >= debt -> amount, "Возникла какая-то чудовищная ошибка: у пайщика недостаточно средств в долговом кошельке для того, чтобы принять возврат долга. Такого вообще не должно было быть, но если произошло, пожалуйста, срочно обратитесь в поддержку.");
creauthors.modify(creauthor, coopname, [&](auto &ra) {
ra.debt_amount -= debt -> amount;
ra.provisional_amount += debt -> amount;
});
debts.erase(debt);
}
@@ -0,0 +1,3 @@
void capital::settledebt(name coopname){
}
@@ -16,20 +16,30 @@ void capital::approveexpns(name coopname, name application, name approver, check
auto contributor = get_active_contributor_or_fail(coopname, expense -> project_hash, expense -> username);
auto exist_result = get_result(coopname, expense -> result_hash);
eosio::check(exist_result.has_value(),"Результат не найден");
eosio::check(exist_result -> available >= expense -> amount, "Недостаточно средств в результате для списания расходов");
auto exist_assignment = get_assignment(coopname, expense -> assignment_hash);
eosio::check(exist_assignment.has_value(),"Задание не найдено");
eosio::check(exist_assignment -> available >= expense -> amount, "Недостаточно средств в результате для списания расходов");
result_index results(_capital, coopname.value);
auto result = results.find(exist_result -> id);
assignment_index assignments(_capital, coopname.value);
auto assignment = assignments.find(exist_assignment -> id);
results.modify(result, coopname, [&](auto &r){
assignments.modify(assignment, coopname, [&](auto &r){
r.available -= expense -> amount;
});
//отправляем в совет
action(permission_level{ _capital, "active"_n}, _soviet, "createagenda"_n,
std::make_tuple(coopname, expense -> username, _capital, _capital_expense_authorize_action, expense -> id, expense -> expense_statement, std::string("")))
.send();
std::make_tuple(
coopname,
expense -> username,
get_valid_soviet_action("capresexpns"_n),
expense_hash,
_capital,
"capauthexpns"_n,
"capdeclexpns"_n,
expense -> expense_statement,
std::string("")
)
).send();
}
@@ -1,10 +1,11 @@
void capital::capauthexpns(eosio::name coopname, uint64_t expense_id, document authorization) {
void capital::capauthexpns(eosio::name coopname, checksum256 expense_hash, document authorization) {
require_auth(_soviet);
expense_index expenses(_capital, coopname.value);
auto expense = expenses.find(expense_id);
auto exist_expense = get_expense(coopname, expense_hash);
eosio::check(exist_expense.has_value(), "Расход не найден");
eosio::check(expense != expenses.end(), "Расход не найден");
expense_index expenses(_capital, coopname.value);
auto expense = expenses.find(exist_expense -> id);
auto contributor = get_active_contributor_or_fail(coopname, expense -> project_hash, expense -> username);
eosio::check(contributor.has_value(), "Договор УХД с пайщиком по проекту не найден");
@@ -16,8 +17,16 @@ void capital::capauthexpns(eosio::name coopname, uint64_t expense_id, document a
});
// создаём объект исходящего платежа в gateway с коллбэком после обработки
action(permission_level{ _capital, "active"_n}, _gateway, _gateway_create_expense_withdraw_action,
std::make_tuple(coopname, ""_n, expense -> username, expense -> expense_hash, expense -> amount, expense -> expense_statement, _capital, _gateway_to_capital_expense_callback_type, std::string("")))
.send();
action(permission_level{ _capital, "active"_n}, _gateway, "createoutpay"_n,
std::make_tuple(
coopname,
expense -> username,
expense -> expense_hash,
expense -> amount,
_capital,
"exppaycnfrm"_n,
"capdeclexpns"_n
)
).send();
}
@@ -0,0 +1,3 @@
void capital::capdeclexpns(name coopname) {
}
@@ -1,4 +1,4 @@
void capital::createexpnse(eosio::name coopname, eosio::name application, checksum256 expense_hash, checksum256 result_hash, name creator, uint64_t fund_id, asset amount, std::string description, document statement){
void capital::createexpnse(eosio::name coopname, eosio::name application, checksum256 expense_hash, checksum256 assignment_hash, name creator, uint64_t fund_id, asset amount, std::string description, document statement){
check_auth_or_fail(_capital, coopname, application, "createexpns"_n);
verify_document_or_fail(statement);
@@ -7,11 +7,11 @@ void capital::createexpnse(eosio::name coopname, eosio::name application, checks
check(amount.is_valid(), "Invalid asset");
check(amount.amount > 0, "Amount must be positive");
result_index results(_capital, coopname.value);
auto result = get_result(coopname, result_hash);
eosio::check(result.has_value(), "Объект результата не существует");
assignment_index assignments(_capital, coopname.value);
auto assignment = get_assignment(coopname, assignment_hash);
eosio::check(assignment.has_value(), "Объект задананиеа не существует");
auto contributor = get_active_contributor_or_fail(coopname, result -> project_hash, creator);
auto contributor = get_active_contributor_or_fail(coopname, assignment -> project_hash, creator);
auto exist_expense = get_expense(coopname, expense_hash);
eosio::check(!exist_expense.has_value(), "Расход с указанным хэшем уже существует");
@@ -25,12 +25,12 @@ void capital::createexpnse(eosio::name coopname, eosio::name application, checks
i.coopname = coopname;
i.application = application;
i.username = creator;
i.project_hash = result -> project_hash;
i.result_hash = result_hash;
i.project_hash = assignment -> project_hash;
i.assignment_hash = assignment_hash;
i.expense_hash = expense_hash;
i.fund_id = fund_id;
i.status = "created"_n;
i.spend_at = current_time_point();
i.spended_at = current_time_point();
i.expense_statement = statement;
i.amount = amount;
i.description = description;
@@ -1,6 +1,6 @@
void capital::expense_withdraw_callback(name coopname, checksum256 expense_hash){
auto payer = check_auth_and_get_payer_or_fail({_gateway});
void capital::exppaycnfrm(eosio::name coopname, checksum256 expense_hash) {
auto payer = check_auth_and_get_payer_or_fail({ _gateway });
auto expense = get_expense(coopname, expense_hash);
eosio::check(expense.has_value(), "Объект расходов не найден");
@@ -8,17 +8,18 @@ void capital::expense_withdraw_callback(name coopname, checksum256 expense_hash)
contributor_index contributors(_capital, coopname.value);
auto contributor_for_modify = contributors.find(contributor -> id);
contributors.modify(contributor_for_modify, coopname, [&](auto &c){
contributors.modify(contributor_for_modify, payer, [&](auto &c){
c.expensed += expense -> amount;
});
auto exist_result = get_result(coopname, expense -> result_hash);
eosio::check(exist_result.has_value(),"Результат не найден");
auto exist_assignment = get_assignment(coopname, expense -> assignment_hash);
eosio::check(exist_assignment.has_value(),"Задание не найдено");
result_index results(_capital, coopname.value);
auto result = results.find(exist_result -> id);
assignment_index assignments(_capital, coopname.value);
auto assignment = assignments.find(exist_assignment -> id);
results.modify(result, coopname, [&](auto &r){
//TODO: make coopname payer
assignments.modify(assignment, payer, [&](auto &r){
r.expensed += expense -> amount;
});
@@ -37,4 +38,4 @@ void capital::expense_withdraw_callback(name coopname, checksum256 expense_hash)
expenses.erase(expense_for_delete);
//TODO: здесь должна быть проводка по фонду
}
}
@@ -15,7 +15,18 @@ void capital::approveinvst(name coopname, name application, name approver, check
});
//отправляем в совет
action(permission_level{ _capital, "active"_n}, _soviet, _capital_invest_authorize_action,
std::make_tuple(coopname, invest -> username, invest -> id, invest -> invest_statement, std::string("")))
.send();
action(permission_level{ _capital, "active"_n}, _soviet, "createagenda"_n,
std::make_tuple(
coopname,
invest -> username,
get_valid_soviet_action("capitalinvst"_n),
invest -> invest_hash,
_capital,
"capauthinvst"_n,
"capdeclinvst"_n,
invest -> invest_statement,
std::string("")
)
).send();
};
@@ -2,14 +2,15 @@
* @brief Принимаем решение совета и вносим средства на кошелек пайщика при УХД
*
*/
void capital::capauthinvst(eosio::name coopname, uint64_t invest_id, document authorization) {
void capital::capauthinvst(eosio::name coopname, checksum256 invest_hash, document authorization) {
require_auth(_soviet);
auto exist_invest = get_invest(coopname, invest_hash);
eosio::check(exist_invest.has_value(), "Инвестиция не найдена");
invest_index invests(_capital, coopname.value);
auto invest = invests.find(invest_id);
eosio::check(invest != invests.end(), "Инвестиция не найдена");
auto invest = invests.find(exist_invest -> id);
auto contributor = get_active_contributor_or_fail(coopname, invest -> project_hash, invest -> username);
eosio::check(contributor.has_value(), "Договор УХД с пайщиком по проекту не найден");
@@ -0,0 +1,3 @@
void capital::capdeclinvst(){
//TODO:
}
@@ -1,4 +1,4 @@
void capital::allocate(eosio::name coopname, eosio::name application, checksum256 project_hash, checksum256 result_hash, eosio::asset amount) {
void capital::allocate(eosio::name coopname, eosio::name application, checksum256 project_hash, checksum256 assignment_hash, eosio::asset amount) {
check_auth_or_fail(_capital, coopname, application, "allocate"_n);
Wallet::validate_asset(amount);
@@ -6,9 +6,9 @@ void capital::allocate(eosio::name coopname, eosio::name application, checksum25
auto project = get_project(coopname, project_hash);
eosio::check(project.has_value(), "Проект с указанным хэшем не найден");
auto result = get_result(coopname, result_hash);
eosio::check(result.has_value(), "Объект результата не найден");
eosio::check(project -> available >= amount, "Недостаточно средств в проекте для аллокации в результат");
auto assignment = get_assignment(coopname, assignment_hash);
eosio::check(assignment.has_value(), "Объект задананиеа не найден");
eosio::check(project -> available >= amount, "Недостаточно средств в проекте для аллокации в задание");
project_index projects(_capital, coopname.value);
auto project_for_modify = projects.find(project -> id);
@@ -18,10 +18,10 @@ void capital::allocate(eosio::name coopname, eosio::name application, checksum25
row.allocated += amount;
});
result_index results(_capital, coopname.value);
auto result_for_modify = results.find(result -> id);
assignment_index assignments(_capital, coopname.value);
auto assignment_for_modify = assignments.find(assignment -> id);
results.modify(result_for_modify, coopname, [&](auto& row) {
assignments.modify(assignment_for_modify, coopname, [&](auto& row) {
row.allocated += amount;
row.available += amount;
});
@@ -1,4 +1,4 @@
void capital::diallocate(eosio::name coopname, eosio::name application, checksum256 project_hash, checksum256 result_hash, eosio::asset amount) {
void capital::diallocate(eosio::name coopname, eosio::name application, checksum256 project_hash, checksum256 assignment_hash, eosio::asset amount) {
check_auth_or_fail(_capital, coopname, application, "diallocate"_n);
Wallet::validate_asset(amount);
@@ -6,14 +6,14 @@ void capital::diallocate(eosio::name coopname, eosio::name application, checksum
auto project = get_project(coopname, project_hash);
eosio::check(project.has_value(), "Проект с указанным хэшем не найден");
auto result = get_result(coopname, result_hash);
eosio::check(result.has_value(), "Объект результата не найден");
eosio::check(result -> available >= amount, "Недостаточно средств в результате для возврата в проект");
auto assignment = get_assignment(coopname, assignment_hash);
eosio::check(assignment.has_value(), "Объект задананиеа не найден");
eosio::check(assignment -> available >= amount, "Недостаточно средств в задананиее для возврата в проект");
result_index results(_capital, coopname.value);
auto result_for_modify = results.find(result -> id);
assignment_index assignments(_capital, coopname.value);
auto assignment_for_modify = assignments.find(assignment -> id);
results.modify(result_for_modify, coopname, [&](auto& row) {
assignments.modify(assignment_for_modify, coopname, [&](auto& row) {
row.available -= amount;
row.allocated -= amount;
});
@@ -1,17 +0,0 @@
void capital::wthdrcallbck(eosio::name coopname, eosio::name callback_type, checksum256 withdraw_hash) {
auto payer = check_auth_and_get_payer_or_fail({ _gateway });
// Обработка на основе типа callback_type
switch(callback_type.value) {
case "expense"_n.value: {
// Логика для типа "expense"
capital::expense_withdraw_callback(coopname, withdraw_hash);
break;
}
default: {
// Обработка для неизвестных типов
eosio::check(false, "Неизвестный тип коллбэка при обработке платежа");
break;
}
}
}
@@ -1,6 +0,0 @@
1. Создаём результат createresult
2. Добавляем создателя addcreator
3.
@@ -0,0 +1,37 @@
void capital::approverslt(eosio::name coopname, eosio::name application, eosio::name approver, checksum256 result_hash, document approved_statement){
require_auth(coopname);
verify_document_or_fail(approved_statement);
auto exist_result = get_result(coopname, result_hash);
eosio::check(exist_result.has_value(), "Клайм пользователя для задания не существует");
auto assignment = get_assignment(coopname, exist_result -> assignment_hash);
eosio::check(assignment.has_value(), "Задание не найдено");
result_index results(_capital, coopname.value);
auto result = results.find(exist_result -> id);
results.modify(result, coopname, [&](auto &a) {
a.status = "approved"_n;
a.approved_statement = approved_statement;
});
//отправляем в совет
Action::send<createagenda_interface>(
_soviet,
"createagenda"_n,
_capital,
coopname,
result -> username,
get_valid_soviet_action("createresult"_n),
result -> result_hash,
_capital,
"authrslt"_n,
"declrslt"_n,
result -> result_statement,
std::string("")
);
};
@@ -0,0 +1,19 @@
void capital::authrslt(eosio::name coopname, checksum256 result_hash, document decision) {
require_auth(_soviet);
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);
// Проверяем статус
eosio::check(result -> status == "statement"_n, "Неверный статус");
results.modify(result, _capital, [&](auto &r){
r.status = "authorized"_n;
r.authorization = decision;
});
};
@@ -1,46 +0,0 @@
void capital::createresult(name coopname, name application, checksum256 project_hash, checksum256 result_hash) {
check_auth_or_fail(_capital, coopname, application, "generate"_n);
auto project = get_project(coopname, project_hash);
eosio::check(project.has_value(), "проект не найден");
// Проверяем, существует ли уже RESULT
result_index results(_capital, coopname.value);
auto result = get_result(coopname, result_hash);
eosio::check(!result.has_value(), "Объект результата уже существует");
// Создаём RESULT
results.emplace(coopname, [&](auto &n){
n.id = get_global_id_in_scope(_capital, coopname, "results"_n);
n.coopname = coopname;
n.project_hash = project_hash;
n.result_hash = result_hash;
n.authors_shares = project -> authors_shares;
n.authors_count = project -> authors_count;
});
authors_index authors(_capital, coopname.value);
auto authors_hash_index = authors.get_index<"byprojecthash"_n>();
// Перебираем всех авторов с данным project_hash
auto author_itr = authors_hash_index.lower_bound(project_hash);
uint64_t authors_count = 0;
resactor_index ractors(_capital, coopname.value);
// Копируем запись автора идеи в result_authors
while(author_itr != authors_hash_index.end() && author_itr->project_hash == project_hash) {
ractors.emplace(coopname, [&](auto &ra){
ra.id = ractors.available_primary_key();
ra.result_hash = result_hash;
ra.project_hash = project_hash;
ra.username = author_itr->username;
ra.authors_shares = author_itr -> shares;
});
authors_count++;
author_itr++;
}
eosio::check(authors_count > 0, "Нельзя создать результат без установленных авторов в проекте");
}
@@ -0,0 +1 @@
declresult
@@ -0,0 +1,20 @@
void capital::pushrslt(name coopname, name application, checksum256 result_hash, document statement) {
require_auth(coopname);
// проверяем заявление
verify_document_or_fail(statement);
// извлекаем клайм
auto result = get_result(coopname, result_hash);
eosio::check(result.has_value(), "Объект результата не найден");
eosio::check(result -> status == "created"_n, "Неверный статус результата");
// обновляем клайм, добавляя заявление
result_index results(_capital, coopname.value);
auto result_for_modify = results.find(result->id);
results.modify(result_for_modify, coopname, [&](auto &c){
c.status = "statement"_n;
c.result_statement = statement;
});
}
@@ -6,10 +6,10 @@ void capital::setact1(eosio::name coopname, eosio::name application, eosio::name
// commits
auto commit = get_commit(coopname, commit_hash);
eosio::check(commit.has_value(), "Объект коммита не найден");
eosio::check(commit -> username == username, "Неверно указано имя пользователя владельца результата");
eosio::check(commit -> username == username, "Неверно указано имя пользователя владельца задананиеа");
auto result = get_result(coopname, commit -> result_hash);
eosio::check(result.has_value(), "Результат не найден");
auto assignment = get_assignment(coopname, commit -> assignment_hash);
eosio::check(assignment.has_value(), "Задание не найдено");
commit_index commits(_capital, coopname.value);
auto commit_for_modify = commits.find(commit -> id);
@@ -17,8 +17,8 @@ void capital::setact1(eosio::name coopname, eosio::name application, eosio::name
// Проверяем статус.
eosio::check(commit_for_modify -> status == "authorized"_n, "Неверный статус для поставки акта приёма-передачи");
commits.modify(commit_for_modify, coopname, [&](auto &n){
n.status = "act1"_n;
n.act1 = act;
});
// commits.modify(commit_for_modify, coopname, [&](auto &n){
// n.status = "act1"_n;
// n.act1 = act;
// });
};
@@ -0,0 +1,55 @@
/**
* @brief Принятие второго акта (act2). Убираем любой CRPS, просто плюсуем бонусы.
*/
void capital::setact2(
eosio::name coopname,
eosio::name application,
eosio::name username,
checksum256 result_hash,
document act
) {
check_auth_or_fail(_capital, coopname, application, "setact2"_n);
verify_document_or_fail(act);
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);
// Проверяем статус
eosio::check(result -> status == "act1"_n, "Неверный статус результата.");
results.modify(result, _capital, [&](auto &r){
r.status = "act2"_n;
r.act1 = act;
});
auto exist = get_project(coopname, result -> project_hash);
eosio::check(exist.has_value(),"Проект не найден");
project_index projects(_capital, coopname.value);
auto project = projects.find(exist -> id);
auto contributor = get_active_contributor_or_fail(coopname, result -> project_hash, result -> username);
contributor_index contributors(_capital, coopname.value);
auto contributor_for_modify = contributors.find(contributor -> id);
contributors.modify(contributor_for_modify, coopname, [&](auto &c){
c.share_balance += result -> creator_base_amount;
});
projects.modify(project, coopname, [&](auto &p) {
p.total_share_balance += result -> creator_base_amount;
});
std::string memo = "Зачёт части целевого паевого взноса по договору УХД с ID: " + std::to_string(contributor -> id) + " в качестве паевого взноса по программе 'Цифровой Кошелёк' с ID: " + std::to_string(result -> id);
//Увеличиваем баланс средств в капитализации
//TODO:
Wallet::add_blocked_funds(_capital, coopname, result -> username, result -> creator_base_amount, _source_program, memo);
//Удаляем объект result за ненадобностью
//???
}
@@ -1,21 +0,0 @@
void capital::startdistrbn(name coopname, name application, checksum256 result_hash) {
check_auth_or_fail(_capital, coopname, application, "start"_n);
auto result = get_result(coopname, result_hash);
eosio::check(result.has_value(), "Результат не найден");
eosio::check(result -> status == "created"_n, "Только результат в статусе created может быть запущен в распределение");
auto project = get_project(coopname, result -> project_hash);
eosio::check(project.has_value(), "проект не найден");
result_index results(_capital, coopname.value);
auto result_for_modify = results.find(result -> id);
results.modify(result_for_modify, coopname, [&](auto& row) {
row.status = "opened"_n;
row.authors_bonus_remain = row.authors_bonus;
row.creators_amount_remain = row.creators_amount;
row.creators_bonus_remain = row.creators_bonus;
row.capitalists_bonus_remain = row.capitalists_bonus;
});
};
@@ -0,0 +1,170 @@
void capital::updaterslt(
eosio::name coopname,
eosio::name application,
eosio::name username,
checksum256 assignment_hash,
checksum256 result_hash
) {
require_auth(coopname);
// Получаем задание (или кидаем ошибку)
auto exist_assignment = get_assignment_or_fail(coopname, assignment_hash, "Задание не найдено");
eosio::check(exist_assignment.status == "closed"_n, "Распределение стоимости задания еще не начато или уже завершено");
// Проверяем, нет ли уже такого клайма
auto existing_result = get_result_by_assignment_and_username(coopname, assignment_hash, username);
eosio::check(!existing_result.has_value(), "Клайм уже существует");
auto exist_result = get_result(coopname, result_hash);
eosio::check(!exist_result.has_value(), "Клайм с указанным хэш уже существует");
// Находим запись в таблице assignments
assignment_index assignments(_capital, coopname.value);
auto assignment = assignments.find(exist_assignment.id);
// Получаем контрибьютора
auto exist_contributor = get_active_contributor_or_fail(coopname, exist_assignment.project_hash, username);
contributor_index contributors(_capital, coopname.value);
auto contributor = contributors.find(exist_contributor->id);
// Попробуем найти creauthor
auto creauthor = get_creauthor(coopname, assignment_hash, username);
//=== (A) Авторская часть ===
eosio::asset author_bonus = asset(0, _root_govern_symbol);
{
if (creauthor.has_value()) {
//общее количество авторских премий всех авторов
uint64_t authors_total = assignment->authors_bonus.amount;
//количество долей автора в результате
uint64_t user_auth_shares = creauthor -> author_shares;
//количество долей всех авторов в результате
uint64_t total_auth_shares = assignment->authors_shares;
if (authors_total > 0 && total_auth_shares > 0 && user_auth_shares > 0) {
//считаем долю автора в авторских премиях задания
uint128_t tmp = (uint128_t)authors_total * (uint128_t)user_auth_shares;
author_bonus.amount = (uint64_t)(tmp / (uint128_t)total_auth_shares);
}
}
}
//=== (B) Создательская часть ===
// Себестоимость
eosio::asset creator_base = asset(0, _root_govern_symbol);
// Премии
eosio::asset creator_bonus = asset(0, _root_govern_symbol);
{
if (creauthor.has_value()) {
//себестоимость потраченного времени создателем возвращаем как есть
creator_base.amount = creauthor -> spended.amount;
//сколько премий создателей на распределении в результате
uint64_t creators_bonus_total = assignment->creators_bonus.amount;
//количество долей премий создателя в результате
uint64_t user_creator_bonus_shares = creauthor -> creator_bonus_shares;
//количество долей премий всех создателей в результате
uint64_t total_creators_bonus_shares = assignment->total_creators_bonus_shares;
if (creators_bonus_total > 0 && total_creators_bonus_shares > 0 && user_creator_bonus_shares > 0) {
uint128_t tmp = (uint128_t)creators_bonus_total * (uint128_t)user_creator_bonus_shares;
//считаем премию автора в результате на основе его доли
creator_bonus.amount = (uint64_t)(tmp / (uint128_t)total_creators_bonus_shares);
}
}
}
//=== (C) Капиталисты ===
eosio::asset capitalist_bonus = asset(0, _root_govern_symbol);
{
//сумма премий капиталистов на распределении
uint64_t capitals_total = assignment->capitalists_bonus.amount;
//количество долей пользователя как капиталиста (участника ЦПП "Капитализация") в премиях капиталистов
uint64_t user_capital_shares = get_capital_user_share_balance(coopname, username);
//общее количество долей капиталистов
uint64_t total_capital_shares = get_capital_program_share_balance(coopname);
if (capitals_total > 0 && total_capital_shares > 0 && user_capital_shares > 0) {
uint128_t tmp = (uint128_t)capitals_total * (uint128_t)user_capital_shares;
//сумма премий пользователя как капиталиста
capitalist_bonus.amount = (uint64_t)(tmp / (uint128_t)total_capital_shares);
}
}
//сумма долга пайщика
eosio::asset debt_amount = asset(0, _root_govern_symbol);
{
if (creauthor.has_value()) {
debt_amount = creauthor -> debt_amount;
}
}
//=== Создаём запись в results ===
result_index results(_capital, coopname.value);
uint64_t result_id = get_global_id_in_scope(_capital, coopname, "results"_n);
eosio::check(creator_base >= debt_amount, "Системная ошибка: долгов больше чем себестоимость взносов. Обратитесь в поддержку");
results.emplace(coopname, [&](auto &n) {
n.id = result_id;
n.username = username;
n.assignment_hash = assignment_hash;
n.result_hash = result_hash;
n.coopname = coopname;
n.status = "created"_n;
//сумма себестоимости взноса создателя
n.creator_base_amount = creator_base;
//сумма долга, которая должна быть погашена отдельным заявлением, если она есть
n.debt_amount = debt_amount;
//доступные средства к возврату или конвертации после выплаты долга
n.available_for_return = creator_base - debt_amount;
//доступные средства к конвертации в "Капитализацию"
n.available_for_convert = author_bonus + creator_bonus + capitalist_bonus;
//разблюдовка по типам премий
n.creator_bonus_amount = creator_bonus;
n.author_bonus_amount = author_bonus;
n.capitalist_bonus_amount = capitalist_bonus;// на эту премию пишем отдельное заявление на взнос
//сумма генераций должна быть внесена отдельным заявлением на взнос, потому - суммируем.
n.generation_amount = creator_base + author_bonus + creator_bonus;
//total_amount - чисто информационный показатель, не используется в расчетах далее
n.total_amount = creator_base + author_bonus + creator_bonus + capitalist_bonus;
});
//проверяем что премий достаточно и ошибок в расчетах нигде нет.
eosio::check(assignment -> authors_bonus_remain >= author_bonus, "Недостаточно средств для выплаты премии автора. Вероятно, техническая ошибка. Пожалуйста, обратитесь в поддержку.");
eosio::check(assignment -> creators_bonus_remain >= creator_bonus, "Недостаточно средств для выплаты премий создателей. Обратитесь в поддержку.");
eosio::check(assignment -> creators_base_remain >= creator_base, "Недостаточно средств для выплаты себестоимости взносов создателей. Обратитесь в поддержку.");
eosio::check(assignment -> capitalists_bonus_remain >= capitalist_bonus, "Недостаточно средств для пайщиков");
//=== Уменьшаем остатки в результате ===
assignments.modify(assignment, coopname, [&](auto &r) {
if (author_bonus.amount > 0) {
r.authors_bonus_remain -= author_bonus;
}
if (creator_bonus.amount > 0) {
r.creators_bonus_remain -= creator_bonus;
}
if (creator_base.amount > 0) {
r.creators_base_remain -= creator_base;
}
if (capitalist_bonus.amount > 0) {
r.capitalists_bonus_remain -= capitalist_bonus;
}
print("capitalists_bonus_remain: ", r.capitalists_bonus_remain.to_string());
print("capitalist_bonus: ", capitalist_bonus.to_string());
print("creators_bonus_remain: ", r.creators_bonus_remain.to_string());
print("creator_bonus: ", creator_bonus.to_string());
});
}
@@ -16,7 +16,17 @@ void capital::approvewthd3(name coopname, name application, name approver, check
//отправляем в совет
action(permission_level{ _capital, "active"_n}, _soviet, "createagenda"_n,
std::make_tuple(coopname, exist_withdraw -> username, _capital, _capital_withdraw_from_program_authorize_action, exist_withdraw -> id, exist_withdraw -> return_statement, std::string("")))
.send();
std::make_tuple(
coopname,
exist_withdraw -> username,
get_valid_soviet_action("capwthdrprog"_n),
withdraw_hash,
_capital,
"capauthwthd3"_n,
"capdeclwthd3"_n,
exist_withdraw -> return_statement,
std::string("")
)
).send();
};
@@ -1,11 +1,15 @@
void capital::capauthwthd3(name coopname, uint64_t withdraw_id, document authorization) {
void capital::capauthwthd3(name coopname, checksum256 withdraw_hash, document authorization) {
require_auth(_soviet);
auto exist_withdraw = get_program_withdraw(coopname, withdraw_hash);
eosio::check(exist_withdraw.has_value(), "Объект возврата не найден");
capital_tables::program_withdraws_index program_withdraws(_capital, coopname.value);
auto withdraw = program_withdraws.find(withdraw_id);
auto withdraw = program_withdraws.find(exist_withdraw -> id);
eosio::check(withdraw != program_withdraws.end(), "Объект возврата не найден");
std::string memo = "Зачёт части целевого паевого взноса по программе 'Капитализация' в качестве паевого взноса по программе 'Цифровой Кошелёк' с ID: " + std::to_string(withdraw_id);
std::string memo = "Зачёт части целевого паевого взноса по программе 'Капитализация' в качестве паевого взноса по программе 'Цифровой Кошелёк' с ID: " + std::to_string(withdraw -> id);
Wallet::sub_blocked_funds(_capital, coopname, withdraw->username, withdraw->amount, _capital_program, memo);
Wallet::add_available_funds(_capital, coopname, withdraw->username, withdraw->amount, _wallet_program, memo);
@@ -0,0 +1,5 @@
void capital::capdeclwthd3(name coopname, checksum256 withdraw_hash, std::string reason) {
require_auth(_soviet);
//TODO: реализовать отмену
}
@@ -16,7 +16,17 @@ void capital::approvewthd2(name coopname, name application, name approver, check
//отправляем в совет
action(permission_level{ _capital, "active"_n}, _soviet, "createagenda"_n,
std::make_tuple(coopname, exist_withdraw -> username, _capital, _capital_withdraw_from_project_authorize_action, exist_withdraw -> id, exist_withdraw -> return_statement, std::string("")))
.send();
std::make_tuple(
coopname,
exist_withdraw -> username,
get_valid_soviet_action("capwthdrproj"_n),
withdraw_hash,
_capital,
"capauthwthd2"_n,
"capdeclwthd2"_n,
exist_withdraw -> return_statement,
std::string("")
)
).send();
};
@@ -1,10 +1,12 @@
void capital::capauthwthd2(name coopname, uint64_t withdraw_id, document authorization) {
void capital::capauthwthd2(name coopname, checksum256 withdraw_hash, document authorization) {
require_auth(_soviet);
auto exist_withdraw = get_project_withdraw(coopname, withdraw_hash);
eosio::check(exist_withdraw.has_value(), "Объект возврата не найден");
capital_tables::project_withdraws_index project_withdraws(_capital, coopname.value);
auto withdraw = project_withdraws.find(withdraw_id);
eosio::check(withdraw != project_withdraws.end(), "Объект возврата не найден");
auto withdraw = project_withdraws.find(exist_withdraw -> id);
std::string memo = "Зачёт части целевого паевого взноса по программе 'Капитализация' в качестве паевого взноса по участию в 'Цифровой Кошелёк'";
Wallet::sub_blocked_funds(_capital, coopname, withdraw->username, withdraw->amount, _capital_program, memo);
@@ -0,0 +1 @@
capdeclwthd2
@@ -42,7 +42,7 @@ void capital::createwthd2(name coopname, name application, name username, checks
}
// Запись возврата
auto exist_withdraw = get_result_withdraw(coopname, withdraw_hash);
auto exist_withdraw = get_project_withdraw(coopname, withdraw_hash);
eosio::check(!exist_withdraw.has_value(), "Заявка на взнос-возврат с таким хэшем уже существует");
capital_tables::project_withdraws_index project_withdraws(_capital, coopname.value);
@@ -16,7 +16,17 @@ void capital::approvewthd1(name coopname, name application, name approver, check
//отправляем в совет
action(permission_level{ _capital, "active"_n}, _soviet, "createagenda"_n,
std::make_tuple(coopname, withdraw -> username, _capital, _capital_withdraw_from_result_authorize_action, withdraw -> id, withdraw -> return_statement, std::string("")))
.send();
std::make_tuple(
coopname,
withdraw -> username,
get_valid_soviet_action("capwthdrres"_n),
withdraw_hash,
_capital,
"capauthwthd1"_n,
"capdeclwthd1"_n,
withdraw -> return_statement,
std::string("")
)
).send();
};
@@ -1,13 +1,15 @@
void capital::capauthwthd1(eosio::name coopname, uint64_t withdraw_id, document authorization) {
void capital::capauthwthd1(eosio::name coopname, checksum256 withdraw_hash, document authorization) {
require_auth(_soviet);
//Получаем объект возврата
auto exist_withdraw = get_result_withdraw(coopname, withdraw_hash);
eosio::check(exist_withdraw.has_value(), "Объект возврата не найден");
capital_tables::result_withdraws_index result_withdraws(_capital, coopname.value);
auto withdraw = result_withdraws.find(withdraw_id);
eosio::check(withdraw != result_withdraws.end(), "Объект взноса-возврата не найден");
auto withdraw = result_withdraws.find(exist_withdraw -> id);
auto exist_contributor = capital::get_active_contributor_or_fail(coopname, withdraw -> project_hash, withdraw -> username);
auto exist_result = get_result_or_fail(coopname, withdraw -> result_hash, "Результат не найден");
auto exist_assignment = get_assignment_or_fail(coopname, withdraw -> assignment_hash, "Задание не найдено");
// списание с УХД
std::string memo_out = "Зачёт части целевого паевого взноса по договору УХД с ID: " + std::to_string(exist_contributor -> id) + " в качестве паевого взноса по программе 'Цифровой Кошелёк'";
@@ -1,58 +1,46 @@
void capital::createwthd1(eosio::name coopname, eosio::name application, eosio::name username, checksum256 result_hash, checksum256 withdraw_hash, asset amount, document return_statement) {
void capital::createwthd1(eosio::name coopname, eosio::name application, eosio::name username, checksum256 assignment_hash, checksum256 withdraw_hash, asset amount, document return_statement) {
check_auth_or_fail(_capital, coopname, application, "createwthd1"_n);
verify_document_or_fail(return_statement);
Wallet::validate_asset(amount);
auto exist_result = get_result(coopname, result_hash);
eosio::check(exist_result.has_value(), "Результат с указанным хэшем не найден");
auto exist_assignment = get_assignment(coopname, assignment_hash);
eosio::check(exist_assignment.has_value(), "Задание с указанным хэшем не найден");
auto exist_project = get_project(coopname, exist_result -> project_hash);
auto exist_project = get_project(coopname, exist_assignment -> project_hash);
eosio::check(exist_project.has_value(), "Проект с указанным хэшем не найден");
//обновить сумму выводов по проекту
project_index projects(_capital, coopname.value);
auto project = projects.find(exist_project->id);
eosio::check(project -> total_share_balance >= amount, "Недостаточно средств в project.total_share_balance проекта для осуществления возврата");
projects.modify(project, _capital, [&](auto &p){
p.withdrawed += amount;
p.total_share_balance -= amount;
});
auto exist_contributor = capital::get_active_contributor_or_fail(coopname, exist_result -> project_hash, username);
auto exist_contributor = capital::get_active_contributor_or_fail(coopname, exist_assignment -> project_hash, username);
contributor_index contributors(_capital, coopname.value);
auto contributor = contributors.find(exist_contributor -> id);
eosio::check(contributor -> share_balance >= amount, "Недостаточно средств в contributor.share_balance для осуществления возврата");
contributors.modify(contributor, coopname, [&](auto &c) {
c.withdrawed += amount;
c.share_balance -= amount;
});
auto exist_resactor = get_resactor_or_fail(coopname, exist_result->result_hash, username, "Объект актора в результате не найден");
auto exist_creauthor = get_creauthor_or_fail(coopname, exist_assignment -> assignment_hash, username, "Объект актора в результате не найден");
resactor_index ractors(_capital, coopname.value);
auto resactor = ractors.find(exist_resactor.id);
creauthor_index creathors(_capital, coopname.value);
auto creauthor = creathors.find(exist_creauthor.id);
eosio::check(resactor -> available >= amount, "Недостаточно средств для создания возврата");
eosio::check(resactor -> creators_bonus_shares >= amount.amount, "Недостаточно средств в creators_bonus_shares для создания возврата");
eosio::check(creauthor -> available >= amount, "Недостаточно средств для создания возврата");
ractors.modify(resactor, coopname, [&](auto &ra) {
creathors.modify(creauthor, coopname, [&](auto &ra) {
ra.available -= amount;
ra.creators_bonus_shares -= amount.amount;
});
result_index results(_capital, coopname.value);
auto result = results.find(exist_result -> id);
assignment_index assignments(_capital, coopname.value);
auto assignment = assignments.find(exist_assignment -> id);
eosio::check(result -> total_creators_bonus_shares >= amount.amount, "Недостаточное количество result -> total_creators_bonus_shares для осуществления возврата");
results.modify(result, coopname, [&](auto &row) {
row.total_creators_bonus_shares -= amount.amount;
});
auto exist_withdraw = get_result_withdraw(coopname, withdraw_hash);
eosio::check(!exist_withdraw.has_value(), "Заявка на возврат с таким хэшем уже существует");
@@ -63,10 +51,11 @@ void capital::createwthd1(eosio::name coopname, eosio::name application, eosio::
w.id = get_global_id_in_scope(_capital, coopname, "withdraws1"_n);
w.coopname = coopname;
w.withdraw_hash = withdraw_hash;
w.result_hash = result_hash;
w.project_hash = exist_result -> project_hash;
w.assignment_hash = assignment_hash;
w.project_hash = exist_assignment -> project_hash;
w.username = username;
w.amount = amount;
w.return_statement = return_statement;
});
};
+9 -148
View File
@@ -2,6 +2,15 @@
#include <ctime>
#include <eosio/transaction.hpp>
#include <eosio/crypto.hpp>
#include "src/createdraft.cpp"
#include "src/createtrans.cpp"
#include "src/deldraft.cpp"
#include "src/deltrans.cpp"
#include "src/editdraft.cpp"
#include "src/edittrans.cpp"
#include "src/upversion.cpp"
using namespace eosio;
[[eosio::action]]
@@ -12,151 +21,3 @@ void draft::migrate() {
void draft::newid(eosio::name scope, uint64_t id) { require_auth(_draft); };
name get_payer_and_check_auth_in_scope(eosio::name scope, eosio::name username, eosio::name action){
eosio::name payer;
if (scope == _draft) {
require_auth(_system);
payer = _system;
}
else {
get_cooperative_or_fail(scope);
check_auth_or_fail(_draft, scope, username, action);
payer = username;
};
return payer;
}
void draft::createdraft(eosio::name scope, eosio::name username, uint64_t registry_id, eosio::name lang, std::string title,
std::string description, std::string context, std::string model, std::string translation_data) {
eosio::name payer = get_payer_and_check_auth_in_scope(scope, username, "createdraft"_n);
drafts_index drafts(_draft, scope.value);
// Проверка существующих записей с тем же registry_id
auto exist = drafts.find(registry_id);
eosio::check(exist == drafts.end(), "Уже существует шаблон с тем же идентификатором. Обновите его.");
// Создание нового шаблона
uint64_t translation_id = get_global_id(_draft, "translation"_n);
drafts.emplace(payer, [&](auto &d) {
d.registry_id = registry_id;
d.version = 1;
d.default_translation_id = translation_id;
d.title = title;
d.description = description;
d.context = context;
d.model = model;
});
translations_index translations(_draft, scope.value);
translations.emplace(payer, [&](auto &t) {
t.id = translation_id;
t.draft_id = registry_id;
t.lang = lang;
t.data = translation_data;
});
// Отправка действия для обновления идентификаторов
action(permission_level{_draft, "active"_n}, _draft, "newid"_n,
std::make_tuple(scope, registry_id))
.send();
};
void draft::editdraft(eosio::name scope, eosio::name username, uint64_t registry_id, std::string title, std::string description, std::string context, std::string model){
eosio::name payer = get_payer_and_check_auth_in_scope(scope, username, "editdraft"_n);
drafts_index drafts(_draft, scope.value);
auto exist = drafts.find(registry_id);
eosio::check(exist != drafts.end(), "Шаблон не найден");
drafts.modify(exist, payer, [&](auto &d){
d.title = title;
d.description = description;
d.context = context;
d.model = model;
});
}
void draft::upversion(eosio::name scope, eosio::name username, uint64_t registry_id){
eosio::name payer = get_payer_and_check_auth_in_scope(scope, username, "editdraft"_n);
drafts_index drafts(_draft, scope.value);
auto exist = drafts.find(registry_id);
eosio::check(exist != drafts.end(), "Шаблон не найден");
drafts.modify(exist, payer, [&](auto &d){
d.version = exist -> version + 1;
});
}
void draft::deldraft(eosio::name scope, eosio::name username, uint64_t registry_id) {
eosio::name payer = get_payer_and_check_auth_in_scope(scope, username, "deldraft"_n);
drafts_index drafts(_draft, _draft.value);
auto draft = drafts.find(registry_id);
eosio::check(draft != drafts.end(), "Шаблон не найден");
drafts.erase(draft);
};
void draft::createtrans(eosio::name scope, eosio::name username, uint64_t registry_id, eosio::name lang, std::string data) {
eosio::name payer = get_payer_and_check_auth_in_scope(scope, username, "createtrans"_n);
drafts_index drafts(_draft, scope.value);
auto draft = drafts.find(registry_id);
eosio::check(draft != drafts.end(), "Документ не найден");
translations_index translations(_draft, scope.value);
auto trans_index_by_draft_and_lang = translations.template get_index<"bydraftlang"_n>();
auto trans_combined_index = combine_ids(registry_id, lang.value);
auto trans = trans_index_by_draft_and_lang.find(trans_combined_index);
eosio::check(trans == trans_index_by_draft_and_lang.end(), "Перевод уже создан для документа");
uint64_t translation_id = get_global_id(_draft, "translation"_n);
translations.emplace(payer, [&](auto &d){
d.id = translation_id;
});
};
void draft::edittrans(eosio::name scope, eosio::name username, uint64_t translate_id, std::string data) {
eosio::name payer = get_payer_and_check_auth_in_scope(scope, username, "edittrans"_n);
translations_index translations(_draft, scope.value);
auto trans = translations.find(translate_id);
eosio::check(trans != translations.end(), "Перевод не найден");
translations.modify(trans, _system, [&](auto &t){
t.data = data;
});
};
void draft::deltrans(eosio::name scope, eosio::name username, uint64_t translate_id) {
eosio::name payer = get_payer_and_check_auth_in_scope(scope, username, "deltrans"_n);
translations_index translations(_draft, scope.value);
auto trans = translations.find(translate_id);
eosio::check(trans != translations.end(), "Объект с переводом не найден");
translations.erase(trans);
};
+17
View File
@@ -37,4 +37,21 @@ public:
[[eosio::action]] void upversion(eosio::name scope, eosio::name username, uint64_t registry_id);
struct [[eosio::table, eosio::contract(DRAFT)]] counts : counts_base {};
inline name get_payer_and_check_auth_in_scope(eosio::name scope, eosio::name username, eosio::name action){
eosio::name payer;
if (scope == _draft) {
require_auth(_system);
payer = _system;
}
else {
get_cooperative_or_fail(scope);
check_auth_or_fail(_draft, scope, username, action);
payer = username;
};
return payer;
}
};
@@ -0,0 +1,39 @@
void draft::createdraft(eosio::name scope, eosio::name username, uint64_t registry_id, eosio::name lang, std::string title,
std::string description, std::string context, std::string model, std::string translation_data) {
eosio::name payer = draft::get_payer_and_check_auth_in_scope(scope, username, "createdraft"_n);
drafts_index drafts(_draft, scope.value);
// Проверка существующих записей с тем же registry_id
auto exist = drafts.find(registry_id);
eosio::check(exist == drafts.end(), "Уже существует шаблон с тем же идентификатором. Обновите его.");
// Создание нового шаблона
uint64_t translation_id = get_global_id(_draft, "translation"_n);
drafts.emplace(payer, [&](auto &d) {
d.registry_id = registry_id;
d.version = 1;
d.default_translation_id = translation_id;
d.title = title;
d.description = description;
d.context = context;
d.model = model;
});
translations_index translations(_draft, scope.value);
translations.emplace(payer, [&](auto &t) {
t.id = translation_id;
t.draft_id = registry_id;
t.lang = lang;
t.data = translation_data;
});
// Отправка действия для обновления идентификаторов
action(permission_level{_draft, "active"_n}, _draft, "newid"_n,
std::make_tuple(scope, registry_id))
.send();
};
@@ -0,0 +1,21 @@
void draft::createtrans(eosio::name scope, eosio::name username, uint64_t registry_id, eosio::name lang, std::string data) {
eosio::name payer = draft::get_payer_and_check_auth_in_scope(scope, username, "createtrans"_n);
drafts_index drafts(_draft, scope.value);
auto draft = drafts.find(registry_id);
eosio::check(draft != drafts.end(), "Документ не найден");
translations_index translations(_draft, scope.value);
auto trans_index_by_draft_and_lang = translations.template get_index<"bydraftlang"_n>();
auto trans_combined_index = combine_ids(registry_id, lang.value);
auto trans = trans_index_by_draft_and_lang.find(trans_combined_index);
eosio::check(trans == trans_index_by_draft_and_lang.end(), "Перевод уже создан для документа");
uint64_t translation_id = get_global_id(_draft, "translation"_n);
translations.emplace(payer, [&](auto &d){
d.id = translation_id;
});
};
@@ -0,0 +1,10 @@
void draft::deldraft(eosio::name scope, eosio::name username, uint64_t registry_id) {
eosio::name payer = draft::get_payer_and_check_auth_in_scope(scope, username, "deldraft"_n);
drafts_index drafts(_draft, _draft.value);
auto draft = drafts.find(registry_id);
eosio::check(draft != drafts.end(), "Шаблон не найден");
drafts.erase(draft);
};
@@ -0,0 +1,11 @@
void draft::deltrans(eosio::name scope, eosio::name username, uint64_t translate_id) {
eosio::name payer = draft::get_payer_and_check_auth_in_scope(scope, username, "deltrans"_n);
translations_index translations(_draft, scope.value);
auto trans = translations.find(translate_id);
eosio::check(trans != translations.end(), "Объект с переводом не найден");
translations.erase(trans);
};
@@ -0,0 +1,15 @@
void draft::editdraft(eosio::name scope, eosio::name username, uint64_t registry_id, std::string title, std::string description, std::string context, std::string model){
eosio::name payer = draft::get_payer_and_check_auth_in_scope(scope, username, "editdraft"_n);
drafts_index drafts(_draft, scope.value);
auto exist = drafts.find(registry_id);
eosio::check(exist != drafts.end(), "Шаблон не найден");
drafts.modify(exist, payer, [&](auto &d){
d.title = title;
d.description = description;
d.context = context;
d.model = model;
});
}
@@ -0,0 +1,12 @@
void draft::edittrans(eosio::name scope, eosio::name username, uint64_t translate_id, std::string data) {
eosio::name payer = draft::get_payer_and_check_auth_in_scope(scope, username, "edittrans"_n);
translations_index translations(_draft, scope.value);
auto trans = translations.find(translate_id);
eosio::check(trans != translations.end(), "Перевод не найден");
translations.modify(trans, _system, [&](auto &t){
t.data = data;
});
};
@@ -0,0 +1,12 @@
void draft::upversion(eosio::name scope, eosio::name username, uint64_t registry_id){
eosio::name payer = draft::get_payer_and_check_auth_in_scope(scope, username, "editdraft"_n);
drafts_index drafts(_draft, scope.value);
auto exist = drafts.find(registry_id);
eosio::check(exist != drafts.end(), "Шаблон не найден");
drafts.modify(exist, payer, [&](auto &d){
d.version = exist -> version + 1;
});
}
+17 -632
View File
@@ -3,11 +3,27 @@
#include <ctime>
#include <eosio/transaction.hpp>
#include "src/addaccum.cpp"
#include "src/addcirculate.cpp"
#include "src/addexpense.cpp"
#include "src/addinitial.cpp"
#include "src/authorize.cpp"
#include "src/delfund.cpp"
#include "src/complete.cpp"
#include "src/createfund.cpp"
#include "src/editfund.cpp"
#include "src/fundwithdraw.cpp"
#include "src/init.cpp"
#include "src/spreadamount.cpp"
#include "src/subaccum.cpp"
#include "src/subcirculate.cpp"
#include "src/subinitial.cpp"
using namespace eosio;
[[eosio::action]] void fund::migrate() {
require_auth(_fund);
}
/**
@@ -33,634 +49,3 @@ using namespace eosio;
uint64_t id) {
require_auth(_fund);
};
/**
* @brief Вызывается при запуске кооператива для создания кооперативного кошелька и некоторых фондов
*
* @param coopname
* @param initial
*/
[[eosio::action]] void fund::init(eosio::name coopname, eosio::asset initial) {
eosio::name payer = check_auth_and_get_payer_or_fail({_soviet, _registrator});
coopwallet_index coopwallet(_fund, coopname.value);
accfunds_index accfunds(_fund, coopname.value);
expfunds_index expfunds(_fund, coopname.value);
auto exist = coopwallet.find(0);
if (exist == coopwallet.end()) {
// кошелёк кооператива
coopwallet.emplace(payer, [&](auto &row) {
row.id = 0;
row.coopname = coopname;
row.circulating_account.available = asset(0, initial.symbol);
row.circulating_account.withdrawed = asset(0, initial.symbol);
row.initial_account.available = asset(0, initial.symbol);
row.initial_account.withdrawed = asset(0, initial.symbol);
row.accumulative_account.available = asset(0, initial.symbol);
row.accumulative_account.withdrawed = asset(0, initial.symbol);
row.accumulative_expense_account.available = asset(0, initial.symbol);
row.accumulative_expense_account.withdrawed = asset(0, initial.symbol);
});
// неделимый
accfunds.emplace(payer, [&](auto &a) {
a.id = get_global_id_in_scope(_fund, coopname, "funds"_n); // 1
a.coopname = coopname;
a.contract = ""_n;
a.name = "Неделимый фонд";
a.description = "";
a.percent = 1 * ONE_PERCENT;
a.available = asset(0, initial.symbol);
a.withdrawed = asset(0, initial.symbol);
});
// резервный
accfunds.emplace(payer, [&](auto &a) {
a.id = get_global_id_in_scope(_fund, coopname, "funds"_n); // 2
a.coopname = coopname;
a.contract = ""_n;
a.name = "Резервный фонд";
a.description = "";
a.percent = 15 * ONE_PERCENT;
a.available = asset(0, initial.symbol);
a.withdrawed = asset(0, initial.symbol);
});
// развития
accfunds.emplace(payer, [&](auto &a) {
a.id = get_global_id_in_scope(_fund, coopname, "funds"_n); // 3
a.coopname = coopname;
a.contract = ""_n;
a.name = "Фонд развития кооперации";
a.description = "";
a.percent = 5 * ONE_PERCENT;
a.available = asset(0, initial.symbol);
a.withdrawed = asset(0, initial.symbol);
});
auto e_id = expfunds.available_primary_key();
// хозяйственный
expfunds.emplace(payer, [&](auto &e) {
e.id = get_global_id_in_scope(_fund, coopname, "funds"_n); // 4
e.coopname = coopname;
e.contract = ""_n;
e.name = "Хозяйственный фонд";
e.description = "";
e.expended = asset(0, initial.symbol);
});
// взаимный
expfunds.emplace(payer, [&](auto &e) {
e.id = get_global_id_in_scope(_fund, coopname, "funds"_n); // 5
e.coopname = coopname;
e.contract = ""_n;
e.name = "Фонд взаимного обеспечения";
e.description = "";
e.expended = asset(0, initial.symbol);
});
}
}
// атомарные транзакции фондового кошелька
// паевый счет
[[eosio::action]] void fund::addcirculate(
eosio::name coopname,
eosio::asset quantity) /// < добавить сумму в паевый фонд
{
eosio::name payer = check_auth_and_get_payer_or_fail({_gateway, _marketplace});
auto cooperative = get_cooperative_or_fail(coopname);
coopwallet_index coopwallet(_fund, coopname.value);
auto wal = coopwallet.find(0);
eosio::check(wal != coopwallet.end(), "Кошелёк кооператива не найден");
coopwallet.modify(wal, _gateway, [&](auto &w) {
w.circulating_account.available += quantity;
});
};
[[eosio::action]] void fund::subcirculate(
eosio::name coopname,
eosio::asset quantity,
bool skip_available_check
) /// < списать сумму из паевого фонда
{
// Только контракт шлюза или маркетплейса может списывать оборотные средства из фонда
eosio::name payer = check_auth_and_get_payer_or_fail({_gateway, _marketplace});
auto cooperative = get_cooperative_or_fail(coopname);
coopwallet_index coopwallet(_fund, coopname.value);
auto wal = coopwallet.find(0);
eosio::check(wal != coopwallet.end(), "Фондовый кошелёк не найден");
if (!skip_available_check)
eosio::check(wal->circulating_account.available >= quantity,
"Недостаточно средств для списания на паевом счете кооператива");
coopwallet.modify(wal, payer, [&](auto &w) {
w.circulating_account.available -= quantity;
w.circulating_account.withdrawed += quantity;
});
};
// фонды накопления
[[eosio::action]] void fund::addaccum(eosio::name coopname, uint64_t fund_id,
eosio::asset quantity) {
require_auth(_fund);
coopwallet_index coopwallet(_fund, coopname.value);
auto wal = coopwallet.find(0);
eosio::check(wal != coopwallet.end(), "Кошелёк кооператива не найден");
coopwallet.modify(wal, _fund, [&](auto &row) {
row.accumulative_account.available += quantity;
});
accfunds_index accfunds(_fund, coopname.value);
auto afund = accfunds.find(fund_id);
eosio::check(afund != accfunds.end(), "Фонд не найден");
accfunds.modify(afund, _fund, [&](auto &a) { a.available += quantity; });
};
[[eosio::action]] void fund::subaccum(eosio::name coopname, uint64_t fund_id,
eosio::asset quantity) {
// списать можно только с помощью вызова метода withdraw смарт-контракта
require_auth(_fund);
coopwallet_index coopwallet(_fund, coopname.value);
auto wal = coopwallet.find(0);
eosio::check(wal != coopwallet.end(), "Фондовый кошелёк не найден");
eosio::check(wal->accumulative_account.available >= quantity,
"Недостаточно средств для списания");
coopwallet.modify(wal, _fund, [&](auto &row) {
row.accumulative_account.available -= quantity;
row.accumulative_account.withdrawed += quantity;
});
accfunds_index accfunds(_fund, coopname.value);
auto afund = accfunds.find(fund_id);
eosio::check(afund != accfunds.end(), "Фонд не найден");
accfunds.modify(afund, _fund, [&](auto &a) {
a.available -= quantity;
a.withdrawed += quantity;
});
};
// фонды списания
[[eosio::action]] void fund::addexpense(eosio::name coopname, uint64_t fund_id,
eosio::asset quantity) {
require_auth(_fund);
coopwallet_index coopwallet(_fund, coopname.value);
auto wal = coopwallet.find(0);
eosio::check(wal != coopwallet.end(), "Фондовый кошелёк не найден");
eosio::check(wal->accumulative_expense_account.available +
wal->initial_account.available >=
quantity,
"Недостаточно средств для списания");
coopwallet.modify(wal, _fund, [&](auto &w) {
/**
* @brief проверить что списание идет по хозяйственному фонду
если да - уменьшить счет членских взносов автоматически на всю возможную
сумму. Остаток снять с накопительного счета списания.
*/
if (fund_id == 4) {
// Списываем из initial_account
eosio::asset from_initial =
std::min(quantity, w.initial_account.available);
w.initial_account.available -= from_initial;
w.initial_account.withdrawed += from_initial;
// Если осталось списание, снимаем с accumulative_expense_account
eosio::asset remaining = quantity - from_initial;
if (remaining.amount > 0) {
w.accumulative_expense_account.available -= remaining;
w.accumulative_expense_account.withdrawed += remaining;
}
} else {
w.accumulative_expense_account.available -= quantity;
w.accumulative_expense_account.withdrawed += quantity;
}
});
expfunds_index expfunds(_fund, coopname.value);
auto efund = expfunds.find(fund_id);
eosio::check(efund != expfunds.end(), "Фонд не найден");
expfunds.modify(efund, _fund, [&](auto &a) { a.expended += quantity; });
};
/**
* @brief Атомарный метод добавления вступительного взноса на счет кошелька кооператива.
*
* @param coopname
* @param quantity
*/
[[eosio::action]] void fund::addinitial(eosio::name coopname,
eosio::asset quantity) {
eosio::name payer = check_auth_and_get_payer_or_fail({_gateway});
auto cooperative = get_cooperative_or_fail(coopname);
coopwallet_index coopwallet(_fund, coopname.value);
auto wal = coopwallet.find(0);
coopwallet.modify(
wal, payer, [&](auto &row) { row.initial_account.available += quantity; });
}
/**
* @brief Атомарный метод списания вступительного взноса. Используется только при отмене операции вступления.
*
* @param coopname
* @param quantity
*/
[[eosio::action]] void fund::subinitial(eosio::name coopname,
eosio::asset quantity) {
eosio::name payer = check_auth_and_get_payer_or_fail({_gateway});
auto cooperative = get_cooperative_or_fail(coopname);
coopwallet_index coopwallet(_fund, coopname.value);
auto wal = coopwallet.find(0);
coopwallet.modify(
wal, payer, [&](auto &row) { row.initial_account.available -= quantity; });
}
//
/**
* @brief Метод распределения членской части взноса по фондам накопления с остатком в
кошельке для распределения по фондам списания. Распределить членские взносы по фондам накопления, положив остаток на накопительный счет списания. На входе мы получаем общую членскую часть, которую распределяем по счетам.
*
* @param coopname
* @param quantity
*/
[[eosio::action]] void fund::spreadamount(eosio::name coopname,
eosio::asset quantity) {
eosio::name payer = check_auth_and_get_payer_or_fail({_marketplace, _gateway, _soviet});
auto cooperative = get_cooperative_or_fail(coopname);
coopwallet_index coopwallet(_fund, coopname.value);
auto wal = coopwallet.find(0);
eosio::check(wal != coopwallet.end(), "Кошелёк кооператива не найден");
accfunds_index accfunds(_fund, coopname.value);
eosio::asset total_accumulated = asset(0, cooperative.initial.symbol);
// готовим пакет транзакций пополнения счетов накопления
for (auto it = accfunds.begin(); it != accfunds.end(); it++) {
eosio::asset fund_quantity = it->percent * quantity / HUNDR_PERCENTS;
total_accumulated += fund_quantity;
action(permission_level{_fund, "active"_n}, _fund, "addaccum"_n,
std::make_tuple(coopname, it->id, fund_quantity))
.send();
};
// считаем сколько не выплачено по счетам накопления
eosio::asset remain_amount = quantity - total_accumulated;
// начисляем на накопительный счет списания
coopwallet.modify(wal, payer, [&](auto &w) {
w.accumulative_expense_account.available += remain_amount;
});
};
/* Метод создания вывода средств из фонда накопления или по фонду списания
* Использование метода не накладывает условие проверки на наличие средств в
* целевом фонде. Проверка на наличие происходит в момент списания (complete)
* после утверждения решения советом. Т.е. совет может принять решение о будущем
* расходе, но фактическое списание осуществится только при наличии средств в
* фондах.
*/
[[eosio::action]] void fund::fundwithdraw(eosio::name coopname,
eosio::name username,
eosio::name type, uint64_t fund_id,
document document,
eosio::asset quantity,
std::string bank_data_id) {
eosio::check(type == _afund_withdraw_action || type == _efund_withdraw_action,
"Неверный тип фонда");
eosio::name payer;
if (type == _afund_withdraw_action) {
accfunds_index accfunds(_fund, coopname.value);
auto afund = accfunds.find(fund_id);
eosio::check(afund != accfunds.end(), "Фонд не найден");
payer = afund->contract != ""_n ? afund->contract : username;
} else if (type == _efund_withdraw_action) {
expfunds_index expfunds(_fund, coopname.value);
auto efund = expfunds.find(fund_id);
eosio::check(efund != expfunds.end(), "Фонд не найден");
payer = efund->contract != ""_n ? efund->contract : username;
}
require_auth(payer);
if (payer == username) {
staff_index staff(_soviet, coopname.value);
auto persona = staff.find(username.value);
eosio::check(persona != staff.end(),
"Указанный аккаунт не является сотрудником");
eosio::check(persona->has_right(_soviet, "complete"_n),
"Недостаточно прав доступа");
};
fundwithdraws_index fundwithdraws(_fund, coopname.value);
uint64_t fundwithdraw_id = get_global_id(_fund, "fundwithdraw"_n);
fundwithdraws.emplace(payer, [&](auto &s) {
s.id = fundwithdraw_id;
s.type = type;
s.status = "pending"_n;
s.coopname = coopname;
s.username = username;
s.fund_id = fund_id;
s.quantity = quantity;
s.document = document;
s.bank_data_id = bank_data_id;
});
action(permission_level{_fund, "active"_n}, _soviet, "fundwithdraw"_n,
std::make_tuple(coopname, username, type, fundwithdraw_id, document))
.send();
// оповещаем себя же
action(permission_level{_fund, "active"_n}, _fund, "newwithdraw"_n,
std::make_tuple(coopname, type, fundwithdraw_id))
.send();
}
[[eosio::action]] void fund::authorize(eosio::name coopname, eosio::name type,
uint64_t withdraw_id) {
require_auth(_soviet);
fundwithdraws_index fundwithdraws(_fund, coopname.value);
auto withdraw = fundwithdraws.find(withdraw_id);
eosio::check(withdraw != fundwithdraws.end(), "Вывод не найден");
fundwithdraws.modify(withdraw, _soviet,
[&](auto &s) { s.status = "authorized"_n; });
};
[[eosio::action]] void fund::complete(eosio::name coopname,
eosio::name username,
uint64_t withdraw_id) {
require_auth(username);
// todo check rights for username
staff_index staff(_soviet, coopname.value);
auto persona = staff.find(username.value);
eosio::check(persona != staff.end(),
"Указанный аккаунт не является сотрудником");
eosio::check(persona->has_right(_soviet, "complete"_n),
"Недостаточно прав доступа");
fundwithdraws_index fundwithdraws(_fund, coopname.value);
auto withdraw = fundwithdraws.find(withdraw_id);
eosio::check(withdraw != fundwithdraws.end(), "Вывод не найден");
fundwithdraws.modify(withdraw, _soviet, [&](auto &s) {
s.status = "completed"_n;
s.expired_at = eosio::time_point_sec(
eosio::current_time_point().sec_since_epoch() + 30 * 86400);
});
if (withdraw->type == _afund_withdraw_action) {
accfunds_index accfunds(_fund, coopname.value);
auto afund = accfunds.find(withdraw->fund_id);
eosio::check(afund != accfunds.end(), "Фонд не найден");
action(permission_level{_fund, "active"_n}, _fund, "subaccum"_n,
std::make_tuple(coopname, withdraw->fund_id, withdraw->quantity))
.send();
} else if (withdraw->type == _efund_withdraw_action) {
expfunds_index expfunds(_fund, coopname.value);
auto efund = expfunds.find(withdraw->fund_id);
eosio::check(efund != expfunds.end(), "Фонд не найден");
action(permission_level{_fund, "active"_n}, _fund, "addexpense"_n,
std::make_tuple(coopname, withdraw->fund_id, withdraw->quantity))
.send();
}
};
// type: accumulation, expend
[[eosio::action]] void fund::createfund(eosio::name coopname,
eosio::name username, eosio::name type,
eosio::name contract, std::string name,
std::string description,
uint64_t percent) {
require_auth(username);
eosio::check(type == "accumulation"_n || type == "expend"_n,
"Неверный тип фонда");
auto cooperative = get_cooperative_or_fail(coopname);
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
auto chairman = soviet.get_chairman();
eosio::check(username == chairman,
"Только председатель может управлять фондами");
uint64_t id;
if (type == "accumulation"_n) {
eosio::check(percent > 0 && percent <= HUNDR_PERCENTS,
"Процент фонда накопления должен быть больше нуля и меньше 1 "
"000 000 (= 100%)");
accfunds_index accfunds(_fund, coopname.value);
uint64_t total_percent = percent;
// получаем сумму процентов всех фондов
for (auto it = accfunds.begin(); it != accfunds.end(); it++) {
total_percent += it->percent;
}
// сумма не должна превышать 100%
check(total_percent <= HUNDR_PERCENTS,
"Сумма всех процентов превышает 100% (1 000 000)");
id = get_global_id(_fund, "funds"_n);
accfunds.emplace(username, [&](auto &a) {
a.id = get_global_id_in_scope(_fund, coopname, "funds"_n);
a.coopname = coopname;
a.contract = contract;
a.name = name;
a.description = description;
a.percent = percent;
a.available = asset(0, cooperative.initial.symbol);
a.withdrawed = asset(0, cooperative.initial.symbol);
});
} else if (type == "expend"_n) {
eosio::check(
percent == 0,
"Процент для фонда списания должен быть равен нулю (не используется)");
expfunds_index expfunds(_fund, coopname.value);
id = get_global_id_in_scope(_fund, coopname, "funds"_n);
expfunds.emplace(username, [&](auto &e) {
e.id = id;
e.coopname = coopname;
e.contract = contract;
e.name = name;
e.description = description;
e.expended = asset(0, cooperative.initial.symbol);
});
};
action(permission_level{_fund, "active"_n}, _fund, "newfund"_n,
std::make_tuple(coopname, type, id))
.send();
};
[[eosio::action]] void fund::editfund(eosio::name coopname,
eosio::name username, eosio::name type,
uint64_t fund_id, eosio::name contract,
std::string name, std::string description,
uint64_t percent) {
require_auth(username);
eosio::check(type == "accumulation"_n || type == "expend"_n,
"Неверный тип фонда");
auto cooperative = get_cooperative_or_fail(coopname);
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
auto chairman = soviet.get_chairman();
eosio::check(username == chairman,
"Только председатель может управлять фондами");
if (fund_id <= 5){
/**
Фонд развития кооперации (3) и резервный фонд (2) проценты менять можно.
Названия фондов менять нельзя (id < 5).
*/
eosio::check(fund_id != 0, "Неделимый фонд не может быть отредактирован");
}
if (type == "accumulation"_n) {
accfunds_index accfunds(_fund, coopname.value);
auto afund = accfunds.find(fund_id);
eosio::check(afund != accfunds.end(), "Фонд не найден");
if (fund_id <= 3){
eosio::check(fund_id == 1, "Нельзя изменить процент неделимого фонда накопления");
eosio::check(name == afund -> name, "Нельзя изменить имя обязательного фонда накопления");
eosio::check(contract == ""_n, "Нельзя передать в управление обязательный фонд");
}
eosio::check(
percent > 0 && percent <= HUNDR_PERCENTS,
"Процент фонда накопления должен быть больше нуля и меньше 100%");
uint64_t total_percent = 0;
// получаем сумму процентов всех фондов
for (auto it = accfunds.begin(); it != accfunds.end(); it++) {
total_percent += it->percent;
}
// вычитаем старый процент и добавляем новый
total_percent += percent - afund->percent;
// сумма процентов не должна превышать 100%
check(total_percent <= HUNDR_PERCENTS,
"Сумма всех процентов превышает 100%");
accfunds.modify(afund, username, [&](auto &a) {
a.contract = contract;
a.name = name;
a.description = description;
a.percent = percent;
});
} else if (type == "expend"_n) {
eosio::check(
percent == 0,
"Процент для фонда списания должен быть равен нулю (не используется)");
expfunds_index expfunds(_fund, coopname.value);
auto efund = expfunds.find(fund_id);
eosio::check(efund != expfunds.end(), "Фонд не найден");
if (fund_id <= 5){
eosio::check(name == efund -> name, "Нельзя изменить имя обязательного фонда списания");
eosio::check(contract == ""_n, "Нельзя передать в управление обязательный фонд");
}
expfunds.modify(efund, username, [&](auto &e) {
e.contract = contract;
e.name = name;
e.description = description;
});
};
};
[[eosio::action]] void fund::delfund(eosio::name coopname, eosio::name username,
eosio::name type, uint64_t fund_id) {
require_auth(username);
eosio::check(type == "accumulation"_n || type == "expend"_n,
"Неверный тип фонда");
auto cooperative = get_cooperative_or_fail(coopname);
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
auto chairman = soviet.get_chairman();
eosio::check(username == chairman,
"Только председатель может управлять фондами");
eosio::check(fund_id > 5, "Нельзя удалить обязательные фонды");
if (type == "accumulation"_n) {
accfunds_index accfunds(_fund, coopname.value);
auto afund = accfunds.find(fund_id);
eosio::check(afund != accfunds.end(), "Фонд не найден");
eosio::check(afund->available.amount == 0,
"Только пустой фонд накопления может быть удален");
accfunds.erase(afund);
} else if (type == "expend"_n) {
expfunds_index expfunds(_fund, coopname.value);
auto efund = expfunds.find(fund_id);
eosio::check(efund != expfunds.end(), "Фонд не найден");
expfunds.erase(efund);
}
};
@@ -0,0 +1,21 @@
// фонды накопления
[[eosio::action]] void fund::addaccum(eosio::name coopname, uint64_t fund_id,
eosio::asset quantity) {
require_auth(_fund);
coopwallet_index coopwallet(_fund, coopname.value);
auto wal = coopwallet.find(0);
eosio::check(wal != coopwallet.end(), "Кошелёк кооператива не найден");
coopwallet.modify(wal, _fund, [&](auto &row) {
row.accumulative_account.available += quantity;
});
accfunds_index accfunds(_fund, coopname.value);
auto afund = accfunds.find(fund_id);
eosio::check(afund != accfunds.end(), "Фонд не найден");
accfunds.modify(afund, _fund, [&](auto &a) { a.available += quantity; });
};

Some files were not shown because too many files have changed in this diff Show More