Compare commits

..

144 Commits

Author SHA1 Message Date
Alex Ant 4ccf2c743c процесс голосования через подпись номера решения 2025-05-12 10:41:37 +05:00
Alex Ant 418d43af58 almost done 2025-05-09 17:31:41 +05:00
Alex Ant 869a75f080 SDK компилируется 2025-05-07 08:23:59 +05:00
Alex Ant c7bc2d1a20 Merge branch 'dev' into document2 2025-05-05 18:56:04 +05:00
Alex Ant 3ea4cd6d73 chore(release): publish 2025-05-05 18:55:34 +05:00
Alex Ant 6a955737e0 update version 2 2025-05-05 18:55:01 +05:00
Alex Ant 51bf49b063 update version 2025-05-05 18:53:25 +05:00
Alex Ant 12468b16c8 Merge branch 'testnet' 2025-05-05 18:50:23 +05:00
Alex Ant 5ebd28d181 откатываем новую таблицу кооперативов 2025-05-05 18:49:08 +05:00
Alex Ant defe482ccd контракты скомпилировались 2025-05-05 18:46:44 +05:00
Alex Ant e99d141f28 before contract migrate 2025-05-05 17:25:56 +05:00
Alex Ant aa0a6731de chore(release): publish 2025-05-05 12:53:42 +05:00
Alex Ant 46f7f10dce coop migration 2025-05-05 12:28:54 +05:00
Alex Ant 552333faf6 chore(release): publish 2025-05-05 11:17:29 +05:00
Alex Ant e23589d4d3 Функционал миграций по версиям с интеграцией блокчейна и двух баз данных 2025-05-04 14:17:27 +05:00
Alex Ant 104b08e1ba chore(release): publish 2025-05-04 12:42:52 +05:00
Alex Ant 1b81cfc0a9 fix workflow #2 2025-05-04 12:42:23 +05:00
Alex Ant 6e51433ae9 update publish workflow 2025-05-04 12:39:52 +05:00
Alex Ant 421f356987 update version 2025-05-04 12:37:53 +05:00
Alex Ant abd9f1931d chore(release): publish 2025-05-04 12:32:16 +05:00
Alex Ant 2b87034abb fix workflows 2025-05-04 12:26:07 +05:00
Alex Ant 8d7c919332 chore(release): publish 2025-05-04 12:08:16 +05:00
Alex Ant 4785ea2886 publish workflow 2025-05-04 12:07:22 +05:00
Alex Ant 4daa06dac1 Обновление структуры signature_info: добавлен идентификатор подписи. Исправлено условие фильтрации истекших решений в ChairmanPlugin. 2025-05-04 11:40:00 +05:00
Alex Ant abc253f47d create document2 format 2025-05-04 11:02:25 +05:00
Alex Ant 5fea2f8f34 chore(release): publish 2025-05-03 16:19:40 +05:00
Alex Ant c29e224f2f Дефолтные конфиги для расширений, фиксы визуалов, микросервис отмены вопроса с повестки 2025-05-03 15:56:08 +05:00
Alex Ant b930543c2b chore(release): publish 2025-05-02 10:40:41 +05:00
Alex Ant 2768d72e90 update 2025-05-01 22:51:07 +05:00
Alex Ant 8ccefe8fac update 2025-05-01 22:46:58 +05:00
Alex Ant c93e59908d update 2025-05-01 22:29:40 +05:00
Alex Ant 22f0fd8a10 update 2025-05-01 22:28:01 +05:00
Alex Ant 8e17d96529 update 2025-05-01 22:23:56 +05:00
Alex Ant 494a164ee8 update 2025-05-01 22:21:00 +05:00
Alex Ant ee28ae1719 update 2025-05-01 22:17:07 +05:00
Alex Ant 3266061359 update 2025-05-01 22:13:38 +05:00
Alex Ant f69d849c4f update lock-file 2025-05-01 21:57:50 +05:00
Alex Ant 2628120e35 update 2025-05-01 21:54:59 +05:00
Alex Ant edf7d9ab62 docs #2 2025-05-01 21:52:49 +05:00
Alex Ant 2095e448e5 docs 2025-05-01 21:50:39 +05:00
Alex Ant beff507f4a Удалены отладочные логи из функций входа и выхода пользователя, а также обновлен способ проверки переменных окружения в функции работы с IndexedDB. 2025-05-01 18:23:56 +05:00
Alex Ant bf4fbfabac update 2025-05-01 16:46:36 +05:00
Alex Ant 2f1d88a74c Добавлены отладочные логи в функции входа и выхода пользователя, а также обновлен способ загрузки переменных окружения в конфигурации Quasar для режима разработки. 2025-05-01 16:00:45 +05:00
Alex Ant b444004f4a Удалены отладочные логи из функции получения переменных окружения и обновлен middleware для инъекции переменных окружения в браузер с использованием ssrMiddleware. 2025-05-01 14:33:41 +05:00
Alex Ant 6862f2881a update 2025-05-01 13:42:00 +05:00
Alex Ant 73afcabee2 Изменен способ определения переменных окружения в конфигурации Quasar: теперь они устанавливаются только для клиентской стороны, если режим не SSR. 2025-05-01 13:27:05 +05:00
Alex Ant b4168a4053 update 2025-05-01 11:34:33 +05:00
Alex Ant 1fe14d5811 update 2025-05-01 11:22:00 +05:00
Alex Ant 89e5c97ef8 update 2025-05-01 11:12:30 +05:00
Alex Ant 258b8304ff update 2025-05-01 11:01:19 +05:00
Alex Ant 69fe9c1348 update 2025-05-01 10:31:01 +05:00
Alex Ant fa4af94fa0 update 2025-05-01 10:29:53 +05:00
Alex Ant 2ac84a6910 update 2025-05-01 10:27:41 +05:00
Alex Ant 3f79cf9a1b update 2025-05-01 10:24:33 +05:00
Alex Ant c155605ca8 update 2025-05-01 09:59:02 +05:00
Alex Ant 80a94f79da update 2025-05-01 09:51:43 +05:00
Alex Ant 89ba6cb356 update 2025-05-01 09:41:37 +05:00
Alex Ant c7bd0326ba Обновление Dockerfile: замена команды установки зависимостей на принудительную, улучшение структуры образа. Изменение конфигурации GitHub Actions для отключения кеширования и упрощения сборки образов. 2025-05-01 09:36:57 +05:00
Alex Ant 2c551a0d9e Оптимизация Dockerfile: добавление этапа сборки для зависимостей, упрощение копирования файлов и улучшение структуры образа. Обновление конфигурации GitHub Actions для сборки и публикации образов с использованием Docker Buildx. 2025-04-30 21:49:16 +05:00
Alex Ant ba103a969e Добавление шагов для отладки в GitHub Actions: вывод последних коммитов, текущей ветки и содержимого компонентов. Обновление комментариев и улучшение сборки образов без кеша. 2025-04-30 21:32:51 +05:00
Alex Ant 0d9177f98f update 2025-04-30 21:29:50 +05:00
Alex Ant 60c32be299 update 2025-04-30 20:54:13 +05:00
Alex Ant 83c107b8b2 Добавление middleware для инъекции переменных окружения в window.__ENV__ и рефакторинг функции получения переменных окружения с учетом различных сред (SSR, SPA). Теперь переменные окружения экспортируются через единую функцию getEnv(). 2025-04-30 20:53:02 +05:00
Alex Ant b6d582fb2c Обновление конфигурации проекта: замена использования переменных окружения на новый модуль env, упрощение Dockerfile, улучшение сборки контейнеров и исправление мелких ошибок в компонентах. 2025-04-30 20:30:42 +05:00
Alex Ant a5818bdc53 update 2025-04-30 18:37:23 +05:00
Alex Ant de0fe5465f update 2025-04-30 18:00:07 +05:00
Alex Ant d43b188a30 update 2025-04-30 17:47:08 +05:00
Alex Ant bfb2be2a72 update 2025-04-30 17:45:28 +05:00
Alex Ant 73c2cd9cb3 update 2025-04-30 17:35:42 +05:00
Alex Ant 61cfc442ef update 2025-04-30 17:33:58 +05:00
Alex Ant 0ed04c2c27 update 2025-04-30 17:29:37 +05:00
Alex Ant 6773509ede update 2025-04-30 17:17:55 +05:00
Alex Ant acd86df785 update 2025-04-30 17:05:31 +05:00
Alex Ant c554b167b3 update 2025-04-30 16:53:55 +05:00
Alex Ant 18cf7b61e3 print env 2025-04-30 16:47:47 +05:00
Alex Ant e42b396a68 VITE_ prefix 2025-04-30 16:39:33 +05:00
Alex Ant c47e61d42a правки SSR проблема рендиринга 2025-04-30 15:55:07 +05:00
Alex Ant 92559370fa modify quasar conf 2025-04-30 12:49:12 +05:00
Alex Ant c3a2ec46b3 update 2025-04-30 11:32:47 +05:00
Alex Ant fe55d23e65 хук на деплой 2025-04-30 10:29:59 +05:00
Alex Ant b0d4f5d542 Добавление сборки и публикации нового Docker-образа для приложения @coopenomics/desktop в workflow. Обновление конфигурации сборки контейнеров. 2025-04-29 23:23:13 +05:00
Alex Ant be48f7a158 Обновление Dockerfile для упрощения сборки с использованием lerna. Добавление новых прокси-файлов для модулей @dicebear/core, @dicebear/collection и email-regex. Обновление зависимостей в package.json и pnpm-lock.yaml. Изменение конфигурации Quasar для оптимизации работы с новыми модулями. 2025-04-29 23:21:10 +05:00
Alex Ant 83bd744100 update workflow 2025-04-29 21:49:48 +05:00
Alex Ant ff1615f7b1 workflow initialization 2025-04-29 21:40:51 +05:00
Alex Ant 696e1a0d2c Merge branch 'main' into dev 2025-04-29 16:32:03 +05:00
Alex Ant 397670473a chore(release): publish 2025-04-29 16:27:09 +05:00
Alex Ant cab8061bc3 Merge branch 'testnet' 2025-04-29 16:21:40 +05:00
Alex Ant 3c2b9bcb4c update script 2025-04-29 16:21:22 +05:00
Alex Ant 7a274b7ab1 update templates 2025-04-29 16:17:58 +05:00
Alex Ant e524244bab Добавление нового поля 'type' в сущность Payment и соответствующие изменения в DTO, схемах и селекторах. Обновление логики обновления статусов платежей с учетом нового поля. Исправление параметров в методах для получения заказов. 2025-04-29 13:36:04 +05:00
Alex Ant 14ecaf259a Добавление нового поля 'coop' в интерфейс Model и обновление схемы валидации для SelectBranchStatement. Обновление логики комбинирования данных в классе Factory для включения данных о кооперативе. Комментарии в стилях инпутов переведены в закомментированный вид. 2025-04-29 12:25:08 +05:00
Alex Ant 749c13dc91 Обновление скриптов публикации и логики миграции. Изменены команды для публикации на продакшн, добавлен новый скрипт publish-prod.sh. Обновлена логика удаления кандидатов в функции миграции и исправлены ссылки на индексы кандидатов в confirmreg.cpp. 2025-04-29 11:05:03 +05:00
Alex Ant ab2ee4cc92 lerna update 2025-04-29 10:16:02 +05:00
Alex Ant 2907352d61 disable meet for temp 2025-04-29 10:11:14 +05:00
Alex Ant 390b995eb6 Добавление новых скриптов для развертывания и обновление интерфейсов для обработки голосования на общем собрании. Обновлены типы данных в интерфейсах и DTO, добавлены новые методы для генерации документов и улучшения функционала. Удалены устаревшие интерфейсы и компоненты, улучшена структура кода для повышения эффективности. 2025-04-28 23:55:55 +05:00
Alex Ant 24b3dd3c60 Удаление устаревших интерфейсов и таблиц, связанных с кошельками. Обновление логики обработки платежей и интерфейсов для работы с новыми типами данных. Улучшение структуры компонентов и интерфейсов для более эффективного взаимодействия с API. Обновление стилей и компонентов пользовательского интерфейса для улучшения визуального восприятия. 2025-04-26 17:14:22 +05:00
Alex Ant 55444a4b72 Обновление интерфейсов и DTO для работы с документами и собраниями. Добавлены новые поля и типы данных, улучшена обработка документов в сервисах. Удалены устаревшие селекторы и обновлены существующие для соответствия новым требованиям. Исправлены типы данных в селекторах и добавлены новые экспорты. 2025-04-25 23:44:32 +05:00
Alex Ant 76854207a7 Сейчас будем переводить сервис документов на агрегатор. Обновление интерфейсов и DTO для обработки документов. Изменены типы полей в интерфейсах DocumentPackageDomainInterface, DocumentRepository и DocumentDomainService для поддержки значений null. Добавлены проверки на наличие данных в DocumentDomainEntity и улучшена обработка документов в DocumentDomainService. Обновлены методы для работы с документами в API и хранилище. 2025-04-23 19:03:18 +05:00
Alex Ant 4fcd700e39 Первичная мобильная верстка страниц стола совета 2025-04-23 12:39:23 +05:00
Alex Ant 05dfcced1b Обновление конфигурации и добавление тестового режима для собраний. Изменен порт для state-history-endpoint, добавлен флаг TEST_MODE в контракте meet, обновлены проверки дат открытия и закрытия собраний. Обновлены интерфейсы и DTO для обработки новых типов данных и улучшения функционала. 2025-04-22 23:23:09 +05:00
Alex Ant 931c366907 Добавление функционала для генерации бюллетеней голосования на общем собрании. Обновлены интерфейсы и DTO для обработки новых типов документов, добавлены методы для генерации бюллетеней и голосования. Исправлены типы данных для идентификаторов вопросов повестки. 2025-04-21 20:29:12 +05:00
Alex Ant 71d35b74d7 Добавление новых методов для подписания решений председателем и секретарем на общем собрании. Удален метод закрытия собрания, обновлены интерфейсы и DTO для обработки новых типов документов и действий. 2025-04-21 16:44:27 +05:00
Alex Ant 21fac5c7df Добавление новых типов и методов для работы с собраниями в модуле общих собраний. Реализованы методы получения данных о собраниях, обновлены интерфейсы и DTO для обработки информации о голосованиях и документах собраний. 2025-04-20 16:41:20 +05:00
Alex Ant 0ba9d55081 Добавление новых входных типов и методов для получения данных о собраниях в модуле общих собраний. Реализованы методы getMeet и getMeets, а также обновлены интерфейсы и DTO для обработки данных. 2025-04-20 12:56:42 +05:00
Alex Ant b5b388e95d Обновление документации и DTO для модуля общих собраний. Добавлены описания для полей в DTO, улучшена структура и добавлены новые типы данных для обработки информации о собраниях и голосованиях. 2025-04-20 11:59:29 +05:00
Alex Ant 9c643395b3 Добавление модуля общих собраний и расширение DTO для документов, включая новые интерфейсы и классы для обработки документов собраний и решений. 2025-04-20 11:39:27 +05:00
Alex Ant b989074170 meet module & extends all documents DTOs 2025-04-18 15:27:35 +05:00
Alex Ant 261f010a49 update release script 2025-04-16 22:03:36 +05:00
Alex Ant af46867939 update releases script 2025-04-16 22:02:08 +05:00
Alex Ant be2355e5c5 generate changelog releases 2025-04-16 21:42:37 +05:00
Alex Ant 8b4a20ed3f make Templates correct 2025-04-16 20:08:50 +05:00
Alex Ant 1ab1e1d3c5 move templates #1 2025-04-16 20:08:24 +05:00
Alex Ant c9aab02aab wtf?! 2025-04-16 20:06:49 +05:00
Alex Ant 0643604778 add meet contract, cooptypes and empty documents templates 2025-04-16 19:58:49 +05:00
Alex Ant fe79f3ff98 desktop bug fixs 2025-04-15 15:57:55 +05:00
Alex Ant f919a14927 remove Env.ts file from desktop and move params to .env 2025-04-15 12:27:29 +05:00
Alex Ant dda389ad75 fix system contract action name 2025-04-15 10:27:53 +05:00
Alex Ant aa42864029 fix contracts build 2025-04-15 10:14:07 +05:00
Alex Ant 41bb17224a coopStore release 2025-04-11 18:29:07 +05:00
Alex Ant fd6f628c43 refactoring desktop 2025-04-10 11:40:14 +05:00
Alex Ant 28ca615d96 claim -> result, result -> assignment 2025-04-06 12:56:55 +05:00
Alex Ant 76203a88d8 before make refactoring 2025-04-06 11:25:29 +05:00
Alex Ant aa891adddf shared actions 2025-04-05 11:36:06 +05:00
Alex Ant ecdac6fff9 loan contract 2025-04-03 18:52:53 +05:00
Alex Ant bcba24ea5e contracts refactoring 2025-04-03 09:36:48 +05:00
Alex Ant 44b1f423cc appStore & medusa component & capital contract (start rework) 2025-03-28 18:42:28 +05:00
Alex Ant 85cc7361d7 refactoring participant widget to page 2025-03-27 19:14:02 +05:00
Alex Ant e5e28ff652 chore(release): publish 2025-03-27 18:13:33 +05:00
Alex Ant 4957182480 update mutation simplification 2025-03-27 17:30:31 +05:00
Alex Ant 84927e943a make participants editable 2025-03-27 14:32:23 +05:00
Alex Ant 0d06114f00 change decision dto and logout on some auth problems 2025-03-26 22:18:12 +05:00
Alex Ant 2654760e16 Merge pull request #201 from coopenomics/kamilocean/sprint-4
Kamilocean/sprint 4
2025-03-26 20:09:37 +03:00
Alex Ant d02e7ba2be README.md for cleos wallet 2025-03-26 18:27:05 +05:00
Alex Ant e18fd94336 make cleos separated component 2025-03-26 18:19:52 +05:00
Alex Ant 03e2410556 make cleos wallet accessable 2025-03-26 13:08:04 +05:00
Alex Ant 0f1f34f621 make unnesassary result 2025-03-26 11:42:05 +05:00
Alex Ant c08473618f some updates on contracts and tests 2025-03-22 13:58:51 +05:00
Alex Ant 3b4fb195bb update 2025-03-21 17:58:33 +05:00
Alex Ant 827eb10cc2 Merge branch 'dev' into TSK-900 2025-03-19 10:58:53 +05:00
Alex Ant 6e7c60de6e Создание кошелька при регистрации участника 2025-03-19 10:58:20 +05:00
kamilocean 54a4449e0b Merge branch 'dev' into kamilocean/sprint-4 2025-03-17 16:07:50 +03:00
Ilia Smirnov a6011f5d46 Merged readme fixes 2025-03-17 12:52:02 +03:00
Ilia Smirnov a1300e2b3c Merge branch 'dev' of github.com:coopenomics/mono into TSK-878 2025-03-17 12:49:40 +03:00
Alex Ant 8f58672403 Merge pull request #217 from coopenomics/dacom-dark-sun/sprint-4
Спринт-4 | dacom-dark-sun
2025-03-17 09:57:34 +03:00
3523 changed files with 33943 additions and 205214 deletions
Vendored
BIN
View File
Binary file not shown.
-3
View File
@@ -1,3 +0,0 @@
{
"agentCanUpdateSnapshot": true
}
-8
View File
@@ -1,8 +0,0 @@
---
description:
globs:
alwaysApply: true
---
ВАЖНО!!! Мы работаем в МОНО-репозитории pnpm, любая установка пакетов производится ЧЕРЕЗ ФИЛЬТР компонента в корне: pnpm add glob --filter notificator2.
ВАЖНО!!! НИКОГДА НЕ ЗАПУСКАЙ ПРИЛОЖЕНИЕ ИЛИ ЕГО СБОРКУ!!! Просто отчитайся что всё сделал.
-1
View File
@@ -10,7 +10,6 @@ alwaysApply: false
2. Типы IName, IChecksum256, ITimePointSec - это просто строки.
3. Домен должен быть полностью изолирован от инфраструктурных деталей.
4. Направление зависимостей - внутрь (к ядру, домену).
5. Домен, инфраструктура и приложение связаны через App.module, НЕ НУЖНО импортировать их друг в друга, а достаточно просто импортировать домен на уровне приложения чтобы использовать все экспорты домена.
## Структура проекта
- `domain/` - доменный слой (бизнес-логика, независимая от инфраструктуры)
+3 -3
View File
@@ -1,12 +1,12 @@
---
description:
globs:
alwaysApply: true
globs: **/desktop/**
---
# DESKTOP
- Пишем код фронтенда в архитектуре Feature Sliced Design (FSD).
- Стиль шаблонов PUG на composite API.
- Всегда создаём index.ts файлы для vue компонент.
- Создавай индексные файлы для vue через export {default as NAME} from './where', а на уровне выше, если это необходимо, делай export * from './ui'
- Всегда удаляем неиспользуемые импорты.
- Не используем emit избыточно. Вместо них используем композабл функции моделей фич, если необходимо. Emit только в крайнем случае.
-62
View File
@@ -1,62 +0,0 @@
---
description:
globs:
alwaysApply: false
---
Файл [main.py](mdc:monocoop/monocoop/monocoop/components/docs/main.py) автоматически генерит ссылки на документацию SDK и GraphQL, которые формируются и публикуются автоматически. При создании документации к методам всегда применяй ссылки на SDK и GraphQL по форме:
{{ get_sdk_doc("Mutations", "Accounts", "RegisterAccount") }} | {{ get_graphql_doc("Mutation.registerAccount") }}
### 🎯 Основные паттерны использования
**1. Стандартная структура для методов API:**
```markdown
## Название действия
{{ get_sdk_doc("Namespace", "Module", "Method") }} | {{ get_graphql_doc("Type.methodName") }}
{{ get_typedoc_input("Namespace.Module.Method") }}
Результат:
{{ get_typedoc_definition("Namespace.Module.Method", "IOutput") }}
```
**2. Описание + пример (для сложных методов):**
```markdown
{{ get_typedoc_desc("Namespace.Module.Method") }}
{{ get_typedoc_input("Namespace.Module.Method") }}
```
**3. Ссылки на типы данных:**
```markdown
У каждого аккаунта есть объект {{ get_graphql_definition("Account") }} в GraphQL-API
```
### 🔧 Макросы и их применение
| Макрос | Назначение | Пример использования |
|--------|------------|---------------------|
| `get_sdk_doc` | Ссылка на SDK документацию | `{{ get_sdk_doc("Mutations", "Payments", "CreateDeposit") }}` |
| `get_graphql_doc` | Ссылка на GraphQL операцию | `{{ get_graphql_doc("Query.getAccount") }}` |
| `get_graphql_definition` | Ссылка на GraphQL тип | `{{ get_graphql_definition("Account") }}` |
| `get_class_doc` | Ссылка на класс/метод SDK | `{{ get_class_doc("Document", "sign") }}` |
| `get_typedoc_input` | **Полный пример** TypeScript вызова | `{{ get_typedoc_input("Mutations.Auth.Login") }}` |
| `get_typedoc_definition` | **Структура интерфейса** | `{{ get_typedoc_definition("Mutations.Auth.Login", "IOutput") }}` |
| `get_typedoc_desc` | Описание + примеры | `{{ get_typedoc_desc("Mutations.Auth.Login") }}` |
| `get_typedoc_value` | Значение константы | `{{ get_typedoc_value("Constants.API_VERSION") }}` |
### 📝 Правила оформления
**ОБЯЗАТЕЛЬНО:**
- Всегда используй `get_typedoc_input` для демонстрации **КАК вызывать** метод
- Всегда используй `get_typedoc_definition` для показа **ЧТО возвращается**
- Комбинируй SDK и GraphQL ссылки через ` | `: `{{ get_sdk_doc(...) }} | {{ get_graphql_doc(...) }}`
**ЖЕЛАТЕЛЬНО:**
- Для сложных методов добавляй `get_typedoc_desc` в начало
- Используй `get_graphql_definition` для ссылок на типы данных в тексте
- Группируй связанные операции в одном разделе
**ФОРМАТ ССЫЛОК:**
- SDK: `"Namespace", "Module", "Method"` (3 аргумента)
- GraphQL: `"Type.methodName"` (точка между типом и методом)
- Определения: просто имя типа `"TypeName"`
-96
View File
@@ -1,96 +0,0 @@
---
description:
globs:
alwaysApply: false
---
# Структура файлов шаблонов документов и алгоритм правки
## Основные директории и файлы
### 1. Определение типов и интерфейсов (cooptypes)
**Путь:** `/cooptypes/src/cooperative/registry/[ID.DocumentName]/index.ts`
В этих файлах определяются:
- Интерфейсы данных (`IAgendaMeet`, etc.)
- Основная модель данных документа (`Model`)
- HTML шаблон документа (`context`)
- Переводы строк (`translations`)
- Пример данных для тестирования (`exampleData`)
**Для изменения шаблона нужно править:**
- Интерфейсы модели данных при изменении структуры
- HTML в переменной `context` для изменения внешнего вида
- Строки переводов в `translations.ru`
### 2. Схемы валидации (factory)
**Путь:** `/factory/src/Schema/[SchemaName].ts`
Содержат:
- JSON схемы для валидации данных
- Определение обязательных полей
**При изменении структуры данных:**
- Обновите соответствующую схему валидации
- Обновите список обязательных полей в `required`
### 3. Фабрики документов (factory)
**Путь:** `/factory/src/Actions/[ID.DocumentName].ts`
Обрабатывают:
- Получение данных из разных источников
- Сборку модели для шаблонизатора
- Валидацию данных по схеме
- Генерацию PDF
**При изменении логики сборки документа:**
- Обновите метод `generateDocument()`
- Добавьте новые источники данных
### 4. Шаблоны (factory)
**Путь:** `/factory/src/Templates/[ID.DocumentName].ts`
Связывают:
- Шаблон из cooptypes
- Схему валидации
- Метаданные документа
**После изменения в cooptypes:**
- Убедитесь, что схема в Templates соответствует новой модели
### 5. Тесты (factory)
**Путь:** `/factory/test/[category].test.ts`
Содержат:
- Тестовые данные для генерации документов
- Вызовы функции `testDocumentGeneration`
**После внесения изменений:**
- Обновите тестовые данные в соответствии с новой структурой
- Запустите тесты командой `pnpm test`
## Алгоритм внесения изменений
1. **Модификация типов**
- Изменить файл в `/cooptypes/src/cooperative/registry/[ID.DocumentName]/index.ts`
- Обновить интерфейсы, шаблон HTML и переводы
2. **Обновление схемы валидации**
- Изменить соответствующую схему в `/factory/src/Schema/`
- Убедиться, что обязательные поля совпадают с интерфейсом
3. **Компиляция библиотеки типов**
- Выполнить `cd /cooptypes && pnpm build`
4. **Обновление тестовых данных**
- Привести тестовые данные в соответствие с новой структурой
5. **Запуск тестов**
- Выполнить `cd /factory && pnpm test [filename].test.ts`
## Особенности шаблонизации
- Используется Nunjucks для шаблонов
- Поддерживаются конструкции: `{% if %}`, `{% for %}`, `{% trans %}`
- Переменные вставляются через `{{ variable }}`
- Переводы через `{% trans 'KEY' %}`
-261
View File
@@ -1,261 +0,0 @@
---
description:
globs: src/**/*.ts
alwaysApply: false
---
# Фабрика Документов Кооперативов
## Общая Архитектура
Фабрика документов — это система генерации PDF документов для кооперативов, построенная на TypeScript с использованием MongoDB для хранения данных. Система состоит из трех основных частей:
1. **registry/** — JSON-шаблоны документов (статические данные)
2. **factory/** — основная фабрика с логикой генерации
3. **cooptypes/** — типы данных и интерфейсы
## Структура Registry
В корневой папке `registry/` находятся JSON файлы с номерными названиями, представляющие шаблоны документов:
### Основные документы:
- `1.walletProgramAgreement.json` — соглашение о кошельке
- `2.regulationElectronicSignature.json` — регламент электронной подписи
- `3.privacyPolicy.json` — политика конфиденциальности
- `4.userAgreement.json` — пользовательское соглашение
- `50.CoopenomicsAgreement.json` — соглашение с партнерами
- `100.participantApplication.json` — заявление участника
- `101.selectBranchStatement.json` — заявление о выборе филиала
### Документы общих собраний (300-304):
- `300.annualGeneralMeetingAgenda.json` — предложение повестки дня
- `301.annualGeneralMeetingSovietDecision.json` — протокол заседания совета
- `302.annualGeneralMeetingNotification.json` — уведомление о собрании
- `303.annualGeneralMeetingVotingBallot.json` — бюллетень для голосования
- `304.annualGeneralMeetingDecision.json` — протокол общего собрания
### Инвестиционные документы (1000+):
- `1000.investAgreement.json` — инвестиционное соглашение
- `1001.investByResultStatement.json` — заявление о зачете по результатам
- `1002.investByResultAct.json` — акт зачета по результатам
- `1005.investByMoneyStatement.json` — заявление о зачете денежных средств
### Структура JSON-шаблона:
```json
{
"context": "<div>...HTML шаблон с переменными...</div>",
"model": {...данные для примера...},
"translation": {...переводы ключей...},
"object_model": {...схема объектной модели...}
}
```
## Фабрика (factory/)
### Основные компоненты:
#### src/index.ts — Главный класс Generator
```typescript
export class Generator implements IGenerator {
// Хранилище фабрик для каждого типа документа
factories: { [K in Numbers]: DocFactory<IGenerate> }
// MongoDB коннектор
public storage: MongoDBConnector
// Основной метод генерации
async generate(data: IGenerate, options?: IGenerationOptions): Promise<IGeneratedDocument>
}
```
#### Архитектура Factory Pattern:
- Базовый класс `DocFactory<T>` в `src/Factory/index.ts`
- Каждый документ имеет свою фабрику в `src/Actions/`
- Фабрики наследуются от `DocFactory` и реализуют метод `generateDocument()`
### Сервисы:
#### Services/Generator/ — PDF генерация
- `PDFService` — конвертирует HTML в PDF через WeasyPrint
- Использует шрифт Arial (base64)
- Добавляет метаданные в PDF
- Вычисляет SHA-256 хеш документа
#### Services/Templator/ — Шаблонизация
- Основан на Nunjucks
- Поддерживает кастомное расширение `{% trans %}` для переводов
- Рендерит HTML из шаблона с подстановкой переменных
#### Services/Validator/ — Валидация
- Использует AJV для JSON Schema валидации
- Поддерживает кастомные форматы (телефон)
- Локализация ошибок на русском языке
#### Services/Databazor/ — База данных
- `MongoDBConnector` — работа с MongoDB
- `DataService` — абстракция над данными
- Коллекции: `deltas`, `actions`, `documents`, и другие
### Модели данных (src/Models/):
#### Основные типы пользователей:
- `Individual` — физические лица (ФИО, паспорт, адрес)
- `Organization` — организации (ИНН, ОГРН, представитель)
- `Entrepreneur` — ИП (ФИО + ИНН/ОГРН)
#### Кооперативные данные:
- `Cooperative` — данные кооператива
- `PaymentMethod` — платежные методы
- `Vars` — переменные кооператива
- `Project` — проекты
### Система Action-ов:
Каждый документ имеет Action класс в `src/Actions/` с методом `generateDocument()`:
1. **Получение шаблона** — из локального Registry или MongoDB
2. **Сбор данных** — пользователь, кооператив, переменные, специфичные данные
3. **Валидация** — проверка по JSON схеме
4. **Рендеринг** — HTML из шаблона + данные
5. **PDF генерация** — HTML → PDF с метаданными
6. **Сохранение** — в MongoDB (если не skip_save)
## Система типов (cooptypes/)
### cooperative/registry/ — Типы документов
Каждый документ имеет папку с интерфейсами:
- `Action` — входные данные для генерации
- `Model` — модель данных для шаблона
- `Template` — структура шаблона
### contracts/ — Блокчейн контракты
- `registrator/` — регистрация кооперативов
- `soviet/` — управление советом
- `meet/` — общие собрания
- `wallet/`, `capital/`, `fund/` — финансовые операции
## База данных MongoDB
### Основные коллекции:
#### deltas — Состояние блокчейна
```javascript
{
block_num: number,
present: boolean,
code: string, // название контракта
scope: string, // область действия
table: string, // имя таблицы
primary_key: string,
value: {...} // данные записи
}
```
#### actions — Действия блокчейна
```javascript
{
block_num: number,
account: string,
name: string, // имя действия
receiver: string,
data: {...} // данные действия
}
```
#### documents — Сгенерированные документы
```javascript
{
hash: string, // SHA-256 хеш
binary: Uint8Array, // PDF данные
html: string, // HTML исходник
meta: { // метаданные
title: string,
created_at: string,
lang: string
}
}
```
### Таблицы данных:
- `coops` — кооперативы
- `meets` — собрания
- `questions` — вопросы собраний
- `decisions` — решения совета
- `individuals` — физические лица
- `organizations` — организации
- `entrepreneurs` — ИП
- `paymentMethods` — платежные методы
- `vars` — переменные кооперативов
## Генерация документов
### Процесс генерации:
1. **Вызов** `generator.generate(action, options)`
2. **Поиск фабрики** по `registry_id`
3. **Загрузка шаблона** (локально или из БД)
4. **Сбор данных** из MongoDB по `coopname`, `username`, `block_num`
5. **Создание модели** — объединение всех данных
6. **Валидация** модели по JSON схеме
7. **Рендеринг HTML** через Nunjucks
8. **Генерация PDF** через WeasyPrint
9. **Добавление метаданных** в PDF
10. **Вычисление хеша** SHA-256
11. **Сохранение** в MongoDB
### Пример использования:
```typescript
const generator = new Generator()
await generator.connect(mongoUri)
const document = await generator.generate({
registry_id: '300',
coopname: 'voskhod',
username: 'ant',
block_num: 0,
meet: {...},
questions: [...]
})
```
## Особенности реализации
### Шаблонизация:
- HTML шаблоны с CSS стилями
- Переменные в формате `{{variable.field}}`
- Условная логика `{% if condition %}`
- Циклы `{% for item in array %}`
- Переводы `{% trans 'KEY', var1, var2 %}`
### Подписи:
- Цифровые подписи вместо физических
- Текст "Подписано электронной подписью"
- Убраны подчеркивания для подписей
### Типы собраний:
- `regular` — очередное
- `extraordinary` — внеочередное
- Условная логика в шаблонах
### Филиалы:
- `coop.is_branched` — проверка на наличие филиалов
- "пайщиков" vs "уполномоченных" в зависимости от типа
### Форматирование дат:
- Формат: "г. Москва, 15 декабря 2024 г."
- Без кавычек вокруг дат
- Запятая после города
## Тестирование
### test/utils/index.ts — Тестовые утилиты:
- `preLoading()` — инициализация тестовых данных
- Создание кооператива, пользователей, платежных методов
- Настройка данных собраний и решений
- Очистка временных файлов
### Тестовые данные:
- Кооператив "ВОСХОД"
- Пользователи: ant, individual, entrepreneur
- Организации: voskhod, branch, exampleorg
- Собрания с вопросами и решениями
Фабрика поддерживает полный цикл создания документов кооператива от заявлений до протоколов собраний с возможностью кастомизации под разные типы кооперативов и требования.
-20
View File
@@ -1,20 +0,0 @@
---
globs: notifications/src/workflows/**/*.ts
---
# Правила валидации воркфлоу
## 🚫 ID воркфлоу
- **Длина ID не должна превышать 32 символа**
- Используйте описательные, но короткие идентификаторы малыми латинскими буквами и тире.
## 🚫 Условия в шаблонах
- **Запрещено использовать JavaScript выражения в шаблонах Novu**
- Нельзя использовать:
- Тернарные операторы: `{{condition ? "text1" : "text2"}}`
- Логические операторы: `{{field && "text"}}`
- Любые другие JS конструкции
## ✅ Рекомендации
- Добавляйте текстовые поля в payload для условной логики
- Вычисляйте значения на стороне сервера перед отправкой уведомления
- Используйте только простые переменные: `{{payload.fieldName}}`
+5 -33
View File
@@ -14,7 +14,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
ref: testnet
fetch-depth: 0
- name: Debug info
@@ -76,47 +76,19 @@ jobs:
echo "CMD [\"pnpm\", \"-F\", \"coop-notificator\", \"run\", \"start\"]" >> Dockerfile.notificator
docker build -t dicoop/notificator:${{ env.DOCKER_TAG }} -f Dockerfile.notificator .
docker push dicoop/notificator:${{ env.DOCKER_TAG }}
- name: Build notifications image
run: |
echo "FROM dicoop/mono-base:${{ env.DOCKER_TAG }}" > Dockerfile.notifications
echo "CMD [\"pnpm\", \"-F\", \"@coopenomics/notifications\", \"run\", \"sync\"]" >> Dockerfile.notifications
docker build -t dicoop/notifications:${{ env.DOCKER_TAG }} -f Dockerfile.notifications .
docker push dicoop/notifications:${{ env.DOCKER_TAG }}
# Отправка хука для деплоя
- name: Trigger deployment webhook
if: ${{ success() }}
run: |
if [[ $GITHUB_REF == refs/tags/latest ]]; then
# Хук для тестнета
curl -X POST ${{ vars.TESTNET_WEBHOOK_URL }} \
-H 'Content-Type: application/json' \
-d '${{ env.DOCKER_TAG }}'
elif [[ $GITHUB_REF == refs/tags/* ]]; then
if [[ $GITHUB_REF == refs/tags/* ]]; then
# Хук для продакшена
curl -X POST ${{ vars.PRODUCTION_WEBHOOK_URL }} \
-H 'Content-Type: application/json' \
-d '${{ env.DOCKER_TAG }}'
else
# Хук для тестнета (обычная ветка)
# Хук для тестнета
curl -X POST ${{ vars.TESTNET_WEBHOOK_URL }} \
-H 'Content-Type: application/json' \
-d '${{ env.DOCKER_TAG }}'
fi
# Уведомление в Telegram об успехе
- name: Telegram notify success
if: ${{ success() }}
run: |
curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \
-d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} \
-d text="✅ [GITHUB MONO] Успешная сборка контейнеров: $GITHUB_REPOSITORY ($GITHUB_REF) [${{ env.DOCKER_TAG }}]"
# Уведомление в Telegram об ошибке
- name: Telegram notify failure
if: ${{ failure() }}
run: |
curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \
-d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} \
-d text="❌ [GITHUB MONO] Ошибка при сборке контейнеров: $GITHUB_REPOSITORY ($GITHUB_REF) [${{ env.DOCKER_TAG }}]"
fi
@@ -1,18 +0,0 @@
# .github/workflows/trigger-coopenomics.yml
name: Trigger Contracts Docs Deploy
on:
push:
branches: [dev, testnet, main, capital] # или когда нужно триггерить
jobs:
trigger-coopenomics:
runs-on: ubuntu-latest
steps:
- name: Trigger Coopenomics deployment
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.COOPENOMICS_PAT }}
repository: coopenomics/coopenomics # укажи правильный owner/repo
event-type: deploy_from_mono
client-payload: '{"repository": "${{ github.repository }}", "sha": "${{ github.sha }}", "ref": "${{ github.ref }}", "actor": "${{ github.actor }}"}'
-4
View File
@@ -5,7 +5,3 @@ blockchain-data/
scripts/changelog-prompt.md
scripts/changelog-release.md
scripts/release-info.md
components/docs/docs/sdk
dist/
.env
.DS_Store
-7
View File
@@ -1,7 +0,0 @@
{
"recommendations": [
"vue.volar",
"vue.vscode-typescript-vue-plugin"
]
}
+5 -60
View File
@@ -1,71 +1,16 @@
{
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": true,
"editor.formatOnSave": true,
"editor.tabCompletion": "onlySnippets",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint":"explicit"
},
"eslint.run": "onType",
"eslint.validate": ["javascript", "typescript", "vue"],
"i18n-ally.localesPaths": ["src/i18n"],
"typescript.format.enable": true,
"typescript.format.indentSwitchCase": false,
"typescript.validate.enable": true,
"notebook.defaultFormatter": "Vue.volar",
// Оптимизация для монорепозитория
"typescript.preferences.useAliasesForRenames": false,
"typescript.preferences.includePackageJsonAutoImports": "on",
"typescript.suggest.autoImports": true,
"typescript.suggest.paths": true,
"typescript.updateImportsOnFileMove.enabled": "always",
"typescript.workspaceSymbols.scope": "currentProject",
// Настройки для снижения нагрузки
"files.watcherExclude": {
"**/node_modules/**": true,
"**/dist/**": true,
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/*/**": true,
"**/.cache/**": true,
"**/.quasar/**": true,
"**/*.tsbuildinfo": true
},
"search.exclude": {
"**/node_modules": true,
"**/dist": true,
"**/.cache": true,
"**/.quasar": true,
"**/*.tsbuildinfo": true
},
"files.exclude": {
"**/.cache": true,
"**/*.tsbuildinfo": true,
"**/node_modules/.cache": true
},
// TypeScript server настройки для монорепозитория
"typescript.tsserver.maxTsServerMemory": 8192,
"typescript.tsserver.watchOptions": {
"excludeDirectories": [
"**/node_modules",
"**/dist",
"**/.cache",
"**/.quasar",
"**/build"
]
},
// Использовать локальный TypeScript из монорепо
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
// КРИТИЧЕСКИ ВАЖНО: включить project references для монорепозитория
"typescript.tsserver.useSyntaxServer": "auto",
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
// Оптимизация для больших монорепозиториев
"typescript.disableAutomaticTypeAcquisition": true,
"typescript.surveys.enabled": false
"typescript.validate.enable": true
}
-104
View File
@@ -1,107 +1,3 @@
# v2025.9.1
В системе Кооперативной Экономики развернут смарт-контракт CAPITAL v0.2 для генерации и капитализации результатов интеллектуальной деятельности. Контракт описывает и обеспечивает:
- бизнес-процесс производства результатов интеллектуальной деятельности в кооперативах создателями, авторами, инвесторами, координаторами, мастерами и собственниками имущества в кооперации на проектах;
- приём результатов интеллектуальной деятельности в качестве паевых взносов в кооператив;
- распределение потока членских взносов среди вкладчиков;
- капитализацию результатов интеллектуальной деятельности новыми результатами по модели золотого сечения;
- оценку вкладов авторов и создателей по методу Водянова;
В основе математической модели контракта лежит принцип распределения справедливой выгоды между вкладчиками согласно концепции "Общественно-полезного времени", разработанной в рамках теорий Кузнецова и академика Глушкова при работе над проектом общегосударственной автоматизированной системы учета и обработки информации (ОГАС).
Подробнее в документации: https://coopenomics.world/contracts/group__public__capital.html
✨ Новые функции
- [#301](https://github.com/coopenomics/mono/issues/301): Реализация контракта "Капитализация" v0.2: регистрация вкладчиков, создание и управление проектами, поддержка инвестиций, ссуд, членских взносов, проведение голосований, расчет капитализации, интеграция с внешними контрактами, поддержка импорта данных.
#releases
---
# v2025.7.1-1
В этом релизе реализованы механизмы возврата паевого взноса пайщика и инструменты управления этим процессом для членов совета. Также внесены визуальные доработки интерфейсов для улучшения восприятия информации.
✨ Новые функции
- [#276](https://github.com/coopenomics/mono/issues/276): Реализовать путь возврата паевого взноса из кошелька пайщика
- [#281](https://github.com/coopenomics/mono/issues/281): Генерация заявления на возврат паевого взноса
- [#282](https://github.com/coopenomics/mono/issues/282): Генерация решения совета на возврат паевого взноса
- [#278](https://github.com/coopenomics/mono/issues/278): Введение методов управления возвратами паевых взносов в контроллере
- [#279](https://github.com/coopenomics/mono/issues/279): Отобразить исходящие платежи с кнопками управления в реестре платежей для совета
🐛 Исправления ошибок
- [#286](https://github.com/coopenomics/mono/issues/286): Поправить ошибки склонений времени
- [#262](https://github.com/coopenomics/mono/issues/262): Баг: рабочий стол совета включается, однако, страница всегда открывается со стола пайщика
- [#261](https://github.com/coopenomics/mono/issues/261): Баг: первая загрузка переадресует на главную страницу всегда - прямой переход на собрание становится недоступен.
🔧 Улучшения
- [#285](https://github.com/coopenomics/mono/issues/285): Корректировка дизайна кошелька, профиля, повестки совета, реестра документов, реестра платежей
- [#284](https://github.com/coopenomics/mono/issues/284): Разместить кошелек на главную вместо профиля
- [#283](https://github.com/coopenomics/mono/issues/283): Ввести лоадер на переходе между рабочими столами
- [#280](https://github.com/coopenomics/mono/issues/280): Мигрировать имеющиеся данные о входящих платежах в новую модель
- [#277](https://github.com/coopenomics/mono/issues/277): Рефакторинг модуля платежей: переход на унифицированную модель входящего и исходящего платежа
#releases
---
# v2025.6.14
Разработан модуль для проведения очередных общих собраний пайщиков. Исправлены баги, внесены улучшения пользовательского интерфейса.
✨ Новые функции
- [#264](https://github.com/coopenomics/mono/issues/264): Разработан смарт-контракт общего собрания пайщиков (meet)
- [#263](https://github.com/coopenomics/mono/issues/263): Реализован модуль оповещений на электронные почты по жизненному циклу общего собрания пайщиков
- [#273](https://github.com/coopenomics/mono/issues/273): Встроены реальные шаблоны документов общего собрания
- [#268](https://github.com/coopenomics/mono/issues/268): Добавлена страница результатов общего собрания
- [#267](https://github.com/coopenomics/mono/issues/267): Добавлена страница просмотра и скачивания бюллетеней и уведомлений по собранию
- [#270](https://github.com/coopenomics/mono/issues/270): Ссылка в оповещении ведет на страницу собрания с документом-уведомлением для подписи
🐛 Исправления ошибок
- [#262](https://github.com/coopenomics/mono/issues/262): Исправлено некорректное открытие рабочего стола совета
- [#261](https://github.com/coopenomics/mono/issues/261): Исправлена ошибка с редиректом при первой загрузке и прямом переходе на собрание
- [#259](https://github.com/coopenomics/mono/issues/259): Исправлены отступы в мобильной карточке пайщика
- [#258](https://github.com/coopenomics/mono/issues/258): Убран hover-эффект на документе и пайщике в таблице
🔧 Улучшения
- [#274](https://github.com/coopenomics/mono/issues/274): Настроена рассылка оповещений на почту при получении решения о проведении собрания
- [#272](https://github.com/coopenomics/mono/issues/272): Подписанные уведомления сохраняются и извлекаются из реестра по Graph-QL
- [#271](https://github.com/coopenomics/mono/issues/271): Ведется подсчет количества пайщиков в каждом кооперативе при добавлении и удалении
- [#260](https://github.com/coopenomics/mono/issues/260): Введен счетчик количества пайщиков в кооперативе на контракте регистратора
- [#256](https://github.com/coopenomics/mono/issues/256): Отображается статус членства каждого пайщика
- [#255](https://github.com/coopenomics/mono/issues/255): В разделе Платежи отображается ФИО/Наименование плательщика
- [#269](https://github.com/coopenomics/mono/issues/269): Реализована рассылка уведомлений перед началом собрания
- [#265](https://github.com/coopenomics/mono/issues/265): Проведена отладка и тестирование процесса общего собрания пайщиков
- [#266](https://github.com/coopenomics/mono/issues/266): Протестированы все процессы общего собрания
#releases
---
# v2025.5.14
В этом релизе реализован новый стандарт передачи и хранения документов по блокчейну с поддержкой неограниченного количества подписей и их валидацией. Также внесены улучшения в интерфейс и исправлены ошибки.
✨ Новые функции
- [#252](https://github.com/coopenomics/mono/issues/252): Внедрение обновленного стандарта хранения и передачи документов по блокчейну
- [#251](https://github.com/coopenomics/mono/issues/251): Реализация версионированного мигратора данных для контроллера кооператива
- [#244](https://github.com/coopenomics/mono/issues/244): Обновление стандарта сборки документов и переход на хэш-идентификаторы
🐛 Исправления ошибок
- [#249](https://github.com/coopenomics/mono/issues/249): Исправлена спутанная маршрутизация между рабочими столами кооперативов
- [#253](https://github.com/coopenomics/mono/issues/253): Исправлена избыточная точность суммы оплаты в заявлении на вступление
🔧 Улучшения
- [#259](https://github.com/coopenomics/mono/issues/259): Исправлены отступы в мобильной карточке пайщика на странице пайщиков
- [#258](https://github.com/coopenomics/mono/issues/258): Удалён hover-эффект для документов и пайщиков в таблице
- [#257](https://github.com/coopenomics/mono/issues/257): Добавлено сохранение светлой/тёмной темы в localStorage и восстановление при загрузке страницы
- [#256](https://github.com/coopenomics/mono/issues/256): Отображение статуса членства каждого пайщика в разделе "Пайщики"
- [#255](https://github.com/coopenomics/mono/issues/255): Отображение ФИО/Наименования плательщика в разделе "Платежи"
#releases
---
# v2025.5.2
В этом релизе рабочие столы переведены на серверный рендеринг, что улучшает стабильность развертывания и упрощает автоматизацию поставки ПО. Данный релиз является подготовительным к переходу на новый стандарт цифровых документов на платформе.
-129
View File
@@ -1,129 +0,0 @@
# 📋 Резюме создания компонентов Notifications
## ✅ Что создано
### 1. 📚 Библиотека `@monocoop/notifications`
**Расположение:** `components/notifications/`
**Функции:**
- ✅ Типизированные интерфейсы для workflow с Zod
- ✅ Builder паттерн для создания workflow
- ✅ Базовые шаблоны для email, in-app, push, SMS
- ✅ Автоматическая конвертация Zod схем в JSON Schema для Novu
- ✅ Экспорт всех workflow для использования в других пакетах
**Структура:**
```
src/
├── types/ # Базовые типы и интерфейсы
├── base/ # Утилиты и настройки по умолчанию
├── workflows/ # Папки с workflow
│ └── welcome/ # Пример приветственного workflow
└── index.ts # Главный экспорт
```
### 2. 🚀 NestJS приложение `@monocoop/notificator2`
**Расположение:** `components/notificator2/`
**Функции:**
- ✅ Автоматический upsert всех workflow в Novu при запуске
- ✅ RESTful API для триггера уведомлений
- ✅ Типизированная валидация payload
- ✅ Health check endpoints
- ✅ Использует библиотеку notifications для типов
**API Endpoints:**
- `GET /api/notifications/health` - Health check
- `GET /api/notifications/workflows` - Список workflow
- `POST /api/notifications/trigger` - Универсальный триггер
- `POST /api/notifications/trigger/welcome` - Триггер welcome workflow
- `POST /api/notifications/workflows/upsert-all` - Принудительный upsert
## 🔧 Как использовать
### 1. Настройка библиотеки notifications
```bash
cd components/notifications
pnpm install # или npm install
pnpm build # для компиляции TypeScript
```
### 2. Настройка notificator2
```bash
cd components/notificator2
pnpm install # или npm install
# Настройка окружения
cp .env.example .env
# Добавить NOVU_API_KEY в .env
# Запуск
pnpm start:dev
```
### 3. Добавление нового workflow
**В библиотеке notifications:**
```typescript
// components/notifications/src/workflows/order/order-workflow.ts
export const orderWorkflow = WorkflowBuilder
.create<OrderPayload>()
.name('Order Confirmation')
.workflowId('order-confirmation')
.payloadSchema(orderPayloadSchema)
.addSteps([...])
.build();
```
**Регистрация в index.ts:**
```typescript
// components/notifications/src/workflows/index.ts
import { orderWorkflow } from './order';
export const allWorkflows = [
welcomeWorkflow,
orderWorkflow, // ← добавить новый workflow
];
```
### 4. Использование API
**Триггер welcome workflow:**
```bash
curl -X POST http://localhost:3000/api/notifications/trigger/welcome \
-H "Content-Type: application/json" \
-d '{
"subscriberId": "user-123",
"email": "user@example.com",
"payload": {
"userName": "Иван Иванов",
"userEmail": "user@example.com",
"age": 25
}
}'
```
## 🎯 Преимущества архитектуры
1. **Типобезопасность** - Zod схемы обеспечивают валидацию на уровне TypeScript и runtime
2. **Разделение ответственности** - Библиотека содержит только типы, сервер - логику
3. **Расширяемость** - Легко добавлять новые workflow
4. **Автоматизация** - Workflow автоматически синхронизируются с Novu
5. **Переиспользование** - Типы можно использовать в других частях системы
## 🔄 Workflow при запуске
1. **Запуск notificator2**
2. **Чтение всех workflow из библиотеки**
3. **Проверка существования в Novu**
4. **Создание/обновление workflow**
5. **Готов к приёму запросов на триггеры**
## 📁 Структура как в testFramework2.ts
Вся логика из `testFramework2.ts` была перенесена в structured архитектуру:
-`buildWorkflowData``WorkflowBuilder`
-`baseSteps``createEmailStep`, `createInAppStep`, etc.
-`upsertWorkflow``NovuService.upsertWorkflow`
-`triggerWorkflow``NovuService.triggerWorkflow`
- ✅ Типизация payload → Zod схемы + TypeScript типы
BIN
View File
Binary file not shown.
-22
View File
@@ -1,22 +0,0 @@
{
"configurations": [
{
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/lib/**",
"${workspaceFolder}/lib/eosiolib/contracts/**",
"${workspaceFolder}/lib/eosiolib/core/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"int128_t=__int128_t",
"uint128_t=__uint128_t"
],
"cStandard": "c17",
"cppStandard": "c++20"
}
],
"version": 4
}
-68
View File
@@ -1,68 +0,0 @@
{
// Enable the ESlint flat config support
"prettier.enable": false,
"editor.formatOnSave": true,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
// "source.fixAll": "explicit",
// "source.organizeImports": "never"
},
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{
"rule": "style/*",
"severity": "off"
},
{
"rule": "*-indent",
"severity": "off"
},
{
"rule": "*-spacing",
"severity": "off"
},
{
"rule": "*-spaces",
"severity": "off"
},
{
"rule": "*-order",
"severity": "off"
},
{
"rule": "*-dangle",
"severity": "off"
},
{
"rule": "*-newline",
"severity": "off"
},
{
"rule": "*quotes",
"severity": "off"
},
{
"rule": "*semi",
"severity": "off"
}
],
// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml"
],
// "[cpp]": {
// "editor.defaultFormatter": "ms-vscode.cpptools",
// },
// "C_Cpp.vcFormat.indent.withinParentheses": "indent",
"C_Cpp.errorSquiggles": "disabled"
}
+1 -1
View File
@@ -9,7 +9,7 @@ export default antfu(
},
{
rules: {
'no-unused-vars': 'off',
'no-unused-vars': 'warn',
'unused-imports/no-unused-vars': 'off',
'no-console': 'off',
'unicorn/prefer-number-properties': 'off',
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@coopenomics/boot",
"type": "module",
"version": "2025.10.16",
"version": "2025.5.5-2",
"private": true,
"packageManager": "pnpm@9.0.6",
"description": "",
-47
View File
@@ -1,47 +0,0 @@
#!/bin/bash
# Останавливаем и удаляем контейнеры вместе с volumes
echo "Останавливаем и удаляем контейнеры с volumes..."
docker compose down -v mongo postgres cooparser || true
# Останавливаем blockchain контейнер перед удалением данных
echo "Останавливаем blockchain контейнер..."
docker compose stop node || true
# Удаляем blockchain data
echo "Удаляем blockchain data..."
# sudo chmod -R 755 ../blockchain-data/ 2>/dev/null || true
sudo rm -rf ../blockchain-data/
# Пересоздаем и запускаем базы данных
echo "Пересоздаем и запускаем базы данных..."
docker compose up -d mongo postgres
# Ждем готовности MongoDB
echo "Ждем готовности MongoDB..."
until docker exec mongo mongosh --eval "db.adminCommand('ping')" --quiet > /dev/null 2>&1; do
echo "MongoDB еще не готов, ждем..."
sleep 2
done
echo "MongoDB готов!"
# Ждем готовности PostgreSQL
echo "Ждем готовности PostgreSQL..."
until docker exec postgres pg_isready -U postgres -d voskhod > /dev/null 2>&1; do
echo "PostgreSQL еще не готов, ждем..."
sleep 2
done
echo "PostgreSQL готов!"
# Запускаем boot процесс
echo "Запускаем boot процесс..."
pnpm run boot
# Запускаем parser
echo "Запускаем parser..."
docker compose up -d cooparser
echo "Запускаем контроллер..."
docker compose restart coopback || true
echo "Перезапуск завершен!"
+11 -41
View File
@@ -15,7 +15,6 @@ import EosApi from 'eosjs-api'
// @ts-expect-error заменить на antelope
import ecc from 'eosjs-ecc'
import config from '../configs'
import { consoleIt } from '../tests/shared/consoleIt'
import type { Contract, Feature, Keys, Network } from '../types'
@@ -608,10 +607,10 @@ export default class Blockchain {
}
async registerUser(
params: RegistratorContract.Actions.RegisterUser.IRegisterUser,
params: RegistratorContract.Actions.RegisterUser.IRegistrerUser,
) {
await this.update_pass_instance()
console.dir(params, { depth: null })
await this.api.transact(
{
actions: [
@@ -624,7 +623,9 @@ export default class Blockchain {
permission: 'active',
},
],
data: params,
data: {
...params,
},
},
],
},
@@ -639,7 +640,7 @@ export default class Blockchain {
async transfer(params: TokenContract.Actions.Transfer.ITransfer) {
await this.update_pass_instance()
console.dir(params, { depth: null })
const result = await this.api.transact(
{
actions: [
@@ -747,7 +748,7 @@ export default class Blockchain {
name: SovietContract.Actions.Decisions.VoteFor.actionName,
authorization: [
{
actor: params.username,
actor: params.member,
permission: 'active',
},
],
@@ -832,7 +833,7 @@ export default class Blockchain {
}
async ConfirmPayment(
params: GatewayContract.Actions.CompleteIncome.ICompleteIncome,
params: GatewayContract.Actions.CompleteIncome.ICompeteIncome,
) {
await this.update_pass_instance()
@@ -876,7 +877,7 @@ export default class Blockchain {
name: SovietContract.Actions.Boards.CreateBoard.actionName,
authorization: [
{
actor: params.coopname,
actor: params.username,
permission: 'active',
},
],
@@ -922,7 +923,7 @@ export default class Blockchain {
},
)
console.log('Шаблон создан: ', params.registry_id)
console.log('Шаблон создан: ', params)
}
async createTranslation(
@@ -954,7 +955,7 @@ export default class Blockchain {
},
)
console.log('Перевод создан: ', params.registry_id)
console.log('Перевод создан: ', params)
}
async createProgram(
@@ -988,35 +989,4 @@ export default class Blockchain {
console.log('Программа установлена: ', params)
}
/**
* Выполняет транзакцию и автоматически выводит консоль логи через consoleIt
* @param actions - массив действий для транзакции
* @param options - опции транзакции (по умолчанию стандартные)
* @param options.blocksBehind - количество блоков позади для подтверждения
* @param options.expireSeconds - время жизни транзакции в секундах
* @returns результат транзакции
*/
async transactWithLogs(
actions: any[],
options: { blocksBehind?: number, expireSeconds?: number } = {},
): Promise<any> {
await this.update_pass_instance()
const defaultOptions = {
blocksBehind: 3,
expireSeconds: 30,
...options,
}
const result = await this.api.transact(
{ actions },
defaultOptions,
)
// Автоматически выводим консоль логи
consoleIt(result)
return result
}
}
-10
View File
@@ -65,16 +65,6 @@ export default [
path: path.resolve(process.cwd(), '../contracts/build/contracts/fund'),
target: 'fund',
},
{
name: 'ledger',
path: path.resolve(process.cwd(), '../contracts/build/contracts/ledger'),
target: 'ledger',
},
{
name: 'test',
path: path.resolve(process.cwd(), '../contracts/build/contracts/test'),
target: 'test',
},
{
name: 'contributor',
path: path.resolve(process.cwd(), '../contracts/build/contracts/contributor'),
-8
View File
@@ -91,14 +91,6 @@ export default {
name: 'loan',
code_permissions_to: ['loan'],
},
{
name: 'ledger',
code_permissions_to: ['ledger'],
},
{
name: 'test',
code_permissions_to: ['test'],
},
{
name: 'meet',
code_permissions_to: ['meet'],
+2
View File
@@ -120,6 +120,8 @@ program
}
try {
await clearDirectory(basePath)
await clearDB()
await runContainer()
await sleep(5000)
+2 -2
View File
@@ -1,5 +1,5 @@
import { startInfra } from './infra'
import { startCoop } from './cooperative'
import { startInfra } from "./infra"
import { startCoop } from "./cooperative"
export async function boot() {
await startInfra()
+36 -78
View File
@@ -1,9 +1,12 @@
import type { Cooperative } from 'cooptypes'
import type { Keys } from '../types'
import axios from 'axios'
import { describe, expect, it } from 'vitest'
import { Registry } from '@coopenomics/factory'
import { Cooperative as TCooperative } from 'cooptypes'
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'
import { processLastDecision } from '../tests/soviet/processLastDecision'
const test_hash
= '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777'
@@ -12,23 +15,11 @@ const test_sign
const test_pkey = 'EOS5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzxrLg3J'
const test_meta = JSON.stringify({})
const document: Cooperative.Document.ISignedDocument2 = {
const document = {
hash: test_hash,
signatures: [
{
id: 1,
signed_hash: test_hash,
public_key: test_pkey,
signed_at: '2025-05-14T12:22:26',
meta: test_meta,
signer: 'cooperative1',
signature: test_sign,
},
],
signature: test_sign,
public_key: test_pkey,
meta: test_meta,
version: '1.0.0',
doc_hash: test_hash,
meta_hash: test_hash,
}
export class CooperativeClass {
@@ -42,7 +33,7 @@ export class CooperativeClass {
await this.blockchain.createProgram({
coopname,
username: coopname,
type: 'marketplace',
type: 'cooplace',
title: 'КООПЛЕЙС',
announce: '',
description: '',
@@ -54,38 +45,6 @@ export class CooperativeClass {
meta: '',
is_can_coop_spend_share_contributions: false,
})
await this.blockchain.createProgram({
coopname,
username: coopname,
type: 'source',
title: 'Генерация',
announce: '',
description: '',
preview: '',
images: '',
calculation_type: 'free',
fixed_membership_contribution: `${Number(0).toFixed(4)} ${GOVERN_SYMBOL}`,
membership_percent_fee: 0, // 10%
meta: '',
is_can_coop_spend_share_contributions: true,
})
await this.blockchain.createProgram({
coopname,
username: coopname,
type: 'capital',
title: 'Благосостояние',
announce: '',
description: '',
preview: '',
images: '',
calculation_type: 'free',
fixed_membership_contribution: `${Number(0).toFixed(4)} ${GOVERN_SYMBOL}`,
membership_percent_fee: 0, // 10%
meta: '',
is_can_coop_spend_share_contributions: true,
})
}
async createCooperative(username: string, keys?: Keys) {
@@ -123,36 +82,30 @@ export class CooperativeClass {
income_hash: registration_hash,
})
await processLastDecision(this.blockchain, config.provider)
// console.log('Голосуем по решению в провайдере')
console.log('Голосуем по решению в провайдере')
// await this.blockchain.votefor({
// version: '1.0.0',
// signed_at: '2025-05-14T12:22:26',
// signed_hash: test_hash,
// signature: test_sign,
// public_key: test_pkey,
// coopname: config.provider,
// username: config.provider_chairman,
// decision_id: 1,
// })
await this.blockchain.votefor({
coopname: config.provider,
member: config.provider_chairman,
decision_id: 1,
})
// console.log('Утверждаем решение в провайдере')
console.log('Утверждаем решение в провайдере')
// await this.blockchain.authorize({
// coopname: config.provider,
// chairman: config.provider_chairman,
// decision_id: 1,
// document,
// })
await this.blockchain.authorize({
coopname: config.provider,
chairman: config.provider_chairman,
decision_id: 1,
document,
})
// console.log('Исполняем решение в провайдере')
console.log('Исполняем решение в провайдере')
// await this.blockchain.exec({
// executer: config.provider_chairman,
// coopname: config.provider,
// decision_id: 1,
// })
await this.blockchain.exec({
executer: config.provider_chairman,
coopname: config.provider,
decision_id: 1,
})
console.log('Отправляем подписанное положение о ЦПП Кошелька оператору')
@@ -161,7 +114,12 @@ export class CooperativeClass {
administrator: config.provider,
username: username!,
agreement_type: 'wallet',
document,
document: { // отправляем произвольный документ с валидной подписью
hash: '157192B276DA23CC84AB078FC8755C051C5F0430BF4802E55718221E6B76C777',
public_key: 'PUB_K1_5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzzEtUA4',
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
meta: '{}',
},
})
console.log('Переводим аккаунт в кооператив')
@@ -183,7 +141,7 @@ export class CooperativeClass {
})
console.log('Переводим инициализационные токены')
await this.blockchain.transfer({ from: 'eosio', to: account.username, quantity: `100.0000 ${SYMBOL}`, memo: '' })
await this.blockchain.transfer({ from: 'eosio', to: username, quantity: `100.0000 ${SYMBOL}`, memo: '' })
console.log(`Арендуем ресурсы кооперативу`)
+1 -1
View File
@@ -330,7 +330,7 @@ export async function startInfra() {
await blockchain.transfer({
from: 'eosio',
to: config.provider,
quantity: `1000.0000 ${config.token.symbol}`,
quantity: `100.0000 ${config.token.symbol}`,
memo: '',
})
+27 -102
View File
@@ -6,96 +6,29 @@ import type { Account, Contract, Keys } from '../types'
import config, { GOVERN_SYMBOL, SYMBOL } from '../configs'
import Blockchain from '../blockchain'
import { sendPostToCoopbackWithSecret, sleep } from '../utils'
import { fakeDocument } from '../tests/shared/fakeDocument'
export class ParticipantsClass {
// const test_hash
// = '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777'
// const test_sign
// = 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8'
// const test_pkey = 'EOS5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzxrLg3J'
// const test_meta = JSON.stringify({})
// const document = {
// hash: test_hash,
// signature: test_sign,
// public_key: test_pkey,
// meta: test_meta,
// }
export class CooperativeClass {
public blockchain: Blockchain
constructor(blockchain: Blockchain) {
this.blockchain = blockchain
}
async addUser2(coopname: string, username: string, keys?: Keys, referer?: string) {
const account = await this.blockchain.generateKeypair(
username,
keys,
'Аккаунт участника',
)
console.log('Регистрируем аккаунт')
const data: RegistratorContract.Actions.AddUser.IAddUser = {
coopname,
referer: referer || '',
username: account.username,
type: 'individual',
created_at: '2025-02-05T09:34:27',
initial: '100.0000 RUB',
minimum: '100.0000 RUB',
spread_initial: false,
meta: '',
}
await this.blockchain.api.transact({
actions: [
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.AddUser.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
}, {
blocksBehind: 3,
expireSeconds: 30,
})
console.log('Регистрируем аккаунт как пользователя')
const changeKey: RegistratorContract.Actions.ChangeKey.IChangeKey = {
coopname,
username,
public_key: account.publicKey,
changer: coopname,
}
await this.blockchain.api.transact({
actions: [
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.ChangeKey.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: changeKey,
},
],
}, {
blocksBehind: 3,
expireSeconds: 30,
})
// console.log('Отправляем подписанное положение о ЦПП Кошелька оператору')
// await this.blockchain.sendAgreement({
// coopname: config.provider,
// administrator: config.provider,
// username: username!,
// agreement_type: 'wallet',
// document: fakeDocument,
// })
// console.log('создаём кошелёк')
// console.log(`Арендуем ресурсы кооперативу`)
// await this.blockchain.powerup({
// payer: 'eosio',
// receiver: username,
// days: config.powerup.days,
// payment: `100.0000 ${config.token.symbol}`,
// transfer: true,
// })
}
async addUser(username: string, keys?: Keys, referer?: string) {
async addUser(username: string, keys?: Keys) {
const account = await this.blockchain.generateKeypair(
username,
keys,
@@ -105,7 +38,7 @@ export class ParticipantsClass {
const data: RegistratorContract.Actions.AddUser.IAddUser = {
coopname: config.provider,
referer: referer || '',
referer: '',
username: account.username,
type: 'individual',
created_at: '2025-02-05T09:34:27',
@@ -159,7 +92,12 @@ export class ParticipantsClass {
administrator: config.provider,
username: username!,
agreement_type: 'wallet',
document: fakeDocument,
document: { // отправляем произвольный документ с валидной подписью
hash: '157192B276DA23CC84AB078FC8755C051C5F0430BF4802E55718221E6B76C777',
public_key: 'PUB_K1_5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzzEtUA4',
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
meta: '{}',
},
})
console.log('создаём кошелёк')
@@ -176,28 +114,15 @@ export class ParticipantsClass {
}
}
export async function addUser(username: string, referer?: string) {
export async function addUser(username: string) {
// инициализируем инстанс с ключами
const blockchain = new Blockchain(config.network, config.private_keys)
const participants = new ParticipantsClass(blockchain)
const cooperative = new CooperativeClass(blockchain)
await participants.addUser(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
publicKey: process.env.EOSIO_PUB_KEY!,
}, referer)
}
export async function addUser2(coopname: string, username: string, referer?: string) {
// инициализируем инстанс с ключами
const blockchain = new Blockchain(config.network, config.private_keys)
const participants = new ParticipantsClass(blockchain)
await participants.addUser2(coopname, username, {
// eslint-disable-next-line node/prefer-global/process
privateKey: process.env.EOSIO_PRV_KEY!,
// eslint-disable-next-line node/prefer-global/process
publicKey: process.env.EOSIO_PUB_KEY!,
}, referer)
})
}
@@ -1,450 +0,0 @@
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { CapitalContract, SovietContract } from 'cooptypes'
import Blockchain from '../blockchain'
import config from '../configs'
import { getTotalRamUsage, globalRamStats } from '../utils/getTotalRamUsage'
import { addUser, addUser2 } from '../init/participant'
import { generateRandomUsername } from '../utils/randomUsername'
import { generateRandomSHA256 } from '../utils/randomHash'
import { sleep } from '../utils'
import { CooperativeClass } from '../init/cooperative'
import { getCirculationAccount, getCoopProgramWallet, getLedgerAccountById, getUserProgramWalletAmount } from './wallet/walletUtils'
import { registerContributor } from './capital/registerContributor'
import { signAppendix } from './capital/signAppendix'
import { capitalProgramId, circulationAccountId, sourceProgramId } from './capital/consts'
import { commitToResult } from './capital/commitToResult'
import { refreshSegment } from './capital/refreshSegment'
import { signWalletAgreement } from './wallet/signWalletAgreement'
import { fakeDocument } from './shared/fakeDocument'
import { depositToWallet } from './wallet/depositToWallet'
import { signCapitalAgreement } from './capital/signCapitalAgreement'
import { addAuthor } from './capital/addAuthor'
import { getSegment } from './capital/getSegment'
import { investInProject } from './capital/investInProject'
import { processDebt } from './capital/processDebt'
import { processCreateProjectProperty } from './capital/processCreateProjectProperty'
import { processCreateProgramProperty } from './capital/processCreateProgramProperty'
import { processStartVoting } from './capital/processStartVoting'
import { createVoteDistribution, submitVote } from './capital/submitVote'
import { processCompleteVoting } from './capital/processCompleteVoting'
import { processPushResult } from './capital/processPushResult'
import { processConvertSegment } from './capital/processConvertSegment'
import { processCalculateVotes } from './capital/processCalculateVotes'
import { processAddContributor } from './capital/processAddContributor'
import { processDeleteProject } from './capital/processDeleteProject'
import { getProject } from './capital/getProject'
import { processFundProgram } from './capital/processFundProgram'
import { processRefreshProg } from './capital/processRefreshProg'
import { processFundProject } from './capital/processFundProject'
import { processRefreshProj } from './capital/processRefreshProj'
// const CLI_PATH = 'src/index.ts'
const blockchain = new Blockchain(config.network, config.private_keys)
let metaProject: CapitalContract.Actions.CreateProject.ICreateProject
let componentProject: CapitalContract.Actions.CreateProject.ICreateProject
let newComponentProject: CapitalContract.Actions.CreateProject.ICreateProject
let testCoop: string
let chairman: string
let tester1: string
let tester2: string
let tester3: string
let tester4: string
let tester5: string
let investor1: string
let investor2: string
let investor3: string
// let result1: CapitalContract.Actions.CreateResult.ICreateResult
// let result2: CapitalContract.Actions.CreateResult.ICreateResult
// let result3: CapitalContract.Actions.CreateResult.ICreateResult
const commits: string[] = []
const tester1CommitHours = 10
const tester2CommitHours = 20
const tester3CommitHours = 100
const investAmount1 = 100000
const investAmount2 = 25000
const investAmount3 = 25000
const totalToCapitalConvertAmount = 0
const totalToProjectConvertAmount = 0
// Хранение хэшей долгов для каждого пользователя
let userDebtHashes: { [username: string]: string[] } = {}
beforeAll(async () => {
testCoop = generateRandomUsername()
chairman = generateRandomUsername()
await blockchain.update_pass_instance()
const coop = new CooperativeClass(blockchain)
await coop.createCooperative(testCoop, {
privateKey: process.env.EOSIO_PRV_KEY!,
publicKey: process.env.EOSIO_PUB_KEY!,
})
await blockchain.preInit({
coopname: testCoop,
username: config.provider,
status: 'active',
})
await addUser2(testCoop, chairman)
await blockchain.createBoard({
coopname: testCoop,
username: testCoop,
type: 'soviet',
members: [{
username: chairman,
is_voting: true,
position_title: 'Председатель совета',
position: 'chairman',
}],
name: 'Совет',
description: '',
})
console.log('testcoop: ', testCoop)
investor1 = generateRandomUsername()
console.log('investor1: ', investor1)
await addUser2(testCoop, investor1)
tester1 = generateRandomUsername()
console.log('tester1: ', tester1)
await addUser2(testCoop, tester1)
tester2 = generateRandomUsername()
console.log('tester2: ', tester2)
await addUser2(testCoop, tester2)
tester3 = generateRandomUsername()
console.log('tester3: ', tester3)
await addUser2(testCoop, tester3)
tester4 = generateRandomUsername()
console.log('tester4: ', tester4)
await addUser2(testCoop, tester4)
tester5 = generateRandomUsername()
console.log('tester5: ', tester5)
await addUser2(testCoop, tester5)
// Инициализируем массивы хэшей долгов для каждого пользователя
userDebtHashes = {
[tester1]: [],
[tester2]: [],
[tester3]: [],
[tester4]: [],
[tester5]: [],
[investor1]: [],
[investor2]: [],
[investor3]: [],
}
investor2 = generateRandomUsername()
console.log('investor2: ', investor2)
await addUser2(testCoop, investor2, tester4)
investor3 = generateRandomUsername()
console.log('investor3: ', investor3)
await addUser2(testCoop, investor3, tester5)
// const { stdout } = await execa('esno', [CLI_PATH, 'boot'], { stdio: 'inherit' })
// expect(stdout).toContain('Boot process completed')
}, 500_000)
afterAll(() => {
console.log('\n📊 **RAM USAGE SUMMARY** 📊')
let totalGlobalRam = 0
for (const [key, ramUsed] of Object.entries(globalRamStats)) {
const ramKb = (ramUsed / 1024).toFixed(2)
console.log(` ${key} = ${ramKb} kb`)
totalGlobalRam += ramUsed
}
console.log(`\n💾 **TOTAL RAM USED IN TESTS**: ${(totalGlobalRam / 1024).toFixed(2)} kb\n`)
console.log(`testCoop: ${testCoop}\ntester1: ${tester1}\ntester2: ${tester2}\ntester3: ${tester3}\ntester4: ${tester4}\ntester5: ${tester5}\ninvestor1: ${investor1}\ninvestor2: ${investor2}\ninvestor3: ${investor3}`)
})
describe('тест контракта CAPITAL', () => {
it('создаём программу генерации', async () => {
const program = await getCoopProgramWallet(blockchain, testCoop, sourceProgramId)
if (!program) {
const data: SovietContract.Actions.Programs.CreateProgram.ICreateProgram = {
coopname: testCoop,
is_can_coop_spend_share_contributions: true,
username: chairman,
title: 'Договор УХД',
announce: '',
description: '',
preview: '',
images: '',
calculation_type: 'free',
fixed_membership_contribution: '0.0000 RUB',
membership_percent_fee: '0',
meta: '',
type: 'source',
}
const result = await blockchain.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Programs.CreateProgram.actionName,
authorization: [
{
actor: chairman,
permission: 'active',
},
],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
}
})
it('создаём программу капитализации', async () => {
const program = await getCoopProgramWallet(blockchain, testCoop, capitalProgramId)
if (!program) {
const data: SovietContract.Actions.Programs.CreateProgram.ICreateProgram = {
coopname: testCoop,
is_can_coop_spend_share_contributions: false,
username: chairman,
title: 'Капитализация',
announce: '',
description: '',
preview: '',
images: '',
calculation_type: 'free',
fixed_membership_contribution: '0.0000 RUB',
membership_percent_fee: '0',
meta: '',
type: 'capital',
}
const result = await blockchain.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Programs.CreateProgram.actionName,
authorization: [
{
actor: chairman,
permission: 'active',
},
],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
}
})
it('импорт вкладчика ДО установки конфига - должен работать', async () => {
// Создаем тестового пользователя для импорта
const importUsername = generateRandomUsername()
console.log('importUsername: ', importUsername)
await addUser2(testCoop, importUsername)
const contributorHash = generateRandomSHA256()
const contributionAmount = '10000.0000 RUB'
// Получаем состояние паевого счета ДО импорта
const shareFundBefore = await getCirculationAccount(blockchain, testCoop)
const shareFundBalanceBefore = parseFloat(shareFundBefore.available)
console.log('Share fund before import:', shareFundBalanceBefore)
// Импортируем вкладчика
const importData: CapitalContract.Actions.ImportContributor.IImportContributor = {
coopname: testCoop,
username: importUsername,
contributor_hash: contributorHash,
contribution_amount: contributionAmount,
memo: 'договор №123 от такого то числа',
}
const result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.ImportContributor.actionName,
authorization: [
{
actor: testCoop,
permission: 'active',
},
],
data: importData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
// Проверяем, что вкладчик был создан
const contributorTable = await blockchain.getTableRows(
CapitalContract.contractName.production,
testCoop,
'contributors',
)
console.log('contributorTable: ', contributorTable)
expect(contributorTable.length).toBe(1)
const contributor = contributorTable[0]
expect(contributor.username).toBe(importUsername)
expect(contributor.status).toBe('active')
// Проверяем, что кошелек программы был создан
const wallet = await getUserProgramWalletAmount(blockchain, testCoop, importUsername, capitalProgramId)
expect(parseFloat(wallet)).toBe(parseFloat(contributionAmount))
// Проверяем увеличение паевого счета
const shareFundAfter = await getCirculationAccount(blockchain, testCoop)
const shareFundBalanceAfter = parseFloat(shareFundAfter.available)
console.log('Share fund after import:', shareFundBalanceAfter)
expect(shareFundBalanceAfter).toBe(shareFundBalanceBefore + parseFloat(contributionAmount))
})
it('инициализируем контракт CAPITAL и сверяем конфиг', async () => {
const data: CapitalContract.Actions.SetConfig.ISetConfig = {
coopname: testCoop,
config: {
coordinator_bonus_percent: 4,
expense_pool_percent: 100,
coordinator_invite_validity_days: 30,
voting_period_in_days: 7,
authors_voting_percent: 38.2,
creators_voting_percent: 38.2,
},
}
const result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.SetConfig.actionName,
authorization: [
{
actor: testCoop,
permission: 'active',
},
],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
const state = (await blockchain.getTableRows(
CapitalContract.contractName.production,
CapitalContract.contractName.production,
'state',
1,
testCoop,
testCoop,
))[0]
console.log('state: ', state)
expect(state).toBeDefined()
expect(state.coopname).toBe(testCoop)
expect(parseFloat(state.config.coordinator_bonus_percent)).toBe(data.config.coordinator_bonus_percent)
expect(parseFloat(state.config.expense_pool_percent)).toBe(data.config.expense_pool_percent)
expect(parseFloat(state.config.coordinator_invite_validity_days)).toBe(data.config.coordinator_invite_validity_days)
expect(parseFloat(state.config.voting_period_in_days)).toBe(data.config.voting_period_in_days)
expect(parseFloat(state.config.authors_voting_percent)).toBe(data.config.authors_voting_percent)
expect(parseFloat(state.config.creators_voting_percent)).toBe(data.config.creators_voting_percent)
})
it('импорт вкладчика ПОСЛЕ установки конфига - должен провалиться', async () => {
// Создаем еще одного тестового пользователя для импорта
const importUsername2 = generateRandomUsername()
console.log('importUsername2: ', importUsername2)
await addUser(importUsername2)
const contributorHash2 = generateRandomSHA256()
const contributionAmount2 = '5000.0000 RUB'
// Пытаемся импортировать вкладчика после установки конфига
const importData2: CapitalContract.Actions.ImportContributor.IImportContributor = {
coopname: testCoop,
username: importUsername2,
contributor_hash: contributorHash2,
contribution_amount: contributionAmount2,
memo: 'договор №123 от такого то числа',
}
// Ожидаем, что транзакция провалится
await expect(
blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.ImportContributor.actionName,
authorization: [
{
actor: testCoop,
permission: 'active',
},
],
data: importData2,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
),
).rejects.toThrow() // Ожидаем ошибку
// Проверяем, что вкладчик НЕ был создан
const contributorTable = await blockchain.getTableRows(
CapitalContract.contractName.production,
testCoop,
'contributors',
)
expect(contributorTable.length).toBe(1)
// Проверяем, что кошелек программы НЕ был создан
const wallet = await getUserProgramWalletAmount(blockchain, testCoop, importUsername2, capitalProgramId)
expect(wallet).toBe('0.0000 RUB')
})
})
File diff suppressed because it is too large Load Diff
@@ -1,75 +0,0 @@
import { CapitalContract } from 'cooptypes'
import { expect } from 'vitest'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
export async function addAuthor(blockchain: Blockchain, coopname: string, project_hash: string, author: string) {
const data: CapitalContract.Actions.AddAuthor.IAddAuthor = {
coopname,
project_hash,
author,
}
const result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.AddAuthor.actionName,
authorization: [
{
actor: coopname,
permission: 'active',
},
],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
const key = makeCombinedChecksum256NameIndexKey(project_hash, author)
const rows = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
CapitalContract.Tables.Segments.tableName,
1000,
key,
key,
3,
'i128',
))
const segment = rows[0]
console.log('segment: ', segment)
expect(segment).toBeDefined()
expect(segment.username).toBe(author)
expect(segment.project_hash).toBe(project_hash)
expect(segment.is_author).toBe(1)
const project = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
CapitalContract.Tables.Projects.tableName,
1000,
project_hash,
project_hash,
3,
'sha256',
))[0]
expect(project).toBeDefined()
return {
project,
segment,
}
}
@@ -2,20 +2,34 @@ import { expect } from 'vitest'
import { CapitalContract, SovietContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { fakeDocument } from '../shared/fakeDocument'
import { processMasterApprove } from './processMasterApprove'
import { fakeDocument } from '../soviet/fakeDocument'
import { processDecision } from '../soviet/processDecision'
import { ratePerHour } from './consts'
export async function commitToResult(
blockchain: any,
coopname: string,
resultHash: string,
projectHash: string,
creator: string,
spendHours: number,
) {
const commitHash = generateRandomSHA256()
console.log(`\n🔹 Начало коммита: ${commitHash}, проект: ${projectHash}, часы: ${spendHours}`)
const totalSpended = `${(spendHours * parseFloat(ratePerHour)).toFixed(4)} RUB`
console.log(`\n🔹 Начало коммита: ${commitHash}, часы: ${spendHours}, сумма: ${totalSpended}`)
// Получение текущих данных перед коммитом
const prevResult = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'results',
1,
resultHash,
resultHash,
2,
'sha256',
))[0] || { spended: '0.0000 RUB', commits_count: 0 }
// Получение текущих данных проекта перед коммитом
const prevProject = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
@@ -39,32 +53,21 @@ export async function commitToResult(
))[0] || { contributed_hours: 0, available: '0.0000 RUB' }
console.log('📊 Балансы до коммита:')
console.log('▶ Результат:', prevResult)
console.log('▶ Проект:', prevProject)
console.log('▶ Контрибьютор:', prevContributor)
// Обновим состояние проекта после возможной смены статуса
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
projectHash,
projectHash,
3,
'sha256',
))[0]
const prevTotalCommits = projectBefore?.counts?.total_commits ?? 0
const prevCreatorsHours = projectBefore?.fact?.creators_hours ?? 0
// Создание коммита
const commitData: CapitalContract.Actions.CreateCommit.ICommit = {
coopname,
username: creator,
project_hash: projectHash,
application: coopname,
result_hash: resultHash,
commit_hash: commitHash,
creator_hours: spendHours,
contributed_hours: spendHours,
username: creator,
}
console.log(`\n🚀 Отправка транзакции CreateCommit для результата ${resultHash}`)
const createCommitResult = await blockchain.api.transact(
{
actions: [
@@ -86,7 +89,7 @@ export async function commitToResult(
expect(createCommitResult.transaction_id).toBeDefined()
// Проверка созданного коммита
const blockchainCommit = (await blockchain.getTableRows(
let blockchainCommit = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'commits',
@@ -100,30 +103,39 @@ export async function commitToResult(
console.log('🔍 Коммит после создания:', blockchainCommit)
expect(blockchainCommit).toBeDefined()
expect(blockchainCommit.commit_hash).toBe(commitHash)
expect(blockchainCommit.project_hash).toBe(projectHash)
expect(blockchainCommit.spended).toBe(totalSpended)
expect(blockchainCommit.status).toBe('created')
// Получение данных проекта для определения мастера
const projectData = (await blockchain.getTableRows(
CapitalContract.contractName.production,
// Утверждение коммита
const approveCommitData: SovietContract.Actions.Approves.ConfirmApprove.IConfirmApprove = {
coopname,
'projects',
1,
projectHash,
projectHash,
3,
'sha256',
))[0]
approval_hash: commitHash,
approved_document: fakeDocument,
}
expect(projectData).toBeDefined()
expect(projectData.master).toBeDefined()
console.log(`\n✅ Подтверждение коммита ${commitHash}`)
const approveCommitResult = await blockchain.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Approves.ConfirmApprove.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: approveCommitData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
// Утверждение коммита мастером проекта
console.log(`\n✅ Подтверждение коммита ${commitHash} мастером ${projectData.master}`)
const approveCommitResult = await processMasterApprove(blockchain, coopname, commitHash, projectData.master)
getTotalRamUsage(approveCommitResult)
expect(approveCommitResult.transaction_id).toBeDefined()
// Проверка утвержденного коммита
const blockchainEmptyCommit = (await blockchain.getTableRows(
blockchainCommit = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'commits',
@@ -135,9 +147,96 @@ export async function commitToResult(
))[0]
// Коммит удаляем после утверждения
expect(blockchainEmptyCommit).toBeUndefined()
expect(blockchainCommit).toBeUndefined()
// // Получение всех решений и выполнение последнего
// 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)
// {
// // Утверждение коммита
// 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,
// },
// )
// getTotalRamUsage(act1Result)
// expect(act1Result.transaction_id).toBeDefined()
// }
// {
// // Утверждение коммита
// 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,
// },
// )
// getTotalRamUsage(act2Result)
// expect(act2Result.transaction_id).toBeDefined()
// }
// Проверка результата после утверждения коммита
const finalResult = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'results',
1,
resultHash,
resultHash,
2,
'sha256',
))[0]
// Проверка проекта после утверждения коммита
const finalProject = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
@@ -161,19 +260,23 @@ export async function commitToResult(
))[0]
console.log('\n📊 Балансы после утверждения коммита:')
console.log('▶ Результат:', finalResult)
console.log('▶ Проект:', finalProject)
console.log('▶ Контрибьютор:', finalContributor)
// Проверки по проекту
expect(finalProject.counts.total_commits).toBe(prevTotalCommits + 1)
expect(parseInt(finalProject.fact.creators_hours)).toBe(prevCreatorsHours + spendHours)
// 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)
// expect(parseFloat(finalContributor.available)).toBe(parseFloat(prevContributor.available) + parseFloat(totalSpended))
console.log(`\n✅ Коммит ${commitHash} завершен успешно!`)
return {
commitHash,
commit: blockchainCommit,
transactionId: approveCommitResult.transaction_id,
transaction: approveCommitResult,
finalResult,
finalProject,
finalContributor,
}
@@ -4,4 +4,3 @@ export const walletProgramId = 1
export const ratePerHour = '1000.0000 RUB'
export const sourceProgramName = 'source'
export const capitalProgramName = 'capital'
export const circulationAccountId = 80
@@ -1,27 +0,0 @@
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
/**
* Получает кошелек капитализации пользователя
* @param blockchain экземпляр блокчейна
* @param coopname название кооператива
* @param username имя пользователя
* @returns кошелек капитализации или null если не найден
*/
export async function getCapitalWallet(
blockchain: Blockchain,
coopname: string,
username: string,
) {
const capitalWallets = await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'capwallets',
100,
username,
username,
2, // by_username index
)
return capitalWallets[0] || null
}
@@ -1,18 +0,0 @@
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
export async function getProject(blockchain: Blockchain, coopname: string, project_hash: string): Promise<CapitalContract.Tables.Projects.IProject> {
const rows = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
CapitalContract.Tables.Projects.tableName,
1000,
project_hash,
project_hash,
3,
'sha256',
))
return rows[0]
}
@@ -1,30 +0,0 @@
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
/**
* Получает кошелек проекта пользователя
* @param blockchain экземпляр блокчейна
* @param coopname название кооператива
* @param projectHash хеш проекта
* @param username имя пользователя
* @returns кошелек проекта или null если не найден
*/
export async function getProjectWallet(
blockchain: Blockchain,
coopname: string,
projectHash: string,
username: string,
) {
const projectWallets = await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projwallets',
100,
username,
username,
3, // by_username index
)
// Ищем кошелек для конкретного проекта
return projectWallets.find((wallet: any) => wallet.project_hash === projectHash) || null
}
@@ -1,19 +0,0 @@
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
export async function getSegment(blockchain: Blockchain, coopname: string, project_hash: string, author: string): Promise<CapitalContract.Tables.Segments.ISegment> {
const key = makeCombinedChecksum256NameIndexKey(project_hash, author)
const rows = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
CapitalContract.Tables.Segments.tableName,
1000,
key,
key,
3,
'i128',
))
return rows[0]
}
@@ -4,9 +4,7 @@ import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { getCoopProgramWallet, getUserProgramWallet } from '../wallet/walletUtils'
import { processDecision } from '../soviet/processDecision'
import { processApprove } from './processApprove'
import { getSegment } from './getSegment'
import { sourceProgramId } from './consts'
import { capitalProgramId, sourceProgramId } from './consts'
export async function investInProject(
blockchain: any,
@@ -19,6 +17,18 @@ export async function investInProject(
const investHash = generateRandomSHA256()
console.log(`\n🔹 Начало инвестиции: ${investHash}`)
// Получение текущих данных перед инвестицией
const prevContributor = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'contributors',
1,
investor,
investor,
2,
'i64',
))[0] || { invested: '0.0000 RUB' }
const prevProject = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
@@ -30,17 +40,19 @@ export async function investInProject(
'sha256',
))[0] || { invested: '0.0000 RUB', available: '0.0000 RUB' }
const prevUserWallet = await getUserProgramWallet(blockchain, coopname, investor, sourceProgramId) || { blocked: '0.0000 RUB' }
const prevProgramWallet = await getCoopProgramWallet(blockchain, coopname, sourceProgramId) || { blocked: '0.0000 RUB', share_contributions: '0.0000 RUB' }
const prevUserWallet = await getUserProgramWallet(blockchain, coopname, investor, capitalProgramId) || { blocked: '0.0000 RUB' }
const prevProgramWallet = await getCoopProgramWallet(blockchain, coopname, capitalProgramId) || { blocked: '0.0000 RUB', share_contributions: '0.0000 RUB' }
console.log('📊 Балансы до инвестиции:')
console.log('▶ Контрибьютор:', prevContributor)
console.log('▶ Проект:', prevProject)
console.log('▶ Кошелек пользователя:', prevUserWallet)
console.log('▶ Кошелек программы:', prevProgramWallet)
// Создание инвестиции
const createInvestData: CapitalContract.Actions.CreateProjectInvest.ICreateInvest = {
const createInvestData: CapitalContract.Actions.CreateInvest.ICreateInvest = {
coopname,
application: coopname,
project_hash: projectHash,
username: investor,
invest_hash: investHash,
@@ -54,7 +66,7 @@ export async function investInProject(
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CreateProjectInvest.actionName,
name: CapitalContract.Actions.CreateInvest.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: createInvestData,
},
@@ -69,7 +81,7 @@ export async function investInProject(
getTotalRamUsage(createInvestResult)
expect(createInvestResult.transaction_id).toBeDefined()
const blockchainInvest = (await blockchain.getTableRows(
let blockchainInvest = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'invests',
@@ -85,11 +97,36 @@ export async function investInProject(
expect(blockchainInvest.status).toBe('created')
// Утверждение инвестиции
console.log(`\n✅ Подтверждение инвестиции ${investHash}`)
const approveInvestResult = await processApprove(blockchain, coopname, investHash)
const approveInvestData: CapitalContract.Actions.ApproveInvest.IApproveInvest = {
coopname,
application: coopname,
approver: 'ant',
invest_hash: investHash,
approved_statement: fakeDocument,
}
// Проверка утвержденной инвестиции
const blockchainEmptyInvest = (await blockchain.getTableRows(
console.log(`\n✅ Подтверждение инвестиции ${investHash}`)
const approveInvestResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.ApproveInvest.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: approveInvestData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(approveInvestResult)
expect(approveInvestResult.transaction_id).toBeDefined()
blockchainInvest = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'invests',
@@ -100,10 +137,49 @@ export async function investInProject(
'sha256',
))[0]
// Инвестиция удаляется после утверждения
expect(blockchainEmptyInvest).toBeUndefined()
console.log('🔍 Инвестиция после утверждения:', blockchainInvest)
expect(blockchainInvest).toBeDefined()
expect(blockchainInvest.status).toBe('approved')
const finalProject = (await blockchain.getTableRows(
// Получение всех решений и выполнение последнего
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)
blockchainInvest = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'invests',
1,
investHash,
investHash,
2,
'sha256',
))[0]
console.log('📌 Инвестиция после завершения:', blockchainInvest)
expect(blockchainInvest).toBeUndefined()
// Получение новых данных после инвестиции
const contributor = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'contributors',
1,
investor,
investor,
2,
'i64',
))[0]
const project = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
@@ -114,30 +190,24 @@ export async function investInProject(
'sha256',
))[0]
const finalUserWallet = await getUserProgramWallet(blockchain, coopname, investor, sourceProgramId)
const finalProgramWallet = await getCoopProgramWallet(blockchain, coopname, sourceProgramId)
// Получение сегмента инвестора для данного проекта
const segment = await getSegment(blockchain, coopname, projectHash, investor)
const userWallet = await getUserProgramWallet(blockchain, coopname, investor, capitalProgramId)
const programWallet = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
console.log('\n📊 Балансы после инвестиции:')
console.log('▶ Проект:', finalProject)
console.log('▶ Сегмент:', segment)
console.log('▶ Кошелек пользователя:', finalUserWallet)
console.log('▶ Кошелек программы:', finalProgramWallet)
console.log('▶ Контрибьютор:', contributor)
console.log('▶ Проект:', project)
console.log('▶ Кошелек пользователя:', userWallet)
console.log('▶ Кошелек программы:', programWallet)
// Проверка изменения балансов
expect(parseFloat(finalUserWallet.blocked)).toBeCloseTo(parseFloat(prevUserWallet.blocked) + parseFloat(investAmount), 1)
expect(parseFloat(finalProgramWallet.blocked)).toBeCloseTo(parseFloat(prevProgramWallet.blocked) + parseFloat(investAmount), 1)
expect(parseFloat(contributor.invested)).toBe(parseFloat(prevContributor.invested) + parseFloat(investAmount))
expect(parseFloat(project.invested)).toBe(parseFloat(prevProject.invested) + parseFloat(investAmount))
expect(parseFloat(project.available)).toBe(parseFloat(prevProject.available) + parseFloat(investAmount))
expect(parseFloat(userWallet.blocked)).toBe(parseFloat(prevUserWallet.blocked) + parseFloat(investAmount))
expect(parseFloat(programWallet.blocked)).toBe(parseFloat(prevProgramWallet.blocked) + parseFloat(investAmount))
expect(parseFloat(programWallet.share_contributions)).toBe(parseFloat(prevProgramWallet.share_contributions) + parseFloat(investAmount))
console.log(`\n✅ Инвестирование на ${investAmount} завершено успешно!`)
return {
investHash,
invest: blockchainInvest,
transactionId: approveInvestResult.transaction_id,
prevProject,
project: finalProject,
segment,
}
return { investHash, transactionId: approveInvestResult.transaction_id }
}
@@ -1,53 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { getSegment } from './getSegment'
export async function processAddContributor(
blockchain: Blockchain,
coopname: string,
project_hash: string,
username: string,
) {
// Состояние сегмента до добавления (может не существовать)
let segmentBefore: any = null
try {
segmentBefore = await getSegment(blockchain, coopname, project_hash, username)
}
catch (error) {
// Сегмент может не существовать - это нормально
}
const data: CapitalContract.Actions.RegisterShare.IRegisterShare = {
coopname,
project_hash,
username,
}
// Выполняем добавление участника в проект
const txAdd = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.RegisterShare.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txAdd)
expect(txAdd.transaction_id).toBeDefined()
// Состояние сегмента после добавления
const segmentAfter = await getSegment(blockchain, coopname, project_hash, username)
return {
transactionId: txAdd.transaction_id,
segmentBefore,
segmentAfter,
}
}
@@ -1,36 +0,0 @@
import { expect } from 'vitest'
import { SovietContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { fakeDocument } from '../shared/fakeDocument'
export async function processApprove(blockchain: any, coopname: string, approvalHash: string) {
fakeDocument.signatures[0].signer = 'ant'
const data: SovietContract.Actions.Approves.ConfirmApprove.IConfirmApprove = {
coopname,
approval_hash: approvalHash,
approved_document: fakeDocument,
}
const result = await blockchain.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Approves.ConfirmApprove.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
return result
}
@@ -1,111 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
/**
* Функция для расчета голосов участника по методу Водянова
* @param blockchain экземпляр блокчейна
* @param data данные для расчета голосов
*/
export async function processCalculateVotes(
blockchain: Blockchain,
data: CapitalContract.Actions.CalculateVotes.IFinalVoting,
) {
const { coopname, username, project_hash } = data
console.log(`\n🧮 Начало расчета голосов для участника ${username} в проекте ${project_hash}`)
// Состояние до: данные проекта
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
// Состояние до: данные сегмента участника
const segmentKey = makeCombinedChecksum256NameIndexKey(project_hash, username)
const segmentBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'segments',
1,
segmentKey,
segmentKey,
3,
'i128',
))[0]
// console.log('📊 Состояние до расчета голосов:')
// console.log('▶ Статус проекта:', projectBefore.status)
// console.log('▶ Голосов получено:', projectBefore.voting.votes_received)
// console.log('▶ Всего участников голосования:', projectBefore.voting.total_voters)
// console.log(`▶ Сегмент участника ${username}:`)
// console.log(' - voting_bonus:', segmentBefore.voting_bonus)
// console.log(' - equal_author_bonus:', segmentBefore.equal_author_bonus)
// console.log(' - direct_creator_bonus:', segmentBefore.direct_creator_bonus)
// console.log(' - total_segment_cost:', segmentBefore.total_segment_cost)
// Отправляем транзакцию расчета голосов
const txResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CalculateVotes.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txResult)
expect(txResult.transaction_id).toBeDefined()
// Состояние после: данные проекта
const projectAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
// Состояние после: данные сегмента участника
const segmentAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'segments',
1,
segmentKey,
segmentKey,
3,
'i128',
))[0]
console.log('📊 Состояние после расчета голосов:')
console.log('▶ Статус проекта:', projectAfter.status)
console.log(`▶ Сегмент участника ${username}:`)
console.log(' - voting_bonus:', segmentAfter.voting_bonus)
console.log(' - total_voting_pool:', projectAfter.voting.amounts.total_voting_pool)
console.log(' - active_voting_amount:', projectAfter.voting.amounts.active_voting_amount)
console.log(' - equal_voting_amount:', projectAfter.voting.amounts.equal_voting_amount)
return {
txId: txResult.transaction_id,
projectBefore,
projectAfter,
segmentBefore,
segmentAfter,
}
}
@@ -1,82 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
/**
* Функция для завершения голосования по проекту
* @param blockchain экземпляр блокчейна
* @param data данные для завершения голосования
*/
export async function processCompleteVoting(
blockchain: Blockchain,
data: CapitalContract.Actions.CompleteVoting.ICompleteVoting,
) {
const { coopname, project_hash } = data
console.log(`\n✅ Начало завершения голосования для проекта ${project_hash}`)
// Состояние до: данные проекта
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
console.log('📊 Состояние до завершения голосования:')
console.log('▶ Статус проекта:', projectBefore.status)
console.log('▶ Голосов получено:', projectBefore.voting.votes_received)
console.log('▶ Всего участников голосования:', projectBefore.voting.total_voters)
console.log('▶ Дедлайн голосования:', projectBefore.voting.voting_deadline)
// Отправляем транзакцию завершения голосования
const txResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CompleteVoting.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txResult)
expect(txResult.transaction_id).toBeDefined()
// Состояние после: данные проекта
const projectAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
console.log('📊 Состояние после завершения голосования:')
console.log('▶ Статус проекта:', projectAfter.status)
console.log('▶ Голосов получено:', projectAfter.voting.votes_received)
console.log('▶ Всего участников голосования:', projectAfter.voting.total_voters)
// Проверяем что статус изменился на completed
expect(projectAfter.status).toBe('completed')
console.log(`\n✅ Голосование для проекта ${project_hash} успешно завершено!`)
return {
projectHash: project_hash,
txId: txResult.transaction_id,
projectBefore,
projectAfter,
}
}
@@ -1,173 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { fakeDocument } from '../shared/fakeDocument'
import type Blockchain from '../../blockchain'
import { getCoopProgramWallet, getUserProgramWallet } from '../wallet/walletUtils'
import { getSegment } from './getSegment'
import { getProjectWallet } from './getProjectWallet'
import { capitalProgramId, walletProgramId } from './consts'
import { getProject } from './getProject'
/**
* Функция для конвертации сегмента участника
* @param blockchain экземпляр блокчейна
* @param coopname название кооператива
* @param username имя пользователя
* @param projectHash хеш проекта
* @param walletAmount сумма для конвертации в главный кошелек
* @param capitalAmount сумма для конвертации в капитализацию
* @param projectAmount сумма для конвертации в кошелек проекта
*/
export async function processConvertSegment(
blockchain: Blockchain,
coopname: string,
username: string,
projectHash: string,
walletAmount: string,
capitalAmount: string,
projectAmount: string,
) {
const convertHash = generateRandomSHA256()
console.log(`\n🔹 Начало процесса конвертации сегмента: ${convertHash}`)
// Получаем сегмент до конвертации
const segmentBefore = await getSegment(blockchain, coopname, projectHash, username)
console.log('📊 Сегмент до конвертации:', segmentBefore)
// Получаем кошельки до конвертации
const mainWalletBefore = await getCoopProgramWallet(blockchain, coopname, walletProgramId)
const capitalWalletBefore = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
const userCapitalWalletBefore = await getUserProgramWallet(blockchain, coopname, username, capitalProgramId)
const projectWalletBefore = await getProjectWallet(blockchain, coopname, projectHash, username)
const projectBefore = await getProject(blockchain, coopname, projectHash)
console.log('💰 Кошельки до конвертации:')
console.log(' ▶ Главный кошелек программы:', mainWalletBefore)
console.log(' ▶ Глобальный кошелек программы капитализации:', capitalWalletBefore)
console.log(' ▶ Кошелек пользователя в программе капитализации:', userCapitalWalletBefore)
console.log(' ▶ Кошелек проекта:', projectWalletBefore)
console.log(' ▶ Проект до конвертации: ', projectBefore)
// 1. Конвертируем сегмент
const convertSegmentData: CapitalContract.Actions.ConvertSegment.IConvertSegment = {
coopname,
username,
project_hash: projectHash,
convert_hash: convertHash,
wallet_amount: walletAmount,
capital_amount: capitalAmount,
project_amount: projectAmount,
convert_statement: fakeDocument,
}
console.log(`\n🚀 Отправка транзакции ConvertSegment для ${username}`)
console.log(` В главный кошелек: ${walletAmount}`)
console.log(` В капитализацию: ${capitalAmount}`)
console.log(` В проект: ${projectAmount}`)
const convertResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.ConvertSegment.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: convertSegmentData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(convertResult)
expect(convertResult.transaction_id).toBeDefined()
console.log('✅ Сегмент конвертирован:', convertHash)
// Получаем сегмент после конвертации (должен быть удален)
const segmentAfter = await getSegment(blockchain, coopname, projectHash, username)
// Получаем кошельки после конвертации
const mainWalletAfter = await getCoopProgramWallet(blockchain, coopname, walletProgramId)
const capitalWalletAfter = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
const userCapitalWalletAfter = await getUserProgramWallet(blockchain, coopname, username, capitalProgramId)
const projectWalletAfter = await getProjectWallet(blockchain, coopname, projectHash, username)
console.log('💰 Кошельки после конвертации:')
console.log(' ▶ Главный кошелек программы:', mainWalletAfter)
console.log(' ▶ Глобальный кошелек программы капитализации:', capitalWalletAfter)
console.log(' ▶ Кошелек пользователя в программе капитализации:', userCapitalWalletAfter)
console.log(' ▶ Кошелек проекта:', projectWalletAfter)
// Проверяем что сегмент удален после конвертации
expect(segmentAfter).toBeUndefined()
console.log('🔍 Сегмент после конвертации: ', segmentAfter)
// Проверяем изменения в кошельках
console.log('\n🔍 Проверка изменений в кошельках:')
// Проверяем главный кошелек программы (wallet_amount)
if (walletAmount !== '0.0000 RUB') {
const walletAmountValue = parseFloat(walletAmount.split(' ')[0])
const beforeAvailable = mainWalletBefore ? parseFloat(mainWalletBefore.available.split(' ')[0]) : 0
const afterAvailable = mainWalletAfter ? parseFloat(mainWalletAfter.available.split(' ')[0]) : 0
const expectedIncrease = beforeAvailable + walletAmountValue
console.log(`✅ Главный кошелек программы: ${beforeAvailable}${afterAvailable} (+${walletAmountValue})`)
expect(afterAvailable).toBeCloseTo(expectedIncrease, 1)
}
// Проверяем глобальный кошелек программы капитализации (capital_amount)
if (capitalAmount !== '0.0000 RUB') {
const capitalAmountValue = parseFloat(capitalAmount.split(' ')[0])
const beforeBlocked = capitalWalletBefore ? parseFloat(capitalWalletBefore.blocked.split(' ')[0]) : 0
const afterBlocked = capitalWalletAfter ? parseFloat(capitalWalletAfter.blocked.split(' ')[0]) : 0
const expectedIncrease = beforeBlocked + capitalAmountValue
console.log(`✅ Глобальный кошелек программы капитализации: ${beforeBlocked}${afterBlocked} (+${capitalAmountValue})`)
expect(afterBlocked).toBeCloseTo(expectedIncrease, 1)
}
// Проверяем кошелек пользователя в программе капитализации (capital_amount)
if (capitalAmount !== '0.0000 RUB') {
const capitalAmountValue = parseFloat(capitalAmount.split(' ')[0])
const beforeBlocked = userCapitalWalletBefore ? parseFloat(userCapitalWalletBefore.blocked.split(' ')[0]) : 0
const afterBlocked = userCapitalWalletAfter ? parseFloat(userCapitalWalletAfter.blocked.split(' ')[0]) : 0
const expectedIncrease = beforeBlocked + capitalAmountValue
console.log(`✅ Кошелек пользователя в программе капитализации: ${beforeBlocked}${afterBlocked} (+${capitalAmountValue})`)
expect(afterBlocked).toBeCloseTo(expectedIncrease, 1)
}
// Проверяем кошелек проекта (project_amount)
if (projectAmount !== '0.0000 RUB' && projectBefore.parent_hash.includes('0000000000')) {
const projectAmountValue = parseFloat(projectAmount.split(' ')[0])
const beforeShares = projectWalletBefore ? parseFloat(projectWalletBefore.shares.split(' ')[0]) : 0
const afterShares = projectWalletAfter ? parseFloat(projectWalletAfter.shares.split(' ')[0]) : 0
const expectedIncrease = beforeShares + projectAmountValue
console.log(`✅ Кошелек проекта: ${beforeShares}${afterShares} (+${projectAmountValue})`)
expect(afterShares).toBeCloseTo(expectedIncrease, 1)
}
console.log('\n📊 Результаты после конвертации сегмента:')
console.log('▶ Сегмент до:', segmentBefore)
console.log('▶ Сегмент после удалён: ', segmentAfter)
console.log(`\n✅ Процесс конвертации сегмента завершен успешно!`)
return {
convertHash,
segmentBefore,
segmentAfter,
mainWalletBefore,
mainWalletAfter,
capitalWalletBefore,
capitalWalletAfter,
userCapitalWalletBefore,
userCapitalWalletAfter,
projectWalletBefore,
projectWalletAfter,
transactionId: convertResult.transaction_id,
}
}
@@ -1,109 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { getCoopProgramWallet, getLedgerAccountById } from '../wallet/walletUtils'
import { processLastDecision } from '../soviet/processLastDecision'
import { processApprove } from './processApprove'
import { capitalProgramId, circulationAccountId } from './consts'
export async function processCreateProgramProperty(
blockchain: Blockchain,
data: CapitalContract.Actions.CreateProgramProperty.ICreateProgramProperty,
fakeDocument: any,
) {
const { coopname, username, property_hash } = data
// Состояния до: кошелек программы капитализации и паевой счет в бухгалтерии
const programWalletBefore = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
const ledgerShareBefore = await getLedgerAccountById(blockchain, coopname, circulationAccountId)
// 1) Создаём программное предложение имущественного взноса
const txCreate = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CreateProgramProperty.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txCreate)
expect(txCreate.transaction_id).toBeDefined()
// 2) Подтверждение одобрения председателем (через soviet)
await processApprove(blockchain as any, coopname, property_hash as unknown as string)
// 3) Обработка решения совета (AUTHORIZE_PROGRAM_PROPERTY)
await processLastDecision(blockchain, coopname)
// 4) Акт 1 — подписывает участник-вноситель (username)
fakeDocument.signatures[0].signer = username
const act1Data = {
coopname,
username,
property_hash,
act: fakeDocument,
} as CapitalContract.Actions.Act1ProgramProperty.IAct1ProgramProperty
const txAct1 = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.Act1ProgramProperty.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: act1Data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txAct1)
expect(txAct1.transaction_id).toBeDefined()
// 5) Акт 2 — подписывает председатель совета кооператива ('ant')
fakeDocument.signatures[0].signer = 'ant'
const act2Data = {
coopname,
property_hash,
username: 'ant',
act: fakeDocument,
} as CapitalContract.Actions.Act2ProgramProperty.IAct2ProgramProperty
const txAct2 = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.Act2ProgramProperty.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: act2Data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txAct2)
expect(txAct2.transaction_id).toBeDefined()
// Состояния после
const programWalletAfter = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
const ledgerShareAfter = await getLedgerAccountById(blockchain, coopname, circulationAccountId)
return {
propertyHash: property_hash,
txCreateId: txCreate.transaction_id,
txAct1Id: txAct1.transaction_id,
txAct2Id: txAct2.transaction_id,
programWalletBefore,
programWalletAfter,
ledgerShareBefore,
ledgerShareAfter,
}
}
@@ -1,69 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { processLastDecision } from '../soviet/processLastDecision'
import { processApprove } from './processApprove'
import { getSegment } from './getSegment'
export async function processCreateProjectProperty(
blockchain: Blockchain,
data: CapitalContract.Actions.CreateProjectProperty.ICreateProjectProperty,
) {
const { coopname, username, project_hash, property_hash } = data
// Состояния до: сегмент проекта и данные проекта
const segmentBefore = await getSegment(blockchain, coopname, project_hash, username)
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
// 1) Создаём проектное предложение имущественного взноса
const txCreate = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CreateProjectProperty.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txCreate)
expect(txCreate.transaction_id).toBeDefined()
// 2) Подтверждение одобрения председателем (через soviet)
await processApprove(blockchain as any, coopname, property_hash as unknown as string)
// Состояния после: сегмент проекта и данные проекта
const segmentAfter = await getSegment(blockchain, coopname, project_hash, username)
const projectAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
return {
propertyHash: property_hash,
txCreateId: txCreate.transaction_id,
segmentBefore,
segmentAfter,
projectBefore,
projectAfter,
}
}
@@ -1,179 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract, GatewayContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { processLastDecision } from '../soviet/processLastDecision'
import type Blockchain from '../../blockchain'
import { formatDateToISOString } from '../shared/formatDateToISOString'
import { processApprove } from './processApprove'
import { getSegment } from './getSegment'
export async function processDebt(
blockchain: Blockchain,
coopname: string,
username: string,
projectHash: string,
debtAmount: string,
fakeDocument: any,
repaidAtDate: Date,
) {
const debtHash = generateRandomSHA256()
console.log(`\n🔹 Начало процесса создания долга: ${debtHash}`)
// Дата возврата через месяц
const repaidAt = formatDateToISOString(repaidAtDate)
// Получаем сегмент до создания долга
const segmentBefore = await getSegment(blockchain, coopname, projectHash, username)
console.log('📊 Сегмент до долга:', segmentBefore)
// Получаем контрибьютора до создания долга
const contributorsBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'contributors',
1,
username,
username,
2,
'i64',
))
const contributorBefore = contributorsBefore[0]
console.log('📊 Контрибьютор до долга:', contributorBefore)
fakeDocument.signatures[0].signer = username
// 1. Создаем долг
const createDebtData: CapitalContract.Actions.CreateDebt.ICreateDebt = {
coopname,
username,
debt_hash: debtHash,
project_hash: projectHash,
amount: debtAmount,
repaid_at: repaidAt,
statement: fakeDocument,
}
console.log(`\n🚀 Отправка транзакции CreateDebt для ${username} на сумму ${debtAmount}`)
const createDebtResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CreateDebt.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: createDebtData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(createDebtResult)
expect(createDebtResult.transaction_id).toBeDefined()
console.log('✅ Долг создан:', debtHash)
// Проверяем что долг создался
const debts = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'debts',
1,
debtHash,
debtHash,
3,
'sha256',
))
const createdDebt = debts[0]
console.log('🔍 Долг в блокчейне:', createdDebt)
expect(createdDebt).toBeDefined()
expect(createdDebt.status).toBe('created')
// 2. Одобряем долг через processApprove (soviet контракт)
console.log(`\n✅ Подтверждение долга ${debtHash} через soviet`)
await processApprove(blockchain, coopname, debtHash)
console.log('✅ Долг одобрен советом')
// 3. Процессим решение совета
await processLastDecision(blockchain, coopname)
console.log('✅ Решение совета принято')
// 5. Подтверждаем завершение вывода (gateway сам вызовет callback на capital)
const confirmOutcomeData: GatewayContract.Actions.CompleteOutcome.ICompleteOutcome = {
coopname,
outcome_hash: debtHash,
}
console.log(`\n✅ Подтверждение завершения вывода`)
const confirmResult = await blockchain.api.transact(
{
actions: [
{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteOutcome.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: confirmOutcomeData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(confirmResult)
expect(confirmResult.transaction_id).toBeDefined()
console.log('✅ Оплата долга подтверждена')
// Получаем финальные состояния
const segmentAfter = await getSegment(blockchain, coopname, projectHash, username)
const contributorsAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'contributors',
1,
username,
username,
2,
'i64',
))
const contributorAfter = contributorsAfter[0]
// Получаем долг после подтверждения оплаты
const debtsAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'debts',
1,
debtHash,
debtHash,
3,
'sha256',
))
const debtAfter = debtsAfter[0]
console.log('\n📊 Результаты после обработки долга:')
console.log('▶ Сегмент до:', segmentBefore)
console.log('▶ Сегмент после:', segmentAfter)
console.log('▶ Контрибьютор до:', contributorBefore)
console.log('▶ Контрибьютор после:', contributorAfter)
console.log('▶ Долг после оплаты:', debtAfter)
console.log(`\n✅ Процесс долга на ${debtAmount} завершен успешно!`)
return {
debtHash,
debt: createdDebt,
debtAfter,
segmentBefore,
segmentAfter,
contributorBefore,
contributorAfter,
transactionId: confirmResult.transaction_id,
}
}
@@ -1,62 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
export async function processDeleteProject(
blockchain: Blockchain,
coopname: string,
project_hash: string,
) {
// Состояние проекта до удаления
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
const data: CapitalContract.Actions.DeleteProject.IDeleteProject = {
coopname,
project_hash,
}
// Выполняем удаление проекта
const txDelete = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.DeleteProject.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txDelete)
expect(txDelete.transaction_id).toBeDefined()
// Проверяем что проект удален
const projectsAfter = await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
)
return {
transactionId: txDelete.transaction_id,
projectBefore,
projectAfter: projectsAfter[0] || null, // null если удален
}
}
@@ -1,61 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { getCapitalWallet } from './getCapitalWallet'
export async function processFundProgram(
blockchain: any,
coopname: string,
amount: string,
memo: string = 'Тестовое финансирование программы',
) {
console.log(`\n🔹 Начало финансирования программы капитализации: сумма=${amount}, memo=${memo}`)
// Получение данных ДО финансирования
const programWalletBefore = await getCapitalWallet(blockchain, coopname, coopname)
console.log('📊 Состояние программы ДО финансирования:')
console.log('▶ Кошелек программы:', programWalletBefore)
// Финансирование программы капитализации
const fundData: CapitalContract.Actions.FundProgram.IFundProgram = {
coopname,
amount,
memo,
}
const fundResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.FundProgram.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: fundData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(fundResult)
expect(fundResult.transaction_id).toBeDefined()
// Получение данных ПОСЛЕ финансирования
const programWalletAfter = await getCapitalWallet(blockchain, coopname, coopname)
console.log('📊 Состояние программы ПОСЛЕ финансирования:')
console.log('▶ Кошелек программы:', programWalletAfter)
console.log(`\n✅ Финансирование программы капитализации завершено успешно!`)
return {
transactionId: fundResult.transaction_id,
transaction: fundResult,
programWalletBefore,
programWalletAfter,
}
}
@@ -1,63 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { getProjectWallet } from './getProjectWallet'
export async function processFundProject(
blockchain: any,
coopname: string,
projectHash: string,
amount: string,
memo: string = 'Тестовое финансирование проекта',
) {
console.log(`\n🔹 Начало финансирования проекта: проект=${projectHash}, сумма=${amount}, memo=${memo}`)
// Получение данных ДО финансирования
const projectWalletBefore = await getProjectWallet(blockchain, coopname, projectHash, coopname)
console.log('📊 Состояние проекта ДО финансирования:')
console.log('▶ Кошелек проекта:', projectWalletBefore)
// Финансирование проекта
const fundData: CapitalContract.Actions.FundProject.IFundProject = {
coopname,
project_hash: projectHash,
amount,
memo,
}
const fundResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.FundProject.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: fundData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(fundResult)
expect(fundResult.transaction_id).toBeDefined()
// Получение данных ПОСЛЕ финансирования
const projectWalletAfter = await getProjectWallet(blockchain, coopname, projectHash, coopname)
console.log('📊 Состояние проекта ПОСЛЕ финансирования:')
console.log('▶ Кошелек проекта:', projectWalletAfter)
console.log(`\n✅ Финансирование проекта завершено успешно!`)
return {
transactionId: fundResult.transaction_id,
transaction: fundResult,
projectWalletBefore,
projectWalletAfter,
}
}
@@ -1,30 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
export async function processMasterApprove(
blockchain: any,
coopname: string,
commitHash: string,
master: string,
) {
const data: CapitalContract.Actions.CommitApprove.ICommitApprove = {
coopname,
master,
commit_hash: commitHash,
}
const result = await blockchain.transactWithLogs([
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CommitApprove.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
])
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
return result
}
@@ -1,180 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract, SovietContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { setDocumentSignatures } from '../../utils/setDocumentSignatures'
import { processLastDecision } from '../soviet/processLastDecision'
import { fakeDocument } from '../shared/fakeDocument'
import type Blockchain from '../../blockchain'
import { getSegment } from './getSegment'
import { processApprove } from './processApprove'
/**
* Функция для внесения результата в кооператив
* @param blockchain экземпляр блокчейна
* @param coopname название кооператива
* @param username имя пользователя
* @param projectHash хеш проекта
* @param contributionAmount сумма взноса
* @param debtAmount сумма долга к погашению
* @param debtHashes вектор хэшей долгов для погашения
*/
export async function processPushResult(
blockchain: Blockchain,
coopname: string,
username: string,
projectHash: string,
contributionAmount: string,
debtAmount: string,
debtHashes: string[] = [],
) {
const resultHash = generateRandomSHA256()
console.log(`\n🔹 Начало процесса внесения результата: ${resultHash}`)
// Получаем сегмент до внесения результата
const segmentBefore = await getSegment(blockchain, coopname, projectHash, username)
console.log('📊 Сегмент до внесения результата:', segmentBefore)
// Устанавливаем подписи документа
const documentWithSignatures = setDocumentSignatures([username])
// 1. Вносим результат
const pushResultData: CapitalContract.Actions.PushResult.IPushResult = {
coopname,
username,
project_hash: projectHash,
result_hash: resultHash,
contribution_amount: contributionAmount,
debt_amount: debtAmount,
statement: documentWithSignatures,
debt_hashes: debtHashes,
}
console.log(`\n🚀 Отправка транзакции PushResult для ${username} на сумму ${contributionAmount}`)
const pushResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.PushResult.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: pushResultData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(pushResult)
expect(pushResult.transaction_id).toBeDefined()
console.log('✅ Результат внесен:', resultHash)
// Проверяем что результат создался
const results = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'results',
1,
resultHash,
resultHash,
3,
'sha256',
))
const createdResult = results[0]
console.log('🔍 Результат в блокчейне:', createdResult)
expect(createdResult).toBeDefined()
expect(createdResult.status).toBe('created')
// 2. Одобряем результат через processApprove (soviet контракт)
console.log(`\n✅ Одобрение результата ${resultHash} через soviet`)
await processApprove(blockchain, coopname, resultHash)
console.log('✅ Результат одобрен советом')
// 3. Процессим решение совета
await processLastDecision(blockchain, coopname)
console.log('✅ Решение совета принято')
console.log('🔍 Подписываем акт №1 вкладчиком', documentWithSignatures)
// 4. Подписываем акт №1 вкладчиком
const signAct1Data: CapitalContract.Actions.SignAct1.ISignAct1 = {
coopname,
username,
result_hash: resultHash,
act: documentWithSignatures,
}
console.log(`\n✅ Подписание акта №1 вкладчиком ${username}`)
const signAct1Result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.SignAct1.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: signAct1Data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(signAct1Result)
expect(signAct1Result.transaction_id).toBeDefined()
console.log('✅ Акт №1 подписан вкладчиком')
// Создаем документ с подписями вкладчика и председателя
const documentWithChairmanSignature = setDocumentSignatures([username, 'ant'])
// 5. Подписываем акт №2 председателем
const signAct2Data: CapitalContract.Actions.SignAct2.ISignAct2 = {
coopname,
result_hash: resultHash,
act: documentWithChairmanSignature,
chairman: 'ant',
}
console.log(`\n✅ Подписание акта №2 председателем`, documentWithChairmanSignature)
const signAct2Result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.SignAct2.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: signAct2Data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(signAct2Result)
expect(signAct2Result.transaction_id).toBeDefined()
console.log('✅ Акт №2 подписан председателем')
// Получаем финальные состояния
const segmentAfter = await getSegment(blockchain, coopname, projectHash, username)
console.log('\n📊 Результаты после внесения результата:')
console.log('▶ Сегмент до:', segmentBefore)
console.log('▶ Сегмент после:', segmentAfter)
console.log(`\n✅ Процесс внесения результата на ${contributionAmount} завершен успешно!`)
return {
resultHash,
result: createdResult,
segmentBefore,
segmentAfter,
transactionId: signAct2Result.transaction_id,
}
}
@@ -1,59 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { getCapitalWallet } from './getCapitalWallet'
export async function processRefreshProg(
blockchain: any,
coopname: string,
username: string,
) {
console.log(`\n🔹 Начало обновления CRPS пайщика в программе капитализации: пользователь=${username}`)
// Получение данных ДО обновления
const programWalletBefore = await getCapitalWallet(blockchain, coopname, username)
console.log('📊 Состояние пайщика ДО обновления CRPS:')
console.log('▶ Кошелек пайщика:', programWalletBefore)
// Обновление CRPS пайщика в программе капитализации
const refreshData: CapitalContract.Actions.RefreshProgram.IRefreshProgram = {
coopname,
username,
}
const refreshResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.RefreshProgram.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: refreshData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(refreshResult)
expect(refreshResult.transaction_id).toBeDefined()
// Получение данных ПОСЛЕ обновления
const programWalletAfter = await getCapitalWallet(blockchain, coopname, username)
console.log('📊 Состояние пайщика ПОСЛЕ обновления CRPS:')
console.log('▶ Кошелек пайщика:', programWalletAfter)
console.log(`\n✅ Обновление CRPS пайщика в программе капитализации завершено успешно!`)
return {
transactionId: refreshResult.transaction_id,
transaction: refreshResult,
programWalletBefore,
programWalletAfter,
}
}
@@ -1,61 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { getProjectWallet } from './getProjectWallet'
export async function processRefreshProj(
blockchain: any,
coopname: string,
projectHash: string,
username: string,
) {
console.log(`\n🔹 Начало обновления CRPS пайщика в проекте: проект=${projectHash}, пользователь=${username}`)
// Получение данных ДО обновления
const projectWalletBefore = await getProjectWallet(blockchain, coopname, projectHash, username)
console.log('📊 Состояние кошелька проекта ДО обновления CRPS:')
console.log('▶ Кошелек проекта:', projectWalletBefore)
// Обновление CRPS пайщика в проекте
const refreshData: CapitalContract.Actions.RefreshProject.IRefreshProject = {
coopname,
project_hash: projectHash,
username,
}
const refreshResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.RefreshProject.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: refreshData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(refreshResult)
expect(refreshResult.transaction_id).toBeDefined()
// Получение данных ПОСЛЕ обновления
const projectWalletAfter = await getProjectWallet(blockchain, coopname, projectHash, username)
console.log('📊 Состояние кошелька проекта ПОСЛЕ обновления CRPS:')
console.log('▶ Кошелек проекта:', projectWalletAfter)
console.log(`\n✅ Обновление CRPS пайщика в проекте завершено успешно!`)
return {
transactionId: refreshResult.transaction_id,
transaction: refreshResult,
projectWalletBefore,
projectWalletAfter,
}
}
@@ -1,59 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
export async function processStartVoting(
blockchain: Blockchain,
data: CapitalContract.Actions.StartVoting.IStartVoting,
) {
const { coopname, project_hash } = data
// Состояние до: данные проекта
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
// 1) Запускаем голосование по проекту
const txStart = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.StartVoting.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txStart)
expect(txStart.transaction_id).toBeDefined()
// Состояние после: данные проекта
const projectAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
return {
projectHash: project_hash,
txStartId: txStart.transaction_id,
projectBefore,
projectAfter,
}
}
@@ -1,61 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import type Blockchain from '../../blockchain'
export async function refreshSegment(
blockchain: Blockchain,
coopname: string,
projectHash: string,
username: string,
) {
console.log(`\n🔄 Обновляем сегмент CRPS для ${username} в проекте ${projectHash}`)
// Получение данных сегмента перед обновлением
const prevSegment = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
CapitalContract.Tables.Segments.tableName,
1000,
)).filter((s: any) => s.project_hash === projectHash && s.username === username)[0]
// console.log('📊 Сегмент до обновления:', prevSegment)
// Обновление сегмента через rfrshsegment
const refreshSegmentData: CapitalContract.Actions.RefreshSegment.IRefreshSegment = {
coopname,
project_hash: projectHash,
username,
}
const refreshResult = await blockchain.transactWithLogs(
[
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.RefreshSegment.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: refreshSegmentData,
},
],
)
getTotalRamUsage(refreshResult)
expect(refreshResult.transaction_id).toBeDefined()
// Получение обновленного сегмента
const updatedSegment = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
CapitalContract.Tables.Segments.tableName,
1000,
)).filter((s: any) => s.project_hash === projectHash && s.username === username)[0]
// console.log('📊 Сегмент после обновления:', updatedSegment)
console.log(`✅ Сегмент для ${username} обновлен успешно!`)
return {
transactionId: refreshResult.transaction_id,
prevSegment,
updatedSegment,
}
}
@@ -1,64 +1,3 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { fakeDocument } from '../shared/fakeDocument'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { processApprove } from './processApprove'
export function registerContributor() {
export async function registerContributor(
blockchain: any,
coopname: string,
username: string,
contributorHash: string,
ratePerHour: string,
) {
const contract = fakeDocument
contract.signatures[0].signer = username
const data: CapitalContract.Actions.RegisterContributor.IRegisterContributor = {
coopname,
username,
contributor_hash: contributorHash,
rate_per_hour: ratePerHour,
is_external_contract: false,
contract,
}
const result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.RegisterContributor.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
// Аппрув председателя (через совет) по хэшу договора УХД
await processApprove(blockchain, coopname, contributorHash)
// Проверяем, что контрибьютор активирован и договор сохранён
const contributor = (
await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'contributors',
1,
username,
username,
2,
'i64',
)
)[0]
expect(contributor).toBeDefined()
expect(contributor.status).toBe('active')
expect(contributor.contract).toBeDefined()
}
@@ -1,62 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { fakeDocument } from '../shared/fakeDocument'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { processApprove } from './processApprove'
export async function signAppendix(
blockchain: any,
coopname: string,
username: string,
projectHash: string,
appendixHash: string,
) {
// Подписание приложения пайщиком (через кооператив, как и в контракте)
const data: CapitalContract.Actions.GetClearance.IGetClearance = {
coopname,
username,
project_hash: projectHash,
appendix_hash: appendixHash,
document: fakeDocument,
}
const result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.GetClearance.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
// Аппрув председателя (через совет) по хэшу приложения
await processApprove(blockchain, coopname, appendixHash)
// Проверяем, что приложение применилось к контрибьютору, а запись удалена
const contributor = (
await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'contributors',
1,
username,
username,
2,
'i64',
)
)[0]
expect(contributor).toBeDefined()
expect(contributor.appendixes).toContain(projectHash)
}
@@ -1,166 +0,0 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
/**
* Интерфейс для распределения голосов
*/
export interface VoteInput {
recipient: string
amount: string
}
/**
* Создает равномерное распределение голосующей суммы между участниками (кроме самого голосующего)
* @param voters список всех участников голосования
* @param voter участник который голосует (исключается из распределения)
* @param votingAmount общая голосующая сумма для распределения
* @returns массив распределения голосов
*/
export function createVoteDistribution(
voters: string[],
voter: string,
votingAmount: string,
): VoteInput[] {
const otherVoters = voters.filter(v => v !== voter)
const votingAmountFloat = parseFloat(votingAmount.split(' ')[0])
const currency = votingAmount.split(' ')[1]
// Равномерно распределяем сумму между остальными участниками
const amountPerVoter = votingAmountFloat / otherVoters.length
return otherVoters.map(recipient => ({
recipient,
amount: `${amountPerVoter.toFixed(4)} ${currency}`,
}))
}
/**
* Функция для голосования по методу Водянова
* @param blockchain экземпляр блокчейна
* @param coopname имя кооператива
* @param voter участник который голосует
* @param projectHash хэш проекта
* @param voteDistribution массив голосов для распределения
*/
export async function submitVote(
blockchain: Blockchain,
coopname: string,
voter: string,
projectHash: string,
voteDistribution: VoteInput[],
) {
console.log(`\n🗳️ Начало голосования участника ${voter}`)
// Состояние до: данные проекта
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
projectHash,
projectHash,
3,
'sha256',
))[0]
// Получаем все голоса по проекту до голосования
const votesBefore = await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'votes',
100,
projectHash,
projectHash,
2,
'sha256',
)
// Получаем голоса конкретного участника до голосования
const voterVotesBefore = votesBefore.filter((vote: any) =>
vote.project_hash === projectHash && vote.voter === voter,
)
console.log('📊 Состояние до голосования:')
console.log('▶ Проект votes_received:', projectBefore.voting.votes_received)
console.log('▶ Всего голосов в таблице:', votesBefore.filter((v: any) => v.project_hash === projectHash).length)
console.log('▶ Голосов от участника:', voterVotesBefore.length)
// Формируем данные для транзакции
const votes = voteDistribution.map(v => ({
recipient: v.recipient,
amount: v.amount,
}))
const data: any = {
coopname,
voter,
project_hash: projectHash,
votes,
}
console.log('📝 Данные голосования:', JSON.stringify(data, null, 2))
// Отправляем транзакцию голосования
const txResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.SubmitVote.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txResult)
expect(txResult.transaction_id).toBeDefined()
// Состояние после: данные проекта
const projectAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
projectHash,
projectHash,
3,
'sha256',
))[0]
// Получаем все голоса по проекту после голосования
const votesAfter = await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'votes',
100,
projectHash,
projectHash,
2,
'sha256',
)
// Получаем голоса конкретного участника после голосования
const voterVotesAfter = votesAfter.filter((vote: any) =>
vote.project_hash === projectHash && vote.voter === voter,
)
console.log('📊 Состояние после голосования:')
console.log('▶ Проект votes_received:', projectAfter.voting.votes_received)
console.log('▶ Всего голосов в таблице:', votesAfter.filter((v: any) => v.project_hash === projectHash).length)
console.log('▶ Голосов от участника:', voterVotesAfter.length)
return {
txId: txResult.transaction_id,
projectBefore,
projectAfter,
votesBefore: votesBefore.filter((v: any) => v.project_hash === projectHash),
votesAfter: votesAfter.filter((v: any) => v.project_hash === projectHash),
voterVotesBefore,
voterVotesAfter,
voteInput: voteDistribution,
}
}
@@ -1,57 +0,0 @@
// Комбинирование индекса (project_hash + username) как в C++ combine_checksum_ids
// Возвращает десятичную строку uint128 для использования с key_type 'i128'
function charToSymbol(c: string): number {
if (c === '.') return 0
if (c >= '1' && c <= '5') return c.charCodeAt(0) - '1'.charCodeAt(0) + 1
if (c >= 'a' && c <= 'z') return c.charCodeAt(0) - 'a'.charCodeAt(0) + 6
return 0
}
function nameToUint64(name: string): bigint {
let value = 0n
let i = 0
for (; i < 12 && i < name.length; i++) {
value <<= 5n
value |= BigInt(charToSymbol(name[i]))
}
value <<= 4n + 5n * BigInt(12 - i)
if (i < name.length) {
value |= BigInt(charToSymbol(name[i])) & 0x0fn
}
return value
}
function hexToBytes(hex: string): Uint8Array {
const clean = hex.startsWith('0x') ? hex.slice(2) : hex
if (clean.length !== 64) throw new Error('Invalid sha256 hex')
const out = new Uint8Array(32)
for (let i = 0; i < 32; i++) {
out[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16)
}
return out
}
function readBigUInt64LE(bytes: Uint8Array, offset = 0): bigint {
let result = 0n
for (let i = 0; i < 8; i++) {
result |= BigInt(bytes[offset + i]) << (8n * BigInt(i))
}
return result
}
export function makeCombinedChecksum256NameIndexKey(projectHashHex: string, username: string): string {
const bytes = hexToBytes(projectHashHex)
const truncatedHashLE = readBigUInt64LE(bytes, 0)
const nameValue = nameToUint64(username)
const key128 = (truncatedHashLE << 64n) | nameValue
return key128.toString()
}
export function makeCombinedChecksum256Uint64IndexKey(projectHashHex: string, id: number): string {
const bytes = hexToBytes(projectHashHex)
const truncatedHashLE = readBigUInt64LE(bytes, 0)
const idValue = BigInt(id)
const key128 = (truncatedHashLE << 64n) | idValue
return key128.toString()
}
@@ -1,84 +0,0 @@
interface ActionTrace {
act?: {
account: string
name: string
authorization?: { actor: string }[]
}
console?: string
inline_traces?: ActionTrace[]
}
interface TransactionResult {
processed?: {
action_traces?: ActionTrace[]
}
}
/**
* Извлекает и печатает все консоль логи из результата транзакции
* @param transaction - результат транзакции blockchain.api.transact
*/
export function consoleIt(transaction: TransactionResult): void {
if (!transaction?.processed?.action_traces) {
console.log('❌ Нет данных о транзакции или action traces')
return
}
console.log('\n📋 КОНСОЛЬ ЛОГИ ТРАНЗАКЦИИ:')
console.log('='.repeat(50))
let hasConsoleLogs = false
function processActionTrace(trace: ActionTrace, depth = 0): void {
const indent = ' '.repeat(depth)
// Получаем информацию о действии
const account = trace.act?.account || 'unknown'
const action = trace.act?.name || 'unknown'
const actor = trace.act?.authorization?.[0]?.actor || 'unknown'
// Печатаем заголовок действия
console.log(`${indent}🔧 ${account}:${action} (actor: ${actor})`)
// Если есть консоль логи, печатаем их
if (trace.console) {
hasConsoleLogs = true
const logs = trace.console.split('\n').filter(log => log.trim())
if (logs.length > 0) {
console.log(`${indent}📝 Console output:`)
logs.forEach((log) => {
console.log(`${indent} ${log}`)
})
}
}
else {
console.log(`${indent} (нет консоль логов)`)
}
// Обрабатываем inline traces рекурсивно
if (trace.inline_traces?.length) {
console.log(`${indent} ↳ Inline actions:`)
trace.inline_traces.forEach((inlineTrace) => {
processActionTrace(inlineTrace, depth + 1)
})
}
if (depth === 0) {
console.log('') // Пустая строка между действиями верхнего уровня
}
}
// Обрабатываем все action traces
transaction.processed.action_traces.forEach((trace, index) => {
console.log(`\n📍 Action ${index + 1}:`)
processActionTrace(trace, 0)
})
if (!hasConsoleLogs) {
console.log('ℹ️ В транзакции не найдено консоль логов')
}
console.log('='.repeat(50))
console.log('🏁 Конец логов транзакции\n')
}
@@ -1,16 +0,0 @@
export const fakeDocument = {
version: '1.0.0',
hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
doc_hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
meta_hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
meta: '{}',
signatures: [{
id: 1,
signed_hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
signer: 'cooperative1',
public_key: 'EOS5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzxrLg3J',
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
signed_at: '2025-05-14T12:22:26',
meta: '{}',
}],
}
@@ -1,10 +0,0 @@
export const fakeVote = {
version: '1.0.0',
coopname: 'voskhod',
username: 'ant',
decision_id: '1',
signed_at: '2025-05-14T12:22:26.000',
signed_hash: '157192B276DA23CC84AB078FC8755C051C5F0430BF4802E55718221E6B76C777',
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
public_key: 'PUB_K1_5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzzEtUA4',
}
@@ -1,8 +0,0 @@
/**
* Форматирует дату в строку формата 'YYYY-MM-DDTHH:mm:ss'
* @param date - Дата для форматирования
* @returns Строка в формате 'YYYY-MM-DDTHH:mm:ss'
*/
export function formatDateToISOString(date: Date): string {
return date.toISOString().slice(0, 19);
}
@@ -1,5 +0,0 @@
export * from './combinedKeys'
export * from './consoleIt'
export * from './fakeDocument'
export * from './fakeVote'
export * from './formatDateToISOString'
@@ -1,12 +1,14 @@
import { SovietContract } from 'cooptypes'
import { expect } from 'vitest'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { fakeDocument } from '../shared/fakeDocument'
import type Blockchain from '../../blockchain'
import { fakeVote } from '../shared/fakeVote'
import { fakeDocument } from './fakeDocument'
export async function processDecision(blockchain: Blockchain, decisionId: number) {
const voteData: SovietContract.Actions.Decisions.VoteFor.IVoteForDecision = fakeVote
voteData.decision_id = decisionId
export async function processDecision(blockchain: any, decisionId: number) {
const voteData: SovietContract.Actions.Decisions.VoteFor.IVoteForDecision = {
coopname: 'voskhod',
member: 'ant',
decision_id: decisionId,
}
const authData: SovietContract.Actions.Decisions.Authorize.IAuthorize = {
coopname: 'voskhod',
@@ -51,4 +53,5 @@ export async function processDecision(blockchain: Blockchain, decisionId: number
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
}
@@ -1,17 +0,0 @@
import { SovietContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { processDecision } from './processDecision'
export async function processLastDecision(blockchain: Blockchain, coopname: string) {
// Получаем последнее решение для голосования
const decisions = await blockchain.getTableRows(
SovietContract.contractName.production,
coopname,
SovietContract.Tables.Decisions.tableName,
1,
)
const lastDecision = decisions[0]
// 3. Голосуем за решение совета
await processDecision(blockchain, lastDecision.id)
}
@@ -3,7 +3,7 @@ import { GatewayContract, WalletContract } from 'cooptypes'
import { expect } from 'vitest'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { compareTokenAmounts, getCirculationAccount, getCoopProgramWallet, getDeposit, getUserProgramWallet } from './walletUtils'
import { compareTokenAmounts, getCoopProgramWallet, getCoopWallet, getDeposit, getUserProgramWallet } from './walletUtils'
export async function depositToWallet(blockchain: any, coopname: string, username: string, amount: number) {
const depositId = randomInt(100000)
@@ -48,7 +48,8 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
const prevUserWallet = await getUserProgramWallet(blockchain, coopname, username, 1)
const prevUserWalletAvailable = prevUserWallet?.available || '0.0000 RUB'
const prevCoopWalletAvailable = (await getCirculationAccount(blockchain, coopname)).available
const prevCoopWallet = await getCoopWallet(blockchain, coopname)
const prevCoopWalletAvailable = prevCoopWallet?.circulating_account?.available || '0.0000 RUB'
const data2: GatewayContract.Actions.CompleteIncome.ICompleteIncome = {
coopname,
@@ -84,7 +85,7 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
// Проверяем изменение балансов
compareTokenAmounts(prevUserWalletAvailable, userWallet.available, amount)
compareTokenAmounts(prevCoopWalletAvailable, (await getCirculationAccount(blockchain, coopname)).available, amount)
compareTokenAmounts(prevCoopWalletAvailable, (await getCoopWallet(blockchain, coopname)).circulating_account.available, amount)
return { depositId, program, userWallet }
}
@@ -1,7 +1,6 @@
import { FundContract, LedgerContract, SovietContract, WalletContract } from 'cooptypes'
import { FundContract, SovietContract, WalletContract } from 'cooptypes'
import { expect } from 'vitest'
import type Blockchain from '../../blockchain'
import { circulationAccountId } from '../capital/consts'
export async function getUserProgramWallet(blockchain: any, coopname: string, username: string, program_id: number) {
const wallets = await blockchain.getTableRows(
@@ -17,11 +16,6 @@ export async function getUserProgramWallet(blockchain: any, coopname: string, us
return wallets.find((el: any) => el.program_id === program_id)
}
export async function getUserProgramWalletAmount(blockchain: any, coopname: string, username: string, program_id: number) {
const wallet = await getUserProgramWallet(blockchain, coopname, username, program_id)
return wallet ? `${(parseFloat(wallet.available) + parseFloat(wallet.blocked)).toFixed(4)} RUB` : '0.0000 RUB'
}
export async function getCoopProgramWallet(blockchain: any, coopname: string, program_id: number) {
const program = await blockchain.getTableRows(
SovietContract.contractName.production,
@@ -35,25 +29,6 @@ export async function getCoopProgramWallet(blockchain: any, coopname: string, pr
return program[0]
}
export async function getLedgerAccounts(blockchain: Blockchain, coopname: string) {
return (await blockchain.getTableRows(
LedgerContract.contractName.production,
coopname,
LedgerContract.Tables.Laccount.tableName,
100,
))
}
export async function getLedgerAccountById(blockchain: Blockchain, coopname: string, accountId: number) {
const accounts = await getLedgerAccounts(blockchain, coopname)
return accounts.find((account: any) => account.id === accountId)
}
export async function getCirculationAccount(blockchain: Blockchain, coopname: string) {
const accounts = await getLedgerAccounts(blockchain, coopname)
return accounts.find((account: any) => account.id === circulationAccountId) || { available: '0.0000 RUB' }
}
export async function getCoopWallet(blockchain: any, coopname: string) {
return (await blockchain.getTableRows(
FundContract.contractName.production,
@@ -81,5 +56,5 @@ export function compareTokenAmounts(prevAmount: string, currentAmount: string, e
const current = parseFloat(currentAmount.split(' ')[0])
const expected = prev + expectedIncrease
expect(current).toBeCloseTo(expected, 4)
expect(current).toBe(expected)
}
@@ -1,29 +0,0 @@
import { fakeDocument } from '../tests/shared/fakeDocument'
/**
* Устанавливает подписи документа на основе массива имен аккаунтов
* @param usernames массив имен аккаунтов для подписи документа
* @returns копия fakeDocument с установленными подписями
*/
export function setDocumentSignatures(usernames: string[]) {
const document = JSON.parse(JSON.stringify(fakeDocument))
// Очищаем существующие подписи
document.signatures = []
// Создаем подписи для каждого пользователя
usernames.forEach((username, index) => {
const signature = {
id: index + 1,
signed_hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
signer: username,
public_key: 'EOS5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzxrLg3J',
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
signed_at: '2025-05-14T12:22:26',
meta: '{}',
}
document.signatures.push(signature)
})
return document
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@coopenomics/cleos",
"version": "2025.10.16",
"version": "2025.5.5-2",
"private": true,
"description": "",
"scripts": {
-2
View File
@@ -1,2 +0,0 @@
./cleos.sh system newaccount eosio test "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" --stake-net "0.0001 AXON" --stake-cpu "0.0001 AXON" --buy-ram "0.0001 AXON"
-1
View File
@@ -1 +0,0 @@
./cleos.sh push action eosio powerup '[eosio, test, 1, "150.0000 AXON", true]' -p eosio
BIN
View File
Binary file not shown.
@@ -1,10 +0,0 @@
---
alwaysApply: true
---
Мы следуем принципам чистой архитектуры. На примере контракта Capital:
- app/ - уровень приложения. Содержит действия, которые вызываются контрактом, которые оркестрируют бизнес-логику сущностей и ядра. Подпапки содержат домены бизнес-логики, где каждый файл - одно действие.
- domain/ - доменный уровень. Содержит сущности и корневую бизнес-логику, которая в своих функциях использует несколько сущностей. На уровне сущностей запрещено вызывать другие сущности.
- entity/ - слой сущностей домена. Содержит выделенные сущности, структуры, индексы их таблиц, и inline реализации простейших бизнес-функций, которые по своим названиям помогают явно определить что происходит с сущностью.
- core/ - слой корневой бизнес-логики, которая объединяет несколько сущностей. Если бизнес-функция сущности требует для своего исполнения нескольких операций с сущностями или она явно относится к доменному уровню приложения за счет своего применения, то такая функция должна быть вынесена в ядро.
Мы допускаем последовательное-повторное обновление сущностей по ходу исполнения действия контракта, если это необходимо в пользу ясности. Длинна типа name не должна превышать 12 символов и не должна содержать специальных знаков.
@@ -1,43 +0,0 @@
---
description:
globs:
alwaysApply: false
---
Мне нужно, чтобы ты прошелся по контракту и разнес все действия по группам так, как это сделано в примере. Там:
- указано краткое описание того что происходит и к чему приводит
- указаны параметры и их описание
- указана группа public_actions и public_soviet_actions - для контракта совета, но у тебя может быть другой контракт
- указан якорь для ссылок внешних в самом верху и с отступом
- указана требуемая авторизация
- если у действия есть проводки по кошельков или фондам - отметить их с помощью тега @post:
- Проводки по кошельку Wallet::add_available_funds, add_available_funds, add_blocked_funds, sub_blocked_funds, block_funds, unblock_funds, pay_membership_fee, unpay_membership_fee
- Проводки по фондам Ledger::add, sub, transfer, block, unblock, writeoff, writeoffcnsl, add_membership_fee, sub_membership_fee, block_membership_fee, unblock_membership_fee
- если у действия есть входящий документ - отметить его также с помощью тега @post
Пример:
/**
*
* @brief Завершение голосования для конкретного участника по методу Водянова.
* Рассчитывает полные итоговые суммы участника включая:
* - Результаты голосования по Водянову: (сумма голосов + общая сумма распределения) / количество голосующих
* - Фиксированные распределения: 61.8% авторских премий равно между авторами, 61.8% исполнительских премий прямо создателям
* Обновляет сегмент участника с итоговыми суммами раздельно по пулам
* @param coopname Наименование кооператива
* @param username Наименование пользователя-участника голосования
* @param project_hash Хеш проекта для которого завершается голосование
* @ingroup public_actions
* @ingroup public_capital_actions
* @note Авторизация требуется от аккаунта: @p coopname
*/
void capital::calcvotes(name coopname, name username, checksum256 project_hash) {
require_auth(coopname);
// Получаем проект и проверяем состояние
auto project = Capital::Projects::get_project_or_fail(coopname, project_hash);
eosio::check(project.status == Capital::Projects::Status::COMPLETED, "Проект не в статусе завершенного голосования");
... и т.д.
ВАЖНО: ВСЕ ДЕЙСТВИЯ НЕОБХОДИМО ДОКУМЕНТИРОВАТЬ В *.cpp файлах, не в .hpp!!!
ВАЖНО2: ПОСЛЕ ПРЕДЛОЖЕНИЯ @BRIEF ДОЛЖНА БЫТЬ ТОЧКА.
@@ -1,6 +0,0 @@
---
description:
globs:
alwaysApply: false
---
Тебе необходимо добавить все константы контракта в группу public_contractname_consts.
@@ -1,32 +0,0 @@
---
description:
globs:
alwaysApply: false
---
Добавь в главный заголовочный файл контракта определение групп как в примере, но вместо capital должен быть тот контракт, на который у тебя задание.
/**
\defgroup public_capital Контракт CAPITAL
* Смарт-контракт капитализации результатов интеллектуальной деятельности предназначен для управления учётом и распределением вознаграждений за результаты интеллектуальной деятельности.
*/
/**
\defgroup public_capital_processes Процессы
\ingroup public_capital
*/
/**
\defgroup public_capital_actions Действия
\ingroup public_capital
*/
/**
\defgroup public_capital_tables Таблицы
\ingroup public_capital
*/
/**
\defgroup public_capital_consts Константы
\ingroup public_capital
*/
@@ -1,82 +0,0 @@
---
description:
globs:
alwaysApply: false
---
На основе предоставленной тебе информации необходимо создать страницы или страницу с описанием процессов контракта, создав новую группу @defgroup public_<contract>_<process>_process добавив их в общую группу @ingroup public_<contract>_proccesses
Каждый процесс должен включать:
- Человеко-читаемое название по своему бизнес-смыслу
- Диаграмму последовательности действий mermaid
- Пошаговое описание происходящего в процессе с обоснованием зачем это нужно
- Каждый шаг - это одно действие в смарт-контракте, на которое необходимо ссылаться через @ref <наименование якоря> "заголовок ссылки".
- ВАЖНО: @ref ссылки делать ТОЛЬКО на процессы (например, @ref capital::regcontrib "подача заявки"), а НЕ на внутренние сущности или функции (НЕ Capital::Contributors::create_contributor).
- На каждом шаге, если таковое есть в самом действии или в вызываемых им функциях, необходимо дополнительно отмечать с помощью тегов @post человеко-читаемые эффекты БЕЗ ссылок на внутренние функции:
- Проводки по кошельку (например, "создается программный кошелек", "блокируются средства")
- Проводки по фондам (например, "списываются средства с фонда", "переводятся средства")
- Передаваемые на вход документы
- Изменения статусов (например, "статус меняется с PENDING на ACTIVE")
- При необходимости делай ссылки на процессы через @ref <contract>::<action> "описание процесса"
- При выделении жирным используй теги <b></b>
- Пункты списков пиши текстом, а не списком. Руководствуйся принципом: "лучше объясни, чем коротко напиши".
## ПРАВИЛА ОФОРМЛЕНИЯ ДИАГРАММ
### Диаграммы последовательности:
- Использовать ТОЛЬКО sequenceDiagram вместо graph или flowchart
- Участники (participant) называть понятными русскими именами:
- "Участник" вместо "User" или "Участник"
- "Capital Contract" вместо технических названий
- "Soviet Contract", "Gateway Contract" для системных контрактов
- В сообщениях использовать ЧЕЛОВЕКО-ЧИТАЕМЫЕ описания вместо технических имен методов:
- "Подача заявления о расходе" вместо "createexpnse()"
- "Запрос предварительного одобрения" вместо "approveexpns()"
- "Создание исходящего платежа" вместо "gateway::createoutpay"
- Использовать Note over для пояснений внутренней логики
- Использовать alt/else для альтернативных путей
### Замена технических терминов:
- Все технические термины в тексте заменять на смысловые описания с @ref ссылками:
- used_expense_pool → "сумма использованных средств"
- accumulated_expense_pool → "накопленные средства пула"
- Gateway::createoutpay → @ref Gateway::createoutpay "создаёт исходящий платеж"
- Soviet::create_agenda → @ref Soviet::create_agenda "направляет на рассмотрение советом"
Пример оформления диаграммы
\htmlonly
<div class="mermaid">
sequenceDiagram
participant V as Участник
participant C as Capital Contract
participant S as Soviet Contract
V->>C: 1. Подача заявления
Note over C: Проверка условий
C->>S: 2. Запрос одобрения
alt Одобрение
S->>C: 3. Авторизация
else Отклонение
S->>C: 3. Отклонение
end
</div>
\endhtmlonly
*/
Пример оформления процесса
/**
@defgroup public_capital_registration_process Процесс регистрации участников
@ingroup public_capital_processes
@pre предварительные условия
Процесс приёма договора УХД от пайщика, фиксации его статуса как участника
и обеспечения технической возможности участвовать в проектах и программе капитализации.
``` cpp
some formula
```
@post эффекты
*/
@@ -1,64 +0,0 @@
---
description:
globs:
alwaysApply: false
---
Мне нужно, чтобы ты прошелся по всем таблицам контракта которые используются в нем. Они могут находиться также где-то в @lib/. Мне нужно, чтобы ты добавил все найденные таблицы в указанную по аналогии с тем как тебе предоставлено в примере.
А именно:
- все описания полей должны быть сделаны через ///<
- У таблицы должно быть описание
- У таблицы должна быть общая группа всех таблиц и частная группа конкретного контракта
- У таблицы должен быть якорь
- У таблицы должна быть область памяти в которой хранятся данные
- У таблицы должно быть указано имя.
Пример:
namespace Capital {
/**
* @brief Таблица проектов хранит информацию о проектах кооператива, которые станут результатами.
* @ingroup public_tables
* @ingroup public_capital_tables
* @par Область памяти (scope): coopname
* @par Имя таблицы (table): projects
*/
struct [[eosio::table, eosio::contract(CAPITAL)]] project {
uint64_t id; ///< ID проекта (внутренний ключ)
name coopname; ///< Имя кооператива
checksum256 project_hash; ///< Хэш проекта (внешний ключ)
checksum256 parent_hash; ///< Хэш родительского проекта (если есть)
eosio::name status; ///< Статус проекта
bool is_opened; ///< Открыт ли проект для инвестиций
// Мастер проекта
name master; ///< Мастер проекта
std::string title; ///< Название проекта
std::string description; ///< Описание проекта
std::string meta; ///< Метаданные проекта
counts_data counts; ///< Счетчики участников проекта
plan_pool plan; ///< Плановые показатели
fact_pool fact; ///< Фактические показатели
crps_data crps; ///< Данные CRPS для распределения наград
// Голосование по методу Водянова
voting_data voting; ///< Данные голосования по методу Водянова
// Членские взносы
membership_crps membership; ///< Данные CRPS для распределения членских взносов
// Время создания проекта
time_point_sec created_at = current_time_point(); ///< Время создания проекта
uint64_t primary_key() const { return id; } ///< Первичный ключ (1)
uint64_t by_created_at() const { return created_at.sec_since_epoch(); } ///< Индекс по времени создания (2)
checksum256 by_hash() const { return project_hash; } ///< Индекс по хэшу проекта (3)
checksum256 by_parent_hash() const { return parent_hash; } ///< Индекс по хэшу родительского проекта (4)
};
+1 -5
View File
@@ -13,9 +13,5 @@ node_modules
build/*
blockchain-data/*
wallet-data/*
docs/*
blocks/*
docs/html
docs/html2
docs/json
docs/node_nodules
docs/xml
@@ -1,11 +0,0 @@
---
trigger: glob
glob: *.cpp,*.hpp
---
Мы следуем принципам чистой архитектуры. На примере контракта Capital:
- app/ - уровень приложения. Содержит действия, которые вызываются контрактом, которые оркестрируют бизнес-логику сущностей и ядра. Подпапки содержат домены бизнес-логики, где каждый файл - одно действие.
- domain/ - доменный уровень. Содержит сущности и корневую бизнес-логику, которая в своих функциях использует несколько сущностей. На уровне сущностей запрещено вызывать другие сущности.
- entity/ - слой сущностей домена. Содержит выделенные сущности, структуры, индексы их таблиц, и inline реализации простейших бизнес-функций, которые по своим названиям помогают явно определить что происходит с сущностью.
- core/ - слой корневой бизнес-логики, которая объединяет несколько сущностей. Если бизнес-функция сущности требует для своего исполнения нескольких операций с сущностями или она явно относится к доменному уровню приложения за счет своего применения, то такая функция должна быть вынесена в ядро.
Мы допускаем последовательное-повторное обновление сущностей по ходу исполнения действия контракта, если это необходимо в пользу ясности. Длинна типа name не должна превышать 12 символов и не должна содержать специальных знаков.
@@ -1,57 +0,0 @@
---
description:
alwaysApply: false
---
Мне нужно, чтобы ты прошелся по контракту и разнес все действия по группам так, как это сделано в примере. Там:
- указано краткое описание того что происходит и к чему приводит
- указаны параметры и их описание
- указана группа public_actions и public_soviet_actions - для контракта совета, но у тебя может быть другой контракт
- указан якорь для ссылок внешних в самом верху и с отступом
- указана требуемая авторизация
- если у действия есть проводки по кошельков или фондам - отметить их с помощью тега @post:
* | Метод | Иконки | Описание |
* |:-----------------------|:---------------|:--------------------------------------|
* | Wallet::add_available_funds | `💰➕✅` | Добавление доступных средств |
* | Wallet::sub_available_funds | `💰➖✅` | Уменьшение доступных средств |
* | Wallet::add_blocked_funds | `💰➕🔒` | Добавление заблокированных средств |
* | Wallet::sub_blocked_funds | `💰➖🔒` | Уменьшение заблокированных средств |
*
- Проводки по фондам - их тоже отметь с тегом @post
*
* | Метод | Иконки | Описание |
* |:-----------------------|:---------------|:--------------------------------------|
* | Ledger::add | `🏛️➕✅` | Добавление средств в счет |
* | Ledger::sub | `🏛️➖✅` | Уменьшение средств на счете |
* | Ledger::block | `🏛️🔒` | Блокировка средств на счёте |
* | Ledger::unblock | `🏛️🔓` | Разблокирование средств на счёте |
- если у действия есть входящий документ - отметить его также с помощью тега @post
Пример:
/**
*
* @brief Завершение голосования для конкретного участника по методу Водянова.
* Рассчитывает полные итоговые суммы участника включая:
* - Результаты голосования по Водянову: (сумма голосов + общая сумма распределения) / количество голосующих
* - Фиксированные распределения: 61.8% авторских премий равно между авторами, 61.8% исполнительских премий прямо создателям
* Обновляет сегмент участника с итоговыми суммами раздельно по пулам
* @param coopname Наименование кооператива
* @param username Наименование пользователя-участника голосования
* @param project_hash Хеш проекта для которого завершается голосование
* @ingroup public_actions
* @ingroup public_capital_actions
* @note Авторизация требуется от аккаунта: @p coopname
*/
void capital::calcvotes(name coopname, name username, checksum256 project_hash) {
require_auth(coopname);
// Получаем проект и проверяем состояние
auto project = Capital::Projects::get_project_or_fail(coopname, project_hash);
eosio::check(project.status == Capital::Projects::Status::COMPLETED, "Проект не в статусе завершенного голосования");
... и т.д.
ВАЖНО: ВСЕ ДЕЙСТВИЯ НЕОБХОДИМО ДОКУМЕНТИРОВАТЬ В *.cpp файлах, не в .hpp!!!
ВАЖНО2: ПОСЛЕ ПРЕДЛОЖЕНИЯ @BRIEF ДОЛЖНА БЫТЬ ТОЧКА.
@@ -1,6 +0,0 @@
---
description:
globs:
alwaysApply: false
---
Тебе необходимо добавить все константы контракта в группу public_contractname_consts.
@@ -1,32 +0,0 @@
---
description:
globs:
alwaysApply: false
---
Добавь в главный заголовочный файл контракта определение групп как в примере, но вместо capital должен быть тот контракт, на который у тебя задание.
/**
\defgroup public_capital Контракт CAPITAL
* Смарт-контракт капитализации результатов интеллектуальной деятельности предназначен для управления учётом и распределением вознаграждений за результаты интеллектуальной деятельности.
*/
/**
\defgroup public_capital_processes Процессы
\ingroup public_capital
*/
/**
\defgroup public_capital_actions Действия
\ingroup public_capital
*/
/**
\defgroup public_capital_tables Таблицы
\ingroup public_capital
*/
/**
\defgroup public_capital_consts Константы
\ingroup public_capital
*/
@@ -1,40 +0,0 @@
---
trigger: manual
---
Используй следующий язык иконок для обозначения операций в тегах @pre действий или в тегах @post эффектов этих действий.
Во всех действиях, в которых в аргументах есть документ - этот документ должен быть указан в @pre и иконкой документа.
Во всех местах, где по коду или в по тексту есть указание на увеличение или уменьшение средств в кошельке - необходимо использовать соответствующую комбинацию символов.
Во всех местах, где по коду или по тексту есть указание на проводку по счету - необходимо отмечать их языком иконок и писать соответствующий текст.
* ## Язык иконок для обозначений
* | Символ | Значение |
* |:------:|:--------------------------|
* | `📄` | **Документ чего-то** |
* | `💰` | **Кошелек** |
* | `🏛️` | **Фонд** |
* | `` | **Добавление** средств |
* | `` | **Списание** средств |
* | `✅` | Статус: **Доступные** |
* | `🔒` | Статус: **Заблокировано** |
* | `🔓` | **Разблокирование** |
*
* ### Графический язык проводок по Кошельку (Wallet)
*
* | Метод | Иконки | Описание |
* |:-----------------------|:---------------|:--------------------------------------|
* | Wallet::add_available_funds | `💰➕✅` | Добавление доступных средств |
* | Wallet::sub_available_funds | `💰➖✅` | Уменьшение доступных средств |
* | Wallet::add_blocked_funds | `💰➕🔒` | Добавление заблокированных средств |
* | Wallet::sub_blocked_funds | `💰➖🔒` | Уменьшение заблокированных средств |
*
* ### Графический язык проводок по счетам (Ledger)
*
* | Метод | Иконки | Описание |
* |:-----------------------|:---------------|:--------------------------------------|
* | Ledger::add | `🏛️➕✅` | Добавление средств в счет |
* | Ledger::sub | `🏛️➖✅` | Уменьшение средств на счете |
* | Ledger::block | `🏛️🔒` | Блокировка средств на счёте |
* | Ledger::unblock | `🏛️🔓` | Разблокирование средств на счёте |
*/
*
@@ -1,28 +0,0 @@
---
trigger: manual
---
Мне нужно, чтобы ты организовал следующий принцип доступа к файлами документации:
1. У каждого эффекта должен быть файл процесса, на который он ссылается.
2. У каждого эффекта должен быть свой файл, который лежит рядом с файлом процесса (в той же директории) и называется вот так <name>-effect.dox
3. Каждый файл эффекта должен содержать информацию о движении средств по кошелькам пользователя или кооператива типа такого (БЕЗ СПЕЦИАЛЬНОЙ DOXYGEN РАЗМЕТКИ!!!):
➕🏛️ Ledger::add на счёт Ledger::accounts::SHARE_FUND на сумму паевого взноса за вычетом ссуды\n
➕🔒 Wallet::add_blocked_funds на кошельке @ref _source_program на сумму паевого взноса за вычетом ссуды\n
➖🏛️ Ledger::sub со счёта Ledger::accounts::LONG_TERM_LOANS на сумму погашаемой ссуды\n
🫳 Loan::settle_debt на сумму погашаемой ссуды
4. Все файлы эффектов должны собираться в единый файл для обзора, который находится в docs/effects-traces.dox вот так:
/**
* @defgroup deltas_overview Эффекты по процессам
* @ingroup public_capital
@ref public_capital_result_process
@include{doc} public_capital_result_process-effect.dox
4. Все файлы процессов должны включать в конце, но перед информацией о связанных процессах, файл с эффектом:
# Эффекты процесса
@include{doc} public_capital_result_process-effect.dox
5. Все пути могут быть без префиксов и директорий и определяться просто уникальным именем файла.
...
*/
@@ -1,13 +0,0 @@
---
trigger: manual
---
Также мне надо, чтобы в каждом процессе в самом начале этого процесса было @note, которое аккумулирует в себе эффекты, что делает весь процесс.
Там, где запрашивается approve из совета - там всегда приходит принятое заявление. Также в совете бывает что запрашивает протокол решения.
Например, в процесса внесения инвестиционного взноса в проект в начале будет:
@note
- 📄 заявление на паевой взнос
- 📄 принятое председателем заявление
- 💰➕🔒 добавление заблокированных средств в кошелек проекта
- 🏛️➕✅ добавление доступных средств на счет паевого фонда
@@ -1,82 +0,0 @@
---
description:
globs:
alwaysApply: false
---
На основе предоставленной тебе информации необходимо создать страницы или страницу с описанием процессов контракта, создав новую группу @defgroup public_<contract>_<process>_process добавив их в общую группу @ingroup public_<contract>_proccesses
Каждый процесс должен включать:
- Человеко-читаемое название по своему бизнес-смыслу
- Диаграмму последовательности действий mermaid
- Пошаговое описание происходящего в процессе с обоснованием зачем это нужно
- Каждый шаг - это одно действие в смарт-контракте, на которое необходимо ссылаться через @ref <наименование якоря> "заголовок ссылки".
- ВАЖНО: @ref ссылки делать ТОЛЬКО на процессы (например, @ref capital::regcontrib "подача заявки"), а НЕ на внутренние сущности или функции (НЕ Capital::Contributors::create_contributor).
- На каждом шаге, если таковое есть в самом действии или в вызываемых им функциях, необходимо дополнительно отмечать с помощью тегов @post человеко-читаемые эффекты БЕЗ ссылок на внутренние функции:
- Проводки по кошельку (например, "создается программный кошелек", "блокируются средства")
- Проводки по фондам (например, "списываются средства с фонда", "переводятся средства")
- Передаваемые на вход документы
- Изменения статусов (например, "статус меняется с PENDING на ACTIVE")
- При необходимости делай ссылки на процессы через @ref <contract>::<action> "описание процесса"
- При выделении жирным используй теги <b></b>
- Пункты списков пиши текстом, а не списком. Руководствуйся принципом: "лучше объясни, чем коротко напиши".
## ПРАВИЛА ОФОРМЛЕНИЯ ДИАГРАММ
### Диаграммы последовательности:
- Использовать ТОЛЬКО sequenceDiagram вместо graph или flowchart
- Участники (participant) называть понятными русскими именами:
- "Участник" вместо "User" или "Участник"
- "Capital Contract" вместо технических названий
- "Soviet Contract", "Gateway Contract" для системных контрактов
- В сообщениях использовать ЧЕЛОВЕКО-ЧИТАЕМЫЕ описания вместо технических имен методов:
- "Подача заявления о расходе" вместо "createexpnse()"
- "Запрос предварительного одобрения" вместо "approveexpns()"
- "Создание исходящего платежа" вместо "gateway::createoutpay"
- Использовать Note over для пояснений внутренней логики
- Использовать alt/else для альтернативных путей
### Замена технических терминов:
- Все технические термины в тексте заменять на смысловые описания с @ref ссылками:
- used_expense_pool → "сумма использованных средств"
- accumulated_expense_pool → "накопленные средства пула"
- Gateway::createoutpay → @ref Gateway::createoutpay "создаёт исходящий платеж"
- Soviet::create_agenda → @ref Soviet::create_agenda "направляет на рассмотрение советом"
Пример оформления диаграммы
\htmlonly
<div class="mermaid">
sequenceDiagram
participant V as Участник
participant C as Capital Contract
participant S as Soviet Contract
V->>C: 1. Подача заявления
Note over C: Проверка условий
C->>S: 2. Запрос одобрения
alt Одобрение
S->>C: 3. Авторизация
else Отклонение
S->>C: 3. Отклонение
end
</div>
\endhtmlonly
*/
Пример оформления процесса
/**
@defgroup public_capital_registration_process Процесс регистрации участников
@ingroup public_capital_processes
@pre предварительные условия
Процесс приёма договора УХД от пайщика, фиксации его статуса как участника
и обеспечения технической возможности участвовать в проектах и программе капитализации.
``` cpp
some formula
```
@post эффекты
*/
@@ -1,64 +0,0 @@
---
description:
globs:
alwaysApply: false
---
Мне нужно, чтобы ты прошелся по всем таблицам контракта которые используются в нем. Они могут находиться также где-то в @lib/. Мне нужно, чтобы ты добавил все найденные таблицы в указанную по аналогии с тем как тебе предоставлено в примере.
А именно:
- все описания полей должны быть сделаны через ///<
- У таблицы должно быть описание
- У таблицы должна быть общая группа всех таблиц и частная группа конкретного контракта
- У таблицы должен быть якорь
- У таблицы должна быть область памяти в которой хранятся данные
- У таблицы должно быть указано имя.
Пример:
namespace Capital {
/**
* @brief Таблица проектов хранит информацию о проектах кооператива, которые станут результатами.
* @ingroup public_tables
* @ingroup public_capital_tables
* @par Область памяти (scope): coopname
* @par Имя таблицы (table): projects
*/
struct [[eosio::table, eosio::contract(CAPITAL)]] project {
uint64_t id; ///< ID проекта (внутренний ключ)
name coopname; ///< Имя кооператива
checksum256 project_hash; ///< Хэш проекта (внешний ключ)
checksum256 parent_hash; ///< Хэш родительского проекта (если есть)
eosio::name status; ///< Статус проекта
bool is_opened; ///< Открыт ли проект для инвестиций
// Мастер проекта
name master; ///< Мастер проекта
std::string title; ///< Название проекта
std::string description; ///< Описание проекта
std::string meta; ///< Метаданные проекта
counts_data counts; ///< Счетчики участников проекта
plan_pool plan; ///< Плановые показатели
fact_pool fact; ///< Фактические показатели
crps_data crps; ///< Данные CRPS для распределения наград
// Голосование по методу Водянова
voting_data voting; ///< Данные голосования по методу Водянова
// Членские взносы
membership_crps membership; ///< Данные CRPS для распределения членских взносов
// Время создания проекта
time_point_sec created_at = current_time_point(); ///< Время создания проекта
uint64_t primary_key() const { return id; } ///< Первичный ключ (1)
uint64_t by_created_at() const { return created_at.sec_since_epoch(); } ///< Индекс по времени создания (2)
checksum256 by_hash() const { return project_hash; } ///< Индекс по хэшу проекта (3)
checksum256 by_parent_hash() const { return parent_hash; } ///< Индекс по хэшу родительского проекта (4)
};
@@ -1,5 +0,0 @@
---
trigger: always_on
alwaysApply: true
---
ВСЕГДА ОТВЕЧАЙ НА РУССКОМ ЯЗЫКЕ. ДОКУМЕНТАЦИЮ ПИШИ КРАТКО - БЕЗ ФАНТАЗИЙ, ПО СУЩЕСТВУ.НЕ ИСПОЛЬЗУЙ СЛОВА, КОТОРЫЕ ВЫБРАСЫВАЮТСЯ БЕЗ ПОТЕРИ СМЫСЛА. НЕ ПИШИ МНЕ ДЛИННЫЕ ОТЧЕТЫ О ПРОДЕЛАННОЙ РАБОТЕ - ОНИ МНЕ НАФИГ НЕ НУЖНЫ!!
-22
View File
@@ -1,22 +0,0 @@
{
"configurations": [
{
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/lib/**",
"${workspaceFolder}/lib/eosiolib/contracts/**",
"${workspaceFolder}/lib/eosiolib/core/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"int128_t=__int128_t",
"uint128_t=__uint128_t"
],
"cStandard": "c17",
"cppStandard": "c++20"
}
],
"version": 4
}
-2
View File
@@ -30,8 +30,6 @@ macro(add_contract_build name)
endmacro()
# Добавляем все контракты
add_contract_build(test)
add_contract_build(ledger)
add_contract_build(meet)
add_contract_build(loan)
add_contract_build(wallet)
+17 -17
View File
@@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = COOPENOMICS
PROJECT_NAME = "Системные контракты"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = v1
PROJECT_NUMBER =
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = Кооперативная Экономика
PROJECT_BRIEF =
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
@@ -874,7 +874,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = cpp/ *.dox #README.md
INPUT = README.md cpp/common/ cpp/registrator/ cpp/marketplace/ cpp/soviet/ cpp/system/ cpp/gateway/ cpp/fund/ cpp/draft cpp/contributor
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -905,7 +905,7 @@ INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.cpp \
*.hpp \
*.dox
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
@@ -953,21 +953,21 @@ EXCLUDE_SYMBOLS =
# that contain example code fragments that are included (see the \include
# command).
EXAMPLE_PATH = .
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank all
# files are included.
EXAMPLE_PATTERNS = *.dox
EXAMPLE_PATTERNS = *
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude commands
# irrespective of the value of the RECURSIVE tag.
# The default value is: NO.
EXAMPLE_RECURSIVE = YES
EXAMPLE_RECURSIVE = NO
# The IMAGE_PATH tag can be used to specify one or more files or directories
# that contain images that are to be included in the documentation (see the
@@ -1029,7 +1029,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
# USE_MDFILE_AS_MAINPAGE = README.md
USE_MDFILE_AS_MAINPAGE = README.md
#---------------------------------------------------------------------------
# Configuration options related to source browsing
@@ -1178,7 +1178,7 @@ HTML_FILE_EXTENSION = .html
# of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER = docs/css/docs/doxygen-custom/header.html
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
# generated HTML page. If the tag is left blank doxygen will generate a standard
@@ -1188,7 +1188,7 @@ HTML_HEADER = docs/css/docs/doxygen-custom/header.html
# that doxygen normally uses.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER = docs/css/docs/doxygen-custom/footer.html
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
# sheet that is used by each HTML page. It can be used to fine-tune the look of
@@ -1213,7 +1213,7 @@ HTML_STYLESHEET =
# list). For an example see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = docs/css/doxygen-awesome.css docs/css/docs/doxygen-custom/custom.css docs/css/doxygen-awesome-sidebar-only.css docs/css/doxygen-awesome-sidebar-only-darkmode-toggle.css
HTML_EXTRA_STYLESHEET = docs/css/doxygen-awesome.css docs/css/doxygen-awesome-sidebar-only.css
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
@@ -1223,9 +1223,9 @@ HTML_EXTRA_STYLESHEET = docs/css/doxygen-awesome.css docs/css/docs/doxygen-cust
# files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES = docs/css/doxygen-awesome-darkmode-toggle.js docs/css/mermaid.min.js docs/css/docs/doxygen-custom/custom.js
HTML_EXTRA_FILES =
# HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5
HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
@@ -1554,7 +1554,7 @@ ENUM_VALUES_PER_LINE = 4
# Minimum value: 0, maximum value: 1500, default value: 250.
# This tag requires that the tag GENERATE_HTML is set to YES.
TREEVIEW_WIDTH = 200
TREEVIEW_WIDTH = 250
# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
# external symbols imported via tag files in a separate window.
@@ -1616,7 +1616,7 @@ FORMULA_MACROFILE =
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
USE_MATHJAX = YES
USE_MATHJAX = NO
# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
# Note that the different versions of MathJax have different requirements with
@@ -2061,7 +2061,7 @@ MAN_LINKS = NO
# captures the structure of the code including all documentation.
# The default value is: NO.
GENERATE_XML = NO
GENERATE_XML = YES
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+1 -1
View File
@@ -1,4 +1,4 @@
# Введение
# Введение.
Репозиторий содержит в себе смарт-контракты операционной системы coopOS, образующих протокол COOPENOMICS.
Binary file not shown.
@@ -8,26 +8,11 @@
using namespace eosio;
/**
* @brief Инициализация контракта кооперативных участков.
* Выполняет начальную настройку контракта.
* @ingroup public_actions
* @ingroup public_branch_actions
* @note Авторизация требуется от аккаунта: @p _branch
*/
[[eosio::action]] void branch::migrate() {
require_auth(_branch);
}
/**
* @brief Инициализация контракта кооперативных участков.
* Выполняет начальную настройку контракта.
* @ingroup public_actions
* @ingroup public_branch_actions
* @note Авторизация требуется от аккаунта: @p _system
*/
[[eosio::action]] void branch::init()
{
require_auth(_system);
@@ -8,40 +8,6 @@
#include "../lib/common.hpp"
/**
\defgroup public_branch Контракт BRANCH
* Смарт-контракт управления кооперативными участками предназначен для создания, редактирования и удаления кооперативных участков, а также управления доверенными лицами.
*/
/**
\defgroup public_branch_processes Процессы
\ingroup public_branch
*/
/**
\defgroup public_branch_actions Действия
\ingroup public_branch
*/
/**
\defgroup public_branch_tables Таблицы
\ingroup public_branch
*/
// /**
// \defgroup public_branch_consts Константы
// \ingroup public_branch
// */
/**
* @ingroup public_consts
* @ingroup public_branch_consts
* @brief Константы контракта кооперативных участков
*/
// Константы будут добавлены по мере необходимости
/**
* \ingroup public_contracts
*

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