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
1101 changed files with 11189 additions and 59153 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 -2
View File
@@ -1,11 +1,12 @@
---
description:
globs:
alwaysApply: true
---
# DESKTOP
- Пишем код фронтенда в архитектуре Feature Sliced Design (FSD).
- Стиль шаблонов PUG на composite API.
- Всегда создаём index.ts файлы для vue компонент.
- Создавай индексные файлы для vue через export {default as NAME} from './where', а на уровне выше, если это необходимо, делай export * from './ui'
- Всегда удаляем неиспользуемые импорты.
- Не используем 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
- Собрания с вопросами и решениями
Фабрика поддерживает полный цикл создания документов кооператива от заявлений до протоколов собраний с возможностью кастомизации под разные типы кооперативов и требования.
+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
-3
View File
@@ -5,6 +5,3 @@ blockchain-data/
scripts/changelog-prompt.md
scripts/changelog-release.md
scripts/release-info.md
components/docs/docs/sdk
dist/
.env
+5 -48
View File
@@ -1,59 +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.include": false,
"typescript.disableAutomaticTypeAcquisition": true,
"typescript.preferences.includePackageJsonAutoImports": "off",
"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": {
"excludeFiles": [
"**/node_modules/**/*",
"**/dist/**/*",
"**/.cache/**/*"
]
}
"typescript.validate.enable": true
}
-84
View File
@@ -1,87 +1,3 @@
# 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.
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@coopenomics/boot",
"type": "module",
"version": "2025.7.23-1",
"version": "2025.5.5-2",
"private": true,
"packageManager": "pnpm@9.0.6",
"description": "",
+10 -8
View File
@@ -607,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: [
@@ -623,7 +623,9 @@ export default class Blockchain {
permission: 'active',
},
],
data: params,
data: {
...params,
},
},
],
},
@@ -638,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: [
@@ -746,7 +748,7 @@ export default class Blockchain {
name: SovietContract.Actions.Decisions.VoteFor.actionName,
authorization: [
{
actor: params.username,
actor: params.member,
permission: 'active',
},
],
@@ -831,7 +833,7 @@ export default class Blockchain {
}
async ConfirmPayment(
params: GatewayContract.Actions.CompleteIncome.ICompleteIncome,
params: GatewayContract.Actions.CompleteIncome.ICompeteIncome,
) {
await this.update_pass_instance()
@@ -921,7 +923,7 @@ export default class Blockchain {
},
)
console.log('Шаблон создан: ', params.registry_id)
console.log('Шаблон создан: ', params)
}
async createTranslation(
@@ -953,7 +955,7 @@ export default class Blockchain {
},
)
console.log('Перевод создан: ', params.registry_id)
console.log('Перевод создан: ', params)
}
async createProgram(
-5
View File
@@ -65,11 +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: 'contributor',
path: path.resolve(process.cwd(), '../contracts/build/contracts/contributor'),
+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()
+17 -25
View File
@@ -1,7 +1,11 @@
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'
const test_hash
@@ -11,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 {
@@ -93,13 +85,8 @@ export class CooperativeClass {
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,
member: config.provider_chairman,
decision_id: 1,
})
@@ -127,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('Переводим аккаунт в кооператив')
@@ -149,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
@@ -1,6 +1,6 @@
{
"name": "@coopenomics/cleos",
"version": "2025.7.23-1",
"version": "2025.5.5-2",
"private": true,
"description": "",
"scripts": {
BIN
View File
Binary file not shown.
@@ -1,4 +0,0 @@
---
alwaysApply: true
---
Длинна типа name не должна превышать 12 символов и не должна содержать специальных знаков.
-1
View File
@@ -30,7 +30,6 @@ macro(add_contract_build name)
endmacro()
# Добавляем все контракты
add_contract_build(ledger)
add_contract_build(meet)
add_contract_build(loan)
add_contract_build(wallet)
Binary file not shown.
-1
View File
@@ -6,7 +6,6 @@
#include "src/addaccum.cpp"
#include "src/addcirculate.cpp"
#include "src/addexpense.cpp"
#include "src/accumfee.cpp"
#include "src/addinitial.cpp"
#include "src/authorize.cpp"
#include "src/delfund.cpp"
+2 -5
View File
@@ -44,10 +44,10 @@ class [[eosio::contract(FUND)]] fund : public eosio::contract {
eosio::name type, uint64_t fund_id);
// атомарные транзакции фондового кошелька
// паевой фонд
// паевый фонд
[[eosio::action]] void addcirculate(
eosio::name coopname,
eosio::asset quantity); /// < добавить сумму в паевой фонд
eosio::asset quantity); /// < добавить сумму в паевый фонд
[[eosio::action]] void subcirculate(
eosio::name coopname,
eosio::asset quantity,
@@ -65,9 +65,6 @@ class [[eosio::contract(FUND)]] fund : public eosio::contract {
[[eosio::action]] void addexpense(eosio::name coopname, uint64_t fund_id,
eosio::asset quantity);
// добавить членский взнос на накопительный счет кооператива для дальнейшего управления
[[eosio::action]] void accumfee(eosio::name coopname, eosio::asset quantity);
//счет вступительных взносов
[[eosio::action]] void addinitial(eosio::name coopname,
eosio::asset quantity);
@@ -1,23 +0,0 @@
/**
* @brief Атомарный метод добавления членского взноса на накопительный счет кооператива.
* Минует процесс spreadamount и добавляет средства напрямую на accumulative_expense_account
* для дальнейшего управления кооперативом (распределение по фондам или использование на расходы).
*
* @param coopname Имя кооператива
* @param quantity Сумма членского взноса
*/
[[eosio::action]] void fund::accumfee(eosio::name coopname, eosio::asset quantity) {
name payer = check_auth_and_get_payer_or_fail(contracts_whitelist);
auto cooperative = get_cooperative_or_fail(coopname);
coopwallet_index coopwallet(_fund, coopname.value);
auto wal = coopwallet.find(0);
eosio::check(wal != coopwallet.end(), "Кошелёк кооператива не найден");
// Добавление членского взноса на накопительный счет кооператива
coopwallet.modify(wal, payer, [&](auto &w) {
w.accumulative_expense_account.available += quantity;
});
}
@@ -1,8 +1,8 @@
// атомарные транзакции фондового кошелька
// паевой счет
// паевый счет
[[eosio::action]] void fund::addcirculate(
eosio::name coopname,
eosio::asset quantity) /// < добавить сумму в паевой фонд
eosio::asset quantity) /// < добавить сумму в паевый фонд
{
name payer = check_auth_and_get_payer_or_fail(contracts_whitelist);
@@ -14,12 +14,6 @@ void gateway::createoutpay(eosio::name coopname, eosio::name username, checksum2
eosio::check(confirm_callback != ""_n, "Действие для коллбэка-успеха должено быть установлено");
eosio::check(decline_callback != ""_n, "Действие для коллбэка-отлонения должно быть установлено");
// Валидируем callback действия если они от wallet контракта
if (callback_contract == _wallet) {
Wallet::get_valid_wallet_action(confirm_callback);
Wallet::get_valid_wallet_action(decline_callback);
}
auto outcome = Gateway::get_outcome(coopname, outcome_hash);
eosio::check(!outcome.has_value(), "Объект возврата уже существует с указанным хэшем");
@@ -1,5 +1,6 @@
void gateway::outcomplete(eosio::name coopname, checksum256 outcome_hash){
require_auth(coopname);
auto exist_outcome = Gateway::get_outcome(coopname, outcome_hash);
eosio::check(exist_outcome.has_value(), "Объект возврата не существует с указанным хэшем");
@@ -8,15 +9,13 @@ void gateway::outcomplete(eosio::name coopname, checksum256 outcome_hash){
auto outcome = outcomes.find(exist_outcome -> id);
eosio::check(outcome -> status == "pending"_n, "Только принятые заявления на вывод могут быть обработаны");
// Используем интерфейс для типизированного вызова callback действия
Action::send<completewthd_interface>(
action(
permission_level{ _gateway, "active"_n},
outcome -> callback_contract,
outcome -> confirm_callback,
_gateway,
coopname,
outcome -> outcome_hash
);
std::make_tuple(coopname, outcome -> outcome_hash)
).send();
outcomes.erase(outcome);
};
@@ -2,22 +2,19 @@ void gateway::outdecline(eosio::name coopname, checksum256 outcome_hash, std::st
require_auth(coopname);
auto exist_outcome = Gateway::get_outcome(coopname, outcome_hash);
eosio::check(exist_outcome.has_value(), "Объект возврата не существует с указанным хэшем");
eosio::check(!exist_outcome.has_value(), "Объект возврата уже существует с указанным хэшем");
Gateway::outcomes_index outcomes(_gateway, coopname.value);
auto outcome = outcomes.find(exist_outcome -> id);
eosio::check(outcome -> status == "pending"_n, "Только принятые заявления на вывод могут быть обработаны");
// Используем интерфейс для типизированного вызова callback действия
Action::send<declinewthd_interface>(
outcome -> callback_contract,
outcome -> decline_callback,
_gateway,
coopname,
outcome -> outcome_hash,
reason
);
action(
permission_level{ _gateway, "active"_n},
outcome -> callback_contract,
outcome -> decline_callback,
std::make_tuple(coopname, outcome -> outcome_hash, reason)
).send();
outcomes.erase(outcome);
};
@@ -1,10 +0,0 @@
cmake_minimum_required(VERSION 3.5)
# Проект
project(ledger)
find_package(cdt)
# Добавление контракта
add_contract(ledger ledger ledger.cpp)
target_compile_definitions(ledger PUBLIC IS_TESTNET=${IS_TESTNET})
@@ -1,17 +0,0 @@
#include "ledger.hpp"
#include <ctime>
#include <eosio/transaction.hpp>
#include "src/common/add.cpp"
#include "src/common/sub.cpp"
#include "src/common/transfer.cpp"
#include "src/common/block.cpp"
#include "src/common/unblock.cpp"
#include "src/common/writeoff.cpp"
#include "src/common/writeoffcnsl.cpp"
#include "src/writeoff/create.cpp"
#include "src/writeoff/auth.cpp"
#include "src/writeoff/complete.cpp"
#include "src/writeoff/decline.cpp"
#include "src/migrate/migrate.cpp"
@@ -1,51 +0,0 @@
#include <eosio/asset.hpp>
#include <eosio/contract.hpp>
#include <eosio/crypto.hpp>
#include <eosio/eosio.hpp>
#include <eosio/multi_index.hpp>
#include <eosio/system.hpp>
#include <eosio/time.hpp>
#include "../lib/common.hpp"
using namespace eosio;
/**
* @ingroup public_contracts
* @brief Контракт ledger для управления бухгалтерской книгой кооператива
* @details Обеспечивает ведение счетов согласно стандартам кооперативной отчетности
* Счета создаются автоматически при первом пополнении и удаляются при обнулении всех балансов
*/
class [[eosio::contract(LEDGER)]] ledger : public eosio::contract {
public:
ledger(eosio::name receiver, eosio::name code,
eosio::datastream<const char *> ds)
: eosio::contract(receiver, code, ds) {}
void apply(uint64_t receiver, uint64_t code, uint64_t action);
[[eosio::action]] void migrate();
// Операции со счетами (common процесс)
[[eosio::action]] void add(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
[[eosio::action]] void sub(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
[[eosio::action]] void transfer(eosio::name coopname, uint64_t from_account_id, uint64_t to_account_id, eosio::asset quantity, std::string comment);
// Операции блокировки/разблокировки средств
[[eosio::action]] void block(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
[[eosio::action]] void unblock(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
// Атомарная операция списания
[[eosio::action]] void writeoff(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
// Атомарная операция отмены списания
[[eosio::action]] void writeoffcnsl(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
// Операции процесса списания через совет (writeoff процесс)
[[eosio::action]] void create(eosio::name coopname, eosio::name username, uint64_t account_id, eosio::asset quantity, std::string reason, document2 document, checksum256 writeoff_hash);
[[eosio::action]] void auth(eosio::name coopname, checksum256 writeoff_hash);
// Коллбэки от gateway
[[eosio::action]] void complete(eosio::name coopname, checksum256 writeoff_hash);
[[eosio::action]] void decline(eosio::name coopname, checksum256 writeoff_hash, std::string reason);
};
@@ -1,43 +0,0 @@
/**
* @brief Пополнение счета с автоматическим созданием если не существует
* @param actor - кто предоставляет авторизацию
* @param coopname - имя кооператива
* @param account_id - идентификатор счета
* @param quantity - сумма пополнения
* @param comment - комментарий к операции
*/
[[eosio::action]]
void ledger::add(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
eosio::name payer = coopname;
if (!has_auth(coopname)) {
payer = check_auth_and_get_payer_or_fail(contracts_whitelist);
}
eosio::check(quantity.is_valid(), "Некорректная сумма");
eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
eosio::check(quantity.symbol == _root_govern_symbol, "Некорректный символ валюты");
laccounts_index accounts(_ledger, coopname.value);
auto account_iter = accounts.find(account_id);
if (account_iter == accounts.end()) {
// Счет не существует - создаем автоматически, получая имя из ACCOUNT_MAP
std::string account_name = Ledger::get_account_name_by_id(account_id);
eosio::check(account_name != "Неизвестный счет", "Счет с таким ID не предусмотрен в плане счетов");
// Создаем новый счет
accounts.emplace(payer, [&](auto& acc) {
acc.id = account_id;
acc.name = account_name;
acc.available = quantity;
acc.blocked = eosio::asset(0, _root_govern_symbol);
acc.writeoff = eosio::asset(0, _root_govern_symbol);
});
} else {
// Счет существует - пополняем available
accounts.modify(account_iter, payer, [&](auto& acc) {
acc.available += quantity;
});
}
}
@@ -1,31 +0,0 @@
/**
* @brief Блокировка средств на счете (перенос из available в blocked)
* @param coopname - имя кооператива
* @param account_id - идентификатор счета
* @param quantity - сумма блокировки
* @param comment - комментарий к операции
*/
[[eosio::action]]
void ledger::block(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
eosio::name payer = coopname;
if (!has_auth(coopname)) {
payer = check_auth_and_get_payer_or_fail(contracts_whitelist);
}
eosio::check(quantity.is_valid(), "Некорректная сумма");
eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
eosio::check(quantity.symbol == _root_govern_symbol, "Некорректный символ валюты");
laccounts_index accounts(_ledger, coopname.value);
auto account_iter = accounts.find(account_id);
eosio::check(account_iter != accounts.end(), "Счет не найден");
// Проверяем достаточность доступных средств
eosio::check(account_iter->available >= quantity, "Недостаточно доступных средств для блокировки");
// Переносим средства из available в blocked
accounts.modify(account_iter, payer, [&](auto& acc) {
acc.available -= quantity;
acc.blocked += quantity;
});
}
@@ -1,38 +0,0 @@
/**
* @brief Списание со счета
* @param actor - кто предоставляет авторизацию
* @param coopname - имя кооператива
* @param account_id - идентификатор счета
* @param quantity - сумма списания
* @param comment - комментарий к операции
*/
[[eosio::action]]
void ledger::sub(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
eosio::name payer = coopname;
if (!has_auth(coopname)) {
payer = check_auth_and_get_payer_or_fail(contracts_whitelist);
}
eosio::check(quantity.is_valid(), "Некорректная сумма");
eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
eosio::check(quantity.symbol == _root_govern_symbol, "Некорректный символ валюты");
laccounts_index accounts(_ledger, coopname.value);
auto account_iter = accounts.find(account_id);
eosio::check(account_iter != accounts.end(), "Счет не найден");
// Проверяем достаточность доступных средств
eosio::check(account_iter->available >= quantity, "Недостаточно доступных средств для списания");
// Обновляем баланс счета
accounts.modify(account_iter, payer, [&](auto& acc) {
acc.available -= quantity;
});
// Проверяем и удаляем счет если он полностью пуст
if (account_iter->is_empty()) {
accounts.erase(account_iter);
}
}
@@ -1,61 +0,0 @@
/**
* @brief Перевод средств между счетами
* @param coopname - имя кооператива
* @param from_account_id - идентификатор счета списания
* @param to_account_id - идентификатор счета зачисления
* @param quantity - сумма перевода
* @param comment - комментарий к операции
*/
[[eosio::action]]
void ledger::transfer(eosio::name coopname, uint64_t from_account_id, uint64_t to_account_id, eosio::asset quantity, std::string comment) {
eosio::name payer = coopname;
if (!has_auth(coopname)) {
payer = check_auth_and_get_payer_or_fail(contracts_whitelist);
}
eosio::check(quantity.is_valid(), "Некорректная сумма");
eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
eosio::check(quantity.symbol == _root_govern_symbol, "Некорректный символ валюты");
eosio::check(from_account_id != to_account_id, "Счета списания и зачисления должны отличаться");
laccounts_index accounts(_ledger, coopname.value);
// Проверяем счет списания
auto from_account_iter = accounts.find(from_account_id);
eosio::check(from_account_iter != accounts.end(), "Счет списания не найден");
eosio::check(from_account_iter->available >= quantity, "Недостаточно доступных средств для перевода");
// Проверяем счет зачисления или создаем его
auto to_account_iter = accounts.find(to_account_id);
// Списываем с источника
accounts.modify(from_account_iter, payer, [&](auto& acc) {
acc.available -= quantity;
});
if (to_account_iter == accounts.end()) {
// Счет получателя не существует - создаем автоматически
std::string account_name = Ledger::get_account_name_by_id(to_account_id);
eosio::check(account_name != "Неизвестный счет", "Счет получателя с таким ID не предусмотрен в плане счетов");
accounts.emplace(payer, [&](auto& acc) {
acc.id = to_account_id;
acc.name = account_name;
acc.available = quantity;
acc.blocked = eosio::asset(0, _root_govern_symbol);
acc.writeoff = eosio::asset(0, _root_govern_symbol);
});
} else {
// Зачисляем на получателя
accounts.modify(to_account_iter, payer, [&](auto& acc) {
acc.available += quantity;
});
}
// Проверяем и удаляем счет источника если он полностью пуст
if (from_account_iter->is_empty()) {
accounts.erase(from_account_iter);
}
}
@@ -1,31 +0,0 @@
/**
* @brief Разблокировка средств на счете (перенос из blocked в available)
* @param coopname - имя кооператива
* @param account_id - идентификатор счета
* @param quantity - сумма разблокировки
* @param comment - комментарий к операции
*/
[[eosio::action]]
void ledger::unblock(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
eosio::name payer = coopname;
if (!has_auth(coopname)) {
payer = check_auth_and_get_payer_or_fail(contracts_whitelist);
}
eosio::check(quantity.is_valid(), "Некорректная сумма");
eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
eosio::check(quantity.symbol == _root_govern_symbol, "Некорректный символ валюты");
laccounts_index accounts(_ledger, coopname.value);
auto account_iter = accounts.find(account_id);
eosio::check(account_iter != accounts.end(), "Счет не найден");
// Проверяем достаточность заблокированных средств
eosio::check(account_iter->blocked >= quantity, "Недостаточно заблокированных средств для разблокировки");
// Переносим средства из blocked в available
accounts.modify(account_iter, payer, [&](auto& acc) {
acc.blocked -= quantity;
acc.available += quantity;
});
}
@@ -1,31 +0,0 @@
/**
* @brief Атомарная операция списания - перевод заблокированных средств в списанные
* @param coopname - имя кооператива
* @param account_id - идентификатор счета
* @param quantity - сумма списания
* @param comment - комментарий к операции
*/
[[eosio::action]]
void ledger::writeoff(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
eosio::name payer = coopname;
if (!has_auth(coopname)) {
payer = check_auth_and_get_payer_or_fail(contracts_whitelist);
}
eosio::check(quantity.is_valid(), "Некорректная сумма");
eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
eosio::check(quantity.symbol == _root_govern_symbol, "Некорректный символ валюты");
laccounts_index accounts(_ledger, coopname.value);
auto account_iter = accounts.find(account_id);
eosio::check(account_iter != accounts.end(), "Счет не найден");
// Проверяем достаточность заблокированных средств
eosio::check(account_iter->blocked >= quantity, "Недостаточно заблокированных средств для списания");
// Атомарно переводим средства из blocked в writeoff
accounts.modify(account_iter, payer, [&](auto& acc) {
acc.blocked -= quantity;
acc.writeoff += quantity;
});
}
@@ -1,32 +0,0 @@
/**
* @brief Атомарная операция отмены списания - перевод списанных средств обратно в заблокированные
* @param coopname - имя кооператива
* @param account_id - идентификатор счета
* @param quantity - сумма отмены списания
* @param comment - комментарий к операции
*/
[[eosio::action]]
void ledger::writeoffcnsl(eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
eosio::name payer = coopname;
if (!has_auth(coopname)) {
payer = check_auth_and_get_payer_or_fail(contracts_whitelist);
}
eosio::check(quantity.is_valid(), "Некорректная сумма");
eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
eosio::check(quantity.symbol == _root_govern_symbol, "Некорректный символ валюты");
laccounts_index accounts(_ledger, coopname.value);
auto account_iter = accounts.find(account_id);
eosio::check(account_iter != accounts.end(), "Счет не найден");
// Проверяем достаточность списанных средств
eosio::check(account_iter->writeoff >= quantity, "Недостаточно списанных средств для отмены списания");
// Атомарно переводим средства из writeoff в blocked
accounts.modify(account_iter, payer, [&](auto& acc) {
acc.writeoff -= quantity;
acc.blocked += quantity;
});
}
@@ -1,50 +0,0 @@
/**
* @brief Миграция данных из контракта fund
* Переносит данные из фондов fund в счета ledger
* Теперь счета создаются только при наличии средств для переноса
*/
void ledger::migrate(){
require_auth(_ledger);
// Получаем все кооперативы
cooperatives2_index coops(_registrator, _registrator.value);
for (auto coop_iter = coops.begin(); coop_iter != coops.end(); ++coop_iter) {
eosio::name coopname = coop_iter->username;
// Проверяем что счета еще не инициализированы
laccounts_index accounts(_ledger, coopname.value);
auto existing = accounts.begin();
if (existing != accounts.end()) {
continue; // Пропускаем уже мигрированные кооперативы
}
// Мигрируем данные из fund контракта
// Читаем кошелек кооператива
coopwallet_index coopwallets(_fund, coopname.value);
auto wallet_iter = coopwallets.find(0); // ID всегда 0
if (wallet_iter != coopwallets.end()) {
// Паевой фонд (circulating_account) -> счет 80 (SHARE_FUND)
if (wallet_iter->circulating_account.available.amount > 0) {
Ledger::add(_ledger, coopname, Ledger::accounts::SHARE_FUND,
wallet_iter->circulating_account.available,
"Миграция: перенос паевого фонда из fund контракта");
}
// Вступительные взносы (initial_account) -> счет 861 (ENTRANCE_FEES)
if (wallet_iter->initial_account.available.amount > 0) {
Ledger::add(_ledger, coopname, Ledger::accounts::ENTRANCE_FEES,
wallet_iter->initial_account.available,
"Миграция: перенос вступительных взносов из fund контракта");
}
// Накопительный счет членских взносов (accumulative_expense_account) -> счет 86 (TARGET_RECEIPTS)
if (wallet_iter->accumulative_expense_account.available.amount > 0) {
Ledger::add(_ledger, coopname, Ledger::accounts::TARGET_RECEIPTS,
wallet_iter->accumulative_expense_account.available,
"Миграция: перенос накопительного счета членских взносов из fund контракта");
}
}
}
}
@@ -1,37 +0,0 @@
/**
* @brief Авторизация операции после положительного решения совета
* @param coopname - имя кооператива
* @param writeoff_hash - хэш операции списания
*/
[[eosio::action]]
void ledger::auth(eosio::name coopname, checksum256 writeoff_hash) {
require_auth(_soviet);
auto writeoff_opt = Ledger::get_writeoff_by_hash(writeoff_hash);
eosio::check(writeoff_opt.has_value(), "Операция не найдена");
auto writeoff = writeoff_opt.value();
eosio::check(writeoff.coopname == coopname, "Неверный кооператив");
eosio::check(writeoff.status == "pending"_n, "Операция уже обработана");
// Обновляем статус операции
writeoffs_index writeoffs(_ledger, _ledger.value);
auto writeoff_iter = writeoffs.find(writeoff.id);
writeoffs.modify(writeoff_iter, _soviet, [&](auto& w) {
w.status = "approved"_n;
});
// Отправляем запрос на создание исходящего платежа в gateway через интерфейс
Action::send<createoutpay_interface>(
_gateway,
"createoutpay"_n,
_ledger,
coopname,
writeoff.username,
writeoff_hash,
writeoff.quantity,
_ledger, // callback_contract
"complete"_n, // confirm_callback
"decline"_n // decline_callback
);
}
@@ -1,33 +0,0 @@
/**
* @brief Коллбэк при успешном завершении платежа
* @param coopname - имя кооператива
* @param writeoff_hash - хэш операции списания
*/
[[eosio::action]]
void ledger::complete(eosio::name coopname, checksum256 writeoff_hash) {
require_auth(_gateway);
auto writeoff_opt = Ledger::get_writeoff_by_hash(writeoff_hash);
eosio::check(writeoff_opt.has_value(), "Операция не найдена");
auto writeoff = writeoff_opt.value();
eosio::check(writeoff.coopname == coopname, "Неверный кооператив");
eosio::check(writeoff.status == "approved"_n, "Операция не одобрена");
// Обновляем статус операции
writeoffs_index writeoffs(_ledger, _ledger.value);
auto writeoff_iter = writeoffs.find(writeoff.id);
writeoffs.modify(writeoff_iter, _gateway, [&](auto& w) {
w.status = "paid"_n;
});
// Списываем средства со счета
laccounts_index accounts(_ledger, coopname.value);
auto account_iter = accounts.find(writeoff.account_id);
eosio::check(account_iter != accounts.end(), "Счет не найден");
accounts.modify(account_iter, _gateway, [&](auto& acc) {
acc.blocked -= writeoff.quantity;
acc.writeoff += writeoff.quantity;
});
}
@@ -1,73 +0,0 @@
/**
* @brief Создание заявления в совет для списания средств
* @param coopname - имя кооператива
* @param username - инициатор заявления
* @param account_id - идентификатор счета для списания
* @param quantity - сумма списания
* @param reason - обоснование списания
* @param document - документ обоснования
* @param writeoff_hash - внешний хэш операции списания
*/
[[eosio::action]]
void ledger::create(eosio::name coopname, eosio::name username, uint64_t account_id, eosio::asset quantity, std::string reason, document2 document, checksum256 writeoff_hash) {
require_auth(coopname);
eosio::check(quantity.is_valid(), "Некорректная сумма");
eosio::check(quantity.amount > 0, "Сумма должна быть положительной");
eosio::check(quantity.symbol == _root_govern_symbol, "Некорректный символ валюты");
eosio::check(reason.length() > 0, "Обоснование должно быть от 1 символа");
// Проверяем что кооператив существует
cooperatives2_index coops(_registrator, _registrator.value);
auto coop_iter = coops.find(coopname.value);
eosio::check(coop_iter != coops.end(), "Кооператив не найден");
// Проверяем что счет существует
laccounts_index accounts(_ledger, coopname.value);
auto account_iter = accounts.find(account_id);
eosio::check(account_iter != accounts.end(), "Счет не найден");
// Проверяем достаточность средств
eosio::check(account_iter -> available >= quantity, "Недостаточно средств на счету");
// Проверяем уникальность writeoff_hash
auto existing_writeoff = Ledger::get_writeoff_by_hash(writeoff_hash);
eosio::check(!existing_writeoff.has_value(), "Операция с таким хэшем уже существует");
// Создаем операцию ожидающую решения совета
auto operation_id = get_global_id_in_scope(_ledger, _ledger, "writeoffs"_n);
writeoffs_index writeoffs(_ledger, _ledger.value);
writeoffs.emplace(username, [&](auto& op) {
op.id = operation_id;
op.coopname = coopname;
op.username = username;
op.account_id = account_id;
op.quantity = quantity;
op.reason = reason;
op.document = document;
op.writeoff_hash = writeoff_hash;
op.status = "pending"_n;
});
// Создаем агенду в совете через Action::send
Action::send<createagenda_interface>(
_soviet,
"createagenda"_n,
username,
coopname,
username,
get_valid_soviet_action("ledgerwthd"_n), // тип решения
writeoff_hash,
_ledger, // callback_contract
"auth"_n, // confirm_callback
"decline"_n, // decline_callback
document,
reason
);
// Блокируем средства на счету
std::string comment = "Служебная записка отправлена в совет для списания средств со счета " + account_iter->name + " на сумму " + quantity.to_string();
Ledger::block(coopname, coopname, account_id, quantity, comment);
}
@@ -1,22 +0,0 @@
/**
* @brief Коллбэк при отклонении платежа или решения совета
* @param coopname - имя кооператива
* @param writeoff_hash - хэш операции списания
* @param reason - причина отклонения
*/
[[eosio::action]]
void ledger::decline(eosio::name coopname, checksum256 writeoff_hash, std::string reason) {
// Может быть вызван от _soviet (отклонение решения) или _gateway (отклонение платежа)
eosio::check(has_auth(_soviet) || has_auth(_gateway), "Недостаточно прав для отклонения операции");
auto writeoff_opt = Ledger::get_writeoff_by_hash(writeoff_hash);
eosio::check(writeoff_opt.has_value(), "Операция не найдена");
auto writeoff = writeoff_opt.value();
eosio::check(writeoff.coopname == coopname, "Неверный кооператив");
// Обновляем статус операции
writeoffs_index writeoffs(_ledger, _ledger.value);
auto writeoff_iter = writeoffs.find(writeoff.id);
writeoffs.erase(writeoff_iter);
}
+6 -7
View File
@@ -164,10 +164,10 @@ struct org_data {
std::string announce; ///< Анонс
std::string description; ///< Описание
eosio::asset initial; ///< Вступительный взнос физического лица / ип
eosio::asset minimum; ///< Минимальный паевой взнос физического лица / ип
eosio::asset minimum; ///< Минимальный паевый взнос физического лица / ип
eosio::asset org_initial; ///< Вступительный взнос юридического лица
eosio::asset org_minimum; ///< Минимальный паевой взнос юридического лица
eosio::asset org_minimum; ///< Минимальный паевый взнос юридического лица
};
@@ -192,11 +192,11 @@ struct [[eosio::table, eosio::contract(REGISTRATOR)]] cooperative {
eosio::asset registration; ///< Регистрационный взнос физического лица / ип
eosio::asset initial; ///< Вступительный членский взнос физического лица / ип
eosio::asset minimum; ///< Минимальный паевой взнос физического лица / ип
eosio::asset minimum; ///< Минимальный паевый взнос физического лица / ип
eosio::binary_extension<eosio::asset> org_registration; ///< Регистрационный взнос юридического лица
eosio::binary_extension<eosio::asset> org_initial; ///< Вступительный членский взнос юридического лица
eosio::binary_extension<eosio::asset> org_minimum; ///< Минимальный паевой взнос юридического лица
eosio::binary_extension<eosio::asset> org_minimum; ///< Минимальный паевый взнос юридического лица
eosio::binary_extension<eosio::name> status; ///< Статус процесса подключения
eosio::binary_extension<eosio::time_point_sec> created_at; ///< Дата поступления заявки на подключение (pending | ... | active | blocked)
@@ -303,16 +303,15 @@ struct [[eosio::table, eosio::contract(REGISTRATOR)]] cooperative2 {
eosio::asset registration; ///< Регистрационный взнос физического лица / ип
eosio::asset initial; ///< Вступительный членский взнос физического лица / ип
eosio::asset minimum; ///< Минимальный паевой взнос физического лица / ип
eosio::asset minimum; ///< Минимальный паевый взнос физического лица / ип
eosio::binary_extension<eosio::asset> org_registration; ///< Регистрационный взнос юридического лица
eosio::binary_extension<eosio::asset> org_initial; ///< Вступительный членский взнос юридического лица
eosio::binary_extension<eosio::asset> org_minimum; ///< Минимальный паевой взнос юридического лица
eosio::binary_extension<eosio::asset> org_minimum; ///< Минимальный паевый взнос юридического лица
eosio::binary_extension<eosio::name> status; ///< Статус процесса подключения
eosio::binary_extension<eosio::time_point_sec> created_at; ///< Дата поступления заявки на подключение (pending | ... | active | blocked)
eosio::binary_extension<document2> document; ///< Подписанный документ соглашения на подключение
eosio::binary_extension<uint64_t> active_participants_count; ///< Счетчик активных пайщиков в кооперативе
/**
+1 -1
View File
@@ -80,7 +80,7 @@ struct [[eosio::table, eosio::contract(SOVIET)]] agreement2 {
};
typedef eosio::multi_index<"agreements3"_n, agreement2,
typedef eosio::multi_index<"agreements2"_n, agreement2,
eosio::indexed_by<"byusername"_n, eosio::const_mem_fun<agreement2, uint64_t, &agreement2::by_username>>,
eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun<agreement2, uint64_t, &agreement2::by_status>>,
eosio::indexed_by<"bydraft"_n, eosio::const_mem_fun<agreement2, uint64_t, &agreement2::by_draft>>,
-15
View File
@@ -76,21 +76,6 @@ struct [[eosio::table, eosio::contract(BRANCH)]] coobranch {
bool is_account_in_trusted(const eosio::name& account) const {
return std::find(trusted.begin(), trusted.end(), account) != trusted.end();
}
/**
* @brief Проверяет права доступа пользователя к данному кооперативному участку
* @param username Имя пользователя для проверки
* @return true если пользователь имеет права доступа, false в противном случае
*/
bool is_user_authorized(const eosio::name& username) const {
// Проверяем является ли пользователь доверенным лицом (trustee)
if (trustee == username) {
return true;
}
// Проверяем находится ли пользователь в списке доверенных (trusted)
return is_account_in_trusted(username);
}
};
typedef eosio::multi_index<"branches"_n, coobranch,
+153 -1
View File
@@ -1,5 +1,157 @@
#pragma once
// Структуры exchange_params и request перенесены в shared_marketplace.hpp
/**
\ingroup public_tables
\brief Параметры заявки на обмен.
*
* Эта структура предоставляет набор данных, которые требуются для создания или обновления заявки на обмен в контракте "marketplace".
*
* @details Следующие параметры определяют заявку:
* - username: Имя пользователя, инициирующего или обновляющего заявку.
* - parent_id: Идентификатор родительской заявки (если есть).
* - coopname: Имя кооператива.
* - units: Количество частей (штук) товара или услуги для обмена.
* - unit_cost: Цена за единицу (штуку) товара или услуги, выраженная в определенном формате asset.
* - data: Содержит дополнительные данные, специфичные для заявки (например, условия обмена).
* - meta: Метаданные, предоставляющие дополнительную информацию о заявке (например, описание товара или условия обмена).
*/
struct exchange_params {
eosio::name username; /*!< Имя пользователя, инициирующего или обновляющего заявку */
uint64_t parent_id; /*!< Идентификатор родительской заявки */
uint64_t program_id; /*!< Идентификатор программы */
eosio::name coopname; /*!< Имя кооператива */
uint64_t units; /*!< Количество частей (штук) товара или услуги */
eosio::asset unit_cost; /*!< Цена за единицу (штуку) товара или услуги */
uint64_t product_lifecycle_secs; /*!< Время жизни продукта, заявляемое поставщиком */
std::optional<document2> document; /*!< Сопутствующий подписанный документ на взнос или возврат взноса */
std::string data; /*!< Дополнительные данные, специфичные для заявки */
std::string meta; /*!< Метаданные о заявке */
};
/**
* @brief Таблица обменов для контракта "marketplace"
* @ingroup public_tables
* Эта таблица используется для хранения информации об обменных заявках в системе.
*
* @param id Идентификатор обмена, используемый как первичный ключ
* @param parent_id Идентификатор родительской заявки (если есть)
* @param type Тип обмена (покупка, продажа и т.д.)
* @param status Статус обмена (например, "опубликовано", "на модерации" и т.д.)
* @param username Имя пользователя, создавшего заявку
* @param token_contract Имя контракта токена
* @param unit_cost Цена за единицу товара в заявке
* @param remain_units Оставшееся количество товара
* @param blocked_units Заблокированное количество товара
* @param delivered_units Количество доставленного товара
* @param data Дополнительные данные, связанные с заявкой
* @param meta Метаданные заявки
*
* Дополнительные индексы по status, type и parent_id позволяют искать заявки по этим полям.
*
* Пример использования:
* @code
* requests_index exchange(_me, coopname.value);
* auto exchange_order = exchange.find(id);
* @endcode
*/
struct [[eosio::table, eosio::contract(MARKETPLACE)]] request {
uint64_t id; /*!< идентификатор обмена */
uint64_t parent_id; /*!< идентификатор родительской заявки */
uint64_t program_id; /*!< идентификатор программы */
eosio::name coopname; /*!< имя аккаунта кооператива */
eosio::name type; /*!< тип обмена */
eosio::name status; /*!< статус обмена */
eosio::name username; /*!< имя аккаунта владельца заявки */
eosio::name parent_username; /*!< имя аккаунта владельца объявления */
eosio::name token_contract; /*!< имя контракта токена */
eosio::asset unit_cost;/*!< себестоимость единицы товара от поставщика */
eosio::asset supplier_amount; /*!< сумма взноса поставщика */
eosio::asset total_cost; /*!< сумма взноса заказчика */
eosio::asset membership_fee; /*!< членский взнос заказчика */
uint64_t remain_units; /*!< оставшееся количество товара */
uint64_t blocked_units; /*!< заблокированное количество товара */
uint64_t delivered_units; /*!< количество доставленного товара */
std::string data; /*!< дополнительные данные */
std::string meta; /*!< метаданные заявки */
eosio::name money_contributor;
eosio::name product_contributor;
document2 contribute_product_statement;
document2 return_product_statement;
uint64_t contribution_product_decision_id;
document2 contribution_product_authorization;
uint64_t return_product_decision_id;
document2 return_product_authorization;
document2 product_contribution_act_validation;
document2 product_contribution_act;
document2 product_recieve_act;
document2 product_recieve_act_validation;
uint64_t product_lifecycle_secs;
uint64_t cancellation_fee; //up to 100
eosio::asset cancellation_fee_amount;
eosio::time_point_sec created_at;
eosio::time_point_sec accepted_at;
eosio::time_point_sec supplied_at;
eosio::time_point_sec delivered_at;
eosio::time_point_sec recieved_at;
eosio::time_point_sec completed_at;
eosio::time_point_sec declined_at;
eosio::time_point_sec disputed_at;
eosio::time_point_sec canceled_at;
// eosio::time_point_sec expired_at;
eosio::time_point_sec warranty_delay_until;
eosio::time_point_sec deadline_for_receipt;
bool is_warranty_return = false;
uint64_t warranty_return_id;
uint64_t primary_key() const { return id; } /*!< return id - primary_key */
uint64_t by_coop() const {return coopname.value;} /*!< кооператив */
uint64_t by_status() const { return status.value; } /*!< индекс по статусу */
uint64_t by_program() const { return program_id; } /*!< индекс по программе */
uint64_t by_type() const { return type.value; } /*!< индекс по типу */
uint64_t by_parent() const { return parent_id; } /*!< индекс по родительскому ID */
uint64_t by_username() const { return username.value;} /*!< индекс по имени аккаунта */
uint64_t by_parent_username() const { return parent_username.value;} /*!< индекс по имени аккаунта владельца объявления */
uint64_t by_created() const { return created_at.sec_since_epoch();}
uint64_t by_completed() const { return completed_at.sec_since_epoch();}
uint64_t by_declined() const { return declined_at.sec_since_epoch();}
uint64_t by_canceled() const { return canceled_at.sec_since_epoch();}
uint64_t by_warranty_id() const { return warranty_return_id;}
// uint64_t by_expired() const { return expired_at.sec_since_epoch();}
};
typedef eosio::multi_index<
"requests"_n, request,
eosio::indexed_by<"bycoop"_n, eosio::const_mem_fun<request, uint64_t, &request::by_coop>>,
eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun<request, uint64_t, &request::by_status>>,
eosio::indexed_by<"bytype"_n, eosio::const_mem_fun<request, uint64_t, &request::by_type>>,
eosio::indexed_by<"byprogram"_n, eosio::const_mem_fun<request, uint64_t, &request::by_program>>,
eosio::indexed_by<"byparent"_n, eosio::const_mem_fun<request, uint64_t, &request::by_parent>>,
eosio::indexed_by<"byusername"_n, eosio::const_mem_fun<request, uint64_t, &request::by_username>>,
eosio::indexed_by<"bypausername"_n, eosio::const_mem_fun<request, uint64_t, &request::by_parent_username>>,
eosio::indexed_by<"bycreated"_n, eosio::const_mem_fun<request, uint64_t, &request::by_created>>,
eosio::indexed_by<"bycompleted"_n, eosio::const_mem_fun<request, uint64_t, &request::by_completed>>,
eosio::indexed_by<"bydeclined"_n, eosio::const_mem_fun<request, uint64_t, &request::by_declined>>,
eosio::indexed_by<"bycanceled"_n, eosio::const_mem_fun<request, uint64_t, &request::by_canceled>>,
eosio::indexed_by<"bywarrantyid"_n, eosio::const_mem_fun<request, uint64_t, &request::by_warranty_id>>
// eosio::indexed_by<"byexpired"_n, eosio::const_mem_fun<request, uint64_t, &request::by_expired>>
>
requests_index; /*!< Тип мультииндекса для таблицы обменов */
+1 -13
View File
@@ -1,17 +1,9 @@
#include <eosio/eosio.hpp>
#include <eosio/asset.hpp>
#include <eosio/symbol.hpp>
#include <eosio/system.hpp>
#include <eosio/crypto.hpp>
#include <eosio/transaction.hpp>
#include <eosio/permission.hpp>
#include "consts.hpp"
#include "registry.hpp"
#include "utils.hpp"
#include "drafts.hpp"
#include "shared_document.hpp"
#include "accounts.hpp"
#include "changes.hpp"
#include "coops.hpp"
#include "counts.hpp"
#include "permissions.hpp"
@@ -25,13 +17,9 @@
#include "shared_actions.hpp"
#include "shared_soviet.hpp"
#include "shared_wallet.hpp"
#include "shared_fund.hpp"
#include "shared_gateway.hpp"
#include "shared_ledger.hpp"
#include "shared_approver.hpp"
#include "shared_registrator.hpp"
#include "shared_loan.hpp"
#include "shared_branch.hpp"
#include "shared_marketplace.hpp"
#include "approves.hpp"
#include "debts.hpp"
+13 -20
View File
@@ -93,7 +93,7 @@ static constexpr eosio::name _result_action = "result"_n;
//program_types
static constexpr eosio::name _wallet_program = "wallet"_n;
static constexpr eosio::name _marketplace_program = "marketplace"_n;
static constexpr eosio::name _sosedi_program = "cooplace"_n;
static constexpr eosio::name _source_program = "source"_n;
static constexpr eosio::name _capital_program = "capital"_n;
@@ -104,8 +104,7 @@ static const std::set<eosio::name> soviet_actions = {
//MEET
"creategm"_n,//предложение повестки планового общего собрание
"completegm"_n, //решение общего собрания пайщиков
"ballot"_n, //бюллетень участника общего собрания пайщиков
"gmnotify"_n, //уведомление участника общего собрания пайщиков
//CAPITAL
"capitalinvst"_n, //заявление на инвестиции по договору УХД
"createresult"_n, //клайм прироста капитализации из задания
@@ -114,21 +113,20 @@ static const std::set<eosio::name> soviet_actions = {
"capwthdrprog"_n, //произвести возврат накопленных членских взносов по программе на капиталиста
"capwthdrproj"_n, //произвести возврат накопленных членских взносов по проекту на актора
"capwthdrres"_n, //произвести возврат из задания
//WALLET
"createwthd"_n, //создать заявление на возврат паевого взноса
//LEDGER
"ledgerwthd"_n, //заявление на списание со счета через ledger
//MARKETPLACE
"authoffs2c"_n, //заявление на взнос имуществом
"authoffc2r"_n, //заявление на возврат паевого взноса имуществом
};
static const std::set<eosio::name> gateway_income_actions = {
"deposit"_n, //паевой взнос по ЦПП Кошелёк
};
static const std::set<eosio::name> gateway_outcome_actions = {
"withdraw"_n, //возврат паевого взноса по ЦПП Кошелёк
};
//program_ids
static constexpr uint64_t _wallet_program_id = 1;
static constexpr uint64_t _marketplace_program_id = 2;
static constexpr uint64_t _sosedi_program_id = 2;
static constexpr uint64_t _capital_program_id = 3;
static constexpr int64_t REWARD_SCALE = 100000000; ///< Масштабный коэффициент для вознаграждений (1e8).
@@ -150,10 +148,7 @@ static constexpr int64_t REWARD_SCALE = 100000000; ///< Масштабный к
#define CAPITAL "capital"
#define WALLET "wallet"
#define LOAN "loan"
#define LEDGER "ledger"
#define MEET "meet"
#define LEDGER "ledger"
/**
* @ingroup public_consts
* @{
@@ -174,7 +169,6 @@ static constexpr int64_t REWARD_SCALE = 100000000; ///< Масштабный к
static constexpr eosio::name _branch = "branch"_n;
static constexpr eosio::name _loan = "loan"_n;
static constexpr eosio::name _meet = "meet"_n;
static constexpr eosio::name _ledger = "ledger"_n;
static constexpr eosio::name _power_account = "eosio.power"_n;
static constexpr eosio::name _saving_account = "eosio.saving"_n;
@@ -199,8 +193,7 @@ static constexpr int64_t REWARD_SCALE = 100000000; ///< Масштабный к
"eosio.msig"_n,
"eosio.wrap"_n,
"eosio.power"_n,
"eosio.saving"_n,
"ledger"_n
"eosio.saving"_n
// Добавьте остальные стандартные или пользовательские контракты по необходимости
};
+1 -1
View File
@@ -165,7 +165,7 @@ struct [[eosio::table, eosio::contract(SOVIET)]] participant {
eosio::name status; //accepted | blocked
bool is_initial = true; ///< флаг, указывающий, внесен ли регистрационный взнос.
bool is_minimum = true; ///< флаг, указывающий, внесен ли минимальный паевой взнос.
bool is_minimum = true; ///< флаг, указывающий, внесен ли минимальный паевый взнос.
bool has_vote = true; ///< флаг, указывающий, имеет ли член право голоса.
eosio::binary_extension<eosio::name> type; ///< individual | entrepreneur | organization
+3 -19
View File
@@ -30,8 +30,6 @@ struct document2 {
// Версия стандарта документа
std::string version;
// uint64_t template_id; ///< было бы удобно но этого нет
// Хэши
eosio::checksum256 hash; // общий хэш (doc_hash + meta_hash)
eosio::checksum256 doc_hash; // хэш содержимого документа
@@ -44,27 +42,13 @@ struct document2 {
std::vector<signature_info> signatures;
};
void verify_document_or_fail(
const document2 &doc,
const std::vector<eosio::name>& required_signers = {}
) {
void verify_document_or_fail(const document2 &doc)
{
for (const auto &sig : doc.signatures) {
// Проверка завершится прерыванием, если восстановление подписи провалится
assert_recover_key(sig.signed_hash, sig.signature, sig.public_key);
}
if (!required_signers.empty()) {
for (const auto& required : required_signers) {
bool found = false;
for (const auto& sig : doc.signatures) {
if (sig.signer == required) {
found = true;
break;
}
}
eosio::check(found, ("Не найдена подпись от обязательного подписанта: " + required.to_string()).c_str());
}
}
}
};
bool is_empty_document(const document2 &doc)
{
+5 -5
View File
@@ -6,7 +6,7 @@
struct [[eosio::table, eosio::contract(SOVIET)]] program {
uint64_t id; /*!< идентификатор обмена */
uint64_t draft_id; ///< Ссылка на шаблон условий
eosio::name program_type; /*!< тип кооперативной программы (wallet | market | capital) */
eosio::name program_type; /*!< тип кооперативной программы (wallet | market | ...) */
eosio::name coopname; /*!< имя аккаунта кооператива */
bool is_active;
@@ -17,9 +17,9 @@ struct [[eosio::table, eosio::contract(SOVIET)]] program {
std::string images;
std::string meta;
eosio::name calculation_type; /*!< LEGACY тип настройки платежей по программе ( absolute | relative | free ) */
uint64_t membership_percent_fee; /*!< LEGACY процент комиссии со взноса */
eosio::asset fixed_membership_contribution; /*!< LEGACY Членский взнос */
eosio::name calculation_type; /*!< тип настройки платежей по программе ( absolute | relative | free ) */
uint64_t membership_percent_fee; /*!< процент комиссии со взноса */
eosio::asset fixed_membership_contribution; /*!< Членский взнос */
eosio::time_point_sec start_at; /*!< Время открытия */
eosio::time_point_sec expired_at; /*!< Временное ограничение */
@@ -120,7 +120,7 @@ struct ProgramInfo {
static const std::map<eosio::name, ProgramInfo> program_map = {
{_wallet_program, {1, 1}}, // program_id = 1, draft_id = 1
{_marketplace_program, {2, 699}}, // program_id = 2, draft_id = 699
{_sosedi_program, {2, 699}}, // program_id = 2, draft_id = 699
{_source_program, {3, 0}}, // program_id = 3, draft_id = 0
{_capital_program, {4, 1000}} // program_id = 4, draft_id = 1000
};
@@ -1,53 +0,0 @@
#pragma once
namespace Branch {
/**
* @brief Получает количество кооперативных участков для данного кооператива
* @param coopname Имя кооператива
* @return Количество участков кооператива
*/
uint64_t get_branch_count(eosio::name coopname) {
branchstat_index stat(_branch, _branch.value);
auto st = stat.find(coopname.value);
if (st == stat.end()) {
return 0;
}
return st->count;
}
/**
* @brief Проверяет, является ли пользователь уполномоченным лицом (trustee) кооперативного участка
* @param coopname Имя кооператива
* @param username Имя пользователя для проверки
* @return true если пользователь является уполномоченным, false в противном случае
*/
bool is_trustee(eosio::name coopname, eosio::name username) {
branch_index branches(_branch, coopname.value);
auto by_trustee_index = branches.get_index<"bytrustee"_n>();
auto trustee_itr = by_trustee_index.find(username.value);
return trustee_itr != by_trustee_index.end();
}
/**
* @brief Проверяет, является ли пользователь доверенным лицом (trusted) в указанном участке кооператива
* @param coopname Имя кооператива
* @param braname Имя участка
* @param username Имя пользователя для проверки
* @return true если пользователь является доверенным, false в противном случае
*/
bool is_trusted(eosio::name coopname, eosio::name braname, eosio::name username) {
branch_index branches(_branch, coopname.value);
auto branch_itr = branches.find(braname.value);
if (branch_itr == branches.end()) {
return false;
}
return branch_itr->is_account_in_trusted(username);
}
}
@@ -1,62 +0,0 @@
namespace Document {
/**
* @brief Проверяет, что переданный документ соответствует ожидаемому шаблону
* @param document Документ для проверки
* @param expected_registry_id Ожидаемый идентификатор шаблона
*/
inline void validate_registry_id(document2 document, uint64_t expected_registry_id) {
uint64_t registry_id = extract_registry_id_from_meta(document.meta);
eosio::check(registry_id == expected_registry_id, "Передан неверный шаблон документа");
}
/*!
* \brief Структура именованного документа
*/
struct named_document {
name name; /*!< имя документа (до 12 символов) */
document2 document; /*!< сам документ */
};
/**
* @brief Добавить документ с проверкой длины имени
* @param docs Вектор документов
* @param name Имя документа
* @param doc Документ
*/
inline void add_document(std::vector<named_document>& docs, const name& name, const document2& doc) {
docs.push_back({name, doc});
}
/**
* @brief Найти документ по имени
* @param docs Вектор документов
* @param name Имя для поиска
* @param found_doc Найденный документ (выходной параметр)
* @return true если документ найден
*/
inline bool find_document(const std::vector<named_document>& docs, const name& name, document2& found_doc) {
for (const auto& nd : docs) {
if (nd.name == name) {
found_doc = nd.document;
return true;
}
}
return false;
}
/**
* @brief Проверить наличие документа
* @param docs Вектор документов
* @param name Имя для поиска
* @return true если документ найден
*/
inline bool has_document(const std::vector<named_document>& docs, const name& name) {
for (const auto& nd : docs) {
if (nd.name == name) {
return true;
}
}
return false;
}
}
@@ -1,3 +0,0 @@
namespace Fund {
}
@@ -3,16 +3,6 @@
using createoutpay_interface = void(CREATEOUTPAY_SIGNATURE);
namespace Gateway {
static const std::set<eosio::name> gateway_income_actions = {
"deposit"_n, //паевой взнос по ЦПП Кошелёк
};
static const std::set<eosio::name> gateway_outcome_actions = {
"withdraw"_n, //возврат паевого взноса по ЦПП Кошелёк
};
inline eosio::name get_valid_income_action(const eosio::name& action) {
eosio::check(gateway_income_actions.contains(action), "Недопустимое имя действия");
return action;
@@ -1,369 +0,0 @@
#pragma once
/**
* @brief Структура бухгалтерского счета
*/
struct [[eosio::table, eosio::contract(LEDGER)]] laccount {
uint64_t id; ///< Идентификатор счета
std::string name; ///< Название счета
eosio::asset available; ///< Доступные средства (было allocation)
eosio::asset blocked; ///< Заблокированные средства
eosio::asset writeoff; ///< Списанные средства
uint64_t primary_key() const { return id; }
/**
* @brief Проверяет, пуст ли счет (все балансы равны нулю)
*/
bool is_empty() const {
return available.amount == 0 && blocked.amount == 0 && writeoff.amount == 0;
}
/**
* @brief Получает общий баланс счета
*/
eosio::asset get_total() const {
return available + blocked + writeoff;
}
/**
* @brief Получает всего доступных + заблокированных средств (без списанных)
*/
eosio::asset get_balance() const {
return available + blocked;
}
};
/**
* @brief Типы индексов для таблиц ledger
*/
typedef eosio::multi_index<"accounts"_n, laccount> laccounts_index;
/**
* @brief Структура для операций ожидающих решения совета
*/
struct [[eosio::table, eosio::contract(LEDGER)]] writeoff_op {
uint64_t id; ///< Идентификатор операции
eosio::name coopname; ///< Имя кооператива
eosio::name username; ///< Инициатор операции
uint64_t account_id; ///< Счет для операции
eosio::asset quantity; ///< Сумма операции
std::string reason; ///< Обоснование
document2 document; ///< Документ обоснования
checksum256 writeoff_hash; ///< Хэш операции списания
eosio::name status; ///< Статус: pending, approved, declined, paid
uint64_t primary_key() const { return id; }
uint64_t by_coop() const { return coopname.value; }
checksum256 by_hash() const { return writeoff_hash; }
};
typedef eosio::multi_index<"writeoffs"_n, writeoff_op,
eosio::indexed_by<"bycoop"_n, eosio::const_mem_fun<writeoff_op, uint64_t, &writeoff_op::by_coop>>,
eosio::indexed_by<"byhash"_n, eosio::const_mem_fun<writeoff_op, checksum256, &writeoff_op::by_hash>>
> writeoffs_index;
/**
* @brief Класс Ledger для интеграции с ledger контрактом
*/
class Ledger {
public:
/**
* @brief Валидные действия ledger для интеграции с другими контрактами
*/
static const std::set<eosio::name> ledger_actions;
/**
* @brief Проверка корректности действия ledger
*/
static eosio::name get_valid_ledger_action(const eosio::name& action);
/**
* @brief Проверка корректности символа валюты для операций ledger
*/
static void check_ledger_symbol(const eosio::asset& amount);
/**
* @brief Проверка положительности суммы для операций ledger
*/
static void check_positive_amount(const eosio::asset& amount);
// Основные операции
static void add(eosio::name actor, eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
static void sub(eosio::name actor, eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
static void transfer(eosio::name actor, eosio::name coopname, uint64_t from_account_id, uint64_t to_account_id, eosio::asset quantity, std::string comment);
// Операции блокировки/разблокировки
static void block(eosio::name actor, eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
static void unblock(eosio::name actor, eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
// Атомарные операции списания
static void writeoff(eosio::name actor, eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
static void writeoffcnsl(eosio::name actor, eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment);
// Специализированные методы для членских взносов
static void add_membership_fee(eosio::name actor, eosio::name coopname, eosio::asset quantity, std::string comment);
static void sub_membership_fee(eosio::name actor, eosio::name coopname, eosio::asset quantity, std::string comment);
static void block_membership_fee(eosio::name actor, eosio::name coopname, eosio::asset quantity, std::string comment);
static void unblock_membership_fee(eosio::name actor, eosio::name coopname, eosio::asset quantity, std::string comment);
/**
* @brief Получает операцию списания по хэшу
*/
static std::optional<writeoff_op> get_writeoff_by_hash(const checksum256 &writeoff_hash);
/**
* @brief Получает название счета по его ID из ACCOUNT_MAP
*/
static std::string get_account_name_by_id(uint64_t account_id);
/**
* @brief Константы счетов
*/
struct accounts {
// Денежные средства
static constexpr uint64_t CASH = 50; // Касса
static constexpr uint64_t BANK_ACCOUNT = 51; // Расчетный счет
// Расчеты с пайщиками
static constexpr uint64_t MEMBER_SETTLEMENTS = 75; // Расчеты с пайщиками по внесению/возврату паевых взносов
static constexpr uint64_t MEMBER_DEBT = 751; // Задолженность пайщиков по внесению взносов в паевой фонд
static constexpr uint64_t INCOME_ACCRUALS = 752; // Начисление доходов участникам от предпринимательской деятельности ПО (кооперативные выплаты)
// Паевой фонд
static constexpr uint64_t SHARE_FUND = 80; // Паевой фонд (складочный капитал)
// Расчеты с пайщиками (дебиторами и кредиторами)
static constexpr uint64_t MEMBER_FEES = 761; // По членским взносам
static constexpr uint64_t PROPERTY_TRANSFER = 762; // По передаче имущества для некоммерческой деятельности
static constexpr uint64_t OTHER_SETTLEMENTS = 763; // Другие расчеты
// Расчеты по займам
static constexpr uint64_t LOANS_ISSUED = 583; // Расчеты по выданным займам
static constexpr uint64_t LOAN_INTEREST = 911; // Внесение процентов за пользование займами
// Финансовые вложения из средств ПО
static constexpr uint64_t FINANCIAL_INVESTMENTS = 58; // Финансовые вложения из средств ПО
static constexpr uint64_t SHARES_AND_STAKES = 581; // Доли, паи и акции в организациях, где участвует ПО
static constexpr uint64_t SECURITIES = 582; // Облигации (государственные ценные бумаги)
// Расчеты с дебиторами и кредиторами
static constexpr uint64_t DEBTORS_CREDITORS = 76; // Расчеты с дебиторами и кредиторами
// Запасы, затраты, расчеты, собственные средства
static constexpr uint64_t FIXED_ASSETS = 1; // Основные средства
static constexpr uint64_t INTANGIBLE_ASSETS = 4; // Нематериальные активы
static constexpr uint64_t MATERIALS_GOODS = 10; // Материалы, товары
static constexpr uint64_t MAIN_PRODUCTION = 20; // Основное производство
static constexpr uint64_t NON_PROFIT_ACTIVITY = 201; // Некоммерческая деятельность
static constexpr uint64_t GENERAL_EXPENSES = 26; // Общехозяйственные расходы (содержание ПО)
static constexpr uint64_t RESERVES = 63; // Резервы по сомнительным долгам
static constexpr uint64_t LONG_TERM_LOANS = 67; // Расчеты по долгосрочным кредитам и займам
static constexpr uint64_t TAXES_FEES = 68; // Расчеты с бюджетом по налогам и сборам
static constexpr uint64_t SOCIAL_INSURANCE = 69; // Расчеты по социальному страхованию и обеспечению
static constexpr uint64_t SALARY = 70; // Заработная плата
static constexpr uint64_t ACCOUNTABLE_PERSONS = 71; // Расчеты с подотчетными лицами
static constexpr uint64_t ADDITIONAL_CAPITAL = 83; // Добавочный капитал
static constexpr uint64_t FUNDS_PO_1 = 831; // Фонды ПО (вариант пополнения фондов ПО)
static constexpr uint64_t CURRENT_YEAR_PROFIT = 841; // Нераспределенная прибыль (убыток) отчетного года
static constexpr uint64_t PREVIOUS_YEARS_PROFIT = 842; // Нераспределенная прибыль (непокрытый убыток) прошлых лет
static constexpr uint64_t FUNDS_PO_2 = 843; // Фонды ПО (вариант пополнения фондов ПО)
static constexpr uint64_t UNDISTRIBUTED_PROFIT = 84; // Нераспределенная прибыль (непокрытый убыток)
static constexpr uint64_t TARGET_RECEIPTS = 86; // Целевые поступления
static constexpr uint64_t ENTRANCE_FEES = 861; // Вступительные взносы
static constexpr uint64_t RESERVE_FUND = 862; // Резервный фонд
static constexpr uint64_t INDIVISIBLE_FUND = 863; // Неделимый фонд
static constexpr uint64_t ECONOMIC_ACTIVITY_FUND = 864; // Фонд обеспечения хозяйственной деятельности
static constexpr uint64_t MUTUAL_SECURITY_FUND = 865; // Фонд взаимного обеспечения
static constexpr uint64_t DEVELOPMENT_FUND = 866; // Фонд развития потребительской кооперации
static constexpr uint64_t OTHER_INCOME_EXPENSES = 91; // Прочие доходы и расходы
static constexpr uint64_t FUTURE_EXPENSES_RESERVE = 96; // Резерв предстоящих расходов
static constexpr uint64_t FUTURE_INCOME = 98; // Доходы будущих периодов
static constexpr uint64_t FREE_RECEIPT = 981; // Безвозмездное получение имущества
};
};
/**
* @brief Карта счетов для инициализации бухгалтерской книги
* СОХРАНЯЕМ - используется для получения названий при автосоздании счетов
*/
static const std::vector<std::tuple<uint64_t, std::string>> ACCOUNT_MAP = {
{Ledger::accounts::FIXED_ASSETS, "Основные средства"},
{Ledger::accounts::INTANGIBLE_ASSETS, "Нематериальные активы"},
{Ledger::accounts::MATERIALS_GOODS, "Материалы, товары"},
{Ledger::accounts::MAIN_PRODUCTION, "Основное производство"},
{Ledger::accounts::NON_PROFIT_ACTIVITY, "Некоммерческая деятельность"},
{Ledger::accounts::GENERAL_EXPENSES, "Общехозяйственные расходы"},
{Ledger::accounts::CASH, "Касса"},
{Ledger::accounts::BANK_ACCOUNT, "Расчетный счет"},
{Ledger::accounts::RESERVES, "Резервы по сомнительным долгам"},
{Ledger::accounts::LONG_TERM_LOANS, "Расчеты по долгосрочным кредитам и займам"},
{Ledger::accounts::TAXES_FEES, "Расчеты с бюджетом по налогам и сборам"},
{Ledger::accounts::SOCIAL_INSURANCE, "Расчеты по социальному страхованию и обеспечению"},
{Ledger::accounts::SALARY, "Заработная плата"},
{Ledger::accounts::ACCOUNTABLE_PERSONS, "Расчеты с подотчетными лицами"},
{Ledger::accounts::MEMBER_SETTLEMENTS, "Расчеты с пайщиками по внесению/возврату паевых взносов"},
{Ledger::accounts::SHARE_FUND, "Паевой фонд (складочный капитал)"},
{Ledger::accounts::ADDITIONAL_CAPITAL, "Добавочный капитал"},
{Ledger::accounts::TARGET_RECEIPTS, "Целевые поступления"},
{Ledger::accounts::OTHER_INCOME_EXPENSES, "Прочие доходы и расходы"},
{Ledger::accounts::FUTURE_EXPENSES_RESERVE, "Резерв предстоящих расходов"},
{Ledger::accounts::FUTURE_INCOME, "Доходы будущих периодов"},
{Ledger::accounts::SHARES_AND_STAKES, "Доли, паи и акции в организациях"},
{Ledger::accounts::SECURITIES, "Облигации (государственные ценные бумаги)"},
{Ledger::accounts::LOANS_ISSUED, "Расчеты по выданным займам"},
{Ledger::accounts::MEMBER_DEBT, "Задолженность пайщиков по внесению взносов в паевой фонд"},
{Ledger::accounts::INCOME_ACCRUALS, "Начисление доходов участникам от предпринимательской деятельности ПО"},
{Ledger::accounts::MEMBER_FEES, "По членским взносам"},
{Ledger::accounts::PROPERTY_TRANSFER, "По передаче имущества для некоммерческой деятельности"},
{Ledger::accounts::OTHER_SETTLEMENTS, "Другие расчеты"},
{Ledger::accounts::DEBTORS_CREDITORS, "Расчеты с дебиторами и кредиторами"},
{Ledger::accounts::ENTRANCE_FEES, "Вступительные взносы"},
{Ledger::accounts::FUNDS_PO_1, "Фонды ПО (вариант пополнения фондов ПО)"},
{Ledger::accounts::CURRENT_YEAR_PROFIT, "Нераспределенная прибыль (убыток) отчетного года"},
{Ledger::accounts::PREVIOUS_YEARS_PROFIT, "Нераспределенная прибыль (непокрытый убыток) прошлых лет"},
{Ledger::accounts::FUNDS_PO_2, "Фонды ПО (вариант пополнения фондов ПО)"},
{Ledger::accounts::UNDISTRIBUTED_PROFIT, "Нераспределенная прибыль (непокрытый убыток)"},
{Ledger::accounts::RESERVE_FUND, "Резервный фонд"},
{Ledger::accounts::INDIVISIBLE_FUND, "Неделимый фонд"},
{Ledger::accounts::ECONOMIC_ACTIVITY_FUND, "Фонд обеспечения хозяйственной деятельности"},
{Ledger::accounts::MUTUAL_SECURITY_FUND, "Фонд взаимного обеспечения"},
{Ledger::accounts::DEVELOPMENT_FUND, "Фонд развития потребительской кооперации"},
{Ledger::accounts::LOAN_INTEREST, "Внесение процентов за пользование займами"},
{Ledger::accounts::FINANCIAL_INVESTMENTS, "Финансовые вложения из средств ПО"},
{Ledger::accounts::FREE_RECEIPT, "Безвозмездное получение имущества"}
};
// Реализация статических методов класса Ledger
const std::set<eosio::name> Ledger::ledger_actions = {
"add"_n, // пополнение счета
"sub"_n, // списание со счета
"transfer"_n, // перевод между счетами
"block"_n, // блокировка средств
"unblock"_n, // разблокировка средств
"writeoff"_n, // атомарное списание средств
"writeoffcnsl"_n, // атомарная отмена списания
"create"_n, // создание заявления на списание
"auth"_n, // авторизация списания
"complete"_n, // завершение операции
"decline"_n // отклонение операции
};
inline eosio::name Ledger::get_valid_ledger_action(const eosio::name& action) {
eosio::check(ledger_actions.contains(action), "Недопустимое имя действия ledger");
return action;
}
inline void Ledger::check_ledger_symbol(const eosio::asset& amount) {
eosio::check(amount.symbol == _root_govern_symbol, "Некорректный символ валюты для операций ledger");
}
inline void Ledger::check_positive_amount(const eosio::asset& amount) {
eosio::check(amount.is_valid(), "Некорректная сумма");
eosio::check(amount.amount > 0, "Сумма должна быть положительной");
}
inline std::string Ledger::get_account_name_by_id(uint64_t account_id) {
for (const auto& account_data : ACCOUNT_MAP) {
if (std::get<0>(account_data) == account_id) {
return std::get<1>(account_data);
}
}
return "Неизвестный счет";
}
inline void Ledger::add(eosio::name actor, eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
eosio::action(
eosio::permission_level{actor, "active"_n},
_ledger,
get_valid_ledger_action("add"_n),
std::make_tuple(coopname, account_id, quantity, comment)
).send();
}
inline void Ledger::sub(eosio::name actor, eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
eosio::action(
eosio::permission_level{actor, "active"_n},
_ledger,
get_valid_ledger_action("sub"_n),
std::make_tuple(coopname, account_id, quantity, comment)
).send();
}
inline void Ledger::transfer(eosio::name actor, eosio::name coopname, uint64_t from_account_id, uint64_t to_account_id, eosio::asset quantity, std::string comment) {
eosio::action(
eosio::permission_level{actor, "active"_n},
_ledger,
get_valid_ledger_action("transfer"_n),
std::make_tuple(coopname, from_account_id, to_account_id, quantity, comment)
).send();
}
inline void Ledger::block(eosio::name actor, eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
eosio::action(
eosio::permission_level{actor, "active"_n},
_ledger,
get_valid_ledger_action("block"_n),
std::make_tuple(coopname, account_id, quantity, comment)
).send();
}
inline void Ledger::unblock(eosio::name actor, eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
eosio::action(
eosio::permission_level{actor, "active"_n},
_ledger,
get_valid_ledger_action("unblock"_n),
std::make_tuple(coopname, account_id, quantity, comment)
).send();
}
inline void Ledger::writeoff(eosio::name actor, eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
eosio::action(
eosio::permission_level{actor, "active"_n},
_ledger,
get_valid_ledger_action("writeoff"_n),
std::make_tuple(coopname, account_id, quantity, comment)
).send();
}
inline void Ledger::writeoffcnsl(eosio::name actor, eosio::name coopname, uint64_t account_id, eosio::asset quantity, std::string comment) {
eosio::action(
eosio::permission_level{actor, "active"_n},
_ledger,
get_valid_ledger_action("writeoffcnsl"_n),
std::make_tuple(coopname, account_id, quantity, comment)
).send();
}
inline void Ledger::add_membership_fee(eosio::name actor, eosio::name coopname, eosio::asset quantity, std::string comment) {
add(actor, coopname, accounts::TARGET_RECEIPTS, quantity, comment);
}
inline void Ledger::sub_membership_fee(eosio::name actor, eosio::name coopname, eosio::asset quantity, std::string comment) {
sub(actor, coopname, accounts::TARGET_RECEIPTS, quantity, comment);
}
inline void Ledger::block_membership_fee(eosio::name actor, eosio::name coopname, eosio::asset quantity, std::string comment) {
block(actor, coopname, accounts::TARGET_RECEIPTS, quantity, comment);
}
inline void Ledger::unblock_membership_fee(eosio::name actor, eosio::name coopname, eosio::asset quantity, std::string comment) {
unblock(actor, coopname, accounts::TARGET_RECEIPTS, quantity, comment);
}
inline std::optional<writeoff_op> Ledger::get_writeoff_by_hash(const checksum256 &writeoff_hash) {
writeoffs_index writeoffs(_ledger, _ledger.value);
auto hash_idx = writeoffs.get_index<"byhash"_n>();
auto op_iter = hash_idx.find(writeoff_hash);
if (op_iter == hash_idx.end()) {
return std::nullopt;
}
return *op_iter;
}
@@ -1,321 +0,0 @@
#pragma once
// Сигнатуры для callback действий marketplace
#define AUTH_SIGNATURE eosio::name coopname, checksum256 request_hash, document2 authorization
using auth_interface = void(AUTH_SIGNATURE);
namespace Marketplace {
/**
* @brief Сегмент поставки - унифицированная структура для работы с документами
* @ingroup public_tables
*
* Сегмент представляет собой путь поставки от пайщика в кооператив или из кооператива до пайщика
*
* Типы сегментов:
* - "contribute" - сегмент поставки имущества в кооператив
* - "return" - сегмент возврата имущества из кооператива
* - "wreturn" - сегмент возврата по гарантии (от заказчика в кооператив)
* - "wsupply" - сегмент выдачи по гарантии (из кооператива поставщику)
*/
struct [[eosio::table, eosio::contract(MARKETPLACE)]] segment {
uint64_t id; /*!< идентификатор сегмента */
uint64_t request_id; /*!< идентификатор заявки, к которой относится сегмент */
eosio::name type; /*!< тип сегмента: "contribute", "return", "wreturn", "wsupply" */
eosio::name status; /*!< статус сегмента */
document2 convert_in; /*!< заявление на конвертацию из кошелька */
document2 statement; /*!< заявление/документ на взнос или возврат */
uint64_t decision_id; /*!< идентификатор решения */
document2 authorization; /*!< документ авторизации */
document2 act1; /*!< акт приёма-передачи */
document2 act2; /*!< акт приёма-передачи */
document2 convert_out; /*!< заявление на конвертацию в кошелек */
// Документы транспортировки
document2 transport_act_1; /*!< акт товарно-транспортной накладной - передача имущества на транспортировку */
document2 transport_act_2; /*!< акт товарно-транспортной накладной - прием имущества водителем */
document2 transport_act_3; /*!< акт товарно-транспортной накладной - доставка до КУ получателя */
document2 transport_act_4; /*!< акт товарно-транспортной накладной - прием имущества председателем КУ */
eosio::name coopactor; /*!< представитель кооператива, который действует в этом сегменте */
eosio::name username; /*!< пользователь, который действует в этом сегменте */
eosio::name driver_username; /*!< водитель-пайщик, который принимает имущество на транспортировку */
eosio::name receive_from_driver_coopactor; /*!< председатель КУ, который принимает имущество от водителя */
eosio::time_point_sec created_at;
eosio::time_point_sec updated_at;
uint64_t primary_key() const { return id; }
uint64_t by_request() const { return request_id; }
uint64_t by_type() const { return type.value; }
uint64_t by_status() const { return status.value; }
};
typedef eosio::multi_index<
"segments"_n, segment,
eosio::indexed_by<"byrequest"_n, eosio::const_mem_fun<segment, uint64_t, &segment::by_request>>,
eosio::indexed_by<"bytype"_n, eosio::const_mem_fun<segment, uint64_t, &segment::by_type>>,
eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun<segment, uint64_t, &segment::by_status>>
> segments_index;
/**
* @brief Перевозка имущества - объединение нескольких заявок в одну транспортную накладную
* @ingroup public_tables
*
* Перевозка представляет собой процесс транспортировки множества единиц имущества
* в рамках одной товарно-транспортной накладной от одного кооперативного участка
* к одному или нескольким получателям.
*
* Статусы перевозки:
* - "loading" - идет загрузка товаров в перевозку
* - "transit" - перевозка в пути (товары загружены и подписана накладная)
* - "arrived" - перевозка прибыла, ожидает подтверждения получателя
* - "completed" - все товары из перевозки переданы получателям
*/
struct [[eosio::table, eosio::contract(MARKETPLACE)]] shipment {
uint64_t id; /*!< идентификатор перевозки */
checksum256 hash; /*!< внешний идентификатор перевозки */
eosio::name coopname; /*!< имя кооператива */
eosio::name driver_username; /*!< водитель-пайщик */
eosio::name source_braname; /*!< КУ отправителя */
eosio::name destination_braname; /*!< КУ назначения (может быть промежуточный) */
eosio::name status; /*!< статус перевозки */
std::vector<checksum256> request_hashes; /*!< массив хэшей заявок в перевозке */
std::vector<Document::named_document> documents; /*!< именованные документы перевозки */
eosio::time_point_sec created_at; /*!< время создания перевозки */
eosio::time_point_sec loaded_at; /*!< время завершения загрузки */
eosio::time_point_sec delivered_at; /*!< время доставки */
eosio::time_point_sec completed_at; /*!< время завершения перевозки */
uint64_t primary_key() const { return id; }
checksum256 by_hash() const { return hash; }
uint64_t by_coop() const { return coopname.value; }
uint64_t by_driver() const { return driver_username.value; }
uint64_t by_source() const { return source_braname.value; }
uint64_t by_destination() const { return destination_braname.value; }
uint64_t by_status() const { return status.value; }
uint64_t by_created() const { return created_at.sec_since_epoch(); }
};
typedef eosio::multi_index<
"shipments"_n, shipment,
eosio::indexed_by<"byhash"_n, eosio::const_mem_fun<shipment, checksum256, &shipment::by_hash>>,
eosio::indexed_by<"bycoop"_n, eosio::const_mem_fun<shipment, uint64_t, &shipment::by_coop>>,
eosio::indexed_by<"bydriver"_n, eosio::const_mem_fun<shipment, uint64_t, &shipment::by_driver>>,
eosio::indexed_by<"bysource"_n, eosio::const_mem_fun<shipment, uint64_t, &shipment::by_source>>,
eosio::indexed_by<"bydest"_n, eosio::const_mem_fun<shipment, uint64_t, &shipment::by_destination>>,
eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun<shipment, uint64_t, &shipment::by_status>>,
eosio::indexed_by<"bycreated"_n, eosio::const_mem_fun<shipment, uint64_t, &shipment::by_created>>
> shipments_index;
/**
* @brief Таблица обменов для контракта "marketplace"
* @ingroup public_tables
*/
struct [[eosio::table, eosio::contract(MARKETPLACE)]] request {
uint64_t id; /*!< идентификатор обмена */
checksum256 hash; /*!< хэш заявки */
eosio::name coopname; /*!< имя аккаунта кооператива */
eosio::name type; /*!< тип обмена: orderoffer или offerorder */
eosio::name status; /*!< статус обмена */
eosio::name username; /*!< имя аккаунта владельца заявки */
eosio::name braname; /*!< имя кооперативного участка */
eosio::name warehouse; /*!< имя КУ где хранится имущество */
eosio::name token_contract; /*!< имя контракта токена */
eosio::name receiver_braname; /*!< КУ заказчика для получения товара */
eosio::name supplier_braname; /*!< КУ поставщика для отправки товара */
eosio::asset unit_cost;/*!< себестоимость единицы товара от поставщика */
eosio::asset base_cost; /*!< базовая стоимость заявки */
eosio::asset membership_fee_amount; /*!< членский взнос заказчика */
eosio::asset total_cost; /*!< общая сумма заявки */
uint64_t units; /*!< количество единиц товара */
std::string meta; /*!< метаданные заявки */
eosio::name money_contributor; /*!< имя аккаунта, который вносит средства */
eosio::name product_contributor; /*!< имя аккаунта, который передаёт товар */
std::vector<Document::named_document> documents; /*!< именованные документы заявки */
uint64_t product_lifecycle_secs; // жизненный цикл продукта
uint64_t warranty_period_secs; // гарантийный срок в секундах
eosio::asset cancellation_fee_amount; // сумма штрафа за отмену заявки
eosio::time_point_sec warranty_delay_until;
eosio::time_point_sec deadline_for_receipt;
bool is_warranty_return = false;
uint64_t warranty_return_id;
eosio::time_point_sec created_at;
eosio::time_point_sec accepted_at;
eosio::time_point_sec supplied_at;
eosio::time_point_sec delivered_at;
eosio::time_point_sec received_at;
eosio::time_point_sec completed_at;
eosio::time_point_sec declined_at;
eosio::time_point_sec disputed_at;
eosio::time_point_sec canceled_at;
uint64_t primary_key() const { return id; }
uint64_t by_coop() const {return coopname.value;}
uint64_t by_status() const { return status.value; }
uint64_t by_type() const { return type.value; }
checksum256 by_hash() const { return hash; }
uint64_t by_username() const { return username.value;}
uint64_t by_created() const { return created_at.sec_since_epoch();}
uint64_t by_completed() const { return completed_at.sec_since_epoch();}
uint64_t by_declined() const { return declined_at.sec_since_epoch();}
uint64_t by_canceled() const { return canceled_at.sec_since_epoch();}
uint64_t by_warranty_id() const { return warranty_return_id;}
// Логические геттеры для получения актуальных ролей
eosio::name get_money_contributor() const {
return is_warranty_return ? product_contributor : money_contributor;
}
eosio::name get_product_contributor() const {
return is_warranty_return ? money_contributor : product_contributor;
}
// Геттеры для понимания ролей в контексте текущей операции
eosio::name get_payer() const {
return get_money_contributor();
}
eosio::name get_supplier() const {
return get_product_contributor();
}
// Специальные геттеры для гарантийного возврата
// Возвращают участников в контексте процесса возврата брака
/**
* @brief Получить пайщика, который возвращает товар в гарантийном возврате
* Это тот, кто изначально получил товар и теперь возвращает его как брак
*/
eosio::name get_product_backer() const {
return money_contributor;
}
/**
* @brief Получить поставщика брака в гарантийном возврате
* Это тот, кто изначально поставил товар и теперь получает его обратно как брак
*/
eosio::name get_defective_supplier() const {
return product_contributor;
}
};
typedef eosio::multi_index<
"requests"_n, request,
eosio::indexed_by<"bycoop"_n, eosio::const_mem_fun<request, uint64_t, &request::by_coop>>,
eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun<request, uint64_t, &request::by_status>>,
eosio::indexed_by<"bytype"_n, eosio::const_mem_fun<request, uint64_t, &request::by_type>>,
eosio::indexed_by<"byhash"_n, eosio::const_mem_fun<request, checksum256, &request::by_hash>>,
eosio::indexed_by<"byusername"_n, eosio::const_mem_fun<request, uint64_t, &request::by_username>>,
eosio::indexed_by<"bycreated"_n, eosio::const_mem_fun<request, uint64_t, &request::by_created>>,
eosio::indexed_by<"bycompleted"_n, eosio::const_mem_fun<request, uint64_t, &request::by_completed>>,
eosio::indexed_by<"bydeclined"_n, eosio::const_mem_fun<request, uint64_t, &request::by_declined>>,
eosio::indexed_by<"bycanceled"_n, eosio::const_mem_fun<request, uint64_t, &request::by_canceled>>,
eosio::indexed_by<"bywarrantyid"_n, eosio::const_mem_fun<request, uint64_t, &request::by_warranty_id>>
> requests_index;
static const std::set<eosio::name> marketplace_callback_actions = {
"authoffs2c"_n, // авторизация сегмента contribute для направления OFFER → ORDER
"authoffc2r"_n, // авторизация сегмента return для направления OFFER → ORDER
"authordcont"_n, // авторизация сегмента contribute для направления ORDER → OFFER
"authordret"_n, // авторизация сегмента return для направления ORDER → OFFER
"declineacc"_n, // отклонение принятия заявки
};
inline eosio::name get_valid_marketplace_action(const eosio::name& action) {
eosio::check(marketplace_callback_actions.contains(action), "Недопустимое имя действия marketplace");
return action;
}
// Вспомогательные функции для поиска заявок по хэшу
static std::optional<request> get_request_by_hash(eosio::name coopname, checksum256 request_hash) {
requests_index requests(_marketplace, coopname.value);
auto idx = requests.get_index<"byhash"_n>();
auto req = idx.find(request_hash);
if (req != idx.end()) {
return *req;
}
return std::nullopt;
}
// Вспомогательные функции для поиска перевозок по хэшу
static std::optional<shipment> get_shipment_by_hash(eosio::name coopname, checksum256 shipment_hash) {
shipments_index shipments(_marketplace, coopname.value);
auto idx = shipments.get_index<"byhash"_n>();
auto ship = idx.find(shipment_hash);
if (ship != idx.end()) {
return *ship;
}
return std::nullopt;
}
static request get_request_by_hash_or_fail(eosio::name coopname, checksum256 request_hash, const std::string& error_msg = "Заявка не найдена по хэшу") {
auto request_opt = get_request_by_hash(coopname, request_hash);
eosio::check(request_opt.has_value(), error_msg);
return *request_opt;
}
static shipment get_shipment_by_hash_or_fail(eosio::name coopname, checksum256 shipment_hash, const std::string& error_msg = "Перевозка не найдена по хэшу") {
auto shipment_opt = get_shipment_by_hash(coopname, shipment_hash);
eosio::check(shipment_opt.has_value(), error_msg);
return *shipment_opt;
}
/*!
* \brief Константы для имен документов маркетплейса (максимум 12 символов)
*/
namespace DocumentNames {
static constexpr const name RETURN_STMT = "returnstmt"_n; // заявление на возврат имущества
static constexpr const name CONVERT_FROM = "convertfrom"_n; // заявление на конвертацию из кошелька
static constexpr const name CONVERT_TO = "convertto"_n; // заявление на конвертацию в кошелек
static constexpr const name CONTRIB_STMT = "contribstmt"_n; // заявление на имущественный паевой взнос
static constexpr const name CONTRIB_AUTH = "contribauth"_n; // авторизация взноса
static constexpr const name RETURN_AUTH = "returnauth"_n; // авторизация возврата
static constexpr const name RECEIVE_ACT = "receiveact"_n; // акт получения
static constexpr const name RECEIVE_ACT_CONF = "receiveconf"_n; // подтверждение акта получения
static constexpr const name TRANSPORT1 = "transport1"_n; // акт транспортировки 1 (УСТАРЕВШИЙ)
static constexpr const name TRANSPORT2 = "transport2"_n; // акт транспортировки 2 (УСТАРЕВШИЙ)
static constexpr const name TRANSPORT3 = "transport3"_n; // акт транспортировки 3 (УСТАРЕВШИЙ)
static constexpr const name TRANSPORT4 = "transport4"_n; // акт транспортировки 4 (УСТАРЕВШИЙ)
static constexpr const name SUPPLY_ACT = "supplyact"_n; // акт поставки
static constexpr const name SUPPLY_ACT_CONF = "supplyconf"_n; // подтверждение акта поставки
static constexpr const name SHIPMENT_ACT = "shipmentact"_n; // товарно-транспортная накладная
static constexpr const name DELIVERY_ACT = "deliveryact"_n; // акт доставки (УСТАРЕВШИЙ)
// Документы новой системы перевозок
static constexpr const name SHIPMENT_SEND_ACT = "shsendact"_n; // акт передачи от отправителя
static constexpr const name SHIPMENT_LOADING_ACT = "shloadact"_n; // акт приёма водителем
static constexpr const name SHIPMENT_ARRIVE_ACT = "sharriveact"_n; // акт прибытия/доставки на склад, подписанный водителем
static constexpr const name SHIPMENT_RECV_ACT = "shrecvact"_n; // акт приёма на складе получателем
// Документы гарантийного возврата
static constexpr const name WDISPUTE = "wdispute"_n; // претензия на гарантийный возврат
static constexpr const name WRETURN_AUTH = "wreturnauth"_n; // авторизация гарантийного возврата
static constexpr const name WSUPPLY_AUTH = "wsupplyauth"_n; // авторизация гарантийной выдачи
static constexpr const name WRETURN_ACT = "wreturnact"_n; // акт гарантийного возврата
static constexpr const name WOFFER_ACT = "wofferact"_n; // акт предложения товара поставщику
static constexpr const name WACCEPT_ACT = "wacceptact"_n; // акт принятия/отказа поставщика
}
} // namespace Marketplace
@@ -51,23 +51,4 @@ namespace Registrator {
return *itr;
}
/**
* @brief Получает количество активных пайщиков кооператива
* @param coopname Имя кооператива
* @return uint64_t Количество активных пайщиков
*/
inline uint64_t get_active_participants_count(eosio::name coopname) {
using namespace eosio;
// Получаем информацию о кооперативе
cooperatives2_index cooperatives(_registrator, _registrator.value);
auto coop_itr = cooperatives.find(coopname.value);
if (coop_itr == cooperatives.end() || !coop_itr->is_cooperative || !coop_itr->active_participants_count.has_value()) {
eosio::check(false, "Счетчик пайщиков кооператива не найден");
}
return coop_itr->active_participants_count.value();
}
}
@@ -24,9 +24,6 @@ using newdeclined_interface = void(NEWDECLINED_SIGNATURE);
#define NEWACT_SIGNATURE name coopname, name username, name action, checksum256 package, document2 document
using newact_interface = void(NEWACT_SIGNATURE);
#define NEWLINK_SIGNATURE name coopname, name username, name action, checksum256 package, document2 document
using newlink_interface = void(NEWLINK_SIGNATURE);
#define NEWAGREEMENT_SIGNATURE name coopname, name username, name type, document2 document
using newagreement_interface = void(NEWAGREEMENT_SIGNATURE);
+87 -113
View File
@@ -1,97 +1,12 @@
#define COMPLETEWTHD_SIGNATURE name coopname, checksum256 withdraw_hash
#define DECLINEWTHD_SIGNATURE name coopname, checksum256 withdraw_hash, std::string reason
using completewthd_interface = void(COMPLETEWTHD_SIGNATURE);
using declinewthd_interface = void(DECLINEWTHD_SIGNATURE);
static const std::set<eosio::name> wallet_callback_actions = {
"authwthd"_n, // авторизация возврата паевого взноса
"declinewthd"_n, // отклонение возврата паевого взноса
"completewthd"_n, // завершение возврата паевого взноса
};
namespace Wallet {
class Wallet {
public:
/**
* @ingroup public_tables
* @brief Таблица `deposits` отслеживает депозиты в контракте WALLET.
*/
struct [[eosio::table, eosio::contract(WALLET)]] deposit {
uint64_t id; /*!< Уникальный идентификатор записи ввода */
eosio::name coopname; /*!< Имя аккаунта кооператива, в контексте которого совершается депозит */
eosio::name username; /*!< Имя аккаунта пользователя, совершившего ввод */
checksum256 deposit_hash; ///< Хэш входящего платежа
eosio::asset quantity; /*!< Количество средств во внутренней валюте */
eosio::name status; /*!< Статус ввода */
eosio::time_point_sec created_at = current_time_point(); ///< Время истечения срока давности
uint64_t primary_key() const { return id; } /*!< Возвращает id как первичный ключ */
uint64_t by_username() const { return username.value; } /*!< Индекс по имени пользователя */
checksum256 by_hash() const { return deposit_hash; } /*!< Индекс по хэшу платежа */
uint64_t by_status() const { return status.value; } /*!< Индекс по статусу вводу */
uint64_t by_created() const { return created_at.sec_since_epoch(); }
};
typedef eosio::multi_index<
"deposits"_n, deposit,
eosio::indexed_by<"byhash"_n, eosio::const_mem_fun<deposit, checksum256, &deposit::by_hash>>,
eosio::indexed_by<"byusername"_n, eosio::const_mem_fun<deposit, uint64_t, &deposit::by_username>>,
eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun<deposit, uint64_t, &deposit::by_status>>,
eosio::indexed_by<"bycreated"_n, eosio::const_mem_fun<deposit, uint64_t, &deposit::by_created>>
> deposits_index;
/**
* @ingroup public_tables
* @brief Таблица `withdraws` отслеживает операции вывода средств в контракте WALLET.
*/
struct [[eosio::table, eosio::contract(WALLET)]] withdraw {
uint64_t id; /*!< Уникальный идентификатор записи вывода */
eosio::name username; /*!< Имя пользователя, осуществляющего вывод средств */
eosio::name coopname; /*!< Имя аккаунта кооператива, в рамках которого осуществляется вывод */
checksum256 withdraw_hash; /*!< Идентификатор возврата */
eosio::name status; /*!< Статус операции вывода */
eosio::asset quantity; /*!< Количество средств для вывода во внутренней валюте */
document2 statement; /*!< Заявление на возврат */
document2 approved_statement; /*!< Предварительно-принятое заявление */
document2 authorization; /*!< Решение совета */
eosio::time_point_sec created_at = current_time_point(); ///< Время истечения срока давности
uint64_t primary_key() const { return id; } /*!< Возвращает id как первичный ключ */
checksum256 by_hash() const { return withdraw_hash; } ///< Индекс по хэшу задачи.
uint64_t by_username() const { return username.value; } /*!< Индекс по имени пользователя */
uint64_t by_status() const { return status.value; } /*!< Индекс по статусу операции вывода */
uint64_t by_created() const { return created_at.sec_since_epoch(); }
};
typedef eosio::multi_index<
"withdraws"_n, withdraw,
eosio::indexed_by<"byusername"_n, eosio::const_mem_fun<withdraw, uint64_t, &withdraw::by_username>>,
eosio::indexed_by<"byhash"_n, const_mem_fun<withdraw, checksum256, &withdraw::by_hash>>,
eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun<withdraw, uint64_t, &withdraw::by_status>>,
eosio::indexed_by<"bycreated"_n, eosio::const_mem_fun<withdraw, uint64_t, &withdraw::by_created>>
> withdraws_index; /*!< Мультииндекс для доступа и манипуляции данными таблицы `withdraws` */
static eosio::name get_valid_wallet_action(const eosio::name& action) {
eosio::check(wallet_callback_actions.contains(action), "Недопустимое имя действия wallet");
return action;
}
static void validate_asset(const eosio::asset& amount) {
inline void validate_asset(const eosio::asset& amount) {
check(amount.symbol == _root_govern_symbol, "Invalid token symbol");
check(amount.is_valid(), "Invalid asset");
check(amount.amount > 0, "Amount must be positive");
}
static void add_available_funds(eosio::name contract, eosio::name coopname, eosio::name username, eosio::asset amount, eosio::name program_type, std::string memo) {
inline void add_available_funds(eosio::name contract, eosio::name coopname, eosio::name username, eosio::asset amount, eosio::name program_type, std::string memo) {
auto program = get_program_or_fail(coopname, get_program_id(program_type));
action(
@@ -103,7 +18,7 @@ public:
}
static void sub_available_funds(eosio::name contract, eosio::name coopname, eosio::name username, eosio::asset amount, eosio::name program_type, std::string memo) {
inline void sub_available_funds(eosio::name contract, eosio::name coopname, eosio::name username, eosio::asset amount, eosio::name program_type, std::string memo) {
auto program = get_program_or_fail(coopname, get_program_id(program_type));
action(
@@ -114,7 +29,7 @@ public:
).send();
}
static void add_blocked_funds(eosio::name contract, eosio::name coopname, eosio::name username, eosio::asset amount, eosio::name program_type, std::string memo) {
inline void add_blocked_funds(eosio::name contract, eosio::name coopname, eosio::name username, eosio::asset amount, eosio::name program_type, std::string memo) {
auto program = get_program_or_fail(coopname, get_program_id(program_type));
action(
@@ -132,7 +47,7 @@ public:
).send();
}
static void sub_blocked_funds(eosio::name contract, eosio::name coopname, eosio::name username, eosio::asset amount, eosio::name program_type, std::string memo) {
inline void sub_blocked_funds(eosio::name contract, eosio::name coopname, eosio::name username, eosio::asset amount, eosio::name program_type, std::string memo) {
auto program = get_program_or_fail(coopname, get_program_id(program_type));
action(
@@ -150,7 +65,7 @@ public:
).send();
}
static void block_funds(eosio::name contract, eosio::name coopname, eosio::name username, eosio::asset amount, eosio::name program_type, std::string memo){
inline void block_funds(eosio::name contract, eosio::name coopname, eosio::name username, eosio::asset amount, eosio::name program_type, std::string memo){
auto program = get_program_or_fail(coopname, get_program_id(program_type));
action(
@@ -161,7 +76,7 @@ public:
).send();
}
static void unblock_funds(eosio::name contract, eosio::name coopname, eosio::name username, eosio::asset amount, eosio::name program_type, std::string memo){
inline void unblock_funds(eosio::name contract, eosio::name coopname, eosio::name username, eosio::asset amount, eosio::name program_type, std::string memo){
auto program = get_program_or_fail(coopname, get_program_id(program_type));
action(
@@ -171,10 +86,17 @@ public:
std::make_tuple(coopname, username, program.id, amount, memo)
).send();
}
inline void pay_membership_fee(name contract, name coopname, name username, name amount, eosio::name program_type, std::string memo) {
auto program = get_program_or_fail(coopname, get_program_id(program_type));
static void pay_membership_fee(name contract, name coopname, name username, eosio::asset amount, uint64_t program_id, std::string memo) {
auto program = get_program_or_fail(coopname, program_id);
action(
permission_level{ contract, "active"_n },
_soviet,
"subbal"_n,
std::make_tuple(coopname, username, program.id, amount, false, memo)
).send();
action(
permission_level{ contract, "active"_n },
_soviet,
@@ -184,20 +106,37 @@ public:
}
static void unpay_membership_fee(name contract, name coopname, name username, eosio::asset amount, uint64_t program_id, std::string memo) {
auto program = get_program_or_fail(coopname, program_id);
/**
* @ingroup public_tables
* @brief Таблица `deposits` отслеживает депозиты в контракте WALLET.
*/
struct [[eosio::table, eosio::contract(WALLET)]] deposit {
uint64_t id; /*!< Уникальный идентификатор записи ввода */
eosio::name coopname; /*!< Имя аккаунта кооператива, в контексте которого совершается депозит */
eosio::name username; /*!< Имя аккаунта пользователя, совершившего ввод */
checksum256 deposit_hash; ///< Хэш входящего платежа
eosio::asset quantity; /*!< Количество средств во внутренней валюте */
eosio::name status; /*!< Статус ввода */
action(
permission_level{ contract, "active"_n },
_soviet,
"submemberfee"_n,
std::make_tuple(coopname, username, program.id, amount, memo)
).send();
}
eosio::time_point_sec created_at = current_time_point(); ///< Время истечения срока давности
uint64_t primary_key() const { return id; } /*!< Возвращает id как первичный ключ */
uint64_t by_username() const { return username.value; } /*!< Индекс по имени пользователя */
checksum256 by_hash() const { return deposit_hash; } /*!< Индекс по хэшу платежа */
uint64_t by_status() const { return status.value; } /*!< Индекс по статусу вводу */
uint64_t by_created() const { return created_at.sec_since_epoch(); }
};
static std::optional<deposit> get_deposit(eosio::name coopname, const checksum256 &hash) {
typedef eosio::multi_index<
"deposits"_n, deposit,
eosio::indexed_by<"byhash"_n, eosio::const_mem_fun<deposit, checksum256, &deposit::by_hash>>,
eosio::indexed_by<"byusername"_n, eosio::const_mem_fun<deposit, uint64_t, &deposit::by_username>>,
eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun<deposit, uint64_t, &deposit::by_status>>,
eosio::indexed_by<"bycreated"_n, eosio::const_mem_fun<deposit, uint64_t, &deposit::by_created>>
> deposits_index;
inline std::optional<deposit> get_deposit(eosio::name coopname, const checksum256 &hash) {
deposits_index primary_index(_wallet, coopname.value);
auto secondary_index = primary_index.get_index<"byhash"_n>();
@@ -207,7 +146,41 @@ public:
}
return *itr;
};
}
/**
* @ingroup public_tables
* @brief Таблица `withdraws` отслеживает операции вывода средств в контракте WALLET.
*/
struct [[eosio::table, eosio::contract(WALLET)]] withdraw {
uint64_t id; /*!< Уникальный идентификатор записи вывода */
eosio::name username; /*!< Имя пользователя, осуществляющего вывод средств */
eosio::name coopname; /*!< Имя аккаунта кооператива, в рамках которого осуществляется вывод */
checksum256 withdraw_hash; /*!< Идентификатор возврата */
eosio::name status; /*!< Статус операции вывода */
eosio::asset quantity; /*!< Количество средств для вывода во внутренней валюте */
document2 statement; /*!< Заявление на возврат */
document2 approved_statement; /*!< Предварительно-принятое заявление */
document2 authorization; /*!< Решение совета */
eosio::time_point_sec created_at = current_time_point(); ///< Время истечения срока давности
uint64_t primary_key() const { return id; } /*!< Возвращает id как первичный ключ */
checksum256 by_hash() const { return withdraw_hash; } ///< Индекс по хэшу задачи.
uint64_t by_username() const { return username.value; } /*!< Индекс по имени пользователя */
uint64_t by_status() const { return status.value; } /*!< Индекс по статусу операции вывода */
uint64_t by_created() const { return created_at.sec_since_epoch(); }
};
typedef eosio::multi_index<
"withdraws"_n, withdraw,
eosio::indexed_by<"byusername"_n, eosio::const_mem_fun<withdraw, uint64_t, &withdraw::by_username>>,
eosio::indexed_by<"byhash"_n, const_mem_fun<withdraw, checksum256, &withdraw::by_hash>>,
eosio::indexed_by<"bystatus"_n, eosio::const_mem_fun<withdraw, uint64_t, &withdraw::by_status>>,
eosio::indexed_by<"bycreated"_n, eosio::const_mem_fun<withdraw, uint64_t, &withdraw::by_created>>
> withdraws_index; /*!< Мультииндекс для доступа и манипуляции данными таблицы `withdraws` */
/**
@@ -216,7 +189,7 @@ public:
* @param hash Хэш возврата.
* @return `std::optional<withdraw>` - найденное действие или `std::nullopt`, если его нет.
*/
static std::optional<withdraw> get_withdraw(eosio::name coopname, const checksum256 &hash) {
inline std::optional<withdraw> get_withdraw(eosio::name coopname, const checksum256 &hash) {
withdraws_index primary_index(_wallet, coopname.value);
auto secondary_index = primary_index.get_index<"byhash"_n>();
@@ -226,6 +199,7 @@ public:
}
return *itr;
};
};
}
}
-40
View File
@@ -57,44 +57,4 @@ uint64_t generate() {
}
return seed;
}
std::string checksum256_to_hex(const eosio::checksum256& hash) {
auto hash_bytes = hash.extract_as_byte_array();
std::string hash_hex;
for (const auto& byte : hash_bytes) {
char hex_char[3];
sprintf(hex_char, "%02x", byte);
hash_hex += hex_char;
}
return hash_hex;
}
uint64_t extract_registry_id_from_meta(const std::string& meta_json) {
// Ищем "registry_id": и извлекаем число после двоеточия
std::string search_key = "\"registry_id\":";
size_t pos = meta_json.find(search_key);
eosio::check(pos != std::string::npos, "registry_id not found in meta");
// Перемещаемся после двоеточия
pos += search_key.length();
// Ищем первую цифру
while (pos < meta_json.length() && (meta_json[pos] < '0' || meta_json[pos] > '9')) {
pos++;
}
eosio::check(pos < meta_json.length(), "registry_id value not found");
// Собираем цифры
uint64_t result = 0;
while (pos < meta_json.length() && meta_json[pos] >= '0' && meta_json[pos] <= '9') {
result = result * 10 + (meta_json[pos] - '0');
pos++;
}
eosio::check(result > 0, "invalid registry_id value");
return result;
}
+11 -2
View File
@@ -11,8 +11,17 @@
### Структуры
- `balance`: Описывает баланс пользователя.
- `requests`: Описывает информацию об обмене.
- `requests_params`: Параметры обмена, включая имя пользователя, контракт, количество единиц, цену за штуку и дополнительные данные и метаданные.
- `exchange`: Описывает информацию об обмене.
- `exchange_params`: Параметры обмена, включая имя пользователя, контракт, количество единиц, цену за штуку и дополнительные данные и метаданные.
### Действия
Контракт включает в себя следующие действия:
- **Действия обмена**: `offer`, `order`, `complete`, `decline`, `accept`.
- **Административные действия**: `moderate`, `prohibit`, `unpublish`, `publish`.
- **Действия авторизации**: `authorize`.
- **Прочие действия**: `newid` (используется для возврата информации из контракта), `update`, `addpieces`.
## Использование
@@ -1,37 +1,38 @@
#include "marketplace.hpp"
#include <eosio/transaction.hpp>
#include "src/accept.cpp"
#include "src/addunits.cpp"
#include "src/authorize.cpp"
#include "src/cancel.cpp"
#include "src/change.cpp"
#include "src/complete.cpp"
#include "src/decline.cpp"
#include "src/delivered.cpp"
#include "src/dispute.cpp"
#include "src/moderate.cpp"
#include "src/offer.cpp"
#include "src/order.cpp"
#include "src/prohibit.cpp"
#include "src/publish.cpp"
#include "src/recieve.cpp"
#include "src/recievecnfrm.cpp"
#include "src/supply.cpp"
#include "src/supplycnfrm.cpp"
#include "src/unpublish.cpp"
#include "src/update.cpp"
// Процесс поставки по заявкам orderoffer
#include "src/deliver_on_offer/orderoffer.cpp"
#include "src/deliver_on_offer/accept.cpp"
#include "src/deliver_on_offer/authcontrib.cpp"
#include "src/deliver_on_offer/authreturn.cpp"
#include "src/deliver_on_offer/declineacc.cpp"
#include "src/deliver_on_offer/supply.cpp"
#include "src/deliver_on_offer/supplcnf.cpp"
#include "src/deliver_on_offer/delivered.cpp"
#include "src/deliver_on_offer/receive.cpp"
#include "src/deliver_on_offer/receivecnf.cpp"
#include "src/deliver_on_offer/complete.cpp"
#include "src/deliver_on_offer/decline.cpp"
#include "src/deliver_on_offer/cancel.cpp"
// Новая система перевозок
#include "src/shipment/createship.cpp"
#include "src/shipment/signbydriver.cpp"
#include "src/shipment/arrived.cpp"
#include "src/shipment/receiveshipm.cpp"
#include "src/shipment/retransport.cpp"
// Диспуты
#include "src/dispute_on_offer/dispute.cpp"
#include "src/dispute_on_offer/wauthorize.cpp"
#include "src/dispute_on_offer/wreturn.cpp"
#include "src/dispute_on_offer/woffer.cpp"
#include "src/dispute_on_offer/waccept.cpp"
/**
* @brief Пустой метод регистрации нового идентификатора
* @ingroup public_actions
* Этот метод используется для возврата информации из контракта.
* @param id идентификатор
* @param type тип идентификатора
*/
[[eosio::action]] void marketplace::newid(uint64_t id, eosio::name type) {
require_auth(_marketplace);
};
[[eosio::action]] void marketplace::migrate(){
// require_auth(_marketplace);
}
require_auth(_marketplace);
}
@@ -8,8 +8,6 @@
#include "../lib/common.hpp"
using namespace Marketplace;
/**
* \ingroup public_contracts
* @brief Класс `marketplace` предоставляет функционал кооперативного маркетплейса, позволяя пользователям
@@ -17,28 +15,12 @@ using namespace Marketplace;
* центральной точкой для всех операций обмена в рамках кооперативной экосистемы.
*
* Основные функции класса:
* - Создание и управление заявками типа orderoffer (заказчик → поставщик).
* - Создание и управление заявками на обмен, включая предложения и заказы.
* - Операции обновления, принятия, отказа и завершения обменных операций.
* - Модерация и управление публикацией заявок на обмен.
* - Административные функции, такие как создание идентификаторов и авторизация операций.
*
* ## Процесс поставки orderoffer:
*
* 1. **orderoffer** - заказчик создает заявку с документами на возврат и конвертацию, средства блокируются
* 2. **accept** - поставщик принимает заявку и предоставляет документы на взнос и конвертацию
* 3. **authcontrib/authreturn** - совет авторизует оба заявления раздельно
* 4. **supply** → **supplcnf** - поставка товара и подтверждение председателем КУ
* 5. **deliver1** → **deliver2** → **deliver3** → **deliver4** - этапы транспортировки
* 6. **receive** → **receivecnf** - получение товара заказчиком
* 7. **complete** - завершение поставки после гарантийного периода
*
* ## Документооборот:
*
* Все документы сохраняются в векторе `std::vector<document2> documents` в заявке.
* Каждый этап процесса добавляет необходимые документы в этот вектор.
*
* \note Контракт маркетплейса является центральной точкой экономической активности на платформе.
* \note Система упрощена для работы с одной заявкой вместо двух встречных.
*/
class [[eosio::contract(MARKETPLACE)]] marketplace : public eosio::contract {
@@ -47,47 +29,54 @@ public:
eosio::datastream<const char *> ds)
: eosio::contract(receiver, code, ds) {}
void apply(uint64_t receiver, uint64_t code, uint64_t action);
[[eosio::action]] void migrate();
// ... определение методов контракта ...
//marketplace.cpp
[[eosio::action]] void newid(uint64_t id, eosio::name type);
// Действия для создания заявок
[[eosio::action]] void orderoffer(eosio::name coopname, eosio::name receiver_braname, eosio::name username, checksum256 hash, uint64_t units, eosio::asset unit_cost, uint32_t product_lifecycle_secs, uint32_t warranty_period_secs, eosio::asset membership_fee_amount, eosio::asset cancellation_fee_amount, document2 product_return_statement, document2 convert_in, std::string meta);
//soviet.cpp
[[eosio::action]] void authorize(eosio::name coopname, uint64_t exchange_id, uint64_t contribution_product_decision_id, document2 contribution_product_authorization, uint64_t return_product_decision_id, document2 return_product_authorization);
static void cancel_request(eosio::name coopname, eosio::name username, checksum256 request_hash);
// Статические методы для отклонения заявок
static void decline_request(eosio::name coopname, const request& change);
//change.cpp
[[eosio::action]] void offer(const exchange_params& params);
[[eosio::action]] void order(const exchange_params& params);
// Методы для направления заявок
[[eosio::action]] void accept(eosio::name coopname, eosio::name supplier_braname, eosio::name username, checksum256 request_hash, document2 convert_out, document2 return_document);
[[eosio::action]] void authcontrib(eosio::name coopname, checksum256 request_hash, document2 authorization);
[[eosio::action]] void authreturn(eosio::name coopname, checksum256 request_hash, document2 authorization);
[[eosio::action]] void declineacc(eosio::name coopname, checksum256 hash, std::string reason);
[[eosio::action]] void supply(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 act);
[[eosio::action]] void supplcnf(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 act);
static void create(eosio::name type, const exchange_params& params);
static void create_parent(eosio::name type, const exchange_params& params);
static void create_child(eosio::name type, const exchange_params& params);
static void cancel_parent(eosio::name coopname, eosio::name username, uint64_t exchange_id);
static void cancel_child(eosio::name coopname, eosio::name username, uint64_t exchange_id);
[[eosio::action]] void accept(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document);
[[eosio::action]] void decline(eosio::name coopname, eosio::name username, uint64_t exchange_id, std::string meta);
[[eosio::action]] void supplycnfrm(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document);
[[eosio::action]] void supply(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document);
[[eosio::action]] void delivered(eosio::name coopname, eosio::name username, uint64_t exchange_id);
[[eosio::action]] void recieve(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document);
[[eosio::action]] void recievecnfrm(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document);
[[eosio::action]] void dispute(eosio::name coopname, eosio::name username, uint64_t exchange_id, document2 document);
// Новая система перевозок
[[eosio::action]] void createship(eosio::name coopname, checksum256 hash, eosio::name driver_username, eosio::name source_braname, eosio::name destination_braname, std::vector<checksum256> request_hashes, document2 transport_act_sender);
[[eosio::action]] void signbydriver(eosio::name coopname, checksum256 hash, document2 transport_act_driver);
[[eosio::action]] void arrived(eosio::name coopname, checksum256 hash, document2 transport_act_delivery);
[[eosio::action]] void receiveshipm(eosio::name coopname, checksum256 hash, document2 warehouse_receipt_act);
[[eosio::action]] void retransport(eosio::name coopname, checksum256 completed_hash, eosio::name new_driver_username, eosio::name source_braname, eosio::name new_destination_braname, std::vector<checksum256> request_hashes, document2 transport_act_sender);
[[eosio::action]] void complete(eosio::name coopname, eosio::name username, uint64_t exchange_id);
[[eosio::action]] void cancel(eosio::name coopname, eosio::name username, uint64_t exchange_id);
// Доставка заказчику
[[eosio::action]] void delivered(eosio::name coopname, eosio::name username, checksum256 request_hash);
[[eosio::action]] void receive(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
[[eosio::action]] void receivecnf(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
[[eosio::action]] void complete(eosio::name coopname, eosio::name username, checksum256 request_hash);
[[eosio::action]] void decline(eosio::name coopname, eosio::name username, checksum256 request_hash, std::string meta);
[[eosio::action]] void cancel(eosio::name coopname, eosio::name username, checksum256 request_hash);
// Методы для работы с диспутом (гарантийный возврат)
[[eosio::action]] void dispute(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
[[eosio::action]] void wauthorize(eosio::name coopname, checksum256 request_hash, uint64_t wreturn_decision_id, document2 wreturn_authorization, uint64_t wsupply_decision_id, document2 wsupply_authorization);
[[eosio::action]] void wreturn(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
[[eosio::action]] void woffer(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document);
[[eosio::action]] void waccept(eosio::name coopname, eosio::name username, checksum256 request_hash, bool accept, document2 document);
[[eosio::action]] void update(eosio::name coopname, eosio::name username, uint64_t exchange_id, uint64_t remain_units, eosio::asset unit_cost, std::string data, std::string meta);
[[eosio::action]] void addunits(eosio::name coopname, eosio::name username, uint64_t exchange_id, uint64_t units);
//admins.cpp
[[eosio::action]] void moderate(eosio::name coopname, eosio::name username, uint64_t exchange_id, uint64_t cancellation_fee);
[[eosio::action]] void prohibit(eosio::name coopname, eosio::name username, uint64_t exchange_id, std::string meta);
[[eosio::action]] void unpublish(eosio::name coopname, eosio::name username, uint64_t exchange_id);
[[eosio::action]] void publish(eosio::name coopname, eosio::name username, uint64_t exchange_id);
struct [[eosio::table, eosio::contract("marketplace")]] balances : balances_base {};
struct [[eosio::table, eosio::contract("marketplace")]] counts : counts_base {};
};
@@ -1,90 +0,0 @@
/**
\ingroup public_actions
\brief Принятие заявки поставщиком.
@details Поставщик принимает заявку orderoffer на поставку имущества и предоставляет необходимые документы.
@param coopname Имя кооператива
@param supplier_braname Имя кооперативного участка поставщика
@param username Имя поставщика
@param request_hash Хэш заявки
@param convert_out Заявление на конвертацию
@param product_contribution_statement Заявление на имущественный паевой взнос
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::accept(eosio::name coopname, eosio::name supplier_braname, eosio::name username, checksum256 request_hash, document2 convert_out, document2 product_contribution_statement) {
require_auth(coopname);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "active"_n, "Только активная заявка может быть принята");
eosio::check(change.type == "orderoffer"_n, "Метод accept применим только к заявкам типа orderoffer");
// Проверяем существование кооперативного участка поставщика
get_branch_or_fail(coopname, supplier_braname);
// Проверяем подписи документов
verify_document_or_fail(convert_out);
verify_document_or_fail(product_contribution_statement);
// Валидируем документы по registry_id (пока что нули)
Document::validate_registry_id(convert_out, 0);
Document::validate_registry_id(product_contribution_statement, 0);
// Получаем первоначальный документ возврата из заявки по имени
document2 initial_return_statement;
bool found = Document::find_document(change.documents, DocumentNames::RETURN_STMT, initial_return_statement);
eosio::check(found, "В заявке отсутствует заявление на возврат имущества");
// Обновляем заявку
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &o){
o.status = "accepted"_n;
o.accepted_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
o.product_contributor = username;
o.supplier_braname = supplier_braname;
// Добавляем новые документы с именами
Document::add_document(o.documents, DocumentNames::CONVERT_TO, convert_out);
Document::add_document(o.documents, DocumentNames::CONTRIB_STMT, product_contribution_statement);
});
// Используем хэш заявки как идентификатор пакета решений
checksum256 agenda_hash = change.hash;
// Отправляем ПЕРВЫЙ вопрос в совет - по заявлению на имущественный паевой взнос
Action::send<createagenda_interface>(
_soviet,
"createagenda"_n,
_marketplace,
coopname,
username,
get_valid_soviet_action("authcontrib"_n), // авторизация взноса имуществом
agenda_hash,
_marketplace,
Marketplace::get_valid_marketplace_action("authcontrib"_n),
"declineacc"_n,
product_contribution_statement,
std::string("")
);
// Отправляем ВТОРОЙ вопрос в совет - по заявлению на возврат имущества
Action::send<createagenda_interface>(
_soviet,
"createagenda"_n,
_marketplace,
coopname,
username,
get_valid_soviet_action("authreturn"_n), // авторизация возврата имуществом
agenda_hash,
_marketplace,
Marketplace::get_valid_marketplace_action("authreturn"_n),
"declineacc"_n,
initial_return_statement,
std::string("")
);
};
@@ -1,45 +0,0 @@
/**
\ingroup public_actions
\brief Авторизация заявления на имущественный паевой взнос советом кооператива.
@details Совет кооператива авторизует заявление на имущественный паевой взнос.
@param coopname Имя кооператива
@param request_hash Хэш заявки
@param authorization Документ авторизации от совета
@note Авторизация требуется от аккаунта: @p _soviet
**/
[[eosio::action]] void marketplace::authcontrib(eosio::name coopname, checksum256 request_hash, document2 authorization) {
require_auth(_soviet);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "accepted"_n, "Только принятая заявка может быть авторизована");
// Проверяем подпись документа
verify_document_or_fail(authorization);
// Валидируем документ по registry_id (пока что ноль)
Document::validate_registry_id(authorization, 0);
// Обновляем заявку
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &o){
// Добавляем документ авторизации взноса с именем
Document::add_document(o.documents, DocumentNames::CONTRIB_AUTH, authorization);
// Проверяем, есть ли уже оба документа авторизации
bool has_contrib_auth = Document::has_document(o.documents, DocumentNames::CONTRIB_AUTH);
bool has_return_auth = Document::has_document(o.documents, DocumentNames::RETURN_AUTH);
// Если получили оба документа авторизации, переводим в статус authorized
if (has_contrib_auth && has_return_auth) {
o.status = "authorized"_n;
}
});
};
@@ -1,45 +0,0 @@
/**
\ingroup public_actions
\brief Авторизация заявления на возврат имущества советом кооператива.
@details Совет кооператива авторизует заявление на возврат имущества.
@param coopname Имя кооператива
@param request_hash Хэш заявки
@param authorization Документ авторизации от совета
@note Авторизация требуется от аккаунта: @p _soviet
**/
[[eosio::action]] void marketplace::authreturn(eosio::name coopname, checksum256 request_hash, document2 authorization) {
require_auth(_soviet);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "accepted"_n, "Только принятая заявка может быть авторизована");
// Проверяем подпись документа
verify_document_or_fail(authorization);
// Валидируем документ по registry_id (пока что ноль)
Document::validate_registry_id(authorization, 0);
// Обновляем заявку
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &o){
// Добавляем документ авторизации возврата с именем
Document::add_document(o.documents, DocumentNames::RETURN_AUTH, authorization);
// Проверяем, есть ли уже оба документа авторизации
bool has_contrib_auth = Document::has_document(o.documents, DocumentNames::CONTRIB_AUTH);
bool has_return_auth = Document::has_document(o.documents, DocumentNames::RETURN_AUTH);
// Если получили оба документа авторизации, переводим в статус authorized
if (has_contrib_auth && has_return_auth) {
o.status = "authorized"_n;
}
});
};
@@ -1,61 +0,0 @@
/**
\ingroup public_actions
\brief Отмена заявки пользователем.
@details Пользователь может отменить свою заявку с учетом комиссии за отмену.
@param coopname Имя кооператива
@param username Имя пользователя
@param request_hash Хэш заявки
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::cancel(eosio::name coopname, eosio::name username, checksum256 request_hash) {
require_auth(coopname);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
// Проверяем права доступа
eosio::check(change.username == username || change.product_contributor == username, "Недостаточно прав доступа");
// Ограничения отмены
eosio::check(change.status != "completed"_n, "Нельзя отменить завершенную заявку");
eosio::check(change.status != "canceled"_n, "Заявка уже отменена");
eosio::check(change.status != "declined"_n, "Нельзя отменить отклоненную заявку");
// Поставщик не может отменить заявку после поставки
if ((change.money_contributor != username && change.product_contributor != username) ||
(change.product_contributor == username && (change.status == "supplied1"_n || change.status == "supplied2"_n ||
change.status == "delivered"_n || change.status == "received1"_n || change.status == "received2"_n))) {
eosio::check(false, "Поставщик не может отменить заявку после поставки");
}
std::string memo = "Отмена заявки №" + std::to_string(change.id);
// Возвращаем средства заказчику с учетом комиссии за отмену
if (username == change.money_contributor) {
eosio::asset return_amount = change.total_cost - change.cancellation_fee_amount;
// Списываем заблокированные средства
Wallet::sub_blocked_funds(_marketplace, coopname, change.money_contributor, change.total_cost, _marketplace_program, memo);
// Возвращаем средства в кошелёк
Wallet::add_available_funds(_marketplace, coopname, change.money_contributor, return_amount, _wallet_program, memo);
// Направляем комиссию в фонд членских взносов
if (change.cancellation_fee_amount.amount > 0) {
// Wallet::add_member_fee(_marketplace, coopname, change.username, _marketplace_program_id, change.cancellation_fee_amount, memo);
}
}
// Удаляем заявку из системы
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для удаления");
requests.erase(change_itr);
};
@@ -1,40 +0,0 @@
/**
\ingroup public_actions
\brief Завершение поставки.
@details После истечения гарантийной задержки происходит завершение поставки.
@param coopname Имя кооператива
@param username Имя пользователя
@param request_hash Хэш заявки
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::complete(eosio::name coopname, eosio::name username, checksum256 request_hash) {
require_auth(coopname);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "received"_n, "Завершение возможно только после статуса received");
eosio::check(eosio::time_point_sec(eosio::current_time_point()) >= change.warranty_delay_until, "Гарантийная задержка ещё не истекла");
// Проводки по кошельку
std::string memo = "Завершение поставки для заказа №" + std::to_string(change.id);
// Поставщик - списываем заблокированный баланс и начисляем в кошелёк
Wallet::sub_blocked_funds(_marketplace, coopname, change.product_contributor, change.base_cost, _marketplace_program, memo);
Wallet::add_available_funds(_marketplace, coopname, change.product_contributor, change.base_cost, _wallet_program, memo);
// Членские взносы (если больше нуля)
if (change.membership_fee_amount.amount > 0) {
// Wallet::add_member_fee(_marketplace, coopname, change.username, _marketplace_program_id, change.membership_fee_amount, memo);
}
// Удаляем заявку из системы
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для удаления");
requests.erase(change_itr);
};
@@ -1,39 +0,0 @@
/**
\ingroup public_actions
\brief Отклонение заявки.
@details Отклонение заявки с указанием причины.
@param coopname Имя кооператива
@param username Имя пользователя
@param request_hash Хэш заявки
@param meta Причина отклонения
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::decline(eosio::name coopname, eosio::name username, checksum256 request_hash, std::string meta) {
require_auth(coopname);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "active"_n || change.status == "accepted"_n, "Можно отклонить только активную или принятую заявку");
std::string memo = "Отклонение заявки №" + std::to_string(change.id) + ": " + meta;
// Возвращаем средства заказчику если они были заблокированы
if (change.status == "active"_n && change.money_contributor != ""_n) {
// Списываем заблокированные средства
Wallet::sub_blocked_funds(_marketplace, coopname, change.money_contributor, change.total_cost, _marketplace_program, memo);
// Возвращаем средства в кошелёк
Wallet::add_available_funds(_marketplace, coopname, change.money_contributor, change.total_cost, _wallet_program, memo);
}
// Удаляем заявку из системы
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для удаления");
requests.erase(change_itr);
};
@@ -1,62 +0,0 @@
/**
\ingroup public_actions
\brief Отклонение принятия заявки советом (declineacc).
@details Данный метод вызывается советом когда заявление на конвертацию, возврат или взнос отклоняется.
После рефакторинга работаем только с одной заявкой orderoffer.
@param coopname Имя кооператива
@param request_hash Хэш заявки, которая должна быть отменена
@param reason Причина отклонения
@note Авторизация требуется от аккаунта: @p _soviet
*/
[[eosio::action]] void marketplace::declineacc(eosio::name coopname, checksum256 request_hash, std::string reason) {
require_auth(_soviet);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
// Если заявка не найдена, ничего не делаем (возможно уже удалена)
if (!change_opt.has_value()) {
print("Заявка уже удалена или не найдена. Кооператив: ", coopname, ", причина: ", reason);
return;
}
auto change = change_opt.value();
// Логирование отклонения
print("Отклонение заявления советом. Заявка ID: ", change.id, ", причина: ", reason);
// Отклоняем заявку
decline_request(coopname, change);
}
/**
* @brief Статический метод для отклонения заявки (используется советом)
*/
void marketplace::decline_request(eosio::name coopname, const request& change) {
requests_index requests(_marketplace, coopname.value);
// Проверяем, что заявка все еще существует
auto change_itr = requests.find(change.id);
if (change_itr == requests.end()) {
return; // Заявка уже удалена
}
std::string memo = "Отклонение заявления советом по программе №" + std::to_string(_marketplace_program_id) + " для заявки ID: " + std::to_string(change.id);
// Возвращаем средства заказчику если они были заблокированы
if (change.type == "orderoffer"_n && change.total_cost.amount > 0 && change.money_contributor != ""_n) {
// Списываем заблокированные средства с маркетплейса
Wallet::sub_blocked_funds(_marketplace, coopname, change.money_contributor, change.total_cost, _marketplace_program, memo);
// Возвращаем средства в кошелёк
Wallet::add_available_funds(_marketplace, coopname, change.money_contributor, change.total_cost, _wallet_program, memo);
}
// Удаляем заявку из системы
requests.erase(change_itr);
}
@@ -1,35 +0,0 @@
/**
\ingroup public_actions
\brief Перевод заявки в статус готово к выдаче.
@details Председатель КУ переводит заявку в статус delivered независимо от транспортировки.
Используется когда транспортировка между КУ не нужна или имущество уже находится на складе.
Просто перевод статуса без дополнительных документов.
@param coopname Имя кооператива
@param username Имя пользователя (может быть поставщиком или заказчиком)
@param request_hash Хэш заявки
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::delivered(eosio::name coopname, eosio::name username, checksum256 request_hash) {
require_auth(coopname);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
// Проверяем что заявка в подходящем статусе (после поставки или перемещения со склада)
eosio::check(change.status == "supplied2"_n || change.status == "shiprecvd"_n, "Перевод в статус delivered возможен только после поставки (supplied2) или после получения на склад (shiprecvd)");
// Обновляем заявку - просто перевод статуса
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &o){
o.status = "delivered"_n;
o.delivered_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
// Устанавливаем крайний срок получения (3 дня)
o.deadline_for_receipt = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch() + 3 * 24 * 60 * 60);
});
};
@@ -1,110 +0,0 @@
/**
\ingroup public_actions
\brief Создать заявку orderoffer - заказчик создает заявку на поставку товара от поставщика.
*
* Данный метод позволяет заказчику создать заявку на поставку товара от поставщика.
* Заявка содержит всю информацию о товаре, стоимости, документах и сразу блокирует средства заказчика.
*
* @param coopname Имя кооператива
* @param receiver_braname Имя кооперативного участка заказчика для получения товара
* @param username Имя заказчика
* @param hash Хэш заявки (уникальный идентификатор)
* @param units Количество единиц товара
* @param unit_cost Цена за единицу товара
* @param product_lifecycle_secs Время жизни продукта
* @param warranty_period_secs Гарантийный срок в секундах
* @param membership_fee_amount Сумма членского взноса
* @param cancellation_fee_amount Сумма комиссии за отмену заявки
* @param product_return_statement Заявление на возврат паевого взноса имуществом
* @param convert_in Заявление на конвертацию из кошелька в маркетплейс
* @param meta Метаданные о заявке
*
* @note Авторизация требуется от аккаунта: @p coopname
*/
[[eosio::action]] void marketplace::orderoffer(eosio::name coopname, eosio::name receiver_braname, eosio::name username, checksum256 hash, uint64_t units, eosio::asset unit_cost, uint32_t product_lifecycle_secs, uint32_t warranty_period_secs, eosio::asset membership_fee_amount, eosio::asset cancellation_fee_amount, document2 product_return_statement, document2 convert_in, std::string meta) {
require_auth(coopname);
// Проверяем, что заявка с таким хэшем не существует
auto existing_request = get_request_by_hash(coopname, hash);
eosio::check(!existing_request.has_value(), "Заявка с таким хэшем уже существует");
// Проверяем, что символ токена совпадает с символом токена кооператива
auto coop = get_cooperative_or_fail(coopname);
eosio::check(unit_cost.symbol == coop.initial.symbol, "Неверный символ токена");
eosio::check(membership_fee_amount.symbol == coop.initial.symbol, "Неверный символ токена для членского взноса");
eosio::check(cancellation_fee_amount.symbol == coop.initial.symbol, "Неверный символ токена для комиссии отмены");
eosio::check(units > 0, "Количество единиц в заявке должно быть больше нуля");
eosio::check(unit_cost.amount >= 0, "Цена не может быть отрицательной");
eosio::check(membership_fee_amount.amount >= 0, "Членский взнос не может быть отрицательным");
eosio::check(cancellation_fee_amount.amount >= 0, "Комиссия за отмену не может быть отрицательной");
// Проверяем, что пользователь является пайщиком кооператива
get_participant_or_fail(coopname, username);
// Проверяем существование кооперативного участка заказчика
get_branch_or_fail(coopname, receiver_braname);
// Проверяем существование программы маркетплейса
auto program = get_program_or_fail(coopname, _marketplace_program_id);
// Гарантийный срок возврата должен быть установлен
eosio::check(product_lifecycle_secs > 0, "Гарантийный срок возврата для имущества должен быть установлен");
eosio::check(warranty_period_secs > 0, "Гарантийный срок должен быть больше нуля");
// Проводим проверку подписи документов
verify_document_or_fail(product_return_statement);
verify_document_or_fail(convert_in);
// Валидируем документы по registry_id (пока что нули как просил пользователь)
Document::validate_registry_id(product_return_statement, 0);
Document::validate_registry_id(convert_in, 0);
// Рассчитываем стоимость
eosio::asset base_cost = unit_cost * units;
eosio::asset total_cost = base_cost + membership_fee_amount;
// Проверяем что комиссия за отмену не превышает общую стоимость
eosio::check(cancellation_fee_amount <= total_cost, "Комиссия за отмену не может превышать общую стоимость заявки");
requests_index requests(_marketplace, coopname.value);
uint64_t request_id = get_global_id(_marketplace, "requests"_n);
std::string memo = "Начало поставки по программе №" + std::to_string(_marketplace_program_id) + " с ID: " + std::to_string(request_id);
// Создаем вектор именованных документов
std::vector<Document::named_document> documents;
Document::add_document(documents, DocumentNames::RETURN_STMT, product_return_statement);
Document::add_document(documents, DocumentNames::CONVERT_FROM, convert_in);
requests.emplace(_marketplace, [&](auto &i) {
i.id = request_id;
i.hash = hash;
i.type = "orderoffer"_n;
i.username = username;
i.coopname = coopname;
i.status = "active"_n;
i.units = units;
i.unit_cost = unit_cost;
i.base_cost = base_cost;
i.membership_fee_amount = membership_fee_amount;
i.total_cost = total_cost;
i.product_lifecycle_secs = product_lifecycle_secs;
i.warranty_period_secs = warranty_period_secs;
i.money_contributor = username;
i.meta = meta;
i.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
i.cancellation_fee_amount = cancellation_fee_amount;
i.receiver_braname = receiver_braname;
i.documents = documents;
});
// Конвертируем и блокируем средства заказчика
std::string convert_memo = "Конвертация средств из ЦПП 'Цифровой Кошелёк' в ЦПП 'Маркетплейс' для заказа №" + std::to_string(request_id);
Wallet::sub_available_funds(_marketplace, coopname, username, total_cost, _wallet_program, convert_memo);
// Добавляем средства на ЦПП маркетплейса
Wallet::add_available_funds(_marketplace, coopname, username, total_cost, _marketplace_program, convert_memo);
// Блокируем средства на программе маркетплейса
Wallet::block_funds(_marketplace, coopname, username, total_cost, _marketplace_program, memo);
};
@@ -1,39 +0,0 @@
/**
\ingroup public_actions
\brief Получение товара заказчиком.
@details Заказчик приходит на КУ для получения имущества. Председатель КУ подписывает акт и передаёт имущество заказчику.
@param coopname Имя кооператива
@param username Имя заказчика
@param request_hash Хэш заявки
@param document Акт получения имущества
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::receive(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document) {
require_auth(coopname);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "delivered"_n, "Получение возможно только после статуса delivered");
eosio::check(change.money_contributor == username, "Недостаточно прав доступа");
// Проверяем подпись документа
verify_document_or_fail(document);
// Валидируем документ по registry_id (пока что ноль)
Document::validate_registry_id(document, 0);
// Обновляем заявку
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &o){
o.status = "received1"_n;
// Добавляем акт получения с именем
Document::add_document(o.documents, DocumentNames::RECEIVE_ACT, document);
});
};
@@ -1,51 +0,0 @@
/**
\ingroup public_actions
\brief Подтверждение получения заказчиком.
@details Заказчик подтверждает факт получения имущества второй подписью акта приёма-передачи.
@param coopname Имя кооператива
@param username Имя заказчика
@param request_hash Хэш заявки
@param document Акт подтверждения получения
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::receivecnf(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document) {
require_auth(coopname);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "received1"_n, "Подтверждение возможно только после статуса received1");
eosio::check(change.money_contributor == username, "Недостаточно прав доступа");
// Проверяем подпись документа
verify_document_or_fail(document);
// Валидируем документ по registry_id (пока что ноль)
Document::validate_registry_id(document, 0);
// Обновляем заявку
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &o){
o.status = "received2"_n;
o.received_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
// Устанавливаем время окончания гарантийной задержки
o.warranty_delay_until = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch() + change.warranty_period_secs);
// Добавляем акт подтверждения с именем
Document::add_document(o.documents, DocumentNames::RECEIVE_ACT_CONF, document);
});
// Проводки по кошельку
std::string memo = "Подтверждение получения для заказа №" + std::to_string(change.id);
// Паевой фонд - уменьшаем циркуляцию
Ledger::sub(_marketplace, coopname, Ledger::accounts::SHARE_FUND, change.base_cost, memo);
// Заказчик - списываем заблокированный баланс
Wallet::sub_blocked_funds(_marketplace, coopname, change.money_contributor, change.total_cost, _marketplace_program, memo);
};
@@ -1,50 +0,0 @@
/**
\ingroup public_actions
\brief Подтверждение поставки председателем КУ.
@details Председатель кооперативного участка или доверенное им лицо подтверждает факт поставки имущества.
@param coopname Имя кооператива
@param username Имя представителя кооператива
@param request_hash Хэш заявки
@param act Акт подтверждения поставки
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::supplcnf(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 act) {
require_auth(coopname);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "supplied1"_n, "Поставка должна быть подтверждена после статуса supplied1");
// Проверяем подпись документа
verify_document_or_fail(act);
// Валидируем документ по registry_id (пока что ноль)
Document::validate_registry_id(act, 0);
// Обновляем заявку
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &o){
o.status = "supplied2"_n;
o.warehouse = change.supplier_braname; // Ставим имущество на склад КУ приёма
o.supplied_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
// Добавляем акт подтверждения с именем
Document::add_document(o.documents, DocumentNames::SUPPLY_ACT_CONF, act);
});
// Проводки по кошельку
std::string memo = "Подтверждение поставки для заказа №" + std::to_string(change.id);
// Паевой фонд - увеличиваем циркуляцию
Ledger::add(_marketplace, coopname, Ledger::accounts::SHARE_FUND, change.total_cost, memo);
// Поставщик - начисляем и блокируем средства
Wallet::add_available_funds(_marketplace, coopname, change.product_contributor, change.base_cost, _marketplace_program, memo);
Wallet::block_funds(_marketplace, coopname, change.product_contributor, change.base_cost, _marketplace_program, memo);
};
@@ -1,40 +0,0 @@
/**
\ingroup public_actions
\brief Поставка имущества в кооператив.
@details Поставщик поставляет имущество на указанный кооперативный участок и предоставляет подписанный акт приёма-передачи.
@param coopname Имя кооператива
@param username Имя поставщика
@param request_hash Хэш заявки
@param act Акт поставки имущества
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::supply(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 act) {
require_auth(coopname);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "authorized"_n, "Только авторизованная заявка может быть поставлена");
eosio::check(change.product_contributor == username, "Недостаточно прав доступа");
// Проверяем подпись документа
verify_document_or_fail(act);
// Валидируем документ по registry_id (пока что ноль)
Document::validate_registry_id(act, 0);
// Обновляем заявку
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &o){
o.status = "supplied1"_n;
o.supplied_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
// Добавляем акт поставки с именем
Document::add_document(o.documents, DocumentNames::SUPPLY_ACT, act);
});
};
@@ -1,56 +0,0 @@
# Система гарантийного возврата (диспут)
## Обзор
Новая система диспута работает через создание дополнительных сегментов в рамках существующей пары заявок, а не через создание новых заявок.
## Типы сегментов диспута
- **wreturn** - сегмент возврата товара от заказчика в кооператив
- **wsupply** - сегмент выдачи товара из кооператива поставщику
## Процесс диспута
### 1. Открытие диспута (`dispute`)
- Заказчик подает претензию с документом
- Создаются два новых сегмента: `wreturn` и `wsupply`
- Статус заявки меняется на `disputed`
### 2. Авторизация диспута (`wauthorize`)
- Совет авторизует принятие товара от заказчика
- Совет авторизует последующую выдачу товара поставщику
- Статус заявки меняется на `wauthorized`
### 3. Возврат товара (`wreturn`)
- Заказчик возвращает товар в кооператив
- Председатель принимает товар и подписывает акт
- Статус заявки меняется на `wreturned`
### 4. Предложение товара поставщику (`woffer`)
- Кооператив предлагает поставщику забрать товар
- Создается акт передачи
- Статус заявки меняется на `woffered`
### 5. Принятие решения поставщиком (`waccept`)
- Поставщик может принять товар (`accept=true`) или отказаться (`accept=false`)
- При принятии: статус становится `wcompleted`
- При отказе: статус становится `wdeclined`
## Статусы заявок диспута
- **disputed** - диспут открыт
- **wauthorized** - диспут авторизован советом
- **wreturned** - товар возвращен в кооператив
- **woffered** - товар предложен поставщику
- **wcompleted** - поставщик принял товар, диспут завершен
- **wdeclined** - поставщик отказался от товара, диспут завершен
## Статусы сегментов
- **created** - сегмент создан
- **statement** - документ/заявление предоставлено
- **authorized** - авторизовано советом
- **returned** - товар возвращен в кооператив
- **offered** - товар предложен поставщику
- **accepted** - поставщик принял товар
- **declined** - поставщик отказался от товара
@@ -1,61 +0,0 @@
/**
\ingroup public_actions
\brief Открытие гарантийного спора по заявке.
@details Заказчик может открыть спор после получения товара, если есть проблемы с качеством или соответствием.
Создается претензия, которая сохраняется в документах заявки.
@param coopname Имя кооператива
@param username Имя заказчика, открывающего спор
@param request_hash Хэш заявки, по которой открывается спор
@param document Документ с описанием претензии
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::dispute(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document){
require_auth(coopname);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.type == "orderoffer"_n, "Спор может быть открыт только по заявке orderoffer");
eosio::check(change.username == username, "Только заказчик может открыть спор");
eosio::check(change.is_warranty_return == false, "Нельзя открыть спор на спор");
eosio::check(change.status == "received"_n || change.status == "completed"_n, "Спор можно открыть только после получения товара");
// Проверяем подпись документа
verify_document_or_fail(document);
// Обновляем статус заявки на "disputed" и добавляем документ с претензией
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &e){
e.status = "disputed"_n;
e.disputed_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
e.is_warranty_return = true;
Document::add_document(e.documents, Marketplace::DocumentNames::WDISPUTE, document);
});
// Используем хэш заявки как идентификатор пакета решений
checksum256 agenda_hash = change.hash;
// Отправляем заявление на гарантийный возврат в совет
Action::send<createagenda_interface>(
_soviet,
"createagenda"_n,
_marketplace,
coopname,
username,
get_valid_soviet_action("mpwreturn"_n),
agenda_hash,
_marketplace,
Marketplace::get_valid_marketplace_action("wauthorize"_n),
"wdecline"_n,
document,
std::string("")
);
}
@@ -1,48 +0,0 @@
/**
\ingroup public_actions
\brief Принятие или отказ поставщика от товара в рамках гарантийного возврата
@details Поставщик может принять товар (accept=true) или отказаться от него (accept=false).
При принятии товар передается поставщику и диспут завершается.
При отказе товар остается у кооператива и диспут завершается.
@param coopname Имя кооператива
@param username Имя поставщика
@param request_hash Хэш заявки с диспутом
@param accept Принимает ли поставщик товар (true/false)
@param document Документ с решением поставщика
@note Авторизация требуется от аккаунта: @p coopname
*/
[[eosio::action]] void marketplace::waccept(eosio::name coopname, eosio::name username, checksum256 request_hash, bool accept, document2 document) {
require_auth(coopname);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "woffered"_n, "Товар должен быть предложен поставщику");
eosio::check(change.product_contributor == username, "Только поставщик может принять решение о товаре");
// Проверяем подпись документа
verify_document_or_fail(document);
requests_index requests(_marketplace, coopname.value);
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
if (accept) {
// Поставщик принимает товар
requests.modify(change_itr, _marketplace, [&](auto &ch) {
ch.status = "wcompleted"_n;
Document::add_document(ch.documents, Marketplace::DocumentNames::WACCEPT_ACT, document);
});
} else {
// Поставщик отказывается от товара
requests.modify(change_itr, _marketplace, [&](auto &ch) {
ch.status = "wdeclined"_n;
Document::add_document(ch.documents, Marketplace::DocumentNames::WACCEPT_ACT, document);
});
}
}
@@ -1,34 +0,0 @@
/**
\ingroup public_actions
\brief Авторизация гарантийного возврата советом
@details Совет авторизует принятие товара от заказчика и его последующую выдачу поставщику
@param coopname Имя кооператива
@param request_hash Хэш заявки с диспутом
@param wreturn_decision_id Идентификатор решения по принятию товара
@param wreturn_authorization Документ авторизации принятия товара
@param wsupply_decision_id Идентификатор решения по выдаче товара поставщику
@param wsupply_authorization Документ авторизации выдачи товара
@note Авторизация требуется от аккаунта: @p _soviet
*/
[[eosio::action]] void marketplace::wauthorize(eosio::name coopname, checksum256 request_hash, uint64_t wreturn_decision_id, document2 wreturn_authorization, uint64_t wsupply_decision_id, document2 wsupply_authorization) {
require_auth(_soviet);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "disputed"_n, "Заявка должна быть в статусе диспута");
// Обновляем статус заявки и добавляем документы авторизации
requests_index requests(_marketplace, coopname.value);
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _soviet, [&](auto &o) {
o.status = "wauthorized"_n;
Document::add_document(o.documents, Marketplace::DocumentNames::WRETURN_AUTH, wreturn_authorization);
Document::add_document(o.documents, Marketplace::DocumentNames::WSUPPLY_AUTH, wsupply_authorization);
});
};
@@ -1,40 +0,0 @@
/**
\ingroup public_actions
\brief Предложение товара поставщику в рамках гарантийного возврата
@details Кооператив предлагает поставщику забрать товар, возвращенный заказчиком.
Создается предложение с актом передачи.
@param coopname Имя кооператива
@param username Имя председателя, предлагающего товар
@param request_hash Хэш заявки с диспутом
@param document Акт передачи товара поставщику
@note Авторизация требуется от аккаунта: @p coopname
*/
[[eosio::action]] void marketplace::woffer(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document) {
require_auth(coopname);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "wreturned"_n, "Товар может быть предложен только после возврата в кооператив");
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
auto chairman = soviet.get_chairman();
eosio::check(username == chairman, "Недостаточно прав доступа для предложения товара");
// Проверяем подпись документа
verify_document_or_fail(document);
// Обновляем статус заявки и добавляем акт предложения товара
requests_index requests(_marketplace, coopname.value);
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &ch) {
ch.status = "woffered"_n;
Document::add_document(ch.documents, Marketplace::DocumentNames::WOFFER_ACT, document);
});
}
@@ -1,40 +0,0 @@
/**
\ingroup public_actions
\brief Возврат товара от заказчика в кооператив
@details Заказчик возвращает товар в кооператив в рамках гарантийного возврата.
Председатель принимает товар и подписывает акт приёма.
@param coopname Имя кооператива
@param username Имя председателя, принимающего товар
@param request_hash Хэш заявки с диспутом
@param document Акт приёма товара от заказчика
@note Авторизация требуется от аккаунта: @p coopname
*/
[[eosio::action]] void marketplace::wreturn(eosio::name coopname, eosio::name username, checksum256 request_hash, document2 document) {
require_auth(coopname);
requests_index requests(_marketplace, coopname.value);
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "wauthorized"_n, "Товар может быть возвращен только по авторизованному диспуту");
auto soviet = get_board_by_type_or_fail(coopname, "soviet"_n);
auto chairman = soviet.get_chairman();
eosio::check(username == chairman, "Недостаточно прав доступа для принятия возврата");
// Проверяем подпись документа
verify_document_or_fail(document);
// Обновляем статус заявки и добавляем акт приёма возврата
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &ch) {
ch.status = "wreturned"_n;
Document::add_document(ch.documents, Marketplace::DocumentNames::WRETURN_ACT, document);
});
}
@@ -1,35 +0,0 @@
/**
\ingroup public_actions
\brief Перевозка прибыла в место назначения.
@details Перевозка переходит в статус arrived и ожидает подписи получателя.
@param coopname Имя кооператива
@param hash Идентификатор перевозки
@param transport_act_delivery Акт доставки подписанный водителем
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::arrived(eosio::name coopname, checksum256 hash, document2 transport_act_delivery) {
require_auth(coopname);
// Проверяем что перевозка существует
auto shipment = Marketplace::get_shipment_by_hash_or_fail(coopname, hash, "Перевозка не найдена");
eosio::check(shipment.status == "transit"_n, "Подписывать акт доставки можно только для перевозки в статусе transit");
// Проверяем подпись документа
verify_document_or_fail(transport_act_delivery, { shipment.driver_username });
// Валидируем документ по registry_id (пока что ноль)
Document::validate_registry_id(transport_act_delivery, 0);
// Обновляем перевозку - добавляем третий акт
shipments_index shipments(_marketplace, coopname.value);
auto hash_index = shipments.get_index<"byhash"_n>();
auto shipment_itr = hash_index.find(hash);
hash_index.modify(shipment_itr, _marketplace, [&](auto &s) {
s.status = "arrived"_n; // Новый статус - прибыл, ожидает приёма получателем
Document::add_document(s.documents, DocumentNames::SHIPMENT_ARRIVE_ACT, transport_act_delivery);
s.delivered_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
});
};
@@ -1,71 +0,0 @@
/**
\ingroup public_actions
\brief Создание новой перевозки с массивом заявок.
@details Создает новую перевозку от одного КУ к другому с указанием водителя и массива заявок.
Представитель КУ отправления подписывает первый акт приёма-передачи.
Заявки остаются на складе до получения подписи от водителя.
@param coopname Имя кооператива
@param hash Внешний идентификатор перевозки
@param driver_username Имя водителя-пайщика
@param source_braname КУ отправителя
@param destination_braname КУ назначения
@param request_hashes Массив хэшей заявок для перевозки
@param transport_act_sender Акт приёма-передачи от представителя КУ отправления
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::createship(eosio::name coopname, checksum256 hash, eosio::name driver_username, eosio::name source_braname, eosio::name destination_braname, std::vector<checksum256> request_hashes, document2 transport_act_sender) {
require_auth(coopname);
// Проверяем что КУ отправителя и назначения разные
eosio::check(source_braname != destination_braname, "КУ отправителя и назначения не могут совпадать");
// Проверяем что есть хотя бы одна заявка
eosio::check(!request_hashes.empty(), "Должна быть указана хотя бы одна заявка");
// Проверяем что перевозка с таким hash не существует
auto existing_shipment = Marketplace::get_shipment_by_hash(coopname, hash);
eosio::check(!existing_shipment.has_value(), "Перевозка с таким идентификатором уже существует");
// Проверяем подпись документа
verify_document_or_fail(transport_act_sender);
// Валидируем документ по registry_id (пока что ноль)
Document::validate_registry_id(transport_act_sender, 0);
requests_index requests(_marketplace, coopname.value);
// Проверяем все заявки но НЕ снимаем их со склада (это будет при signbydriver)
for (auto& request_hash : request_hashes) {
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "supplied2"_n || change.status == "shiprecvd"_n || change.status == "delivered"_n, "Перевозить можно только заявки в статусе supplied2, shiprecvd или delivered");
eosio::check(change.type == "orderoffer"_n, "Перевозить можно только заявки типа orderoffer");
eosio::check(change.warehouse == source_braname, "Заявка должна находиться на складе КУ отправителя");
}
// Получаем новый ID для перевозки
uint64_t next_shipment_id = get_global_id_in_scope(_marketplace, coopname, "shipments"_n);
// Создаем новую перевозку
shipments_index shipments(_marketplace, coopname.value);
shipments.emplace(_marketplace, [&](auto &s) {
s.id = next_shipment_id;
s.hash = hash;
s.coopname = coopname;
s.driver_username = driver_username;
s.source_braname = source_braname;
s.destination_braname = destination_braname;
s.status = "loading"_n;
s.request_hashes = request_hashes;
// Добавляем первый документ
Document::add_document(s.documents, DocumentNames::SHIPMENT_SEND_ACT, transport_act_sender);
s.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
});
};
@@ -1,55 +0,0 @@
/**
\ingroup public_actions
\brief Приём имущества на склад по накладной.
@details Представитель КУ получения принимает имущество на склад по накладной.
Все заявки из перевозки переходят в статус delivered и ставятся на склад КУ получения.
Объект перевозки удаляется.
@param coopname Имя кооператива
@param hash Идентификатор перевозки
@param warehouse_receipt_act Акт приёма на склад подписанный получателем
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::receiveshipm(eosio::name coopname, checksum256 hash, document2 warehouse_receipt_act) {
require_auth(coopname);
// Проверяем что перевозка существует
auto shipment = Marketplace::get_shipment_by_hash_or_fail(coopname, hash, "Перевозка не найдена");
eosio::check(shipment.status == "arrived"_n, "Принимать имущество на склад можно только для перевозки в статусе arrived");
// Проверяем подпись документа
verify_document_or_fail(warehouse_receipt_act);
// Валидируем документ по registry_id (пока что ноль)
Document::validate_registry_id(warehouse_receipt_act, 0);
requests_index requests(_marketplace, coopname.value);
// Обновляем все заявки из перевозки - ставим их на склад получения
for (auto& request_hash : shipment.request_hashes) {
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
// Обновляем заявку
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &o){
o.status = "shiprecvd"_n; // Доставка получена
o.warehouse = shipment.destination_braname; // Ставим на склад КУ получения
o.delivered_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
// Устанавливаем крайний срок получения (3 дня)
o.deadline_for_receipt = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch() + 3 * 24 * 60 * 60);
// Добавляем акт приёма на складе получателем
Document::add_document(o.documents, DocumentNames::SHIPMENT_RECV_ACT, warehouse_receipt_act);
});
}
// Удаляем объект перевозки после завершения
shipments_index shipments(_marketplace, coopname.value);
auto hash_index = shipments.get_index<"byhash"_n>();
auto shipment_itr = hash_index.find(hash);
hash_index.erase(shipment_itr);
};
@@ -1,66 +0,0 @@
/**
\ingroup public_actions
\brief Промежуточная передача товаров между складами.
@details Создает новую перевозку для доставленных заявок, позволяя передать все товары
из текущего склада в другое место назначения с новым водителем.
@param coopname Имя кооператива
@param completed_hash Внешний идентификатор новой перевозки
@param new_driver_username Имя нового водителя-пайщика
@param source_braname КУ с которого забираем товары
@param new_destination_braname Новый КУ назначения
@param request_hashes Массив хэшей заявок для переотправки
@param transport_act_sender Акт передачи от текущего склада
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::retransport(eosio::name coopname, checksum256 completed_hash, eosio::name new_driver_username, eosio::name source_braname, eosio::name new_destination_braname, std::vector<checksum256> request_hashes, document2 transport_act_sender) {
require_auth(coopname);
// Проверяем что есть хотя бы одна заявка
eosio::check(!request_hashes.empty(), "Должна быть указана хотя бы одна заявка");
// Проверяем что перевозка с таким hash не существует
auto existing_shipment = Marketplace::get_shipment_by_hash(coopname, completed_hash);
eosio::check(!existing_shipment.has_value(), "Перевозка с таким идентификатором уже существует");
// Проверяем подпись документа
verify_document_or_fail(transport_act_sender);
// Валидируем документ по registry_id (пока что ноль)
Document::validate_registry_id(transport_act_sender, 0);
requests_index requests(_marketplace, coopname.value);
// Проверяем все заявки но НЕ снимаем их со склада (это будет при signbydriver)
for (auto& request_hash : request_hashes) {
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.status == "delivered"_n, "Переотправлять можно только доставленные заявки");
eosio::check(change.warehouse == source_braname, "Заявка должна находиться на указанном складе");
}
// Получаем новый ID для перевозки
uint64_t next_shipment_id = get_global_id_in_scope(_marketplace, coopname, "shipments"_n);
// Создаем новую перевозку
shipments_index shipments(_marketplace, coopname.value);
shipments.emplace(_marketplace, [&](auto &s) {
s.id = next_shipment_id;
s.hash = completed_hash;
s.coopname = coopname;
s.driver_username = new_driver_username;
s.source_braname = source_braname;
s.destination_braname = new_destination_braname;
s.status = "loading"_n;
s.request_hashes = request_hashes;
// Добавляем первый документ
Document::add_document(s.documents, DocumentNames::SHIPMENT_SEND_ACT, transport_act_sender);
s.created_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
});
};
@@ -1,55 +0,0 @@
/**
\ingroup public_actions
\brief Подпись акта приёма-передачи водителем.
@details Водитель подписывает акт приёма имущества на транспортировку.
Все заявки снимаются со склада отправления.
Перевозка переходит в статус transit.
@param coopname Имя кооператива
@param hash Идентификатор перевозки
@param transport_act_driver Акт приёма-передачи подписанный водителем
@note Авторизация требуется от аккаунта: @p coopname
**/
[[eosio::action]] void marketplace::signbydriver(eosio::name coopname, checksum256 hash, document2 transport_act_driver) {
require_auth(coopname);
// Проверяем что перевозка существует
auto shipment = Marketplace::get_shipment_by_hash_or_fail(coopname, hash, "Перевозка не найдена");
eosio::check(shipment.status == "loading"_n, "Подписывать акт водитель может только для перевозки в статусе loading");
// Проверяем подпись документа
verify_document_or_fail(transport_act_driver);
// Валидируем документ по registry_id (пока что ноль)
Document::validate_registry_id(transport_act_driver, 0);
requests_index requests(_marketplace, coopname.value);
// Снимаем все заявки со склада отправления
for (auto& request_hash : shipment.request_hashes) {
auto change_opt = Marketplace::get_request_by_hash(coopname, request_hash);
eosio::check(change_opt.has_value(), "Заявка не найдена");
auto change = change_opt.value();
eosio::check(change.warehouse == shipment.source_braname, "Заявка должна находиться на складе КУ отправителя");
// Снимаем заявку со склада
auto change_itr = requests.find(change.id);
eosio::check(change_itr != requests.end(), "Заявка не найдена для обновления");
requests.modify(change_itr, _marketplace, [&](auto &o){
o.warehouse = ""_n; // Снимаем со склада
});
}
// Обновляем перевозку
shipments_index shipments(_marketplace, coopname.value);
auto hash_index = shipments.get_index<"byhash"_n>();
auto shipment_itr = hash_index.find(hash);
hash_index.modify(shipment_itr, _marketplace, [&](auto &s) {
s.status = "transit"_n;
Document::add_document(s.documents, DocumentNames::SHIPMENT_LOADING_ACT, transport_act_driver);
s.loaded_at = eosio::time_point_sec(eosio::current_time_point().sec_since_epoch());
});
};
@@ -1,12 +1,9 @@
#define NEWGDECISION_SIGNATURE \
name coopname, /* Имя кооператива */ \
name presider, /* Имя председателя */ \
name secretary, /* Имя секретаря */ \
checksum256 hash, /* Хэш собрания */ \
std::vector<question_result> results, /* Итог по вопросам */ \
uint64_t signed_ballots, /* Число подписанных бюллетеней */ \
double quorum_percent, /* Процент кворума */ \
bool quorum_passed, /* Флаг достигнутого кворума */ \
document2 decision /* Протокол общего собрания */
uint64_t quorum_percent, /* Процент кворума */ \
bool quorum_passed /* Флаг достигнутого кворума */
using newgdecision_interface = void(NEWGDECISION_SIGNATURE);
@@ -1,7 +1,6 @@
// Например, во внешем header можно объявить структуру:
struct question_result {
uint64_t question_id;
uint64_t number;
std::string title;
std::string decision;
std::string context;
@@ -8,8 +8,7 @@ struct [[eosio::table, eosio::contract(MEET)]] meet {
uint64_t id; /// Идентификатор собрания.
checksum256 hash; /// Внешний хэш-идентификатор собрания.
name coopname; ///< Имя кооператива.
name type; ///< Тип общего собрания (regular | extra)
name level; ///< Уровень общего собрания (cooperative | branch)
name type; ///< Тип общего собрания (annual | special)
name initiator; ///< Имя пользователя, который инициировал собрание.
name presider; ///< Имя аккаунта председателя собрания.
name secretary; ///< Имя аккаунта секретаря собрания
@@ -19,13 +18,12 @@ struct [[eosio::table, eosio::contract(MEET)]] meet {
time_point_sec created_at; ///< Дата создания предложения.
time_point_sec open_at; ///< Дата начала собрания.
time_point_sec close_at; ///< Дата завершения собрания.
std::vector<name> notified_users; ///< Пользователи, которые подписали уведомление
double quorum_percent = 75.0; /// Цель по кворуму, чтобы собрание считалось состоявшимся
uint64_t quorum_percent = 75; /// Цель по кворуму, чтобы собрание считалось состоявшимся
uint64_t signed_ballots; /// Общее количество подписанных бюллетеней
double current_quorum_percent = 0; /// Текущий процент кворума
uint64_t current_quorum_percent; /// Текущий процент кворума
uint64_t cycle = 1; /// Цикл общего собрания, который указывает на то, что были предыдущие собрания, которые не состоялись по кворуму
bool quorum_passed = false; /// Флаг пройденного кворума
bool quorum_passed; /// Флаг пройденного кворума
document2 proposal; ///< Предложение
document2 authorization; ///< Решение совета
-18
View File
@@ -8,27 +8,9 @@
#include "src/signbysecr.cpp"
#include "src/signbypresid.cpp"
#include "src/newgdecision.cpp"
#include "src/gmnotify.cpp"
#include <optional>
[[eosio::action]]
void meet::migrate(){
require_auth(_meet);
};
[[eosio::action]]
void meet::delmeet(eosio::name coopname, uint64_t meet_id) {
require_auth(_meet);
Meet::meets_index genmeets(_meet, coopname.value);
auto meet = genmeets.find(meet_id);
eosio::check(genmeets.end() != meet, "Собрание не найдено");
genmeets.erase(meet);
};
std::optional<Meet::meet> meet::get_meet(eosio::name coopname, const checksum256 &hash) {
Meet::meets_index genmeets(_meet, coopname.value);
auto hash_index = genmeets.get_index<"byhash"_n>();
+2 -9
View File
@@ -26,15 +26,12 @@ public:
void authmeet(eosio::name coopname, checksum256 hash, document2 authorization);
[[eosio::action]]
void declmeet(name coopname, checksum256 hash, std::string reason);
[[eosio::action]]
void gmnotify(name coopname, checksum256 hash, name username, document2 notification);
[[eosio::action]]
void vote(name coopname, checksum256 hash, name username, document2 ballot, std::vector<vote_point> votes);
[[eosio::action]]
void restartmeet(name coopname, checksum256 hash, checksum256 new_hash, document2 newproposal, time_point_sec new_open_at, time_point_sec new_close_at);
void restartmeet(name coopname, checksum256 hash, document2 newproposal, time_point_sec new_open_at, time_point_sec new_close_at);
[[eosio::action]]
void signbysecr(name coopname, name username, checksum256 hash, document2 secretary_decision);
@@ -42,10 +39,6 @@ public:
[[eosio::action]]
void signbypresid(name coopname, name username, checksum256 hash, document2 presider_decision);
[[eosio::action]] void migrate();
[[eosio::action]] void delmeet(eosio::name coopname, uint64_t meet_id);
// Сервисное действие:
[[eosio::action]] void newgdecision(NEWGDECISION_SIGNATURE);
private:
@@ -6,8 +6,7 @@ void meet::authmeet(eosio::name coopname, checksum256 hash, document2 authorizat
Meet::meets_index genmeets(_meet, coopname.value);
auto genmeet = genmeets.find(exist_meet -> id);
eosio::check(genmeet -> status == "onrestart"_n || genmeet -> status == "created"_n, "Собрание не находится в неподходящем статусе");
if (!TEST_MODE)
eosio::check(genmeet -> open_at.sec_since_epoch() >= current_time_point().sec_since_epoch() + MIN_OPEN_AGM_DELAY_SEC,
"Дата открытия должна быть по крайней мере через 15 дней от момента решения совета");
@@ -27,8 +27,7 @@ void meet::createmeet(name coopname, checksum256 hash, eosio::name initiator, na
g.id = meet_id;
g.hash = hash;
g.coopname = coopname;
g.type = "regular"_n;
g.level = "cooperative"_n;
g.type = "annual"_n;
g.initiator = initiator;
g.presider = presider;
g.secretary = secretary;
@@ -1,32 +0,0 @@
void meet::gmnotify(name coopname, checksum256 hash, name username, document2 notification) {
require_auth(coopname);
// Проверяем документ уведомления
verify_document_or_fail(notification);
// Получаем собрание по хэшу
Meet::meets_index genmeets(_meet, coopname.value);
auto meet_idx = genmeets.get_index<"byhash"_n>();
auto meet_itr = meet_idx.find(hash);
eosio::check(meet_itr != meet_idx.end(), "Собрание не найдено");
// Проверяем, что пользователь ещё не уведомлял
const auto& notified = meet_itr->notified_users;
eosio::check(std::find(notified.begin(), notified.end(), username) == notified.end(), "Пользователь уже подписал уведомление");
// Добавляем пользователя
meet_idx.modify(meet_itr, coopname, [&](auto& m) {
m.notified_users.push_back(username);
});
Action::send<newlink_interface>(
_soviet,
"newlink"_n,
_meet,
coopname,
username,
get_valid_soviet_action("gmnotify"_n),
hash,
notification
);
}

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