Compare commits

...

797 Commits

Author SHA1 Message Date
Cursor Agent a709e2f1e3 fix(security): update vulnerable deps in desktop, boot, factory
- desktop: axios, dompurify, express
- boot: axios, mongoose
- factory: axios, mongodb

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 14:08:47 +00:00
Cursor Agent 3a34bd74db fix(security): downgrade express to 4.21.2 (v5 has breaking changes with path-to-regexp)
Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 14:07:02 +00:00
Cursor Agent b755cb715a fix(security): update vulnerable dependencies in controller
CRITICAL fixes:
- mongoose 8.5.2 -> 9.2.2
- jspdf 2.5.1 -> 4.2.0
- jsonwebtoken 8.5.1 -> 9.0.3

HIGH fixes:
- express 4.17.1 -> 5.2.1
- axios 1.6.2 -> 1.13.5
- nodemailer 6.3.1 -> 8.0.1
- validator 13.0.0 -> 13.15.26
- ws 8.18.0 -> 8.19.0
- helmet 4.1.0 -> 8.1.0
- compression 1.7.4 -> 1.8.1
- moment 2.24.0 -> 2.30.1
- morgan 1.9.1 -> 1.10.1
- nodemon 2.0.0 -> 3.1.14
- supertest 6.0.1 -> 7.2.2
- cross-spawn 7.0.6 (added)

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 14:02:57 +00:00
Alex Ant 42753fae0a update 2026-02-25 18:25:42 +05:00
Alex Ant d8b8fa2d6e update 2026-02-25 18:14:18 +05:00
Cursor Agent a75277e0b8 docs: update AGENTS.md with docker networking and reboot instructions
Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 13:06:05 +00:00
Alex Ant f95b83674b update 2026-02-25 17:38:55 +05:00
Cursor Agent bb13e780af fix: correct SHiP port mapping 8070:8070 (was 8070:8080)
state-history-endpoint in config.ini listens on container port 8070,
not 8080. Fixed mapping so parser can connect via ws://127.0.0.1:8070

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 10:33:26 +00:00
Cursor Agent 6e80d5187c docs: document SSR hydration issue in desktop dev mode
Root cause: Pinia serializes Vue component objects from extension
routes into __INITIAL_STATE__. After hydration, components are
plain objects without render/setup functions. Production SSR build
works fine. Dev workaround: use SPA mode (quasar dev without --mode ssr).

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 10:23:15 +00:00
Cursor Agent db9b704d1d feat: add dev services to docker-compose, fix SHiP port, CHAIN_ID, postgres version
- docker-compose.yaml: add coopback, cooparser, desktop services for dev mode
- docker-compose.yaml: add SHiP port 8070, pin postgres:16
- components/desktop/.env-example: fix CHAIN_ID to match local blockchain
- components/parser/.env-example: fix SHIP port to 8070
- AGENTS.md: comprehensive dev environment instructions
- Remove docker-compose.override.yaml (changes merged into main file)

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 10:23:15 +00:00
Cursor Agent 65a2b5361c chore: add AGENTS.md and docker-compose.override for dev environment setup
- AGENTS.md: development instructions for Cursor Cloud agents
- docker-compose.override.yaml: pin postgres:16 (postgres:latest v18+ breaks with current volume config)

Co-authored-by: Alex Ant <dacom-dark-sun@users.noreply.github.com>
2026-02-25 10:22:23 +00:00
Alex Ant 7bca73cd7b chore(release): publish 2026-02-25 10:22:23 +00:00
Alex Ant 59383014be chore(release): publish 2026-02-25 10:22:23 +00:00
Alex Ant 60a5296f5f notification center repair 2026-02-25 10:22:23 +00:00
Alex Ant eb4fe333a7 chore(release): publish 2026-02-25 10:22:23 +00:00
Alex Ant 8d06d6d817 chore(release): publish 2026-02-25 10:22:23 +00:00
Alex Ant b6afb536b6 expired decision & walletPage & 30 days for expiration & formatDateFromNow with local time 2026-02-25 10:22:23 +00:00
Alex Ant 790047fa8a chore(release): publish 2026-02-25 10:22:23 +00:00
Alex Ant baf55b6b6d chore(release): publish 2026-02-25 10:22:23 +00:00
Alex Ant 92d85244f1 update docker and packages 2026-02-25 15:20:11 +05:00
Alex Ant 0b26433b1a update 2026-02-21 12:40:54 +05:00
Alex Ant 4f06bb40f8 chore(release): publish 2026-02-21 12:34:00 +05:00
Alex Ant 46d571b65a update 2026-02-21 12:32:34 +05:00
Alex Ant 7da1bfd315 chore(release): publish 2026-02-21 12:15:08 +05:00
Alex Ant b30f47b5aa chore(release): publish 2026-02-21 12:13:59 +05:00
Alex Ant ccc6c7206c publish packages fix 2026-02-21 12:11:38 +05:00
Alex Ant a549f4cae0 chore(release): publish 2026-02-21 11:38:11 +05:00
Alex Ant 07c451ce22 fix npm publish-packages 2026-02-21 11:36:49 +05:00
Alex Ant 62da624369 chore(release): publish 2026-02-21 11:31:18 +05:00
Alex Ant a1a9b03180 chore(release): publish 2026-02-20 16:55:19 +05:00
Alex Ant e4c1d37c35 VideoPlayer and wallet fix 2026-02-20 16:54:06 +05:00
Alex Ant 42cacac256 chore(release): publish 2026-02-20 15:28:41 +05:00
Alex Ant 2816970327 update 2026-02-20 15:27:33 +05:00
Alex Ant 5744fcdcb2 chore(release): publish 2026-02-20 15:21:56 +05:00
Alex Ant 3c412b394d capital -> blagorost program type migration 2026-02-20 15:20:53 +05:00
Alex Ant 4e64cea373 update 2026-02-20 13:17:30 +05:00
Alex Ant db579db87f update 2026-02-20 13:01:59 +05:00
Alex Ant f612a918d9 update 2026-02-20 13:00:39 +05:00
Alex Ant 1a78e06a7a chore(release): publish 2026-02-20 12:49:32 +05:00
Alex Ant 374a98ffc3 update 2026-02-20 12:29:49 +05:00
Alex Ant d723e2e273 chore(release): publish 2026-02-20 11:39:36 +05:00
Alex Ant 41030171b5 update 2026-02-20 11:38:29 +05:00
Alex Ant aa31706bcc chore(release): publish 2026-02-20 11:26:19 +05:00
Alex Ant 4e3810493d update 2026-02-20 11:25:24 +05:00
Alex Ant eeb2db92ed chore(release): publish 2026-02-20 11:09:44 +05:00
Alex Ant 5a2930029b update 2026-02-20 11:09:15 +05:00
Alex Ant 2e9a73923b chore(release): publish 2026-02-20 10:19:35 +05:00
Alex Ant 97e67f8a88 fix workflow for package deployment 2026-02-20 10:18:47 +05:00
Alex Ant 074849af74 chore(release): publish 2026-02-20 09:30:23 +05:00
Alex Ant 6939783518 update 2026-02-20 09:30:01 +05:00
Alex Ant 260e48fa48 chore(release): publish 2026-02-20 09:26:36 +05:00
Alex Ant 52c4ef5df5 fix builder 2026-02-20 09:26:10 +05:00
Alex Ant 46b634e0fa chore(release): publish 2026-02-20 00:12:44 +05:00
Alex Ant a556c29a17 fix docker base image 2026-02-20 00:12:02 +05:00
Alex Ant bbc4f508b5 chore(release): publish 2026-02-19 23:10:26 +05:00
Alex Ant fe0243549a update .env-example 2026-02-19 23:07:12 +05:00
Alex Ant d9196ba650 add stub usernames 2026-02-19 22:53:22 +05:00
Alex Ant a0ea1fa392 pre-release 2026-02-19 22:01:50 +05:00
Alex Ant 195bcb4495 WTF 2026-02-19 16:41:15 +05:00
Alex Ant 929627a191 candidates page and contract calculation fix 2026-02-17 09:37:01 +05:00
Alex Ant 83a313b4b7 tests and fixes 2026-02-15 11:18:32 +05:00
Alex Ant f734bfb50f documents fixs & transcription page 2026-02-12 23:27:21 +05:00
Alex Ant 9efc22d195 chatcoop secretary transcriptor 2026-02-12 16:58:24 +05:00
Alex Ant bd3bef5fb0 chatcoop default room settings change 2026-02-12 11:29:32 +05:00
Alex Ant ddbe49e847 result submission 2026-02-12 10:43:48 +05:00
Alex Ant ed9bddec0b investors are invest to blagorost program directly, not to generation contract 2026-02-10 11:38:32 +05:00
Alex Ant 73a07eeb54 intellectual investments 2026-02-10 10:40:04 +05:00
Alex Ant 6e27aed804 registration process fixs 2026-02-09 23:44:34 +05:00
Alex Ant 5f955781a6 download document package button 2026-02-07 12:41:06 +05:00
Alex Ant c124f7310e some fixes 2026-02-07 12:09:43 +05:00
Alex Ant 73ffcae549 github sync subsystem on blagorost module 2026-02-06 21:15:13 +05:00
Alex Ant 80e55ba0be make requirements as documents and editor readonly mode 2026-02-06 13:46:35 +05:00
Alex Ant eecf955096 import contributors with some peculiarities 2026-02-06 11:16:29 +05:00
Alex Ant 6426db9a5d wallet sync and contributors import 2026-02-05 22:15:42 +05:00
Alex Ant bf4753cd09 gamification fix 2026-02-05 12:50:50 +05:00
Alex Ant a592eaf383 replace editor 2026-02-05 11:18:17 +05:00
Alex Ant 75d654cec3 feat: STORY-001 Исправить логику создания участников
Изменена логика создания Contributor с "при первом вкладе" на "при подтверждении доступа к проекту".
Теперь Contributor создается автоматически при одобрении appendix (confirmClearance).

Acceptance Criteria:
- Участник отображается в виджете сразу после подписания соглашения об участии
- Не требуется делать вклад для отображения в списке участников
- Логика создания Contributor изменена с "при первом вкладе" на "при подтверждении доступа"

Technical Changes:
- Добавлена логика создания Contributor в ClearanceManagementInteractor.handleConfirmClearance()
- Contributor создается со статусом APPROVED при подтверждении доступа
- Добавлены необходимые импорты и зависимости
- Обновлен статус спринта

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-04 11:42:55 +05:00
Alex Ant a18ed37600 blagorost test restore 2026-02-04 11:07:53 +05:00
Alex Ant 6747a8dfbc refactoring possible complete 2026-02-01 13:29:29 +05:00
Alex Ant 583dfb8126 before capital contract refactoring 2026-01-31 11:36:00 +05:00
Alex Ant bda31dd086 access violation bugfix with wharfkit rollback 2026-01-29 23:19:22 +05:00
Alex Ant 283af35f3b looking for access violation bug 2026-01-29 15:51:35 +05:00
Alex Ant 82564e2580 contract access violation bug fix | documents 2026-01-29 01:56:40 +05:00
Alex Ant fc6a9026e6 blagorost documents preparing 2026-01-27 23:00:27 +05:00
Alex Ant e1538bb5ab contract optional blagorost agreement on registration 2026-01-26 10:54:53 +05:00
Alex Ant d47db969ea refactoring vars on blagorost documents 2026-01-25 22:51:35 +05:00
Alex Ant f01de37a59 before make new document models 2026-01-25 13:40:56 +05:00
Alex Ant cee429a0a0 add udata model to a factory 2026-01-25 11:48:49 +05:00
Alex Ant 321ee89676 added all blagorost documents 2026-01-25 10:30:57 +05:00
Alex Ant aa60484315 make invest statement 2026-01-23 13:00:24 +05:00
Alex Ant b807df3ee8 document names refactoring & context component 2026-01-22 18:13:45 +05:00
Alex Ant c11e922573 registration with programs and agreements 2026-01-22 13:41:02 +05:00
Alex Ant 7135a3bbeb new documents 2026-01-21 21:49:35 +05:00
Alex Ant 142b87dd67 before make git 2026-01-20 21:23:02 +05:00
Alex Ant dff4579f28 project & component initialization & InitProjectStatement & InitProjectDecision 2026-01-19 17:39:20 +05:00
Alex Ant 2b3b662a9a generationAgreement Document & project initialization process 2026-01-19 14:24:05 +05:00
Alex Ant 98bc5195b1 blagorost decision tracking factory 2026-01-18 17:31:10 +05:00
Alex Ant a02912ccbb document updates 2026-01-17 11:47:48 +05:00
Alex Ant 2f1d29dfd2 Merge branch 'project-authorization-on-create' into dev 2026-01-16 22:02:48 +05:00
Alex Ant 11efacc4cb 1002 document generation agreement 2026-01-16 22:00:43 +05:00
Alex Ant 8133d0da3d register contributor with UHD 2026-01-16 12:34:47 +05:00
Alex Ant 093d63faae add zeus 2026-01-16 12:34:18 +05:00
Alex Ant 07f5e04488 authorization 2026-01-16 12:32:34 +05:00
Alex Ant acfc97fc7c permissions and boot fixs 2026-01-15 12:15:14 +05:00
Alex Ant 6e30554143 fix button 2026-01-13 18:25:54 +05:00
Alex Ant fa1c788c41 issue and project permissions & selectors 2026-01-13 18:01:59 +05:00
Alex Ant 3a688942c3 finalize_project and convert unused project funds to global program fund 2026-01-09 18:17:34 +05:00
Alex Ant 6797522618 blagorost masterplace 2026-01-07 17:11:47 +05:00
Alex Ant 4730a53313 requirements project -> component -> issue 2026-01-06 00:26:11 +05:00
Alex Ant 5a538792f0 project logs 2026-01-05 23:46:42 +05:00
Alex Ant 5f20289767 blagorost human project log 2026-01-04 23:43:54 +05:00
Alex Ant 4c7ed97a76 blagorost convert segment dialog 2026-01-04 21:43:23 +05:00
Alex Ant a9c2228e3c repair blagorost tests 2026-01-02 19:19:55 +05:00
Alex Ant ec44fcb7cb refactoring: user and certificates 2026-01-02 18:03:17 +05:00
Alex Ant 1f9c8d261b refactoring: wallet & user-certificate 2026-01-02 17:15:43 +05:00
Alex Ant 23ab3bd416 refactoring: system 2026-01-02 16:42:15 +05:00
Alex Ant 79482775d2 refactoring: settings 2026-01-02 10:40:27 +05:00
Alex Ant 2dcb3678b0 refactoring: payment-method 2026-01-02 10:28:48 +05:00
Alex Ant 8d7c206012 refactoring: notification 2026-01-02 10:03:37 +05:00
Alex Ant c21c264426 refactoring: meet interactor 2026-01-01 20:42:29 +05:00
Alex Ant 77a39c18f1 refactoring: make adapters for extensions 2026-01-01 20:11:25 +05:00
Alex Ant 65a00b0a8e refactoring: ledger 2026-01-01 19:11:56 +05:00
Alex Ant 0c2d5679f1 extensions reinit 2026-01-01 19:05:12 +05:00
Alex Ant 75d0e0de43 gateway refactoring 2026-01-01 18:49:59 +05:00
Alex Ant 19ff864eaa gateway refactoring 2026-01-01 14:30:37 +05:00
Alex Ant 6bbee7cb10 refactoring -> clean architecture 2026-01-01 12:57:37 +05:00
Alex Ant e268923cf6 down logger lever for powerup overdrawn balance 2025-12-31 13:14:30 +05:00
Alex Ant 4fb3d7182c chore(release): publish 2025-12-31 00:08:59 +05:00
Alex Ant 6e7a6ec032 chore(release): publish 2025-12-31 00:08:00 +05:00
Alex Ant 0e92c92f87 bug: повторное общее собрание не завершало онбординг кооператива 2025-12-30 21:37:37 +05:00
Alex Ant a6f1eb39ec chore(release): publish 2025-12-30 20:49:54 +05:00
Alex Ant 21364f6239 chore(release): publish 2025-12-30 18:24:48 +05:00
Alex Ant a0527094a9 fix a little 2025-12-30 18:24:17 +05:00
Alex Ant eda890c294 chore(release): publish 2025-12-30 17:59:11 +05:00
Alex Ant 1ed8db87cc openReplay http watcher 2025-12-30 17:57:57 +05:00
Alex Ant 136892478f chore(release): publish 2025-12-30 14:41:51 +05:00
Alex Ant aee90d31a0 chore(release): publish 2025-12-30 14:33:54 +05:00
Alex Ant 6fe632fda3 add repairdec action in soviet contract 2025-12-30 14:33:00 +05:00
Alex Ant fa975a2f5b auto cancel decisions full disable and postgres booter 2025-12-30 13:45:40 +05:00
Alex Ant bd5ab14d2f chore(release): publish 2025-12-28 20:44:51 +05:00
Alex Ant 32890bfda9 chore(release): publish 2025-12-28 15:10:11 +05:00
Alex Ant a3d4113b97 sentry backend integration 2025-12-28 15:09:13 +05:00
Alex Ant 8acfa722f8 chore(release): publish 2025-12-28 14:26:21 +05:00
Alex Ant 234beba06a sentry env 2025-12-28 14:24:55 +05:00
Alex Ant 90c27a0f73 chore(release): publish 2025-12-28 12:47:59 +05:00
Alex Ant 2577a5d2c2 remove logs 2025-12-28 12:47:25 +05:00
Alex Ant 84bf10b65b chore(release): publish 2025-12-28 12:15:59 +05:00
Alex Ant 5d70f5fd97 env refactoring rollback 2025-12-28 12:15:29 +05:00
Alex Ant f42cc59d53 chore(release): publish 2025-12-28 11:33:03 +05:00
Alex Ant d12b222d52 fix tracker assistant (disable it) 2025-12-28 11:32:22 +05:00
Alex Ant b663c7f9d4 env config refactoring 2025-12-28 11:24:26 +05:00
Alex Ant 30abfa7e23 chore(release): publish 2025-12-28 10:58:32 +05:00
Alex Ant 0be32c34c5 update 2025-12-28 10:58:09 +05:00
Alex Ant 8d4863b894 chore(release): publish 2025-12-28 10:43:19 +05:00
Alex Ant ad1ae8fb54 update openScreen assistant 2025-12-28 10:42:46 +05:00
Alex Ant b31e41bfd9 chore(release): publish 2025-12-27 20:49:02 +05:00
Alex Ant 999d5a937d SSR fix 2025-12-27 20:48:15 +05:00
Alex Ant 45ea543a4b chore(release): publish 2025-12-27 20:23:58 +05:00
Alex Ant 9d72a5e02d openScreen 2025-12-27 20:21:56 +05:00
Alex Ant 8973c45b4e chore(release): publish 2025-12-27 20:04:57 +05:00
Alex Ant b3e5a5dc62 complete refactoring for a clean architecture 2025-12-27 20:03:53 +05:00
Alex Ant d8bddadd42 chore(release): publish 2025-12-27 11:42:16 +05:00
Alex Ant aa552d1c80 refactoring #4 2025-12-27 11:41:23 +05:00
Alex Ant 6daac5f442 chore(release): publish 2025-12-26 23:53:27 +05:00
Alex Ant a8c453fbda refactoring #2 2025-12-26 23:52:43 +05:00
Alex Ant 3e15da5ad7 chore(release): publish 2025-12-26 19:46:46 +05:00
Alex Ant 8e776e7d42 chore(release): publish 2025-12-26 18:48:28 +05:00
Alex Ant de3d5654b4 mono monitor mongo -> psql migration & ipn model migration 2025-12-26 18:47:31 +05:00
Alex Ant b0e19ffa30 chore(release): publish 2025-12-26 17:22:25 +05:00
Alex Ant bacf451cf5 chore(release): publish 2025-12-26 16:58:44 +05:00
Alex Ant 2a5600b6af login bugfix 2025-12-26 16:58:13 +05:00
Alex Ant bfbc78d9b7 chore(release): publish 2025-12-26 13:41:36 +05:00
Alex Ant d509281bdc delete some migrations 2025-12-26 13:40:55 +05:00
Alex Ant 866fdfb4f6 chore(release): publish 2025-12-26 13:27:55 +05:00
Alex Ant 6c14d0f4c5 fix generator port 2025-12-26 13:27:23 +05:00
Alex Ant b31a8e3bf8 chore(release): publish 2025-12-26 12:46:11 +05:00
Alex Ant 7f3261e453 migrate tokens from mongo to postgres and clean arch 2025-12-26 12:45:09 +05:00
Alex Ant 861c3617dd payment providers refactoring 2025-12-26 10:51:57 +05:00
Alex Ant 1f4b1f5421 chore(release): publish 2025-12-26 10:04:34 +05:00
Alex Ant f851460a32 fix package 2025-12-26 10:03:09 +05:00
Alex Ant 992ac6d9eb chore(release): publish 2025-12-25 23:46:03 +05:00
Alex Ant 2e9ac57930 big service refactoring 2025-12-25 23:44:07 +05:00
Alex Ant ce8d090f27 chore(release): publish 2025-12-25 14:34:36 +05:00
Alex Ant 4ec8b59fd6 chore(release): publish 2025-12-25 14:32:42 +05:00
Alex Ant 6b086af8b4 upgrade chatwoot sdk 2025-12-25 14:30:57 +05:00
Alex Ant 52f95665ed chore(release): publish 2025-12-25 10:13:13 +05:00
Alex Ant d1ce5ab105 fix build chatwoot 2025-12-25 10:12:39 +05:00
Alex Ant 0fd10649f4 chore(release): publish 2025-12-25 00:24:37 +05:00
Alex Ant 3c65900768 support widget 2025-12-25 00:23:31 +05:00
Alex Ant 0b962b4fee board members refactoring 2025-12-24 17:06:18 +05:00
Alex Ant a58633fc5f chore(release): publish 2025-12-24 13:24:14 +05:00
Alex Ant 24c2ed3ae3 chore(release): publish 2025-12-23 23:22:46 +05:00
Alex Ant 84a77d98cf refactoring sendPOST and sendGET from desktop 2025-12-23 23:19:55 +05:00
Alex Ant 20e50e02e9 delete @coopenomics/controller package from desktop 2025-12-23 19:46:51 +05:00
Alex Ant c8bb354972 chore(release): publish 2025-12-22 18:47:11 +05:00
Alex Ant 3f9b73d211 chore(release): publish 2025-12-22 18:46:36 +05:00
Alex Ant 40bbffed17 bug fix 2025-12-22 18:46:11 +05:00
Alex Ant b16ec4e809 fix blagorost contract 2025-12-22 18:39:56 +05:00
Alex Ant 9629c895d5 chore(release): publish 2025-12-17 23:29:39 +05:00
Alex Ant f4420fcc03 doc update 2025-12-17 23:28:34 +05:00
Alex Ant 17f8c3e134 chore(release): publish 2025-12-17 22:20:38 +05:00
Alex Ant 02512a522c chore(release): publish 2025-12-17 21:47:10 +05:00
Alex Ant 4974f99842 blagorost off 2025-12-17 21:46:08 +05:00
Alex Ant a3d0c32be3 chore(release): publish 2025-12-17 21:33:01 +05:00
Alex Ant 31523160fc chore(release): publish 2025-12-17 21:25:53 +05:00
Alex Ant 183d5cdeec migration and little fix 2025-12-17 21:25:02 +05:00
Alex Ant 52fed810f2 chore(release): publish 2025-12-17 20:53:43 +05:00
Alex Ant f58d63da8d blagorost documents agenda proposals 2025-12-17 20:51:34 +05:00
Alex Ant 6793e6fbdc blagorost documents 2025-12-17 18:21:24 +05:00
Alex Ant 4f88e9b97d blagorost imgs, profile and other fixs 2025-12-17 09:35:32 +05:00
Alex Ant 5004cec9b3 chore(release): publish 2025-12-14 23:42:01 +05:00
Alex Ant 76c81e4209 chore(release): publish 2025-12-14 23:40:47 +05:00
Alex Ant 323043dc01 документация по общему собранию и т.п. 2025-12-14 23:39:41 +05:00
Alex Ant 37902ce70a chore(release): publish 2025-12-13 15:14:11 +05:00
Alex Ant c69e795d49 chore(release): publish 2025-12-13 15:12:28 +05:00
Alex Ant c3b82af95a 1c doc 2025-12-13 15:11:58 +05:00
Alex Ant 24510d4828 chore(release): publish 2025-12-13 13:45:35 +05:00
Alex Ant 11a04a99f7 1ccoop availability 2025-12-13 13:44:11 +05:00
Alex Ant 6e96eeaa05 chore(release): publish 2025-12-13 13:22:25 +05:00
Alex Ant 2c16d4af5a fix 2025-12-13 13:21:46 +05:00
Alex Ant d1f9cf43de chore(release): publish 2025-12-13 12:44:06 +05:00
Alex Ant 26fb11cb30 chore(release): publish 2025-12-13 12:21:45 +05:00
Alex Ant a7a3eb8658 weasyprint -> 67 2025-12-13 12:21:17 +05:00
Alex Ant fbedcd1f5f chore(release): publish 2025-12-13 11:28:37 +05:00
Alex Ant 6033af0f23 fix 2025-12-13 11:28:10 +05:00
Alex Ant 4809786b16 chore(release): publish 2025-12-13 11:19:11 +05:00
Alex Ant a37f40ff6e weasyprint #2 2025-12-13 11:18:25 +05:00
Alex Ant 44f5cc9de0 chore(release): publish 2025-12-13 10:51:12 +05:00
Alex Ant e059d0cbd9 Dockerfile 2025-12-13 10:50:25 +05:00
Alex Ant c51040b0de chore(release): publish 2025-12-13 10:42:39 +05:00
Alex Ant eba2abc108 downgrade weasyprint and upgrade base image 2025-12-13 10:41:46 +05:00
Alex Ant ed7626009a chore(release): publish 2025-12-13 10:11:33 +05:00
Alex Ant 442413ec6b up weasyprint version 2025-12-13 10:09:25 +05:00
Alex Ant 90986df641 onboarding 2025-12-13 09:31:42 +05:00
Alex Ant 8b505dbce3 cooperative onboarding process & chat with union 2025-12-11 20:47:16 +05:00
Alex Ant 2b26e9643b chore(release): publish 2025-12-10 15:31:43 +05:00
Alex Ant eb16f01839 docs and ledger refactoring 2025-12-10 13:50:59 +05:00
Alex Ant 872cf33d1c update 2025-12-02 18:39:54 +05:00
Alex Ant afbce19028 chore(release): publish 2025-12-02 18:13:18 +05:00
Alex Ant e27228e735 chore(release): publish 2025-12-02 18:09:56 +05:00
Alex Ant 3125232114 change CNAME in docs and fix publish CI 2025-12-02 18:09:07 +05:00
Alex Ant e552840cc4 chore(release): publish 2025-12-02 14:39:34 +05:00
Alex Ant 569142845a chore(release): publish 2025-12-02 14:37:38 +05:00
Alex Ant e62d837858 1c-integration and deploy bug fix 2025-12-02 14:35:58 +05:00
Alex Ant 27badfb88a chore(release): publish 2025-11-29 00:50:18 +05:00
Alex Ant e4d0781eb8 chore(release): publish 2025-11-29 00:44:02 +05:00
Alex Ant 93c8373335 disable setInterval on server side 2025-11-29 00:42:51 +05:00
Alex Ant 42bad6b4ea chore(release): publish 2025-11-28 13:08:14 +05:00
Alex Ant 83d4bd88cf chore(release): publish 2025-11-28 13:06:11 +05:00
Alex Ant 4141267104 disable unused routes 2025-11-28 13:05:04 +05:00
Alex Ant d5778f165b remove notifications on update 2025-11-28 12:16:54 +05:00
Alex Ant 7128e26a27 chore(release): publish 2025-11-28 12:15:17 +05:00
Alex Ant 78247e9969 bug fixes and logging improve 2025-11-28 12:14:13 +05:00
Alex Ant 66d36e034e chore(release): publish 2025-11-27 19:52:17 +05:00
Alex Ant 4100ee842c update 2025-11-27 19:49:59 +05:00
Alex Ant d4857c8e6e some fixes 2025-11-27 14:26:34 +05:00
Alex Ant 0f52781489 cmdK z-top & latest tag on docker production container 2025-11-26 17:27:08 +05:00
Alex Ant 332f19cd22 chore(release): publish 2025-11-26 13:13:59 +05:00
Alex Ant 53a1c103d6 chore(release): publish 2025-11-26 12:55:34 +05:00
Alex Ant addf7a3e6b fix 2025-11-26 12:54:19 +05:00
Alex Ant c76078ea06 chore(release): publish 2025-11-26 12:51:24 +05:00
Alex Ant 9299288e8a bug fix: chat loading and console.logs for debug router problem 2025-11-26 12:49:42 +05:00
Alex Ant dc34f24ee2 chore(release): publish 2025-11-26 11:23:40 +05:00
Alex Ant fe83bf1483 fix latest tag on testnet 2025-11-26 11:22:41 +05:00
Alex Ant c184f9281d chore(release): publish 2025-11-26 00:14:11 +05:00
Alex Ant 3127a69b67 chore(release): publish 2025-11-26 00:10:29 +05:00
Alex Ant d7326c4f82 chore(release): publish 2025-11-25 16:12:22 +05:00
Alex Ant f8478284b6 backward capability 2025-11-25 16:11:26 +05:00
Alex Ant 1c910de4b1 chore(release): publish 2025-11-25 10:56:15 +05:00
Alex Ant 2c3cb955ef fix 2025-11-25 10:55:36 +05:00
Alex Ant 1d7746d839 chore(release): publish 2025-11-25 10:10:36 +05:00
Alex Ant e16d2ba238 chore(release): publish 2025-11-24 22:26:05 +05:00
Alex Ant 60f8b448ec coopgram -> chatcoop & mobile client instruction 2025-11-24 22:24:33 +05:00
Alex Ant c0d61f29a8 empty settings fix & matrix room creation fix 2025-11-24 20:10:57 +05:00
Alex Ant c47b99f9ee chore(release): publish 2025-11-24 19:02:09 +05:00
Alex Ant 653429a6d6 docs generation fix & docker compose env missing params fix 2025-11-24 19:01:23 +05:00
Alex Ant c2250e1817 chore(release): publish 2025-11-24 17:35:56 +05:00
Alex Ant 92ca48d65f make common group room & fix deploy workflow 2025-11-24 17:33:17 +05:00
Alex Ant 2f53d9a79d chatcoop prerelease 2025-11-24 11:44:59 +05:00
Alex Ant d025bfe2da Merge branch 'element' into dev 2025-11-22 15:28:41 +05:00
Alex Ant fbc2460ac4 typeorm fix expiration and add complete & fail date 2025-11-22 15:23:49 +05:00
Alex Ant 76c2800275 extensions migration system, extensions logs, powerup log 2025-11-22 10:59:08 +05:00
Alex Ant 536bf984ee powerup page 2025-11-21 19:33:09 +05:00
Alex Ant 23f8af43ee chore(release): publish 2025-11-20 20:26:39 +05:00
Alex Ant eba54bf2e7 fix amount precision in document 2025-11-20 20:25:29 +05:00
Alex Ant 50270e6541 rub to axon convertation 2025-11-20 20:18:36 +05:00
Alex Ant 5f7d9e80ee chore(release): publish 2025-11-20 12:56:57 +05:00
Alex Ant a71ef35d5c delegate fees fund and convert_to_axon action 2025-11-20 12:55:59 +05:00
Alex Ant 2cf5291831 chore(release): publish 2025-11-19 19:40:44 +05:00
Alex Ant 75e3e9dd7b fix convertation system 2025-11-19 19:40:08 +05:00
Alex Ant 7a75365d2f chore(release): publish 2025-11-19 15:38:27 +05:00
Alex Ant 7a23d59aa1 injection AXON to any coop 2025-11-19 15:37:20 +05:00
Alex Ant 2583423600 chore(release): publish 2025-11-19 12:28:35 +05:00
Alex Ant 1d748a0424 generate and process AXON - GOVERN convertation 2025-11-19 12:26:05 +05:00
Alex Ant 86172790d1 chore(release): publish 2025-11-18 11:51:59 +05:00
Alex Ant 92c9103059 currentUser -> session stores refactoring 2025-11-18 11:50:07 +05:00
Alex Ant 16393dbc9d chore(release): publish 2025-11-17 22:10:07 +05:00
Alex Ant e841b0ef03 fix login bug 2025-11-17 22:09:06 +05:00
Alex Ant 27c7ae9d76 chore(release): publish 2025-11-17 14:04:59 +05:00
Alex Ant dc3a40675d fix health check 2025-11-17 14:04:07 +05:00
Alex Ant 31e65ac2e3 chore(release): publish 2025-11-17 13:32:08 +05:00
Alex Ant c2444d1b09 health-check route 2025-11-17 13:31:37 +05:00
Alex Ant 2faadae4cf chore(release): publish 2025-11-17 12:29:25 +05:00
Alex Ant d24e51c355 some fixes 2025-11-17 12:28:37 +05:00
Alex Ant 8ed8af1b9c chore(release): publish 2025-11-16 14:10:06 +05:00
Alex Ant 5f6b416f26 installer fix, parser init fix, service worker fix 2025-11-16 14:06:43 +05:00
Alex Ant 3671c05573 chore(release): publish 2025-11-15 18:53:03 +05:00
Alex Ant db6eed9292 pre-release 2025-11-15 18:51:37 +05:00
Alex Ant 4c903ba0bb chore(release): publish 2025-11-14 12:26:30 +05:00
Alex Ant 9eee216065 fix parser initializator 2025-11-14 12:25:58 +05:00
Alex Ant 9e1dd02d70 chore(release): publish 2025-11-14 11:58:32 +05:00
Alex Ant b986bf9bcc update 2025-11-14 11:56:51 +05:00
Alex Ant a3e0e62efc chore(release): publish 2025-11-14 11:25:42 +05:00
Alex Ant 8646c5acf6 boolean factory fix 2025-11-14 11:24:49 +05:00
Alex Ant dab9465755 chore(release): publish 2025-11-13 20:32:34 +05:00
Alex Ant 3a076cc4ea fix parser 2025-11-13 20:31:38 +05:00
Alex Ant 1b6d1ea0ef chore(release): publish 2025-11-13 20:19:25 +05:00
Alex Ant c490284c65 initializator for parser 2025-11-13 20:17:19 +05:00
Alex Ant c3ca329138 installation step 2025-11-13 18:33:43 +05:00
Alex Ant d0f73c32bb chore(release): publish 2025-11-13 11:18:18 +05:00
Alex Ant 79e0da7cf2 fix user notifications 2025-11-13 11:16:22 +05:00
Alex Ant b261f505aa chore(release): publish 2025-11-12 18:06:40 +05:00
Alex Ant 65c4c95cbe connection page and regcoop payer 2025-11-12 18:05:34 +05:00
Alex Ant 5b3c9c9055 chore(release): publish 2025-11-11 19:12:42 +05:00
Alex Ant d32d4f845c coop connect page 2025-11-11 19:11:25 +05:00
Alex Ant 637950d37f chore(release): publish 2025-11-10 21:01:05 +05:00
Alex Ant 0e1ede58e5 some installation process fixs and regcoop modify instead of emplace when exists 2025-11-10 20:59:36 +05:00
Alex Ant e914a11768 chore(release): publish 2025-11-10 12:47:47 +05:00
Alex Ant 73d66dab35 command+k menu 2025-11-10 12:46:45 +05:00
Alex Ant 634766a08e chore(release): publish 2025-11-10 09:47:19 +05:00
Alex Ant 5fbc450e96 ConnectionStepper 2025-11-10 09:45:11 +05:00
Alex Ant 5acd7ae2c4 chore(release): publish 2025-11-09 20:48:49 +05:00
Alex Ant 09578ddc5d provider integration 2025-11-09 20:47:16 +05:00
Alex Ant 8270801711 chore(release): publish 2025-11-09 16:26:20 +05:00
Alex Ant de91c3cfc8 sdk public constructor 2025-11-09 16:22:43 +05:00
Alex Ant 06888a2061 chore(release): publish 2025-11-08 20:58:10 +05:00
Alex Ant cde2d33d03 chore(release): publish 2025-11-08 20:56:36 +05:00
Alex Ant 9cf89f95d1 fix 2025-11-08 20:55:28 +05:00
Alex Ant 20486e15ea chore(release): publish 2025-11-08 14:18:32 +05:00
Alex Ant 420c403da1 chore(release): publish 2025-11-08 14:14:44 +05:00
Alex Ant cb8fdf3461 startup notification 2025-11-08 14:14:04 +05:00
Alex Ant 0937d08770 chore(release): publish 2025-11-08 13:59:05 +05:00
Alex Ant 9b1fd8e1aa chore(release): publish 2025-11-08 13:58:14 +05:00
Alex Ant d87861142c chore(release): publish 2025-11-08 12:47:15 +05:00
Alex Ant ff5fc1d0f3 update publish scripts 2025-11-08 12:46:45 +05:00
Alex Ant d5a671cbb8 update config 2025-11-08 12:42:51 +05:00
Alex Ant d0508cbd96 chore(release): publish 2025-11-08 11:58:39 +05:00
Alex Ant 12098cf8ac server provisioned notification workflow 2025-11-08 11:48:35 +05:00
Alex Ant 2557c35bea add dev mode to smart-contracts, make notifications external accessable, make boot works 2025-11-07 20:20:17 +05:00
Alex Ant 11f23bbd77 chore(release): publish 2025-11-07 12:25:13 +05:00
Alex Ant 2b2ec729ac external notification resolver 2025-11-07 12:24:18 +05:00
Alex Ant 9292acec8c chore(release): publish 2025-11-06 21:49:46 +05:00
Alex Ant 3095ed5375 installation process 2025-11-06 20:06:40 +05:00
Alex Ant a333e303c1 improve installation process, make privacy policy public and acceptable 2025-11-05 22:13:10 +05:00
Alex Ant bd8512feab clear install 2025-11-03 19:16:57 +05:00
Alex Ant c235fc4a7a fix save wif 2025-11-03 15:55:55 +05:00
Alex Ant 9c283b9aee invite page & set soviet refactoring 2025-11-03 14:16:37 +05:00
Alex Ant 4e824d739b all notifications going throw novu now 2025-11-03 11:09:34 +05:00
Alex Ant fcbbc792b0 make installation starts 2025-11-02 21:58:44 +05:00
Alex Ant ba7cf8e79e Merge branch 'installation' into refactoring 2025-11-02 09:48:41 +05:00
Alex Ant 4ab49d8e43 delete application, domain and infra module aggregators 2025-11-02 09:45:19 +05:00
Alex Ant 7f864db80d wtf 2025-11-01 21:39:23 +05:00
Alex Ant 772a163693 parser fix 2025-11-01 18:04:11 +05:00
Alex Ant c8e106f272 update 2025-11-01 17:53:16 +05:00
Alex Ant ade4f0a1d4 ssr bug fix 2025-11-01 17:14:23 +05:00
Alex Ant 4fcf933dd1 fix: no SSR for some modules 2025-11-01 14:03:45 +05:00
Alex Ant 1fb60771ca fix: synchronize: true in typeorm.module.ts 2025-10-31 23:32:31 +05:00
Alex Ant 2cd391e106 hide capital app for now 2025-10-31 19:52:24 +05:00
Alex Ant 196f3bcba1 level & energy system 2025-10-27 21:36:02 +05:00
Alex Ant bc73c5ca86 contract gamification 2025-10-27 16:19:56 +05:00
Alex Ant f4badc15d1 редактирование участника, шаблон "о себе" 2025-10-27 12:50:51 +05:00
Alex Ant 76f731d0cd poll requests for update content 2025-10-26 18:27:08 +05:00
Alex Ant 5e22858ab4 some bugs and permissions 2025-10-26 15:45:15 +05:00
Alex Ant bc1c54c43e notifications on novu improve (sanity), make a coop settings for chairman and select default workspace / page 2025-10-25 20:19:10 +05:00
Alex Ant 95ac0d36f8 a lot of changes 2025-10-22 19:53:40 +05:00
Alex Ant 2bee3eb179 unique participants count and workflow notification 2025-10-21 17:13:20 +05:00
Alex Ant 52165f2a0b microWallet & workspaceMenu rework 2025-10-20 21:12:53 +05:00
Alex Ant 9f2da226f4 extensionsStore refactoring and make it with multi-desktops 2025-10-20 17:26:24 +05:00
Alex Ant 46b999317d result submission 2025-10-20 15:35:32 +05:00
Alex Ant 80bce3c9c2 refactoring, optimization and improvement 2025-10-17 19:33:07 +05:00
Alex Ant 90b25b4bd8 fab | visual refactoring 2025-10-16 20:34:56 +05:00
Alex Ant f562f1eafd Merge branch 'dev' into capital 2025-10-16 10:27:23 +05:00
Alex Ant ff520622ff chore(release): publish 2025-10-16 10:25:26 +05:00
Alex Ant 1a6c6a4f31 fix parser 2025-10-16 01:32:23 +05:00
Alex Ant 4a9e0388f4 fix parser start block 2025-10-16 01:30:55 +05:00
Alex Ant e47b2a971e chore(release): publish 2025-10-15 23:12:28 +05:00
Alex Ant 96777ec095 delete mongo transactions 2025-10-15 23:00:23 +05:00
Alex Ant 52019deef5 ComponentPage 2025-10-15 22:51:30 +05:00
Alex Ant c26a534ff6 secondary route menu 2025-10-15 20:36:24 +05:00
Alex Ant e5be5c0b05 syncers refactoring and frontend 2025-10-15 13:49:20 +05:00
Alex Ant 5b07f52641 hard update 2025-10-14 22:06:23 +05:00
Alex Ant e0109cb530 pushResult подготовка и отправка документов с ожиданием решения совета 2025-10-12 19:41:53 +05:00
Alex Ant 1496dd1598 approvals with auth start 2025-10-07 19:41:54 +05:00
Alex Ant efcaeb2f00 set plan and other | going to voting 2025-10-07 11:31:56 +05:00
Alex Ant 1252946fe5 permissions and booter 2025-10-05 10:55:53 +05:00
Alex Ant 2913d5f636 some widgets and filters 2025-10-02 17:35:06 +05:00
Alex Ant c50beb3d43 some updates 2025-09-29 13:34:52 +05:00
Alex Ant 46b559001a some refactoring 2025-09-28 16:38:19 +05:00
Alex Ant 55a2e2e060 agreement repo and mutation 2025-09-28 15:48:56 +05:00
Alex Ant 12364a052f desktop agremeent features 2025-09-28 13:18:38 +05:00
Alex Ant 45ac971035 entity log 2025-09-28 11:19:50 +05:00
Alex Ant 26b818c9a6 update 2025-09-27 17:58:04 +05:00
Alex Ant 007836f52e contributor registration #1 2025-09-25 20:16:58 +05:00
Alex Ant ed0982770b mutations for all document generations 2025-09-25 12:47:17 +05:00
Alex Ant dcc72e90b7 factory capital documents basic templates 2025-09-24 21:52:34 +05:00
Alex Ant b428660944 project voting skeleton 2025-09-23 12:56:22 +05:00
Alex Ant 0008dcc0ee time tracker #3 2025-09-22 22:56:45 +05:00
Alex Ant e3fbf3981d time tracker personal time limit 2025-09-22 17:04:09 +05:00
Alex Ant c5e9244a08 selector fix 2025-09-21 23:55:11 +05:00
Alex Ant e1fd823c13 time tracker 2025-09-21 23:53:21 +05:00
Alex Ant 346f0d636c time tracker sdk queries 2025-09-21 20:42:28 +05:00
Alex Ant 315a259e5b lang refactoring 2025-09-21 16:09:19 +05:00
Alex Ant 3157a03c74 time tracking service prototype 2025-09-21 14:50:18 +05:00
Alex Ant 045807a748 edit project and create commit simple button on tasks 2025-09-20 20:52:53 +05:00
Alex Ant ea0e12ac80 update 2025-09-20 17:33:07 +05:00
Alex Ant 692060af6c update 2025-09-19 20:42:16 +05:00
Alex Ant d8b23a4b5c issues create 2025-09-19 10:41:29 +05:00
Alex Ant 074bf55d6c stories complete 2025-09-18 16:28:51 +05:00
Alex Ant 84e505b817 before make pain 2025-09-17 21:23:52 +05:00
Alex Ant a12e6ff603 taskPage 2025-09-17 16:40:18 +05:00
Alex Ant b01d662a1c components 2025-09-16 23:28:34 +05:00
Alex Ant e631dad499 getState, fix some bugs 2025-09-16 17:28:01 +05:00
Alex Ant 7ab0bfdd6b optional right drawer 2025-09-15 22:52:17 +05:00
Alex Ant 35a4db43fb add all entities and make extension store better 2025-09-13 01:02:21 +05:00
Alex Ant 094028006b make new queries and selectors 2025-09-11 22:21:53 +05:00
Alex Ant c12f42fcac some selectors, queries, features and entities 2025-09-11 19:10:37 +05:00
Alex Ant 3e498b098b refactoring #2 2025-09-11 13:03:21 +05:00
Alex Ant bbf1a1a544 refactoring 2025-09-11 12:15:05 +05:00
Alex Ant 42d57f30bf update names 2025-09-10 19:28:13 +05:00
Alex Ant 19967a9ad0 update 2025-09-10 15:23:33 +05:00
Alex Ant 9d1a8f12d8 main domain model 2025-09-10 14:22:14 +05:00
Alex Ant 57bebebe09 domain entities synced with blockchain 2025-09-08 13:47:40 +05:00
Alex Ant d19303cd62 typeorm repositories 2025-09-07 21:06:54 +05:00
Alex Ant 7c96e46a37 domain entities and infrastructure mappers 2025-09-07 20:44:28 +05:00
Alex Ant f75e68f8ce before refactoring 2025-09-06 16:42:29 +05:00
Alex Ant 9b922b00c1 coopgram base plugin 2025-09-06 01:17:31 +05:00
Alex Ant c8d9ee2457 migration fixs 2025-09-05 23:22:49 +05:00
Alex Ant a29f23086a update migrations 2025-09-05 23:07:27 +05:00
Alex Ant 4a76365df4 migrator fix 2025-09-05 22:56:08 +05:00
Alex Ant 972264844a migrator commands and subcommands 2025-09-05 22:46:39 +05:00
Alex Ant e23e6f3c1a some domain modules for capital 2025-09-05 15:03:48 +05:00
Alex Ant 03c10382d1 save deltas and actions and forks to main repositories 2025-09-04 14:21:20 +05:00
Alex Ant 3abe24aa18 refactoring, double bus refactoring, delete notificator module, desktop fix 2025-09-03 22:30:05 +05:00
Alex Ant 2a4eabcda5 update 2025-09-02 11:36:59 +05:00
Alex Ant 280d98c9c4 update docs 2025-09-02 11:35:46 +05:00
Alex Ant 227c5c80c8 chore(release): publish 2025-09-01 20:20:41 +05:00
Alex Ant edd00a6ab5 add capital to whitelist 2025-09-01 19:29:11 +05:00
Alex Ant d57bb17fc7 fix package.json 2025-09-01 19:00:32 +05:00
Alex Ant 098da5c868 small refactoring 2025-09-01 16:41:26 +05:00
Alex Ant dfc17bcc26 complete common tests 2025-09-01 14:18:54 +05:00
Alex Ant 7a5eab104e common capital tests 2025-09-01 11:37:54 +05:00
Alex Ant e0bf815377 cut some in docs 2025-08-27 22:48:37 +05:00
Alex Ant 2cbc6f2890 update 2025-08-27 22:46:38 +05:00
Alex Ant 5859b2c08b some fixs 2025-08-27 22:11:04 +05:00
Alex Ant 44eaa9dc8b economic fix 2025-08-27 22:03:06 +05:00
Alex Ant 49cd7a41dd documentation updates and some capital bug fix 2025-08-27 21:25:27 +05:00
Alex Ant 53de5ece21 test repair 2025-08-27 16:03:00 +05:00
Alex Ant 90dffa5fcc workflow 2025-08-27 00:19:13 +05:00
Alex Ant ac6cd0ca15 update 2025-08-27 00:05:24 +05:00
Alex Ant 0e970128ab update 2025-08-27 00:03:00 +05:00
Alex Ant 9fc373384d update 2025-08-26 23:23:28 +05:00
Alex Ant a76bd2ea55 fix header 2025-08-26 23:15:59 +05:00
Alex Ant 3c610b74d2 update package.json 2025-08-26 20:07:39 +05:00
Alex Ant 0623005ff4 effects, documents and processes 2025-08-26 19:58:20 +05:00
Alex Ant 8b032eba9a some docs 2025-08-25 20:11:42 +05:00
Alex Ant e3229b85fd before update 2025-08-24 11:40:24 +05:00
Alex Ant 1178431b8f update docx 2025-08-22 23:44:35 +05:00
Alex Ant 9d92c4a2b1 update docs 2025-08-22 17:54:10 +05:00
Alex Ant 0bda8707ce before delete anchors 2025-08-22 12:35:16 +05:00
Alex Ant 9cb7d3a0fd docs 2025-08-21 17:01:13 +05:00
Alex Ant f1204f948a update docx 2025-08-20 21:54:54 +05:00
Alex Ant 455b8dafb0 start make doc 2025-08-20 21:53:24 +05:00
Alex Ant fe5a32d95b some tests 2025-08-19 20:19:06 +05:00
Alex Ant b34f6f1690 direct coordinator rewards 2025-08-12 15:18:06 +05:00
Alex Ant a89bc35c6f before coordinators distribution change 2025-08-12 11:51:20 +05:00
Alex Ant e551b6c202 restore losted actions in fund contract 2025-08-08 20:27:09 +05:00
Alex Ant c6ed1afc51 test prepare 2025-08-08 20:24:20 +05:00
Alex Ant 4bedebb74d capital -> cooptypes 2025-08-08 19:38:25 +05:00
Alex Ant 741fc7eb05 готово к отладке 2025-08-08 16:51:46 +05:00
Alex Ant 000da1197c voting by vodyanov system 2025-08-03 16:45:11 +05:00
Alex Ant dd8be29d54 core / startproject 2025-08-01 17:51:15 +05:00
Alex Ant 099c2c7ff4 capital прием инвестиций и расчеты 2025-07-31 21:14:21 +05:00
Alex Ant 874232a152 Merge branch 'dev' into capital 2025-07-30 13:18:14 +05:00
Alex Ant d21f046e1f ignore DS_store 2025-07-30 13:17:58 +05:00
Alex Ant ef0e25878e Merge branch 'dev' into capital 2025-07-30 13:17:35 +05:00
Alex Ant cd37619af1 ledger - переход на debet и credit транзакции 2025-07-30 13:15:58 +05:00
Alex Ant 209ee18943 capital refactoring 2025-07-30 10:15:21 +05:00
Alex Ant aff7733a5b refactoring capital contract 2025-07-29 13:20:42 +05:00
Alex Ant 4bb65921e4 Merge branch 'dev' into capital 2025-07-29 10:02:03 +05:00
Alex Ant 26216a6a8c списание и отмена списания в истории 2025-07-29 10:01:28 +05:00
Alex Ant 60c3f11d7e fix 2025-07-29 00:40:24 +05:00
Alex Ant fcab08bfab история движений средств по счетам 2025-07-29 00:17:33 +05:00
Alex Ant e69ba466e1 chore(release): publish 2025-07-28 21:08:02 +05:00
Alex Ant 675e85ee09 fix precision 2025-07-28 20:26:31 +05:00
Alex Ant 36ec13b2c4 Merge branch 'ledger' into dev 2025-07-28 18:14:06 +05:00
Alex Ant 0b9c36e3ce Merge branch 'dev' into capital 2025-07-28 17:31:25 +05:00
Alex Ant 07cf895447 migration msg change 2025-07-28 17:30:10 +05:00
Alex Ant 028f21f0cf complete ledger contract 2025-07-28 17:14:06 +05:00
Alex Ant 77724e363d замена контракта fund на ledger 2025-07-27 13:13:58 +05:00
Alex Ant 15863d8a5a appendix еще не работает 2025-07-27 10:02:35 +05:00
Alex Ant b836af58f0 Merge branch 'dev' of github.com:coopenomics/mono into capital 2025-07-26 19:02:15 +05:00
Alex Ant ef5d880f14 chore(release): publish 2025-07-23 21:09:51 +05:00
Alex Ant 15fd03b4c0 устранена проверка времени подписи на голосовании 2025-07-23 21:09:07 +05:00
Alex Ant cac06f3e17 chore(release): publish 2025-07-23 15:13:43 +05:00
Alex Ant 6d81c62cdf setWif for cooperative 2025-07-23 15:12:30 +05:00
Alex Ant 60e319589d chore(release): publish 2025-07-20 21:35:08 +05:00
Alex Ant 7723a5785d Merge branch 'testnet' 2025-07-20 21:34:49 +05:00
Alex Ant dd982fe6e2 Merge branch 'dev' into testnet 2025-07-20 21:30:12 +05:00
Alex Ant c02dfeaa97 bug fix на составном префиксе кооператива при голосовании на общем собрании 2025-07-20 21:29:00 +05:00
Alex Ant a3d513afb0 update gitignore 2025-07-20 12:08:36 +05:00
Alex Ant 40fa99a1f9 смарт-контракт адаптирован к ТЗ, рыба контроллера 2025-07-20 12:06:46 +05:00
Alex Ant f26c996095 chore(release): publish 2025-07-15 17:17:20 +05:00
Alex Ant 012c4dfb9f fix 2025-07-15 17:15:14 +05:00
Alex Ant 9337ca2616 chore(release): publish 2025-07-15 16:59:47 +05:00
Alex Ant 5dcec4bcfd Merge branch 'testnet' 2025-07-15 16:57:48 +05:00
Alex Ant ed2e5e4d09 fix build image 2025-07-15 10:37:42 +05:00
Alex Ant 2249f52c32 fix build 2025-07-15 10:19:45 +05:00
Alex Ant f4ef5d2b53 notifications release 2025-07-15 09:59:21 +05:00
Alex Ant a92c5d6ce0 pwa configuration fix and novu subscribers fix 2025-07-14 13:38:34 +05:00
Alex Ant b61ceaead6 widget mode 2025-07-10 19:47:11 +05:00
Alex Ant 0672cfc8f3 delete notificator2 2025-07-10 17:29:43 +05:00
Alex Ant b5859a43cc Merge branch 'dev' into testnet 2025-07-10 16:49:33 +05:00
Alex Ant de1cd7c737 pwa push notifications 2 2025-07-10 16:45:07 +05:00
Alex Ant f588312ad3 pwa push notifications 2025-07-10 16:44:42 +05:00
Alex Ant 643ef1fa45 chore(release): publish 2025-07-08 14:17:04 +05:00
Alex Ant ec38a4ed33 reset and invite emails fix 2025-07-08 14:15:07 +05:00
Alex Ant defbd80ce0 fix contacts 2025-07-08 13:08:42 +05:00
Alex Ant f11b91e7fa auto reload on update 2025-07-08 12:56:05 +05:00
Alex Ant 58bb4d5a7f fix contacts bug 2025-07-08 12:43:01 +05:00
Alex Ant d92af60d9b лист подключенных кооперативов и документация 2025-07-08 10:14:04 +05:00
Alex Ant bca1e043bd chore(release): publish 2025-07-07 10:26:39 +05:00
Alex Ant 7719175e85 update 2025-07-07 10:13:31 +05:00
Alex Ant 046146c4d8 delete sdk docs from repo 2025-07-07 09:45:43 +05:00
Alex Ant 0e2f66b35a add gitignore 2025-07-07 09:45:06 +05:00
Alex Ant 56b9ff1dc9 docs, registration fix and connection button fix 2025-07-07 09:44:06 +05:00
Alex Ant dab8e78cd5 chore(release): publish 2025-07-06 23:15:03 +05:00
Alex Ant 67dd27b9a1 pwa rollback 2025-07-06 23:11:29 +05:00
Alex Ant 4605985a47 fix 2025-07-06 22:45:39 +05:00
Alex Ant 02ac27342a fix exlude error 2025-07-06 22:37:06 +05:00
Alex Ant cbc59a4a1c chore(release): publish 2025-07-06 21:48:06 +05:00
Alex Ant 02758d96c7 PWA service worker update 2025-07-06 21:44:36 +05:00
Alex Ant 12752134fd стол председателя 2025-07-06 17:32:43 +05:00
Alex Ant 504e70441b move exit button 2025-07-06 14:34:58 +05:00
Alex Ant 85cd16e793 navigation guard fix & desktop store left drawer mobile improve 2025-07-06 13:22:15 +05:00
Alex Ant aa2277f05e visual fixs 2025-07-06 11:46:21 +05:00
Alex Ant 27eb96722a chore(release): publish 2025-07-05 18:14:55 +05:00
Alex Ant 0215096c9e изменение принципа отображения кнопок действия 2025-07-05 18:13:31 +05:00
Alex Ant 90d8c51402 фикс гоночного бага при входе, демонстрация входящих оповещений всплывками 2025-07-05 16:45:51 +05:00
Alex Ant ba0bbfefe7 fix boot index 2025-07-05 13:02:25 +05:00
Alex Ant dd65673c1e pwa+ssr configuration 2025-07-05 12:54:30 +05:00
Alex Ant 2f3f8de682 fix 2025-07-05 10:18:32 +05:00
Alex Ant 3ce6fcad3a fix 2025-07-05 09:43:43 +05:00
Alex Ant 4302e7426e update quasar config 2025-07-04 23:09:25 +05:00
Alex Ant 4e246c35c8 try it 2025-07-04 22:44:43 +05:00
Alex Ant 0cbcae9624 update 2025-07-04 21:35:32 +05:00
Alex Ant 313aed606f update 2025-07-04 21:24:47 +05:00
Alex Ant aade735c79 Merge branch 'dev' into testnet 2025-07-04 21:17:50 +05:00
Alex Ant 1e5786a142 looking for bug 2025-07-04 21:14:45 +05:00
Alex Ant 3e5e5eeafa Merge branch 'dev' into testnet 2025-07-04 21:07:56 +05:00
Alex Ant 8a48ee8e14 logging 2025-07-04 21:04:56 +05:00
Alex Ant 6a89981c77 try add env to PWA 2025-07-04 19:59:55 +05:00
Alex Ant 03c014fdda rollback 2025-07-04 19:32:30 +05:00
Alex Ant ae683cd3c9 rollback env 2025-07-04 19:03:26 +05:00
Alex Ant a0dcae0882 pwa env inject 2025-07-04 18:34:43 +05:00
Alex Ant 6e839b210c update 2025-07-04 17:06:08 +05:00
Alex Ant c7f4f62e93 update package.json 2025-07-04 16:50:59 +05:00
Alex Ant 214aba8f01 pwa 2025-07-04 16:07:38 +05:00
Alex Ant 153c9d944f Merge branch 'notifications' into dev 2025-07-04 13:48:05 +05:00
Alex Ant 99a9d15871 pwa implementation 2025-07-04 13:47:42 +05:00
Alex Ant f6fb905ef8 Генерация, синхронизация и выдача данных для центра уведомлений 2025-07-04 13:12:33 +05:00
Alex Ant a5456452f6 http auth 2025-07-03 18:48:50 +05:00
Alex Ant 48cd9af739 Merge commit 'b9ae66e557f82e1265ae8d71c7fd6ec38509b5f1' into notifications 2025-07-03 18:09:23 +05:00
Alex Ant 01fac428d5 chore(release): publish 2025-07-03 18:05:11 +05:00
Alex Ant 3acce100f6 Merge branch 'testnet' 2025-07-03 18:04:50 +05:00
Alex Ant ba2dfe6598 Merge branch 'dev' into testnet 2025-07-03 18:04:27 +05:00
Alex Ant 46600c38a8 fast bug fix 2025-07-03 18:04:17 +05:00
Alex Ant b9ae66e557 чуть не забыл 2025-07-03 18:03:31 +05:00
Alex Ant bed9010918 some steps 2025-07-03 17:58:45 +05:00
Alex Ant d13ccbde71 bug: meet service improve 2025-07-02 13:27:31 +05:00
Alex Ant 2e2aac0413 chore(release): publish 2025-07-02 12:16:36 +05:00
Alex Ant 5f18cff344 fix: meet сервис на отправку уведомления о повторном собрании 2025-07-02 11:58:51 +05:00
Alex Ant 830300ea81 chore(release): publish 2025-07-01 21:03:01 +05:00
Alex Ant 9da2fcfbca migration 2025-07-01 20:31:21 +05:00
Alex Ant ece68df495 добавлены анимации для входа и выхода меню, улучшена логика рендеринга. Обновлены стили и добавлен ключ для перерендера при смене маршрутов. Исправлены мелкие недочеты в других компонентах. 2025-07-01 16:46:33 +05:00
Alex Ant 6a43ddbee9 chore(release): publish 2025-07-01 16:04:01 +05:00
Alex Ant ee58596ed0 Упрощен компонент ShadowHtml: удалена проверка на клиентскую среду, добавлены логи для отладки, обновлено состояние миграции с изменением тестового файла. 2025-07-01 15:45:10 +05:00
Alex Ant 5bce6e77ae Исправлено название типа платежа "Возврат средств" на "Возврат взноса" в PaymentDomainEntity. 2025-07-01 13:36:13 +05:00
Alex Ant 5dec45c0c8 Рефакторинг пользователя в сессии на фронте, правки визуального отображения всех страниц, управление исходящими платежами в шлюзе, возврат паевого взноса в кошельке. 2025-07-01 13:34:46 +05:00
Alex Ant b7ba6500e4 Новая архитектура для входящих и исходящих платежей 2025-06-26 20:20:58 +05:00
Alex Ant 2a263d749b returnByMoneyDecision Document 2025-06-25 20:34:12 +05:00
Alex Ant 3a21af48d1 Добавляем шаблон заявления на возврат паевого взноса с ЦК 2025-06-24 16:18:34 +05:00
Alex Ant cb3bfe99ce chore(release): publish 2025-06-24 09:07:26 +05:00
Alex Ant 328d51622f fix navigation guard bug 2025-06-24 08:50:05 +05:00
Alex Ant 38ead74697 поле ввода для поиска пайщика и извлечения его username 2025-06-23 20:56:07 +05:00
Alex Ant ffd4d72850 search by private account 2025-06-23 13:30:34 +05:00
Alex Ant d23d90a794 Корректное удаление кооперативного участка. Общее собранее уполномоченных. Правки title шаблонов. 2025-06-19 12:09:10 +05:00
Alex Ant c74036ad26 chore(release): publish 2025-06-19 10:00:51 +05:00
Alex Ant a146a9f424 Правка сообщений с ошибками на фронте, расширение шаблонов общих собраний на повторяемость. 2025-06-19 09:41:41 +05:00
Alex Ant ddc0c82cee chore(release): publish 2025-06-14 21:27:59 +05:00
Alex Ant bab09ac0d7 FATALITY BUG FIX 2025-06-14 21:26:41 +05:00
Alex Ant 8a6eb912c5 Revert "откатываем функционал"
This reverts commit e5642cd752.
2025-06-14 20:28:02 +05:00
Alex Ant b5fbd40ea3 update workflow #2 2025-06-14 17:56:14 +05:00
Alex Ant 46b1c085cd workflow fix 2025-06-14 16:09:22 +05:00
Alex Ant e5642cd752 откатываем функционал 2025-06-14 13:40:01 +05:00
Alex Ant be933e9085 fix hash router 2025-06-14 11:07:35 +05:00
Alex Ant 26769675c7 rollback 2025-06-14 09:07:57 +05:00
Alex Ant 672d6d67aa chore(release): publish 2025-06-13 20:45:38 +05:00
Alex Ant b3c6967ed9 update 2025-06-13 20:18:34 +05:00
Alex Ant 9a528b17e8 update 2025-06-13 20:15:39 +05:00
Alex Ant 4532932749 back to hash routing 2025-06-13 19:48:36 +05:00
Alex Ant 639a8e2f35 update 2025-06-13 19:20:02 +05:00
Alex Ant 56c2ab38c6 looking for bug 2025-06-13 17:08:27 +05:00
Alex Ant 9769cc0ed4 enable test mode 2025-06-13 16:15:50 +05:00
Alex Ant 96d66b2205 Обновлено использование индекса cooperatives в shared_registrator.hpp, добавлено новое действие delmeet в meet для удаления собраний. Исправлены проверки на наличие активных пайщиков и добавлено форматирование ссылок в компонентах голосования и повестки. Обновлены формы создания и перезапуска собраний для поддержки новых требований. 2025-06-13 13:26:32 +05:00
Alex Ant fea73109ca Добавлено использование функции parseLinks для форматирования ссылок в контексте и решениях в компонентах повестки и голосования. Упрощен код за счет удаления дублирующей функции parseLinks из компонентов. 2025-06-12 20:26:43 +05:00
Alex Ant e39c15c16f Добавлено форматирование ссылок в повестке собрания. Реализована функция для преобразования текста с ссылками в HTML-формат. Исправлено отображение контекста повестки. 2025-06-12 20:22:33 +05:00
Alex Ant 2a13f60bbd Добавлено новое поле 'level' в интерфейсы и DTO для обработки собраний, обновлены адаптеры для поддержки этого поля. Пофиксена проблема с Float на проценте кворума. 2025-06-12 20:14:55 +05:00
Alex Ant d8e9738d71 fix count 2025-06-12 19:17:45 +05:00
Alex Ant f38b78f821 подсчитываем количество активных пайщиков в кооперативах 2025-06-12 15:47:27 +05:00
Alex Ant 9c21630295 meet fix 2025-06-12 15:45:12 +05:00
Alex Ant ea502727ba добавлено действие migrate в meet и созданы миграции на добавление новых шаблонов 2025-06-12 14:56:42 +05:00
Alex Ant 72f4999162 release 2025-06-12 11:13:51 +05:00
Alex Ant adf1dbe635 Добавлены новые зависимости moment-timezone и @novu/js, обновлены версии существующих пакетов. Внесены изменения в модули и сервисы для поддержки новых функциональностей, включая адаптеры для расширений и обработку уведомлений. Реализована логика работы с временными зонами и обновлены интерфейсы для работы с аккаунтами и собраниями. 2025-06-11 21:39:13 +05:00
Alex Ant af2cdb1ca1 Добавлены новые поля для ответов голосования в схемы и DTO, а также обновлены интерфейсы и типы для поддержки голосования на собраниях. Реализована логика обработки ответов и обновлены компоненты для работы с новыми данными. Добавлена поддержка часового пояса в форматирование дат. 2025-06-10 14:14:44 +05:00
Alex Ant e499c0e990 Обновлены компоненты рабочего стола: добавлены маршруты по умолчанию для рабочих столов "participant" и "soviet", реализована логика выбора рабочего стола на основе прав пользователя и сохраненного выбора. Также добавлены новые методы для навигации и сохранения состояния рабочего стола. Исправлены ошибки в компонентах и улучшена верстка. 2025-06-09 17:54:33 +05:00
Alex Ant 206ab6793f almost works 2025-06-09 15:27:03 +05:00
Alex Ant e207c90fd4 add exampleData 2025-06-03 13:12:35 +05:00
Alex Ant c9864008a3 update 2025-06-03 10:54:14 +05:00
Alex Ant 5c89b27a87 update 2025-06-02 23:15:51 +05:00
Alex Ant 3c482ba017 update cooptypes and factory 2025-06-02 23:00:32 +05:00
Alex Ant 2d684411d4 Добавлена поддержка часового пояса в компоненты создания и перезапуска собраний. Обновлены метки для отображения времени с учетом часового пояса. Добавлены зависимости moment-timezone и обновлены конфигурации окружения. 2025-05-30 22:54:22 +05:00
Alex Ant 408ee36a8b Поправлена верстка. Обновлены компоненты для отображения новых данных о вопросах и результатах голосования. Изменен SDK для корректной мульти-подписи документа. 2025-05-30 21:42:07 +05:00
Alex Ant d9efbad80d Добавлены поля для председателя и секретаря в интерфейсы и DTO собраний, а также обновлены соответствующие селекторы и агрегаты документов. Введены сертификаты для председателя и секретаря, обновлены зависимости и исправлены ссылки на информацию о пользователях в документах. 2025-05-23 17:48:52 +05:00
Alex Ant 92e5c9f71a Добавлены новые типы сертификатов пользователей (EntrepreneurCertificate, IndividualCertificate, OrganizationCertificate, RepresentedByCertificate) и обновлены соответствующие DTO и интерфейсы. Обновлены зависимости в модулях и исправлены ссылки на информацию о подписантах в документах. 2025-05-23 14:14:56 +05:00
Alex Ant baef88be50 expandableDocument for meet 2025-05-23 10:49:03 +05:00
Alex Ant 7eff6ac504 meet aggregation DTO 2025-05-22 17:51:29 +05:00
Alex Ant a3ea4d4e18 chore(release): publish 2025-05-14 16:16:51 +05:00
Alex Ant f12047e36f повторная миграция соглашений 2025-05-14 15:41:34 +05:00
Alex Ant c77f37cc43 chore(release): publish 2025-05-14 12:45:52 +05:00
Alex Ant 8bc463b6d6 Обновлен changelog для версии v2025.5.14. 2025-05-14 12:22:46 +05:00
Alex Ant 953429f0da Добавлена поддержка смены темы (светлая/темная) с сохранением в localStorage, обновлены соответствующие компоненты и стили. Исправлено отображение данных участников в таблицах. 2025-05-14 12:12:00 +05:00
Alex Ant 8598050931 Добавлено поле аккаунта в тип Payment и обновлены соответствующие сущности и DTO, а также интерфейс для отображения ФИО плательщика. 2025-05-13 18:15:21 +05:00
Alex Ant 81566674a8 chore(release): publish 2025-05-13 14:56:31 +05:00
Alex Ant aba9f88352 factory bugs fix 2025-05-13 10:19:44 +05:00
Alex Ant 0093c6ddf7 fix bug on dinamic import 2025-05-13 09:10:04 +05:00
Alex Ant cd5a0c2fbc процесс голосования через подпись номера решения 2025-05-12 10:41:37 +05:00
Alex Ant b1c803d2f1 almost done 2025-05-09 17:31:41 +05:00
Alex Ant 3883b68669 SDK компилируется 2025-05-07 08:23:59 +05:00
Alex Ant 5111bb12cf Merge branch 'dev' into document2 2025-05-05 18:56:04 +05:00
Alex Ant cc2518a346 chore(release): publish 2025-05-05 18:55:34 +05:00
Alex Ant 6e98f4c953 update version 2 2025-05-05 18:55:01 +05:00
Alex Ant 8990b25970 update version 2025-05-05 18:53:25 +05:00
Alex Ant c0cdde8685 Merge branch 'testnet' 2025-05-05 18:50:23 +05:00
Alex Ant 7a7e20e4c2 откатываем новую таблицу кооперативов 2025-05-05 18:49:08 +05:00
Alex Ant 743ccd27b6 контракты скомпилировались 2025-05-05 18:46:44 +05:00
Alex Ant 155d9110c5 before contract migrate 2025-05-05 17:25:56 +05:00
Alex Ant 651e0b3445 chore(release): publish 2025-05-05 12:53:42 +05:00
Alex Ant 82f438cbe4 coop migration 2025-05-05 12:28:54 +05:00
Alex Ant c3c30b4e20 chore(release): publish 2025-05-05 11:17:29 +05:00
Alex Ant 8b4889ce83 Функционал миграций по версиям с интеграцией блокчейна и двух баз данных 2025-05-04 14:17:27 +05:00
Alex Ant 7ccaa0390d chore(release): publish 2025-05-04 12:42:52 +05:00
Alex Ant 5f0ed73d0d fix workflow #2 2025-05-04 12:42:23 +05:00
Alex Ant 54518b63d0 update publish workflow 2025-05-04 12:39:52 +05:00
Alex Ant 1c38f2e5fd update version 2025-05-04 12:37:53 +05:00
Alex Ant 7ba12707e1 chore(release): publish 2025-05-04 12:32:16 +05:00
Alex Ant 01992b26f4 fix workflows 2025-05-04 12:26:07 +05:00
Alex Ant 8670ba08d5 chore(release): publish 2025-05-04 12:08:16 +05:00
Alex Ant 67698757cf publish workflow 2025-05-04 12:07:22 +05:00
Alex Ant 833ef3f5d3 Обновление структуры signature_info: добавлен идентификатор подписи. Исправлено условие фильтрации истекших решений в ChairmanPlugin. 2025-05-04 11:40:00 +05:00
Alex Ant b104f14899 create document2 format 2025-05-04 11:02:25 +05:00
Alex Ant 60c86b1db8 chore(release): publish 2025-05-03 16:19:40 +05:00
Alex Ant b6d2965962 Дефолтные конфиги для расширений, фиксы визуалов, микросервис отмены вопроса с повестки 2025-05-03 15:56:08 +05:00
Alex Ant 1548552af3 chore(release): publish 2025-05-02 10:40:41 +05:00
Alex Ant 002cf8025b update 2025-05-01 22:51:07 +05:00
Alex Ant dfd7e4b62c update 2025-05-01 22:46:58 +05:00
Alex Ant e54380cd81 update 2025-05-01 22:29:40 +05:00
Alex Ant 09b3532793 update 2025-05-01 22:28:01 +05:00
Alex Ant b36e288958 update 2025-05-01 22:23:56 +05:00
Alex Ant bcd3c2739a update 2025-05-01 22:21:00 +05:00
Alex Ant 695e653461 update 2025-05-01 22:17:07 +05:00
Alex Ant b068ff810c update 2025-05-01 22:13:38 +05:00
Alex Ant 1a57f7a047 update lock-file 2025-05-01 21:57:50 +05:00
Alex Ant 90f955e48e update 2025-05-01 21:54:59 +05:00
Alex Ant 14fbce5a6b docs #2 2025-05-01 21:52:49 +05:00
Alex Ant 5cff407379 docs 2025-05-01 21:50:39 +05:00
Alex Ant 79b7ff21f5 Удалены отладочные логи из функций входа и выхода пользователя, а также обновлен способ проверки переменных окружения в функции работы с IndexedDB. 2025-05-01 18:23:56 +05:00
Alex Ant 65996a40e9 update 2025-05-01 16:46:36 +05:00
Alex Ant cf3566255a Добавлены отладочные логи в функции входа и выхода пользователя, а также обновлен способ загрузки переменных окружения в конфигурации Quasar для режима разработки. 2025-05-01 16:00:45 +05:00
Alex Ant 582c6fa36d Удалены отладочные логи из функции получения переменных окружения и обновлен middleware для инъекции переменных окружения в браузер с использованием ssrMiddleware. 2025-05-01 14:33:41 +05:00
Alex Ant aefba8b4df update 2025-05-01 13:42:00 +05:00
Alex Ant e43674fd29 Изменен способ определения переменных окружения в конфигурации Quasar: теперь они устанавливаются только для клиентской стороны, если режим не SSR. 2025-05-01 13:27:05 +05:00
Alex Ant 3856826e4d update 2025-05-01 11:34:33 +05:00
Alex Ant 00ed0b7691 update 2025-05-01 11:22:00 +05:00
Alex Ant aa456f6cff update 2025-05-01 11:12:30 +05:00
Alex Ant 3a9dfa643e update 2025-05-01 11:01:19 +05:00
Alex Ant 28580114da update 2025-05-01 10:31:01 +05:00
Alex Ant 8d47f118dd update 2025-05-01 10:29:53 +05:00
Alex Ant d7b45157ff update 2025-05-01 10:27:41 +05:00
Alex Ant db8a48284f update 2025-05-01 10:24:33 +05:00
Alex Ant b00b8d6ca3 update 2025-05-01 09:59:02 +05:00
Alex Ant 22b3d8f691 update 2025-05-01 09:51:43 +05:00
Alex Ant 432c1cd476 update 2025-05-01 09:41:37 +05:00
Alex Ant ed8e4a059a Обновление Dockerfile: замена команды установки зависимостей на принудительную, улучшение структуры образа. Изменение конфигурации GitHub Actions для отключения кеширования и упрощения сборки образов. 2025-05-01 09:36:57 +05:00
Alex Ant 571c1e31ac Оптимизация Dockerfile: добавление этапа сборки для зависимостей, упрощение копирования файлов и улучшение структуры образа. Обновление конфигурации GitHub Actions для сборки и публикации образов с использованием Docker Buildx. 2025-04-30 21:49:16 +05:00
Alex Ant b294d0eea0 Добавление шагов для отладки в GitHub Actions: вывод последних коммитов, текущей ветки и содержимого компонентов. Обновление комментариев и улучшение сборки образов без кеша. 2025-04-30 21:32:51 +05:00
Alex Ant 0b50fbc55d update 2025-04-30 21:29:50 +05:00
Alex Ant f5d5534290 update 2025-04-30 20:54:13 +05:00
Alex Ant 777eb7e398 Добавление middleware для инъекции переменных окружения в window.__ENV__ и рефакторинг функции получения переменных окружения с учетом различных сред (SSR, SPA). Теперь переменные окружения экспортируются через единую функцию getEnv(). 2025-04-30 20:53:02 +05:00
Alex Ant 9b1be55a9e Обновление конфигурации проекта: замена использования переменных окружения на новый модуль env, упрощение Dockerfile, улучшение сборки контейнеров и исправление мелких ошибок в компонентах. 2025-04-30 20:30:42 +05:00
Alex Ant ef4a7d1fa1 update 2025-04-30 18:37:23 +05:00
Alex Ant e6a7380aa5 update 2025-04-30 18:00:07 +05:00
Alex Ant 2f41591339 update 2025-04-30 17:47:08 +05:00
Alex Ant 8916ef0207 update 2025-04-30 17:45:28 +05:00
Alex Ant c3288f765e update 2025-04-30 17:35:42 +05:00
Alex Ant 11d631bee3 update 2025-04-30 17:33:58 +05:00
Alex Ant e3be78edb0 update 2025-04-30 17:29:37 +05:00
Alex Ant 59f01a8426 update 2025-04-30 17:17:55 +05:00
Alex Ant 76b7c8ef3b update 2025-04-30 17:05:31 +05:00
Alex Ant fbaae9ce76 update 2025-04-30 16:53:55 +05:00
Alex Ant e70b58a3df print env 2025-04-30 16:47:47 +05:00
Alex Ant ec551c9de8 VITE_ prefix 2025-04-30 16:39:33 +05:00
Alex Ant 7a8cb5835a правки SSR проблема рендиринга 2025-04-30 15:55:07 +05:00
Alex Ant 3b8ccf173b modify quasar conf 2025-04-30 12:49:12 +05:00
Alex Ant 172ce29665 update 2025-04-30 11:32:47 +05:00
Alex Ant e7d8a8d2c0 хук на деплой 2025-04-30 10:29:59 +05:00
Alex Ant cc7b43f4ba Добавление сборки и публикации нового Docker-образа для приложения @coopenomics/desktop в workflow. Обновление конфигурации сборки контейнеров. 2025-04-29 23:23:13 +05:00
Alex Ant 71425977f5 Обновление Dockerfile для упрощения сборки с использованием lerna. Добавление новых прокси-файлов для модулей @dicebear/core, @dicebear/collection и email-regex. Обновление зависимостей в package.json и pnpm-lock.yaml. Изменение конфигурации Quasar для оптимизации работы с новыми модулями. 2025-04-29 23:21:10 +05:00
Alex Ant 4549727d0b update workflow 2025-04-29 21:49:48 +05:00
Alex Ant e030d97059 workflow initialization 2025-04-29 21:40:51 +05:00
Alex Ant 13c1ca4f79 Merge branch 'main' into dev 2025-04-29 16:32:03 +05:00
Alex Ant 637d7c0db1 chore(release): publish 2025-04-29 16:27:09 +05:00
Alex Ant 7513a4b6d6 Merge branch 'testnet' 2025-04-29 16:21:40 +05:00
Alex Ant 76100ef07d update script 2025-04-29 16:21:22 +05:00
Alex Ant ab139209db update templates 2025-04-29 16:17:58 +05:00
Alex Ant c6a31baaab Добавление нового поля 'type' в сущность Payment и соответствующие изменения в DTO, схемах и селекторах. Обновление логики обновления статусов платежей с учетом нового поля. Исправление параметров в методах для получения заказов. 2025-04-29 13:36:04 +05:00
Alex Ant 7c9d4629b6 Добавление нового поля 'coop' в интерфейс Model и обновление схемы валидации для SelectBranchStatement. Обновление логики комбинирования данных в классе Factory для включения данных о кооперативе. Комментарии в стилях инпутов переведены в закомментированный вид. 2025-04-29 12:25:08 +05:00
Alex Ant 986858c20d Обновление скриптов публикации и логики миграции. Изменены команды для публикации на продакшн, добавлен новый скрипт publish-prod.sh. Обновлена логика удаления кандидатов в функции миграции и исправлены ссылки на индексы кандидатов в confirmreg.cpp. 2025-04-29 11:05:03 +05:00
Alex Ant a0551095dc lerna update 2025-04-29 10:16:02 +05:00
Alex Ant d0804c886a disable meet for temp 2025-04-29 10:11:14 +05:00
Alex Ant 1befca7e28 Добавление новых скриптов для развертывания и обновление интерфейсов для обработки голосования на общем собрании. Обновлены типы данных в интерфейсах и DTO, добавлены новые методы для генерации документов и улучшения функционала. Удалены устаревшие интерфейсы и компоненты, улучшена структура кода для повышения эффективности. 2025-04-28 23:55:55 +05:00
Alex Ant 3d4482157a Удаление устаревших интерфейсов и таблиц, связанных с кошельками. Обновление логики обработки платежей и интерфейсов для работы с новыми типами данных. Улучшение структуры компонентов и интерфейсов для более эффективного взаимодействия с API. Обновление стилей и компонентов пользовательского интерфейса для улучшения визуального восприятия. 2025-04-26 17:14:22 +05:00
Alex Ant 3cd71f1cd4 Обновление интерфейсов и DTO для работы с документами и собраниями. Добавлены новые поля и типы данных, улучшена обработка документов в сервисах. Удалены устаревшие селекторы и обновлены существующие для соответствия новым требованиям. Исправлены типы данных в селекторах и добавлены новые экспорты. 2025-04-25 23:44:32 +05:00
Alex Ant eebf443c1d Сейчас будем переводить сервис документов на агрегатор. Обновление интерфейсов и DTO для обработки документов. Изменены типы полей в интерфейсах DocumentPackageDomainInterface, DocumentRepository и DocumentDomainService для поддержки значений null. Добавлены проверки на наличие данных в DocumentDomainEntity и улучшена обработка документов в DocumentDomainService. Обновлены методы для работы с документами в API и хранилище. 2025-04-23 19:03:18 +05:00
Alex Ant 7aaf0b9375 Первичная мобильная верстка страниц стола совета 2025-04-23 12:39:23 +05:00
Alex Ant 5ff9675e60 Обновление конфигурации и добавление тестового режима для собраний. Изменен порт для state-history-endpoint, добавлен флаг TEST_MODE в контракте meet, обновлены проверки дат открытия и закрытия собраний. Обновлены интерфейсы и DTO для обработки новых типов данных и улучшения функционала. 2025-04-22 23:23:09 +05:00
Alex Ant 646390edff Добавление функционала для генерации бюллетеней голосования на общем собрании. Обновлены интерфейсы и DTO для обработки новых типов документов, добавлены методы для генерации бюллетеней и голосования. Исправлены типы данных для идентификаторов вопросов повестки. 2025-04-21 20:29:12 +05:00
Alex Ant b1958c5442 Добавление новых методов для подписания решений председателем и секретарем на общем собрании. Удален метод закрытия собрания, обновлены интерфейсы и DTO для обработки новых типов документов и действий. 2025-04-21 16:44:27 +05:00
Alex Ant 01b9590e81 Добавление новых типов и методов для работы с собраниями в модуле общих собраний. Реализованы методы получения данных о собраниях, обновлены интерфейсы и DTO для обработки информации о голосованиях и документах собраний. 2025-04-20 16:41:20 +05:00
Alex Ant 985a288bf3 Добавление новых входных типов и методов для получения данных о собраниях в модуле общих собраний. Реализованы методы getMeet и getMeets, а также обновлены интерфейсы и DTO для обработки данных. 2025-04-20 12:56:42 +05:00
Alex Ant 01dcfb88e7 Обновление документации и DTO для модуля общих собраний. Добавлены описания для полей в DTO, улучшена структура и добавлены новые типы данных для обработки информации о собраниях и голосованиях. 2025-04-20 11:59:29 +05:00
Alex Ant 76415a8dec Добавление модуля общих собраний и расширение DTO для документов, включая новые интерфейсы и классы для обработки документов собраний и решений. 2025-04-20 11:39:27 +05:00
Alex Ant 586b7c14f8 meet module & extends all documents DTOs 2025-04-18 15:27:35 +05:00
Alex Ant 0ee4bf2b25 update release script 2025-04-16 22:03:36 +05:00
Alex Ant 5673e8abe8 update releases script 2025-04-16 22:02:08 +05:00
Alex Ant c446e32a8a generate changelog releases 2025-04-16 21:42:37 +05:00
Alex Ant eee9392fb4 make Templates correct 2025-04-16 20:08:50 +05:00
Alex Ant 82a3961608 move templates #1 2025-04-16 20:08:24 +05:00
Alex Ant f8689fc409 wtf?! 2025-04-16 20:06:49 +05:00
Alex Ant 7de93e1780 add meet contract, cooptypes and empty documents templates 2025-04-16 19:58:49 +05:00
Alex Ant 19e742b7d1 desktop bug fixs 2025-04-15 15:57:55 +05:00
Alex Ant e3f0250fd2 remove Env.ts file from desktop and move params to .env 2025-04-15 12:27:29 +05:00
Alex Ant e4b39b89f3 fix system contract action name 2025-04-15 10:27:53 +05:00
Alex Ant d2888b49e8 fix contracts build 2025-04-15 10:14:07 +05:00
Alex Ant b97ed49a60 coopStore release 2025-04-11 18:29:07 +05:00
Alex Ant 65936dc85b refactoring desktop 2025-04-10 11:40:14 +05:00
Alex Ant b1d02ee302 claim -> result, result -> assignment 2025-04-06 12:56:55 +05:00
Alex Ant cca47abb9c before make refactoring 2025-04-06 11:25:29 +05:00
Alex Ant c810ba62b4 shared actions 2025-04-05 11:36:06 +05:00
Alex Ant dcce7ffacc loan contract 2025-04-03 18:52:53 +05:00
Alex Ant e9fb137146 contracts refactoring 2025-04-03 09:36:48 +05:00
Alex Ant 7100c9c300 appStore & medusa component & capital contract (start rework) 2025-03-28 18:42:28 +05:00
Alex Ant e9b5496387 refactoring participant widget to page 2025-03-27 19:14:02 +05:00
Alex Ant 87ecde390d chore(release): publish 2025-03-27 18:13:33 +05:00
Alex Ant 8b33c66e22 update mutation simplification 2025-03-27 17:30:31 +05:00
Alex Ant b3bbb6624e make participants editable 2025-03-27 14:32:23 +05:00
Alex Ant 2ddac45612 change decision dto and logout on some auth problems 2025-03-26 22:18:12 +05:00
Alex Ant ddd7d47516 Merge pull request #201 from coopenomics/kamilocean/sprint-4
Kamilocean/sprint 4
2025-03-26 20:09:37 +03:00
Alex Ant a770072809 README.md for cleos wallet 2025-03-26 18:27:05 +05:00
Alex Ant 5b38ce0b37 make cleos separated component 2025-03-26 18:19:52 +05:00
Alex Ant 804708076a make cleos wallet accessable 2025-03-26 13:08:04 +05:00
Alex Ant a0ad71a524 make unnesassary result 2025-03-26 11:42:05 +05:00
Alex Ant 9d03694ea7 some updates on contracts and tests 2025-03-22 13:58:51 +05:00
Alex Ant 7a619e0e9e update 2025-03-21 17:58:33 +05:00
Alex Ant 6bd5eb7930 Merge branch 'dev' into TSK-900 2025-03-19 10:58:53 +05:00
Alex Ant 744d1859d5 Создание кошелька при регистрации участника 2025-03-19 10:58:20 +05:00
kamilocean 34a61f49f6 Merge branch 'dev' into kamilocean/sprint-4 2025-03-17 16:07:50 +03:00
kamilocean 08c31171a0 WIP: fix EditableActions instead of ActionButton 2025-03-17 16:07:28 +03:00
Ilia Smirnov 40addbf595 Merged readme fixes 2025-03-17 12:52:02 +03:00
Ilia Smirnov abd6da0a37 Merge branch 'dev' of github.com:coopenomics/mono into TSK-878 2025-03-17 12:49:40 +03:00
Alex Ant 794d1c5019 Merge pull request #217 from coopenomics/dacom-dark-sun/sprint-4
Спринт-4 | dacom-dark-sun
2025-03-17 09:57:34 +03:00
Alex Ant b8d048bf3c README.md 2025-03-17 11:55:18 +05:00
Ilia Smirnov bb7b650ebc docs(install): updated installation instructions 2025-03-17 09:44:10 +03:00
kamilocean 5253de9279 WIP: add EditableOrganizationCard 2025-03-16 21:04:40 +03:00
Alex Ant 094945e350 make it works 2025-03-14 13:20:41 +05:00
kamilocean eb6b58e6db Merge branch 'dev' into kamilocean/sprint-4 2025-03-13 23:54:25 +03:00
kamilocean 7bcb6e30a7 fix: add Zeus type 2025-03-13 23:54:02 +03:00
kamilocean 44ab386835 WIP: EditableAccountCard 2025-03-13 00:30:00 +03:00
6152 changed files with 424433 additions and 1047737 deletions
Vendored
BIN
View File
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
{
"agentCanUpdateSnapshot": true
}
+8
View File
@@ -0,0 +1,8 @@
---
description:
globs:
alwaysApply: true
---
ВАЖНО!!! Мы работаем в МОНО-репозитории pnpm, любая установка пакетов производится ЧЕРЕЗ ФИЛЬТР компонента в корне: pnpm add glob --filter notificator2.
ВАЖНО!!! НИКОГДА НЕ ЗАПУСКАЙ ПРИЛОЖЕНИЕ ИЛИ ЕГО СБОРКУ!!! Просто отчитайся что всё сделал.
+151
View File
@@ -0,0 +1,151 @@
---
description:
globs: components/controller/**
alwaysApply: false
---
# NestJS Controller и Чистая архитектура
## Основные принципы
1. Чистая архитектура - главный архитектурный подход. Пиши код с комментариями.
2. Типы IName, IChecksum256, ITimePointSec - это просто строки.
3. Домен должен быть полностью изолирован от инфраструктурных деталей.
4. Направление зависимостей - внутрь (к ядру, домену).
5. Домен, инфраструктура и приложение связаны через App.module, НЕ НУЖНО импортировать их друг в друга, а достаточно просто импортировать домен на уровне приложения чтобы использовать все экспорты домена.
## Структура проекта
- `domain/` - доменный слой (бизнес-логика, независимая от инфраструктуры)
- `infrastructure/` - инфраструктурный слой (адаптеры к внешним системам)
- `modules/` - слой приложения (DTO, резолверы, сервисы)
## Слои и их взаимодействие
1. **Домен**:
- Содержит чистые доменные интерфейсы и реализованные на них сущности
- НЕ должен зависеть от `cooptypes` (инфраструктурный контракт)
- Использует собственные доменные типы (например, Date вместо строковых timestamp)
- Интерфейсы портов описывают взаимодействие с внешними системами
2. **Инфраструктура**:
- Содержит адаптеры к внешним системам (блокчейн, БД и т.д.)
- Осуществляет преобразование между доменными и инфраструктурными типами
- Общая логика преобразования выносится в утилитарные классы (например, `DomainToBlockchainUtils`)
3. **Модули (приложение)**:
- DTO имплементируют доменные интерфейсы напрямую
- Резолвер вызывает сервис, который принимает DTO
- Сервис передает объекты DTO в интерактор домена
## Поток данных
1. Резолвер принимает входные данные и передает их в сервис
2. Сервис вызывает интерактор домена, передавая ему объекты DTO (имплементирующие доменный интерфейс)
3. Интерактор выполняет бизнес-логику и взаимодействует с портами домена
4. Адаптеры (инфраструктурный слой) преобразуют доменные объекты в формат внешних систем (JSON для хранения мета-данных, ISO для дат и т.д.)
5. Адаптеры возвращают результаты в домен, домен возвращает данные в сервис, который возвращает их в резолвер
## Правила преобразования данных
1. Преобразование DTO → доменный объект: не требуется, если DTO имплементирует доменный интерфейс
2. Преобразование доменный объект → инфраструктурный тип: происходит в адаптерах
3. Преобразование документов, дат и других сложных объектов: используются утилитарные классы уровня инфраструктуры
## Работа с типами данных
1. **Даты**:
- В доменных интерфейсах используется тип `Date`
- В DTO используется тип `Date` с декораторами `@IsDate()` и `@Type(() => Date)`
- В инфраструктурном слое происходит преобразование `Date` → `string` (ISO формат)
```typescript
// Доменный интерфейс
interface MeetDomainInterface {
open_at: Date;
}
// DTO
@Field(() => Date)
@IsDate()
@Type(() => Date)
open_at!: Date;
// Адаптер
const blockchainData = {
open_at: domainToBlockchainUtils.convertDateToBlockchainFormat(data.open_at)
};
```
2. **Документы**:
- В доменных интерфейсах используется собственный тип `SignedDocumentDomainInterface<T>`
- В DTO используется соответствующий DTO-класс (например, `SignedDigitalDocumentInputDTO`)
- В инфраструктурном слое происходит преобразование между форматами (meta-поля в JSON и т.д.)
## Типовые ошибки
1. **Нарушение изоляции домена**: домен должен быть изолирован от инфраструктуры. Не используйте импорты из `cooptypes` в доменных интерфейсах.
```typescript
// Неправильно
import { MeetContract } from 'cooptypes';
export type VoteDomainInterface = MeetContract.Actions.Vote.IInput;
// Правильно
export interface VoteDomainInterface {
coopname: string;
hash: string;
// ... доменные поля
}
```
2. **Преобразование в неправильном слое**: преобразование доменных объектов в инфраструктурные типы должно происходить в адаптерах, а не в доменном слое или сервисе.
```typescript
// Неправильно (в доменном интеракторе)
async vote(data: VoteDomainInterface) {
const blockchainData = { ...data, meta: JSON.stringify(data.meta) };
// ...
}
// Правильно (в адаптере)
async vote(data: VoteDomainInterface) {
const blockchainData = this.convertToBlockchainFormat(data);
// ...
}
```
3. **Дублирование логики преобразования**: общая логика преобразования должна быть вынесена в утилитарные классы.
```typescript
// Неправильно (дублирование в разных адаптерах)
// В MeetAdapter
private convertDoc(doc) { return { ...doc, meta: JSON.stringify(doc.meta) }; }
// В BranchAdapter
private convertDoc(doc) { return { ...doc, meta: JSON.stringify(doc.meta) }; }
// Правильно (общий утилитарный класс)
// DomainToBlockchainUtils
convertSignedDocumentToBlockchainFormat(doc) {
return { ...doc, meta: JSON.stringify(doc.meta) };
}
```
## Примеры
```typescript
// Доменный интерфейс - чистый, без зависимостей от инфраструктуры
export interface VoteOnAnnualGeneralMeetInputDomainInterface {
coopname: string;
hash: string;
member: string;
ballot: VoteItemInputDomainInterface[];
}
// DTO имплементирует доменный интерфейс напрямую
@InputType('VoteOnAnnualGeneralMeetInput')
export class VoteOnAnnualGeneralMeetInputDTO implements VoteOnAnnualGeneralMeetInputDomainInterface {
@Field(() => String)
@IsString()
coopname!: string;
// ... остальные поля
}
// Адаптер преобразует доменный объект в инфраструктурный тип
async vote(data: VoteOnAnnualGeneralMeetInputDomainInterface): Promise<TransactionResult> {
// Преобразуем доменный объект в инфраструктурный тип
const blockchainData: MeetContract.Actions.Vote.IInput = {
coopname: data.coopname,
hash: data.hash,
// ... преобразование других полей
};
// ... отправка данных во внешнюю систему
}
```
@@ -0,0 +1,41 @@
---
description: SDK мутации и запросы для подключения на рабочем столе
globs:
alwaysApply: false
---
Бэкенд реализован здесь components/controller, sdk здесь components/sdk.
Пример создания Query к API через SDK:
``` ts
async function loadBranches(data: IGetBranchesInput): Promise<IBranch[]> {
const { [Queries.Branches.GetBranches.name]: output } = await client.Query(
Queries.Branches.GetBranches.query,
{
variables: {
data
}
}
);
return output;
}
```
Такие запросы обычно хранятся в папке Entities и вызываются из его store.
Пример мутации:
``` ts
async function selectBranch(data: ISelectBranchInput): Promise<boolean>{
const {[Mutations.Branches.SelectBranch.name]: result} = await client.Mutation(Mutations.Branches.SelectBranch.mutation, {variables: {
data
}})
return result
}
```
Мутации обычно храним в папке features.
Как можешь обратить внимание, все мутации и запросы строятся по одному шаблону. Вся необходимая информация уже есть в SDK.
+12
View File
@@ -0,0 +1,12 @@
---
alwaysApply: true
globs: **/desktop/**
---
# DESKTOP
- Пишем код фронтенда в архитектуре Feature Sliced Design (FSD).
- Стиль шаблонов PUG на composite API.
- Всегда создаём index.ts файлы для vue компонент.
- Создавай индексные файлы для vue через export {default as NAME} from './where', а на уровне выше, если это необходимо, делай export * from './ui'
- Всегда удаляем неиспользуемые импорты.
- Не используем emit избыточно. Вместо них используем композабл функции моделей фич, если необходимо. Emit только в крайнем случае.
+62
View File
@@ -0,0 +1,62 @@
---
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
@@ -0,0 +1,96 @@
---
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' %}`
+225
View File
@@ -0,0 +1,225 @@
# Архитектура расширений в MonoCoop
## Основные принципы
1. Расширения построены на модулях NestJS с использованием шаблона "Порты и адаптеры"
2. Каждое расширение наследуется от `BaseExtModule` и реализует интерфейс `OnModuleInit`
3. Расширения могут взаимодействовать с блокчейном через соответствующие порты
4. Конфигурации расширений хранятся в БД и описываются с помощью Zod-схем
5. Расширения регистрируются в глобальном реестре `AppRegistry`
## Структура расширения
Минимальная структура расширения включает:
- `XXX-extension.module.ts` - основной модуль расширения
- `package.json` - информация о пакете
- `README.md` - документация
- `INSTALL.md` - инструкции по установке
- `CHANGELOG.md` - история изменений
## Создание нового расширения
1. Создайте директорию для расширения в `components/controller/src/extensions/`
2. Создайте основной класс расширения, наследующийся от `BaseExtModule`
3. Определите Zod-схему для конфигурации
4. Реализуйте метод `initialize()`
5. Зарегистрируйте расширение в `extensions.registry.ts`
6. Добавьте расширение в список дефолтных приложений в `extension-domain.service.ts`
## Пример структуры модуля расширения
```typescript
// XXX-extension.module.ts
export class XXXPlugin extends BaseExtModule {
constructor(...) {
super();
}
name = 'xxx';
plugin!: ExtensionDomainEntity<IConfig>;
public configSchemas = Schema;
async initialize() {
// Инициализация расширения
// Настройка cron-задач
}
}
@Module({
providers: [XXXPlugin],
})
export class XXXPluginModule {
constructor(private readonly xxxPlugin: XXXPlugin) {}
async initialize() {
await this.xxxPlugin.initialize();
}
}
```
## Управление отображением конфигурации
Zod-схемы используются для автоматического отображения формы настроек в интерфейсе пользователя.
Через интерфейс `DeserializedDescriptionOfExtension` из `components/controller/src/types/shared/extension.types.ts`
можно управлять отображением полей формы:
```typescript
export const Schema = z.object({
// Базовое поле с меткой
simpleField: z.string().describe(
describeField({
label: 'Название поля',
note: 'Подсказка под полем'
})
),
// Скрытое поле для служебного использования
hiddenField: z.string().describe(
describeField({
label: 'Скрытое поле',
visible: false
})
),
// Поле с проверкой значения
validatedField: z.number().describe(
describeField({
label: 'Поле с валидацией',
rules: ['val >= 5', 'val <= 100'],
})
),
// Форматированное поле с префиксом и суффиксом
formattedField: z.number().describe(
describeField({
label: 'Форматированное поле',
prepend: '$',
append: 'USD',
})
),
// Многострочное текстовое поле
multilineField: z.string().describe(
describeField({
label: 'Многострочное поле',
maxRows: 5,
minLength: 10,
maxLength: 1000
})
),
});
```
Доступные поля для управления отображением:
- `label` - название поля (обязательное)
- `note` - пояснение или подсказка
- `visible` - видимость поля (по умолчанию true)
- `rules` - правила валидации в виде строковых выражений
- `mask` - маска для ввода
- `fillMask` - автозаполнение маски
- `minLength` / `maxLength` - ограничения длины для текстовых полей
- `maxRows` - количество строк для многострочного ввода
- `append` / `prepend` - текст до/после значения поля
## Взаимодействие с блокчейном
Для взаимодействия с блокчейном:
1. Определите порт в доменном слое (например, `SovietBlockchainPort`)
2. Инжектируйте порт в конструкторе расширения через DI
3. Используйте методы порта для взаимодействия с блокчейном
```typescript
@Inject(SOVIET_BLOCKCHAIN_PORT) private readonly sovietBlockchainPort: SovietBlockchainPort
// ...
const decisions = await this.sovietBlockchainPort.getDecisions(coopname);
```
## Настройка планировщика задач
Расширения могут использовать cron-задачи для периодического выполнения операций:
```typescript
import cron from 'node-cron';
// Регистрация cron-задачи (каждые N минут)
const cronExpression = `*/${this.plugin.config.checkInterval} * * * *`;
cron.schedule(cronExpression, () => {
this.logger.info('Запуск запланированной задачи');
this.runTask();
});
```
## Работа с конфигурацией
1. Определите Zod-схему для конфигурации
2. Используйте `describeField` для добавления UI-метаданных к полям
3. Получайте и обновляйте конфигурацию через репозиторий `extensionRepository`
```typescript
export const Schema = z.object({
checkInterval: z.number().describe(
describeField({
label: 'Интервал проверки (в минутах)',
note: 'Минимум: 5 минут',
rules: ['val >= 5'],
})
),
});
// Обновление конфигурации
this.plugin.config.lastCheckDate = new Date().toISOString();
await this.extensionRepository.update(this.plugin);
```
## Логирование действий
Расширения должны логировать свои действия:
1. Используйте `WinstonLoggerService` для системного логирования
2. Используйте `LogExtensionDomainRepository` для хранения логов в БД
```typescript
// Системное логирование
this.logger.info(`Выполнение операции для ${id}`);
// Сохранение лога в БД
await this.logExtensionRepository.push(this.name, {
type: 'operation',
timestamp: new Date().toISOString(),
data: { ... },
});
```
## Регистрация расширения
После создания расширения, добавьте его в `extensions.registry.ts`:
```typescript
export const AppRegistry: INamedExtension = {
myExtension: {
is_builtin: false,
is_internal: true,
is_available: true,
is_desktop: false,
title: 'Моё расширение',
description: 'Описание функциональности.',
image: 'https://example.com/image.png',
class: MyExtensionPluginModule,
schema: MyExtensionSchema,
tags: ['тег1', 'тег2'],
readme: getReadmeContent('./myExtension'),
instructions: getInstructionsContent('./myExtension'),
},
};
```
## Дефолтные настройки
Добавьте расширение в список дефолтных приложений в `extension-domain.service.ts`:
```typescript
getDefaultApps(): Partial<ExtensionDomainEntity>[] {
return [
// ...
{
name: 'myExtension',
enabled: true,
config: {
// Дефолтные значения конфигурации
parameter1: 'value1',
parameter2: 42,
},
},
// ...
];
}
```
+168
View File
@@ -0,0 +1,168 @@
---
description:
globs: **/controller/src/extensions/**
alwaysApply: false
---
# Архитектура расширений в MonoCoop
## Основные принципы
1. Расширения построены на модулях NestJS с использованием шаблона "Порты и адаптеры"
2. Каждое расширение наследуется от `BaseExtModule` и реализует интерфейс `OnModuleInit`
3. Расширения могут взаимодействовать с блокчейном через соответствующие порты
4. Конфигурации расширений хранятся в БД и описываются с помощью Zod-схем
5. Расширения регистрируются в глобальном реестре `AppRegistry`
## Структура расширения
Минимальная структура расширения включает:
- `XXX-extension.module.ts` - основной модуль расширения
- `package.json` - информация о пакете
- `README.md` - документация
- `INSTALL.md` - инструкции по установке
- `CHANGELOG.md` - история изменений
## Создание нового расширения
1. Создайте директорию для расширения в `components/controller/src/extensions/`
2. Создайте основной класс расширения, наследующийся от `BaseExtModule`
3. Определите Zod-схему для конфигурации
4. Реализуйте метод `initialize()`
5. Зарегистрируйте расширение в `extensions.registry.ts`
6. Добавьте расширение в список дефолтных приложений в `extension-domain.service.ts`
## Пример структуры модуля расширения
```typescript
// XXX-extension.module.ts
export class XXXPlugin extends BaseExtModule {
constructor(...) {
super();
}
name = 'xxx';
plugin!: ExtensionDomainEntity<IConfig>;
public configSchemas = Schema;
async initialize() {
// Инициализация расширения
// Настройка cron-задач
}
// Дополнительные методы расширения
}
@Module({
providers: [XXXPlugin],
})
export class XXXPluginModule {
constructor(private readonly xxxPlugin: XXXPlugin) {}
async initialize() {
await this.xxxPlugin.initialize();
}
}
```
## Взаимодействие с блокчейном
Для взаимодействия с блокчейном:
1. Определите порт в доменном слое (например, `SovietBlockchainPort`)
2. Инжектируйте порт в конструкторе расширения через DI
3. Используйте методы порта для взаимодействия с блокчейном
```typescript
@Inject(SOVIET_BLOCKCHAIN_PORT) private readonly sovietBlockchainPort: SovietBlockchainPort
// ...
const decisions = await this.sovietBlockchainPort.getDecisions(coopname);
```
## Настройка планировщика задач
Расширения могут использовать cron-задачи для периодического выполнения операций:
```typescript
import cron from 'node-cron';
// Регистрация cron-задачи (каждые N минут)
const cronExpression = `*/${this.plugin.config.checkInterval} * * * *`;
cron.schedule(cronExpression, () => {
this.logger.info('Запуск запланированной задачи');
this.runTask();
});
```
## Работа с конфигурацией
1. Определите Zod-схему для конфигурации
2. Используйте `describeField` для добавления UI-метаданных к полям
3. Получайте и обновляйте конфигурацию через репозиторий `extensionRepository`
```typescript
export const Schema = z.object({
checkInterval: z.number().describe(
describeField({
label: 'Интервал проверки (в минутах)',
note: 'Минимум: 5 минут',
rules: ['val >= 5'],
})
),
});
// Обновление конфигурации
this.plugin.config.lastCheckDate = new Date().toISOString();
await this.extensionRepository.update(this.plugin);
```
## Логирование действий
Расширения должны логировать свои действия:
1. Используйте `WinstonLoggerService` для системного логирования
2. Используйте `LogExtensionDomainRepository` для хранения логов в БД
```typescript
// Системное логирование
this.logger.info(`Выполнение операции для ${id}`);
// Сохранение лога в БД
await this.logExtensionRepository.push(this.name, {
type: 'operation',
timestamp: new Date().toISOString(),
data: { ... },
});
```
## Регистрация расширения
После создания расширения, добавьте его в `extensions.registry.ts`:
```typescript
export const AppRegistry: INamedExtension = {
myExtension: {
is_builtin: false,
is_internal: true,
is_available: true,
is_desktop: false,
title: 'Моё расширение',
description: 'Описание функциональности.',
image: 'https://example.com/image.png',
class: MyExtensionPluginModule,
schema: MyExtensionSchema,
tags: ['тег1', 'тег2'],
readme: getReadmeContent('./myExtension'),
instructions: getInstructionsContent('./myExtension'),
},
};
```
## Дефолтные настройки
Добавьте расширение в список дефолтных приложений в `extension-domain.service.ts`:
```typescript
getDefaultApps(): Partial<ExtensionDomainEntity>[] {
return [
// ...
{
name: 'myExtension',
enabled: true,
config: {
// Дефолтные значения конфигурации
parameter1: 'value1',
parameter2: 42,
},
},
// ...
];
}
```
+261
View File
@@ -0,0 +1,261 @@
---
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
- Собрания с вопросами и решениями
Фабрика поддерживает полный цикл создания документов кооператива от заявлений до протоколов собраний с возможностью кастомизации под разные типы кооперативов и требования.
+59
View File
@@ -0,0 +1,59 @@
---
description:
globs: components/sdk/**
alwaysApply: false
---
# Руководство по работе с GraphQL Zeus в SDK
## Основной процесс
1. **Анализ DTO бэкенда**:
- Изучите структуру DTO классов (`@ObjectType`) в бэкенде
- Обратите внимание на имена типов в декораторах `@ObjectType('ИмяТипа')`
- Отметьте поля, связи и вложенные объекты
2. **Создание селекторов**:
- Для каждого DTO создайте соответствующий селектор с именем `raw<ИмяТипа>Selector`
- Селектор - это объект, где ключи соответствуют полям DTO, а значения - `true`
- Для вложенных объектов используйте вложенные селекторы: `field: nestedSelector`
- Для списков используйте один селектор без массива: `items: itemSelector`
3. **Валидация селекторов**:
- Для каждого селектора создайте проверку типа:
```typescript
const _validate: MakeAllFieldsRequired<ValueTypes['ТочноеИмяГрафКьЭлТипа']> = rawSelector
```
- Имя типа должно точно совпадать с именем в декораторе `@ObjectType`
4. **Экспорт селекторов**:
- Создайте финальный селектор с помощью функции `Selector`:
```typescript
export const typeSelector = Selector('ТочноеИмяГрафКьЭлТипа')(rawTypeSelector)
```
- Экспортируйте сырой селектор для переиспользования
- Экспортируйте тип модели: `export type modelType = ModelTypes['ТочноеИмяГрафКьЭлТипа']`
5. **Создание запросов/мутаций**:
- Используйте селекторы в запросах и мутациях:
```typescript
export const query = Selector('Query')({
queryName: [{ data: $('data', 'ТочноеИмяВходногоТипа!') }, exportedSelector]
})
```
- Для параметров используйте оператор `$` с точным именем входного типа
- Создайте интерфейс входных данных:
```typescript
export interface IInput {
data: ModelTypes['ТочноеИмяВходногоТипа']
}
```
## Особенности работы
- **Документы**: всегда сохраняйте структуру `{ hash, signatures, rawDocument }`
- **Сложные DTO**: разбивайте на атомарные селекторы и комбинируйте их
- **Типы в Zeus**: часто отличаются от имен классов в бэкенде, всегда проверяйте в `schema.gql`
- **Массивы**: Zeus автоматически обрабатывает массивы, не используйте `[selector]`
Это руководство поможет правильно структурировать работу с SDK и избежать типичных ошибок при работе с Zeus.
+20
View File
@@ -0,0 +1,20 @@
---
globs: notifications/src/workflows/**/*.ts
---
# Правила валидации воркфлоу
## 🚫 ID воркфлоу
- **Длина ID не должна превышать 32 символа**
- Используйте описательные, но короткие идентификаторы малыми латинскими буквами и тире.
## 🚫 Условия в шаблонах
- **Запрещено использовать JavaScript выражения в шаблонах Novu**
- Нельзя использовать:
- Тернарные операторы: `{{condition ? "text1" : "text2"}}`
- Логические операторы: `{{field && "text"}}`
- Любые другие JS конструкции
## ✅ Рекомендации
- Добавляйте текстовые поля в payload для условной логики
- Вычисляйте значения на стороне сервера перед отправкой уведомления
- Используйте только простые переменные: `{{payload.fieldName}}`
+163
View File
@@ -0,0 +1,163 @@
name: Build Docker Images
on:
push:
tags:
- '*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
fetch-depth: 0
- name: Debug info
run: |
echo "Текущая ветка:"
git branch --show-current
echo "Последние коммиты:"
git log -n 3 --oneline
echo "Проверяем файлы в директории components/desktop/src-ssr:"
ls -la components/desktop/src-ssr/ || echo "Директория не найдена!"
echo "Проверяем файлы в middlewares:"
ls -la components/desktop/src-ssr/middlewares/ || echo "Директория middlewares не найдена!"
- name: Set docker tags
run: |
if [[ $GITHUB_REF == refs/tags/* ]]; then
TAG_NAME=${GITHUB_REF#refs/tags/}
echo "DOCKER_TAG=$TAG_NAME" >> $GITHUB_ENV
# Проверяем, является ли тег продакшн-тегом (не содержит alpha, beta, rc и т.д.)
if [[ ! $TAG_NAME =~ -(alpha|beta|rc|test) ]]; then
echo "IS_PRODUCTION_TAG=true" >> $GITHUB_ENV
echo "Это продакшн тег, будем добавлять latest"
else
echo "IS_PRODUCTION_TAG=false" >> $GITHUB_ENV
echo "Это не продакшн тег, latest не добавляем"
fi
else
echo "DOCKER_TAG=latest" >> $GITHUB_ENV
echo "IS_PRODUCTION_TAG=false" >> $GITHUB_ENV
fi
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Сначала собираем базовый образ с runtime
- name: Build base image
run: |
docker build --target runtime -t dicoop/mono-base:${{ env.DOCKER_TAG }} .
docker push dicoop/mono-base:${{ env.DOCKER_TAG }}
# Если это продакшн тег, добавляем latest
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
docker tag dicoop/mono-base:${{ env.DOCKER_TAG }} dicoop/mono-base:latest
docker push dicoop/mono-base:latest
fi
# Создаем сервисные образы на основе базового
- name: Build desktop image
run: |
echo "FROM dicoop/mono-base:${{ env.DOCKER_TAG }}" > Dockerfile.desktop
echo "CMD [\"pnpm\", \"-F\", \"@coopenomics/desktop\", \"run\", \"start\"]" >> Dockerfile.desktop
docker build -t dicoop/desktop:${{ env.DOCKER_TAG }} -f Dockerfile.desktop .
docker push dicoop/desktop:${{ env.DOCKER_TAG }}
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
docker tag dicoop/desktop:${{ env.DOCKER_TAG }} dicoop/desktop:latest
docker push dicoop/desktop:latest
fi
- name: Build controller image
run: |
echo "FROM dicoop/mono-base:${{ env.DOCKER_TAG }}" > Dockerfile.coopback
echo "CMD [\"pnpm\", \"-F\", \"@coopenomics/controller\", \"run\", \"start\"]" >> Dockerfile.coopback
docker build -t dicoop/coopback:${{ env.DOCKER_TAG }} -f Dockerfile.coopback .
docker push dicoop/coopback:${{ env.DOCKER_TAG }}
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
docker tag dicoop/coopback:${{ env.DOCKER_TAG }} dicoop/coopback:latest
docker push dicoop/coopback:latest
fi
- name: Build parser image
run: |
echo "FROM dicoop/mono-base:${{ env.DOCKER_TAG }}" > Dockerfile.cooparser
echo "CMD [\"pnpm\", \"-F\", \"@coopenomics/parser\", \"run\", \"start\"]" >> Dockerfile.cooparser
docker build -t dicoop/cooparser:${{ env.DOCKER_TAG }} -f Dockerfile.cooparser .
docker push dicoop/cooparser:${{ env.DOCKER_TAG }}
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
docker tag dicoop/cooparser:${{ env.DOCKER_TAG }} dicoop/cooparser:latest
docker push dicoop/cooparser:latest
fi
- name: Build notificator image
run: |
echo "FROM dicoop/mono-base:${{ env.DOCKER_TAG }}" > Dockerfile.notificator
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 }}
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
docker tag dicoop/notificator:${{ env.DOCKER_TAG }} dicoop/notificator:latest
docker push dicoop/notificator:latest
fi
- 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 }}
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
docker tag dicoop/notifications:${{ env.DOCKER_TAG }} dicoop/notifications:latest
docker push dicoop/notifications:latest
fi
# Отправка хука для деплоя
- name: Trigger deployment webhook
if: ${{ success() }}
run: |
if [[ $GITHUB_REF == refs/tags/*alpha* ]]; then
# Хук для тестнета (alpha теги)
curl -X POST ${{ vars.TESTNET_WEBHOOK_URL }} \
-H 'Content-Type: application/json' \
-d '${{ env.DOCKER_TAG }}'
elif [[ $GITHUB_REF == refs/tags/* ]]; then
# Хук для продакшена (остальные теги)
curl -X POST ${{ vars.PRODUCTION_WEBHOOK_URL }} \
-H 'Content-Type: application/json' \
-d '${{ env.DOCKER_TAG }}'
fi
# Уведомление в Telegram об успехе
- name: Telegram notify success
if: ${{ success() }}
run: |
if [[ "${{ env.IS_PRODUCTION_TAG }}" == "true" ]]; then
ADDITIONAL_INFO=" (с тегом latest)"
else
ADDITIONAL_INFO=""
fi
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 }}]$ADDITIONAL_INFO"
# Уведомление в 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 }}]"
@@ -0,0 +1,18 @@
# .github/workflows/trigger-coopenomics.yml
name: Trigger Contracts Docs Deploy
on:
push:
branches: [dev, testnet, main, capital] # или когда нужно триггерить
jobs:
trigger-coopenomics:
runs-on: ubuntu-latest
steps:
- name: Trigger Coopenomics deployment
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.COOPENOMICS_PAT }}
repository: coopenomics/coopenomics # укажи правильный owner/repo
event-type: deploy_from_mono
client-payload: '{"repository": "${{ github.repository }}", "sha": "${{ github.sha }}", "ref": "${{ github.ref }}", "actor": "${{ github.actor }}"}'
+107
View File
@@ -0,0 +1,107 @@
name: Publish Docs
on:
push:
branches:
- main
- testnet
- dev
jobs:
build-and-publish-docs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 20
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install pnpm
run: npm install -g pnpm
- name: Install Python requirements
run: |
python -m venv venv
source venv/bin/activate
pip install mkdocs-material mkdocs-macros-plugin mkdocs-section-index pymdown-extensions
working-directory: ./components/docs
- name: Install Node.js dependencies
run: pnpm install
working-directory: ./components/docs
- name: Patch spectaql-config.yml for CI
run: |
sed -i.bak "0,/url:.*/s|url:.*|url: 'https://testnet.coopenomics.world/backend/v1/graphql'|" spectaql-config.yml
working-directory: ./components/controller
- name: Show patched spectaql-config.yml
run: cat spectaql-config.yml
working-directory: ./components/controller
- name: Build cooptypes
run: pnpm run build
working-directory: ./components/cooptypes
- name: Generate controller docs
run: pnpm run docs
working-directory: ./components/controller
- name: Copy controller docs
run: |
mkdir -p ./components/docs/docs/graphql
cp -r ./components/controller/docs/* ./components/docs/docs/graphql/
- name: Generate sdk docs
run: pnpm run docs
working-directory: ./components/sdk
- name: Copy sdk docs
run: |
mkdir -p ./components/docs/docs/sdk
cp -r ./components/sdk/docs/* ./components/docs/docs/sdk/
- name: Generate cooptypes docs
run: pnpm run docs
working-directory: ./components/cooptypes
- name: Copy cooptypes docs
run: |
mkdir -p ./components/docs/docs/cooptypes
cp -r ./components/cooptypes/docs/* ./components/docs/docs/cooptypes/
- name: Build docs (mkdocs)
run: |
source venv/bin/activate
mkdocs build
working-directory: ./components/docs
- name: Remove specific large file before publishing
run: |
# Удаляем конкретный большой файл sdk/typedoc.json
rm -f ./components/docs/site/sdk/typedoc.json
# Проверяем, что файл удален
if [ -f "./components/docs/site/sdk/typedoc.json" ]; then
echo "ERROR: typedoc.json still exists!"
exit 1
else
echo "SUCCESS: typedoc.json removed successfully"
fi
- name: Publish to GitHub Pages
run: npx gh-pages --nojekyll -d site --repo https://x-access-token:${GITHUB_TOKEN}@github.com/coopenomics/mono.git
working-directory: ./components/docs
env:
GIT_AUTHOR_NAME: github-actions
GIT_AUTHOR_EMAIL: github-actions@github.com
GIT_COMMITTER_NAME: github-actions
GIT_COMMITTER_EMAIL: github-actions@github.com
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+31
View File
@@ -0,0 +1,31 @@
name: Publish Packages
on:
push:
tags:
- 'v*'
permissions:
contents: read
jobs:
build-and-publish:
if: |
startsWith(github.ref, 'refs/tags/v') &&
!contains(github.ref, '-alpha')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
registry-url: https://registry.npmjs.org
- run: npm install -g pnpm
- run: pnpm install
- run: pnpm lerna run build
- run: pnpm lerna publish from-package --yes
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+8 -2
View File
@@ -1,5 +1,11 @@
node_modules/
lerna-debug.log
components/controller/graph.png
wallet-data/
blockchain-data/
blockchain-data/
scripts/changelog-prompt.md
scripts/changelog-release.md
scripts/release-info.md
components/docs/docs/sdk
dist/
.env
.DS_Store
+8
View File
@@ -0,0 +1,8 @@
{
"semi": false,
"singleQuote": true,
"printWidth": 120,
"plugins": [
"prettier-plugin-sort-imports"
]
}
+7
View File
@@ -0,0 +1,7 @@
{
"recommendations": [
"vue.volar",
"vue.vscode-typescript-vue-plugin"
]
}
+63 -3
View File
@@ -1,14 +1,74 @@
{
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": true,
"editor.formatOnSave": true,
"editor.tabCompletion": "onlySnippets",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
"eslint.run": "onType",
"eslint.validate": ["javascript", "typescript", "vue"],
// "typescript.tsdk": "node_modules/typescript/lib",
"i18n-ally.localesPaths": ["src/i18n"],
"typescript.format.enable": true,
"typescript.format.indentSwitchCase": false,
"typescript.validate.enable": true,
"notebook.defaultFormatter": "Vue.volar",
// Оптимизация для монорепозитория
"typescript.preferences.useAliasesForRenames": false,
"typescript.preferences.includePackageJsonAutoImports": "on",
"typescript.suggest.autoImports": true,
"typescript.suggest.paths": true,
"typescript.updateImportsOnFileMove.enabled": "always",
"typescript.workspaceSymbols.scope": "currentProject",
// Настройки для снижения нагрузки
"files.watcherExclude": {
"**/node_modules/**": true,
"**/dist/**": true,
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/*/**": true,
"**/.cache/**": true,
"**/.quasar/**": true,
"**/*.tsbuildinfo": true
},
"search.exclude": {
"**/node_modules": true,
"**/dist": true,
"**/.cache": true,
"**/.quasar": true,
"**/*.tsbuildinfo": true
},
"files.exclude": {
"**/.cache": true,
"**/*.tsbuildinfo": true,
"**/node_modules/.cache": true
},
// TypeScript server настройки для монорепозитория
"typescript.tsserver.maxTsServerMemory": 8192,
"typescript.tsserver.watchOptions": {
"excludeDirectories": [
"**/node_modules",
"**/dist",
"**/.cache",
"**/.quasar",
"**/build"
]
},
// Использовать локальный TypeScript из монорепо
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
// КРИТИЧЕСКИ ВАЖНО: включить project references для монорепозитория
"typescript.tsserver.useSyntaxServer": "auto",
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
// Оптимизация для больших монорепозиториев
"typescript.disableAutomaticTypeAcquisition": true,
"typescript.surveys.enabled": false,
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
}
}
+73
View File
@@ -0,0 +1,73 @@
# AGENTS.md
## Cursor Cloud specific instructions
### Обзор проекта
Монорепозиторий «Цифровой Кооператив» (monocoop) — платформа управления кооперативами на блокчейне EOSIO. pnpm + Lerna. Компоненты в `components/`.
### Сервисы
| Компонент | Порт | Описание |
|-----------|------|----------|
| controller (coopback) | 2998 | NestJS GraphQL API |
| desktop | 3005 | Vue 3 + Quasar (SPA/SSR) |
| parser (cooparser) | 4000 | Индексация блокчейна через SHiP |
| node (Docker) | 8888 | EOSIO blockchain node |
| mongo (Docker) | 27017 | MongoDB (replica set) |
| redis (Docker) | 6379 | Redis |
| postgres (Docker) | 5432 | PostgreSQL 16 |
| SHiP (Docker) | 8070 | State History Plugin WebSocket |
### Полный цикл запуска dev-окружения
1. **Сборка shared-библиотек** (порядок важен):
```
pnpm --filter cooptypes run build
pnpm --filter @coopenomics/factory run build
pnpm --filter @coopenomics/sdk run build
pnpm --filter @coopenomics/notifications run build
```
2. **Сборка смарт-контрактов** (test-режим позволяет boot с 1 членом совета):
```
cd components/contracts && bash build-all.sh test
```
3. **Запуск инфраструктуры**:
```
docker compose up -d mongo postgres redis node
```
4. **Bootstrap системы**:
```
pnpm run boot
```
5. **Запуск парсера** (после boot; при первом запуске нужен `START_BLOCK=2` в `.env` для полного replay, потом вернуть `START_BLOCK=1`):
```
pnpm --filter @coopenomics/parser run dev
```
6. **Запуск controller и desktop**:
```
pnpm --filter @coopenomics/controller run dev
pnpm --filter @coopenomics/desktop run dev
```
### Критические gotchas
- **Node.js v20** обязателен. pnpm **v9.x** (не v10).
- **`/etc/hosts`**: для Cloud-окружения нужны записи `127.0.0.1 mongo` и `127.0.0.1 monoredis` — boot запускается на хосте и обращается к MongoDB по имени контейнера (replica set инициализирован с `mongo:27017`).
- **`docker-compose.override.yaml`**: в Cloud нужен override с `postgres: image: postgres:16` — `postgres:latest` (v18+) не работает с текущей конфигурацией volumes.
- **`.env` файлы**: controller и parser запускаются в Docker — используют docker hostnames (`mongo`, `node`, `monoredis`, `postgres`). Boot запускается на хосте — использует `127.0.0.1` с хостовыми портами (postgres: 5532, mongo: 27017).
- **Полный перезапуск**: `pnpm run reboot` — всё в одну команду. После первого reboot нужно `docker compose up -d --force-recreate coopback cooparser` если .env менялись.
- **SHiP порт 8070** — конфиг ноды `state-history-endpoint = 0.0.0.0:8070`, НЕ 8080. В `.env` парсера: `SHIP=ws://127.0.0.1:8070`.
- **CHAIN_ID** должен совпадать во всех `.env` файлах. Локальный chain_id: `cae86058a6d8698833afb474ab8a5ad8599c6cf54f9ebcf275dbac7055c16fe1`. В `.env-example` desktop стоял неправильный — исправлен.
- **postgres:latest (v18+)** не работает с текущей конфигурацией volume — используется `postgres:16`.
- **WeasyPrint** — системная зависимость для генерации PDF документов. Нужен в PATH: `pip install WeasyPrint==67`.
- **SSR-режим desktop в dev** — расширения (extensions) не рендерятся в SSR dev-сервере из-за проблемы гидратации Pinia: Vue-компоненты из маршрутов расширений сериализуются в `__INITIAL_STATE__` как plain-объекты `{__name, __file}` без render/setup функций. При гидратации клиент использует эти сломанные компоненты. Production-сборка (`quasar build --mode ssr`) работает корректно. **Для dev используйте SPA-режим**: `quasar dev` (без `--mode ssr`). Корневое решение: вынести `routes` из Pinia-стейта desktop-store, чтобы они не сериализовались при SSR.
- **Парсер и START_BLOCK**: при `START_BLOCK=1` на чистой БД парсер стартует с HEAD и вызывает `initializeFromBlockchain()` (загружает только кооператив и шаблоны, НЕ аккаунты). Для полного replay данных boot установите `START_BLOCK=2`, запустите, потом верните `START_BLOCK=1`.
- **boot/wallet-data и boot/blockchain-data** — создаются Docker от root, может потребоваться `sudo chown`.
- **Reboot-скрипты** (`components/boot/scripts/reboot.sh` и др.) ссылаются на docker-compose сервисы `cooparser` и `coopback`.
- **Тестовые учётные данные** (после boot): email `ivanov@example.com`, ключ `5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3`, пользователь `ant` (председатель).
+282
View File
@@ -1,8 +1,290 @@
# v2025.12.28-8
В этой версии представлен прототип трекера результатов интеллектуальной деятельности, реализован мост в 1С, обновлен интерфейс и существенно повышена стабильность системы.
---
### ✨ Новые функции
- [#332](https://github.com/coopenomics/mono/issues/332): Прототип конструктора требований дополнительных документов при регистрации
- [#330](https://github.com/coopenomics/mono/issues/330): Прототип моста в 1С:Бухгалтерию для передачи документов и проводок
- [#329](https://github.com/coopenomics/mono/issues/329): Интеграция и тестирование LMS TUTOR для образовательных задач
- [#328](https://github.com/coopenomics/mono/issues/328): Размещение прототипов мульти-лендингов на цифровой-кооператив.рф и coopenomics.world
- [#326](https://github.com/coopenomics/mono/issues/326): Минимальный интерфейс трекера результатов интеллектуальной деятельности
- [#324](https://github.com/coopenomics/mono/issues/324): Смарт-контракт генерации и капитализации результатов интеллектуальной деятельности ("Благорост")
- [#322](https://github.com/coopenomics/mono/issues/322): Поставка обновлений ПО с нулевым даунтаймом по blue-green стратегии
- [#321](https://github.com/coopenomics/mono/issues/321): Внедрение системы проводок по фондам для контрактов
- [#319](https://github.com/coopenomics/mono/issues/319): Палитра команд и быстрый доступ к страницам рабочих столов (cmk+k)
- [#316](https://github.com/coopenomics/mono/issues/316): Переход рабочего стола на GraphQL SDK
- [#314](https://github.com/coopenomics/mono/issues/314): Развёртывание GlitchTip для мониторинга ошибок
- [#306](https://github.com/coopenomics/mono/issues/306): Модуль запросов и мутаций для контракта капитализации
### 🐛 Исправления ошибок
- [#312](https://github.com/coopenomics/mono/issues/312): Исправление подписки на изменение статуса коммитов
- [#309](https://github.com/coopenomics/mono/issues/309): Исправление отображения чужих билетов времени в трекере
### 🔧 Улучшения
- [#331](https://github.com/coopenomics/mono/issues/331): Настройка системы мониторинга сбоев и ошибок на базе GlitchTIP, Loki, Prometheus
- [#327](https://github.com/coopenomics/mono/issues/327): Пользовательская документация по интерфейсам цифрового кооператива
- [#325](https://github.com/coopenomics/mono/issues/325): Документирование смарт-контракта программы "Благорост"
- [#308](https://github.com/coopenomics/mono/issues/308): Улучшение отображения рабочих столов в магазине приложений
- [#307](https://github.com/coopenomics/mono/issues/307): Объединение настроек контракта с нативными настройками приложения
- [#305](https://github.com/coopenomics/mono/issues/305): Объединение полей title и description в проекте
- [#304](https://github.com/coopenomics/mono/issues/304): Доменная модель контракта капитализации на бэкенде
- [#303](https://github.com/coopenomics/mono/issues/303): Пересмотр архитектуры парсера и формирования локальной истории
- [#302](https://github.com/coopenomics/mono/issues/302): Рефакторинг архитектуры, внедрение двухконтурной шины данных и обработки микрофорков
- [#301](https://github.com/coopenomics/mono/issues/301): Доработка и отладка контракта "Капитализация РИД" v0.2
- [#222](https://github.com/coopenomics/mono/issues/222): Внедрение метода Водянова для распределения пула премий по программе "Благорост"
- [#212](https://github.com/coopenomics/mono/issues/212): Снижение точности валютных значений до двух знаков после запятой в документах
#releases
---
# v2025.12.28
В этой версии представлен прототип трекера результатов интеллектуальной деятельности, реализован мост в 1С, обновлен интерфейс и существенно повышена стабильность системы.
---
### ✨ Новые функции
- [#332](https://github.com/coopenomics/mono/issues/332): Прототип конструктора требований дополнительных документов при регистрации
- [#330](https://github.com/coopenomics/mono/issues/330): Прототип моста в 1С:Бухгалтерию для передачи документов и проводок
- [#329](https://github.com/coopenomics/mono/issues/329): Интеграция и тестирование LMS TUTOR для образовательных задач
- [#328](https://github.com/coopenomics/mono/issues/328): Размещение прототипов мульти-лендингов на цифровой-кооператив.рф и coopenomics.world
- [#326](https://github.com/coopenomics/mono/issues/326): Минимальный интерфейс трекера результатов интеллектуальной деятельности
- [#324](https://github.com/coopenomics/mono/issues/324): Смарт-контракт генерации и капитализации результатов интеллектуальной деятельности ("Благорост")
- [#322](https://github.com/coopenomics/mono/issues/322): Поставка обновлений ПО с нулевым даунтаймом по blue-green стратегии
- [#321](https://github.com/coopenomics/mono/issues/321): Внедрение системы проводок по фондам для контрактов
- [#319](https://github.com/coopenomics/mono/issues/319): Палитра команд и быстрый доступ к страницам рабочих столов (cmk+k)
- [#316](https://github.com/coopenomics/mono/issues/316): Переход рабочего стола на GraphQL SDK
- [#314](https://github.com/coopenomics/mono/issues/314): Развёртывание GlitchTip для мониторинга ошибок
- [#306](https://github.com/coopenomics/mono/issues/306): Модуль запросов и мутаций для контракта капитализации
### 🐛 Исправления ошибок
- [#312](https://github.com/coopenomics/mono/issues/312): Исправление подписки на изменение статуса коммитов
- [#309](https://github.com/coopenomics/mono/issues/309): Исправление отображения чужих билетов времени в трекере
### 🔧 Улучшения
- [#331](https://github.com/coopenomics/mono/issues/331): Настройка системы мониторинга сбоев и ошибок на базе GlitchTIP, Loki, Prometheus
- [#327](https://github.com/coopenomics/mono/issues/327): Пользовательская документация по интерфейсам цифрового кооператива
- [#325](https://github.com/coopenomics/mono/issues/325): Документирование смарт-контракта программы "Благорост"
- [#308](https://github.com/coopenomics/mono/issues/308): Улучшение отображения рабочих столов в магазине приложений
- [#307](https://github.com/coopenomics/mono/issues/307): Объединение настроек контракта с нативными настройками приложения
- [#305](https://github.com/coopenomics/mono/issues/305): Объединение полей title и description в проекте
- [#304](https://github.com/coopenomics/mono/issues/304): Доменная модель контракта капитализации на бэкенде
- [#303](https://github.com/coopenomics/mono/issues/303): Пересмотр архитектуры парсера и формирования локальной истории
- [#302](https://github.com/coopenomics/mono/issues/302): Рефакторинг архитектуры, внедрение двухконтурной шины данных и обработки микрофорков
- [#301](https://github.com/coopenomics/mono/issues/301): Доработка и отладка контракта "Капитализация РИД" v0.2
- [#222](https://github.com/coopenomics/mono/issues/222): Внедрение метода Водянова для распределения пула премий по программе "Благорост"
- [#212](https://github.com/coopenomics/mono/issues/212): Снижение точности валютных значений до двух знаков после запятой в документах
#releases
---
# v2025.12.28
В этом релизе реализован смарт-контракт генерации и капитализации результатов интеллектуальной деятельности, завершена интеграция с учётными системами, улучшены интерфейсы и документация. Подробнее о контракте: https://coopenomics.world/contracts/group__public__capital.html
✨ Новые функции
- [#324](https://github.com/coopenomics/mono/issues/324): Реализован смарт-контракт генерации и капитализации результатов интеллектуальной деятельности ("Благорост")
- [#301](https://github.com/coopenomics/mono/issues/301): Контракт "Капитализация РИД" v0.2
- [#330](https://github.com/coopenomics/mono/issues/330): Прототип моста в 1С:Бухгалтерию: выгрузка документов и проводки по счетам
- [#322](https://github.com/coopenomics/mono/issues/322): Обновления ПО с нулевым даунтаймом по blue-green стратегии
- [#319](https://github.com/coopenomics/mono/issues/319): Палитра команд и быстрый доступ к страницам рабочих столов (cmk+k)
- [#308](https://github.com/coopenomics/mono/issues/308): Магазин приложений с поддержкой подключения нескольких рабочих столов одним приложением
- [#326](https://github.com/coopenomics/mono/issues/326): Минимальный интерфейс трекера результатов интеллектуальной деятельности по программе "Благорост"
- [#332](https://github.com/coopenomics/mono/issues/332): Прототип конструктора требований дополнительных документов при регистрации
🐛 Исправления ошибок
- [#312](https://github.com/coopenomics/mono/issues/312): Исправлена ошибка со статусом коммитов — подписка теперь работает корректно
- [#309](https://github.com/coopenomics/mono/issues/309): Исправлен баг с отображением чужих билетов времени в трекере
🔧 Улучшения
- [#325](https://github.com/coopenomics/mono/issues/325): Документирован смарт-контракт программы "Благорост"
- [#327](https://github.com/coopenomics/mono/issues/327): Подготовлена пользовательская документация цифрового кооператива по интерфейсам
- [#329](https://github.com/coopenomics/mono/issues/329): Интеграция и тестирование образовательной платформы LMS TUTOR на Wordpress
- [#328](https://github.com/coopenomics/mono/issues/328): Размещены прототипы мульти-лендингов на цифровой-кооператив.рф и coopenomics.world
- [#321](https://github.com/coopenomics/mono/issues/321): Встроена система проводок по фондам и интеграция с контрактами
- [#318](https://github.com/coopenomics/mono/issues/318): Настроены Loki & Grafana для выгрузки логов из контейнеров
- [#316](https://github.com/coopenomics/mono/issues/316): Завершён переход рабочего стола на GraphQL SDK
- [#314](https://github.com/coopenomics/mono/issues/314): Развёрнут GlitchTip как альтернатива Sentry
- [#307](https://github.com/coopenomics/mono/issues/307): Интеграция настроек контракта с нативными настройками приложения, поддержка импорта после конфигурации
- [#306](https://github.com/coopenomics/mono/issues/306): Собран модуль запросов и мутаций контракта капитализации
- [#305](https://github.com/coopenomics/mono/issues/305): Упрощена структура проекта — title & description объединены в одно поле
- [#304](https://github.com/coopenomics/mono/issues/304): Реализована доменная модель контракта капитализации на бэкенде с поддержкой микрофорков
- [#303](https://github.com/coopenomics/mono/issues/303): Пересмотрена архитектура парсера и формирования локальной истории
- [#302](https://github.com/coopenomics/mono/issues/302): Рефакторинг архитектуры, реализована двухконтурная шина данных и обработка микрофорков
- [#212](https://github.com/coopenomics/mono/issues/212): Уменьшена точность валютных значений в документах с четырёх до двух знаков
#releases
---
# v2025.9.1
В системе Кооперативной Экономики развернут смарт-контракт CAPITAL v0.2 для генерации и капитализации результатов интеллектуальной деятельности. Контракт описывает и обеспечивает:
- бизнес-процесс производства результатов интеллектуальной деятельности в кооперативах создателями, авторами, инвесторами, координаторами, мастерами и собственниками имущества в кооперации на проектах;
- приём результатов интеллектуальной деятельности в качестве паевых взносов в кооператив;
- распределение потока членских взносов среди вкладчиков;
- капитализацию результатов интеллектуальной деятельности новыми результатами по модели золотого сечения;
- оценку вкладов авторов и создателей по методу Водянова;
В основе математической модели контракта лежит принцип распределения справедливой выгоды между вкладчиками согласно концепции "Общественно-полезного времени", разработанной в рамках теорий Кузнецова и академика Глушкова при работе над проектом общегосударственной автоматизированной системы учета и обработки информации (ОГАС).
Подробнее в документации: https://coopenomics.world/contracts/group__public__capital.html
✨ Новые функции
- [#301](https://github.com/coopenomics/mono/issues/301): Реализация контракта "Капитализация" v0.2: регистрация вкладчиков, создание и управление проектами, поддержка инвестиций, ссуд, членских взносов, проведение голосований, расчет капитализации, интеграция с внешними контрактами, поддержка импорта данных.
#releases
---
# v2025.7.1-1
В этом релизе реализованы механизмы возврата паевого взноса пайщика и инструменты управления этим процессом для членов совета. Также внесены визуальные доработки интерфейсов для улучшения восприятия информации.
✨ Новые функции
- [#276](https://github.com/coopenomics/mono/issues/276): Реализовать путь возврата паевого взноса из кошелька пайщика
- [#281](https://github.com/coopenomics/mono/issues/281): Генерация заявления на возврат паевого взноса
- [#282](https://github.com/coopenomics/mono/issues/282): Генерация решения совета на возврат паевого взноса
- [#278](https://github.com/coopenomics/mono/issues/278): Введение методов управления возвратами паевых взносов в контроллере
- [#279](https://github.com/coopenomics/mono/issues/279): Отобразить исходящие платежи с кнопками управления в реестре платежей для совета
🐛 Исправления ошибок
- [#286](https://github.com/coopenomics/mono/issues/286): Поправить ошибки склонений времени
- [#262](https://github.com/coopenomics/mono/issues/262): Баг: рабочий стол совета включается, однако, страница всегда открывается со стола пайщика
- [#261](https://github.com/coopenomics/mono/issues/261): Баг: первая загрузка переадресует на главную страницу всегда - прямой переход на собрание становится недоступен.
🔧 Улучшения
- [#285](https://github.com/coopenomics/mono/issues/285): Корректировка дизайна кошелька, профиля, повестки совета, реестра документов, реестра платежей
- [#284](https://github.com/coopenomics/mono/issues/284): Разместить кошелек на главную вместо профиля
- [#283](https://github.com/coopenomics/mono/issues/283): Ввести лоадер на переходе между рабочими столами
- [#280](https://github.com/coopenomics/mono/issues/280): Мигрировать имеющиеся данные о входящих платежах в новую модель
- [#277](https://github.com/coopenomics/mono/issues/277): Рефакторинг модуля платежей: переход на унифицированную модель входящего и исходящего платежа
#releases
---
# v2025.6.14
Разработан модуль для проведения очередных общих собраний пайщиков. Исправлены баги, внесены улучшения пользовательского интерфейса.
✨ Новые функции
- [#264](https://github.com/coopenomics/mono/issues/264): Разработан смарт-контракт общего собрания пайщиков (meet)
- [#263](https://github.com/coopenomics/mono/issues/263): Реализован модуль оповещений на электронные почты по жизненному циклу общего собрания пайщиков
- [#273](https://github.com/coopenomics/mono/issues/273): Встроены реальные шаблоны документов общего собрания
- [#268](https://github.com/coopenomics/mono/issues/268): Добавлена страница результатов общего собрания
- [#267](https://github.com/coopenomics/mono/issues/267): Добавлена страница просмотра и скачивания бюллетеней и уведомлений по собранию
- [#270](https://github.com/coopenomics/mono/issues/270): Ссылка в оповещении ведет на страницу собрания с документом-уведомлением для подписи
🐛 Исправления ошибок
- [#262](https://github.com/coopenomics/mono/issues/262): Исправлено некорректное открытие рабочего стола совета
- [#261](https://github.com/coopenomics/mono/issues/261): Исправлена ошибка с редиректом при первой загрузке и прямом переходе на собрание
- [#259](https://github.com/coopenomics/mono/issues/259): Исправлены отступы в мобильной карточке пайщика
- [#258](https://github.com/coopenomics/mono/issues/258): Убран hover-эффект на документе и пайщике в таблице
🔧 Улучшения
- [#274](https://github.com/coopenomics/mono/issues/274): Настроена рассылка оповещений на почту при получении решения о проведении собрания
- [#272](https://github.com/coopenomics/mono/issues/272): Подписанные уведомления сохраняются и извлекаются из реестра по Graph-QL
- [#271](https://github.com/coopenomics/mono/issues/271): Ведется подсчет количества пайщиков в каждом кооперативе при добавлении и удалении
- [#260](https://github.com/coopenomics/mono/issues/260): Введен счетчик количества пайщиков в кооперативе на контракте регистратора
- [#256](https://github.com/coopenomics/mono/issues/256): Отображается статус членства каждого пайщика
- [#255](https://github.com/coopenomics/mono/issues/255): В разделе Платежи отображается ФИО/Наименование плательщика
- [#269](https://github.com/coopenomics/mono/issues/269): Реализована рассылка уведомлений перед началом собрания
- [#265](https://github.com/coopenomics/mono/issues/265): Проведена отладка и тестирование процесса общего собрания пайщиков
- [#266](https://github.com/coopenomics/mono/issues/266): Протестированы все процессы общего собрания
#releases
---
# v2025.5.14
В этом релизе реализован новый стандарт передачи и хранения документов по блокчейну с поддержкой неограниченного количества подписей и их валидацией. Также внесены улучшения в интерфейс и исправлены ошибки.
✨ Новые функции
- [#252](https://github.com/coopenomics/mono/issues/252): Внедрение обновленного стандарта хранения и передачи документов по блокчейну
- [#251](https://github.com/coopenomics/mono/issues/251): Реализация версионированного мигратора данных для контроллера кооператива
- [#244](https://github.com/coopenomics/mono/issues/244): Обновление стандарта сборки документов и переход на хэш-идентификаторы
🐛 Исправления ошибок
- [#249](https://github.com/coopenomics/mono/issues/249): Исправлена спутанная маршрутизация между рабочими столами кооперативов
- [#253](https://github.com/coopenomics/mono/issues/253): Исправлена избыточная точность суммы оплаты в заявлении на вступление
🔧 Улучшения
- [#259](https://github.com/coopenomics/mono/issues/259): Исправлены отступы в мобильной карточке пайщика на странице пайщиков
- [#258](https://github.com/coopenomics/mono/issues/258): Удалён hover-эффект для документов и пайщиков в таблице
- [#257](https://github.com/coopenomics/mono/issues/257): Добавлено сохранение светлой/тёмной темы в localStorage и восстановление при загрузке страницы
- [#256](https://github.com/coopenomics/mono/issues/256): Отображение статуса членства каждого пайщика в разделе "Пайщики"
- [#255](https://github.com/coopenomics/mono/issues/255): Отображение ФИО/Наименования плательщика в разделе "Платежи"
#releases
---
# v2025.5.2
В этом релизе рабочие столы переведены на серверный рендеринг, что улучшает стабильность развертывания и упрощает автоматизацию поставки ПО. Данный релиз является подготовительным к переходу на новый стандарт цифровых документов на платформе.
✨ Новые функции
- [#245](https://github.com/coopenomics/mono/issues/245): Перевод десктопа на серверный рендеринг с поддержкой динамических переменных окружения
🐛 Исправления ошибок
- [#247](https://github.com/coopenomics/mono/issues/247): Исправлен баг с повторной поставкой ПО при обрывах соединения между серверами
🔧 Улучшения
- [#246](https://github.com/coopenomics/mono/issues/246): Перевод CI/CD на сборку и поставку предсобранных докер-контейнеров
#releases
---
# MONO v2025.4.29
В этом релизе реализована новая архитектура рабочих столов с установкой через маркетплейс. Добавлены стол пайщика и стол совета, переработаны разделы документов и подписей, улучшено разделение кошелька и профиля для повышения удобства пользователей.
✨ Новые функции
- [#234](https://github.com/coopenomics/mono/issues/234): Маркетплейс рабочих столов с поддержкой разных ролей и микросервисной архитектурой
- [#238](https://github.com/coopenomics/mono/issues/238): Контроллер общего собрания пайщиков с поддержкой документооборота и подписей
- [#233](https://github.com/coopenomics/mono/issues/233): Минимальный смарт-контракт общих собраний пайщиков
- [#232](https://github.com/coopenomics/mono/issues/232): Бэкенд полного обозревателя блоков
🐛 Исправления ошибок
- [#231](https://github.com/coopenomics/mono/issues/231): Исправления ошибок регистрации, выхода из системы, отображения платежей и редактирования организации
🔧 Улучшения
- [#243](https://github.com/coopenomics/mono/issues/243): Контроль прав доступа на получении документов пайщика
- [#242](https://github.com/coopenomics/mono/issues/242): Бесконечный скролл на документах пайщика и кооператива
- [#241](https://github.com/coopenomics/mono/issues/241): Раздел "Документы" для пайщика
- [#240](https://github.com/coopenomics/mono/issues/240): Множественные подписи на одном документе
- [#239](https://github.com/coopenomics/mono/issues/239): Последовательные методы подписи протокола общего собрания
- [#237](https://github.com/coopenomics/mono/issues/237): Мобильная вёрстка на страницах стола совета
- [#236](https://github.com/coopenomics/mono/issues/236): Пересобран лендинг для MONO
- [#235](https://github.com/coopenomics/mono/issues/235): Настроен флоу гитхаб-релизов с описаниями
- [#226](https://github.com/coopenomics/mono/issues/226): Встроено редактирование пайщиков
- [#224](https://github.com/coopenomics/mono/issues/224): Добавлено ТЗ по контракту капитализации
---
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.2.10](https://github.com/coopenomics/mono/compare/v2.2.9...v2.2.10) (2025-03-27)
### Bug Fixes
* add Zeus type ([7bcb6e3](https://github.com/coopenomics/mono/commit/7bcb6e30a77b0ab89c5293188b58f08f19c8761e))
## [2.2.9](https://github.com/coopenomics/mono/compare/v2.2.8...v2.2.9) (2025-03-12)
**Note:** Version bump only for package monocoop
+40 -1
View File
@@ -1,10 +1,49 @@
FROM node:18-alpine
FROM node:20-slim AS builder
WORKDIR /app
# Сразу копируем все файлы
COPY . .
# Устанавливаем инструменты
RUN npm install -g pnpm lerna
# Установка зависимостей
# Используем версию pnpm, совместимую с существующим lock-файлом
RUN pnpm install
# Установка системных зависимостей для WeasyPrint и диагностических утилит (Debian/Ubuntu версии)
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
python3-venv \
gcc \
g++ \
python3-dev \
libpango-1.0-0 \
libpangoft2-1.0-0 \
libpangocairo-1.0-0 \
libcairo2 \
libcairo2-dev \
libffi-dev \
shared-mime-info \
zlib1g-dev \
libjpeg-dev \
libopenjp2-7-dev \
procps \
wget \
&& python3 -m venv /venv \
&& /venv/bin/pip install WeasyPrint==67 \
&& rm -rf /var/lib/apt/lists/*
# Сборка всех компонентов
RUN lerna run build
# Финальный образ
FROM builder AS runtime
# Настройка переменных окружения
ENV PATH="/venv/bin:$PATH"
# Проверка WeasyPrint
RUN weasyprint --version
+92 -12
View File
@@ -1,55 +1,135 @@
# MONO
# Цифровой Кооператив
Система управления взаимоотношениями в кооперативе. Включает в себя полный комплект программного обеспечения для подключения к платформе "Кооперативная Экономика" и управления взаимоотношениями с пайщиками в кооперативе и самим кооперативом на основе смарт-контрактов и простой электронной подписи.
## Ветки
- Для разработки используется ветка `dev`.
- Для разработки используется ветка `dev`.
- Для стейджа используется ветка `testnet`.
- Для продакшена используется ветка `main`.
## Установка
```
```sh
pnpm install
```
Также для работы потребуется установленная фабрика документов `weasyprint` для вашей операционной системы: <https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#installation>
## Конфигурация
Для конфигурации потребуетcя установленный `docker` и `docker-compose`. Выполните команду и следуйте инструкциям:
```
```sh
pnpm run setup
```
## Запуск
### Инфраструктура
Для работы системы необходимы базы `mongo`, `postgres` и `redis`.
Для работы системы необходимы базы `mongo`, `postgres` и `redis`.
Команда запуска баз данных в контейнерах для работы в фоновом режиме:
```
```sh
docker compose up -d
```
### Блокчейн и контракты
```
```sh
pnpm boot
```
Команда запустит блокчейн и установит все смарт-контракты, подготовив систему к локальной разработке или применению.
Команда запустит блокчейн и установит все смарт-контракты, подготовив систему к локальной разработке или применению.
### Бэкенд
```
```sh
pnpm run dev:backend
```
Команда запустит набор сервисов бэкенда и точку доступа GraphQL-API на 2998 порту.
### Фронтенд
Команда запустит рабочий стол кооператива на 3005 порту:
```
```sh
pnpm run dev:desktop
```
### Очистка базы
Команда очистит базу mongodb и начнет производство блоков с начала:
```sh
pnpm run clear
```
Правильная последовательность команд при переустановке проекта "с нуля":
```sh
git pull
pnpm install
pnpm run setup
pnpm run clear
pnpm run boot
pnpm run dev:backend
pnpm run dev:desktop
```
### Кошелёк
Для работы с кошельком блокчейна используется утилита cleos, которая позволяет извлекать таблицы смарт-контрактов и отправлять транзакции в блокчейн если это требуется.
Для работы с командным кошельком `cleos` локально необходимо чтобы инфраструктура была поднята:
```
docker compose up -d
```
Запускаем контейнер с кошельком и входим в него командой:
```
pnpm run cleos
```
В кошельке уже находится дефолтный ключ для работы с локальной средой. Этот ключ зашифрован паролем. Пароль хранится в файле `password` и используется автоматически при разблокировке. Для разблокировки кошелька, находясь в контейнере выполняем:
```
./unlock.sh
```
Для использования кошелька выполняем:
```
./cleos.sh <command>
```
Для получения полного списка команд вызываем кошелек без команд:
```
./cleos.sh
```
Например, для получения информации о текущем состоянии блокчейна:
```
./cleos.sh get info
```
### Возможные проблемы
В случае, если при выполнении ./unlock.sh возникает проблема:
`Unable to connect to keosd, if keosd is running please kill the process and try again`, то выполните команду:
```
./reset.sh
```
Данная команда удалит созданный ранее сокет кошелька, после чего, выполните ./unlock.sh повторно.
## Лицензия
Продукт Потребительского Кооператива "ВОСХОД" распространяется по лицензии BY-NC-SA 4.0.
Продукт Потребительского Кооператива "ВОСХОД" распространяется по лицензии BY-NC-SA 4.0.
Разрешено делиться, копировать и распространять материал на любом носителе и форме, адаптировать, делать ремиксы, видоизменять и создавать новое, опираясь на этот материал. При использовании, Вы должны обеспечить указание авторства, предоставить ссылку, и обозначить изменения, если таковые были сделаны. Если вы перерабатываете, преобразовываете материал или берёте его за основу для производного произведения, вы должны распространять переделанные вами части материала на условиях той же лицензии , в соответствии с которой распространяется оригинал. Запрещено коммерческое использование материала. Использование в коммерческих целях – это использование, в первую очередь направленное на получение коммерческого преимущества или денежного вознаграждения.
Юридический текст лицензии: https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.ru
Юридический текст лицензии: <https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.ru>
+129
View File
@@ -0,0 +1,129 @@
# 📋 Резюме создания компонентов 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 типы
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.
+6 -1
View File
@@ -2,9 +2,14 @@ EOSIO_PUB_KEY=EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
EOSIO_PRV_KEY=5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
DATADIR=blockchain-data
BPACCOUNT=eosio
PASSWORD=PASSWORD
PASSWORD=PW5JGe4WsTPGrjnMzkGd4wfVdzoHCEySgbyq2WBWGAxSfevbXqAG4
SERVER_SECRET=SECRET
SERVER_URL=http://127.0.0.1:2998
MONGO_URI=mongodb://127.0.0.1:27017/cooperative-x
SIMPLE_EXPLORER_API=http://localhost:4000
SKIP_BLOCK_FETCH=TRUE
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USERNAME=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DATABASE=voskhod
+22
View File
@@ -0,0 +1,22 @@
{
"configurations": [
{
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/lib/**",
"${workspaceFolder}/lib/eosiolib/contracts/**",
"${workspaceFolder}/lib/eosiolib/core/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"int128_t=__int128_t",
"uint128_t=__uint128_t"
],
"cStandard": "c17",
"cppStandard": "c++20"
}
],
"version": 4
}
+68
View File
@@ -0,0 +1,68 @@
{
// Enable the ESlint flat config support
"prettier.enable": false,
"editor.formatOnSave": false,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
// "source.fixAll": "explicit",
// "source.organizeImports": "never"
},
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{
"rule": "style/*",
"severity": "off"
},
{
"rule": "*-indent",
"severity": "off"
},
{
"rule": "*-spacing",
"severity": "off"
},
{
"rule": "*-spaces",
"severity": "off"
},
{
"rule": "*-order",
"severity": "off"
},
{
"rule": "*-dangle",
"severity": "off"
},
{
"rule": "*-newline",
"severity": "off"
},
{
"rule": "*quotes",
"severity": "off"
},
{
"rule": "*semi",
"severity": "off"
}
],
// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml"
],
// "[cpp]": {
// "editor.defaultFormatter": "ms-vscode.cpptools",
// },
// "C_Cpp.vcFormat.indent.withinParentheses": "indent",
"C_Cpp.errorSquiggles": "disabled"
}
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.2.10](https://github.com/coopenomics/mono/compare/v2.2.9...v2.2.10) (2025-03-27)
**Note:** Version bump only for package @coopenomics/boot
## [2.2.9](https://github.com/coopenomics/mono/compare/v2.2.8...v2.2.9) (2025-03-12)
**Note:** Version bump only for package @coopenomics/boot
+1 -1
View File
@@ -9,7 +9,7 @@ export default antfu(
},
{
rules: {
'no-unused-vars': 'warn',
'no-unused-vars': 'off',
'unused-imports/no-unused-vars': 'off',
'no-console': 'off',
'unicorn/prefer-number-properties': 'off',
+14 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@coopenomics/boot",
"type": "module",
"version": "2.2.9",
"version": "2026.2.22-2",
"private": true,
"packageManager": "pnpm@9.0.6",
"description": "",
@@ -11,14 +11,23 @@
"scripts": {
"enter": "./scripts/enter.sh",
"cli": "esno src/index.ts",
"deploy": "esno src/index.ts deploy",
"cleos": "esno src/index.ts cleos",
"unlock": "esno src/index.ts unlock",
"boot": "esno src/index.ts boot",
"clear": "esno src/index.ts clear",
"boot:clean": "esno src/index.ts boot:clean",
"boot:extra": "esno src/index.ts boot:extra",
"reboot:clean": "cd components/boot/scripts && bash clean_reboot.sh",
"reboot:extra": "cd components/boot/scripts && bash extra_reboot.sh",
"create-coop": "esno src/index.ts create-coop",
"clear": "./scripts/clear.sh",
"start": "./scripts/restart.sh",
"stop": "./scripts/stop.sh"
},
"dependencies": {
"@coopenomics/factory": "workspace:*",
"axios": "^1.6.8",
"@types/pg": "^8.16.0",
"axios": "^1.13.5",
"chai": "^5.1.2",
"commander": "^12.1.0",
"cooptypes": "workspace:*",
@@ -29,7 +38,8 @@
"eosjs-ecc": "^4.0.7",
"execa": "^9.5.2",
"mocha": "^10.7.3",
"mongoose": "^8.10.0"
"mongoose": "^9.2.2",
"pg": "^8.16.3"
},
"devDependencies": {
"@antfu/eslint-config": "^2.18.0",
+47
View File
@@ -0,0 +1,47 @@
#!/bin/bash
# Останавливаем и удаляем контейнеры вместе с volumes
echo "Останавливаем и удаляем контейнеры с volumes..."
docker compose down -v mongo postgres cooparser || true
# Останавливаем blockchain контейнер перед удалением данных
echo "Останавливаем blockchain контейнер..."
docker compose stop node || true
# Удаляем blockchain data
echo "Удаляем blockchain data..."
# sudo chmod -R 755 ../blockchain-data/ 2>/dev/null || true
sudo rm -rf ../blockchain-data/
# Пересоздаем и запускаем базы данных
echo "Пересоздаем и запускаем базы данных..."
docker compose up -d mongo postgres
# Ждем готовности MongoDB
echo "Ждем готовности MongoDB..."
until docker exec mongo mongosh --eval "db.adminCommand('ping')" --quiet > /dev/null 2>&1; do
echo "MongoDB еще не готов, ждем..."
sleep 2
done
echo "MongoDB готов!"
# Ждем готовности PostgreSQL
echo "Ждем готовности PostgreSQL..."
until docker exec postgres pg_isready -U postgres -d voskhod > /dev/null 2>&1; do
echo "PostgreSQL еще не готов, ждем..."
sleep 2
done
echo "PostgreSQL готов!"
# Запускаем boot процесс
echo "Запускаем boot процесс..."
pnpm run boot:clean
# Запускаем parser
echo "Запускаем parser..."
docker compose up -d cooparser
echo "Запускаем контроллер..."
docker compose restart coopback || true
echo "Перезапуск завершен!"
+34
View File
@@ -0,0 +1,34 @@
#!/bin/bash
# Останавливаем и удаляем контейнеры вместе с volumes
echo "Останавливаем и удаляем контейнеры с volumes..."
docker compose down -v mongo postgres cooparser || true
# Останавливаем blockchain контейнер перед удалением данных
echo "Останавливаем blockchain контейнер..."
docker compose stop node || true
# Удаляем blockchain data
echo "Удаляем blockchain data..."
# sudo chmod -R 755 ../blockchain-data/ 2>/dev/null || true
sudo rm -rf ../blockchain-data/
# Пересоздаем и запускаем базы данных
echo "Пересоздаем и запускаем базы данных..."
docker compose up -d mongo postgres
# Ждем готовности MongoDB
echo "Ждем готовности MongoDB..."
until docker exec mongo mongosh --eval "db.adminCommand('ping')" --quiet > /dev/null 2>&1; do
echo "MongoDB еще не готов, ждем..."
sleep 2
done
echo "MongoDB готов!"
# Ждем готовности PostgreSQL
echo "Ждем готовности PostgreSQL..."
until docker exec postgres pg_isready -U postgres -d voskhod > /dev/null 2>&1; do
echo "PostgreSQL еще не готов, ждем..."
sleep 2
done
echo "PostgreSQL готов!"
+51
View File
@@ -0,0 +1,51 @@
#!/bin/bash
# Останавливаем контроллер перед очисткой данных
echo "Останавливаем контроллер..."
docker compose down coopback || true
# Останавливаем и удаляем контейнеры вместе с volumes
echo "Останавливаем и удаляем контейнеры с volumes..."
docker compose down -v mongo postgres cooparser || true
# Останавливаем blockchain контейнер перед удалением данных
echo "Останавливаем blockchain контейнер..."
docker compose stop node || true
# Удаляем blockchain data
echo "Удаляем blockchain data..."
# sudo chmod -R 755 ../blockchain-data/ 2>/dev/null || true
sudo rm -rf ../blockchain-data/
# Пересоздаем и запускаем базы данных
echo "Пересоздаем и запускаем базы данных..."
docker compose up -d mongo postgres
# Ждем готовности MongoDB
echo "Ждем готовности MongoDB..."
until docker exec mongo mongosh --eval "db.adminCommand('ping')" --quiet > /dev/null 2>&1; do
echo "MongoDB еще не готов, ждем..."
sleep 2
done
echo "MongoDB готов!"
# Ждем готовности PostgreSQL
echo "Ждем готовности PostgreSQL..."
until docker exec postgres pg_isready -U postgres -d voskhod > /dev/null 2>&1; do
echo "PostgreSQL еще не готов, ждем..."
sleep 2
done
echo "PostgreSQL готов!"
# Запускаем boot процесс
echo "Запускаем boot процесс..."
pnpm run boot:extra
# Запускаем parser
echo "Запускаем parser..."
docker compose up -d cooparser
echo "Запускаем контроллер..."
docker compose up -d coopback
echo "Перезапуск завершен!"
+47
View File
@@ -0,0 +1,47 @@
#!/bin/bash
# Останавливаем и удаляем контейнеры вместе с volumes
echo "Останавливаем и удаляем контейнеры с volumes..."
docker compose down -v mongo postgres cooparser || true
# Останавливаем blockchain контейнер перед удалением данных
echo "Останавливаем blockchain контейнер..."
docker compose stop node || true
# Удаляем blockchain data
echo "Удаляем blockchain data..."
# sudo chmod -R 755 ../blockchain-data/ 2>/dev/null || true
sudo rm -rf ../blockchain-data/
# Пересоздаем и запускаем базы данных
echo "Пересоздаем и запускаем базы данных..."
docker compose up -d mongo postgres
# Ждем готовности MongoDB
echo "Ждем готовности MongoDB..."
until docker exec mongo mongosh --eval "db.adminCommand('ping')" --quiet > /dev/null 2>&1; do
echo "MongoDB еще не готов, ждем..."
sleep 2
done
echo "MongoDB готов!"
# Ждем готовности PostgreSQL
echo "Ждем готовности PostgreSQL..."
until docker exec postgres pg_isready -U postgres -d voskhod > /dev/null 2>&1; do
echo "PostgreSQL еще не готов, ждем..."
sleep 2
done
echo "PostgreSQL готов!"
# Запускаем boot процесс
echo "Запускаем boot процесс..."
pnpm run boot
# Запускаем parser
echo "Запускаем parser..."
docker compose up -d cooparser
echo "Запускаем контроллер..."
docker compose restart coopback || true
echo "Перезапуск завершен!"
+145 -84
View File
@@ -4,6 +4,7 @@ import { JsSignatureProvider } from 'eosjs/dist/eosjs-jssig'
import { Api, JsonRpc, Serialize } from 'eosjs'
import {
DraftContract,
GatewayContract,
RegistratorContract,
SovietContract,
SystemContract,
@@ -14,6 +15,7 @@ import EosApi from 'eosjs-api'
// @ts-expect-error заменить на antelope
import ecc from 'eosjs-ecc'
import config from '../configs'
import { consoleIt } from '../tests/shared/consoleIt'
import type { Contract, Feature, Keys, Network } from '../types'
@@ -42,8 +44,8 @@ export default class Blockchain {
this.api = new Api({
rpc,
signatureProvider,
textDecoder: new TextDecoder(),
textEncoder: new TextEncoder(),
textDecoder: new TextDecoder() as any,
textEncoder: new TextEncoder() as any,
})
this.api.read = await EosApi({ httpEndpoint: res })
}
@@ -513,8 +515,8 @@ export default class Blockchain {
console.log('Новый кооператив пре-иниализирован: ', params)
}
// TODO change registerOrganization to registerCooperative
async registerOrganization(
// TODO change registerCooperative to registerCooperative
async registerCooperative(
params: RegistratorContract.Actions.RegisterCooperative.IRegisterCooperative,
) {
await this.update_pass_instance()
@@ -573,7 +575,7 @@ export default class Blockchain {
)
}
async registerAccount2(
async createAccount(
params: RegistratorContract.Actions.CreateAccount.ICreateAccount,
) {
await this.update_pass_instance()
@@ -586,7 +588,7 @@ export default class Blockchain {
name: RegistratorContract.Actions.CreateAccount.actionName,
authorization: [
{
actor: params.registrator,
actor: params.coopname,
permission: 'active',
},
],
@@ -606,10 +608,10 @@ export default class Blockchain {
}
async registerUser(
params: RegistratorContract.Actions.RegisterUser.IRegistrerUser,
params: RegistratorContract.Actions.RegisterUser.IRegisterUser,
) {
await this.update_pass_instance()
console.dir(params, { depth: null })
await this.api.transact(
{
actions: [
@@ -618,13 +620,11 @@ export default class Blockchain {
name: RegistratorContract.Actions.RegisterUser.actionName,
authorization: [
{
actor: params.registrator,
actor: params.coopname,
permission: 'active',
},
],
data: {
...params,
},
data: params,
},
],
},
@@ -637,9 +637,69 @@ export default class Blockchain {
console.log('Новый пользователь: ', params)
}
async addUser(
params: RegistratorContract.Actions.AddUser.IAddUser,
) {
await this.update_pass_instance()
console.dir(params, { depth: null })
await this.api.transact(
{
actions: [
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.AddUser.actionName,
authorization: [
{
actor: params.coopname,
permission: 'active',
},
],
data: params,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
console.log('Добавлен пользователь: ', params.username)
}
async changeKey(
params: RegistratorContract.Actions.ChangeKey.IChangeKey,
) {
await this.update_pass_instance()
console.dir(params, { depth: null })
await this.api.transact(
{
actions: [
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.ChangeKey.actionName,
authorization: [
{
actor: params.coopname,
permission: 'active',
},
],
data: params,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
console.log('Изменён ключ для пользователя: ', params.username)
}
async transfer(params: TokenContract.Actions.Transfer.ITransfer) {
await this.update_pass_instance()
console.dir(params, { depth: null })
const result = await this.api.transact(
{
actions: [
@@ -747,7 +807,7 @@ export default class Blockchain {
name: SovietContract.Actions.Decisions.VoteFor.actionName,
authorization: [
{
actor: params.member,
actor: params.username,
permission: 'active',
},
],
@@ -800,36 +860,39 @@ export default class Blockchain {
async exec(params: SovietContract.Actions.Decisions.Exec.IExec) {
await this.update_pass_instance()
await this.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Decisions.Exec.actionName,
authorization: [
{
actor: params.executer,
permission: 'active',
try {
await this.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Decisions.Exec.actionName,
authorization: [
{
actor: params.executer,
permission: 'active',
},
],
data: {
...params,
},
],
data: {
...params,
},
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
}
catch (e) {
console.dir(e, { depth: null })
}
console.log('Решение исполнено: ', params)
}
async joinCoop(
params: RegistratorContract.Actions.JoinCooperative.IJoinCooperative,
async ConfirmPayment(
params: GatewayContract.Actions.CompleteIncome.ICompleteIncome,
) {
await this.update_pass_instance()
@@ -837,11 +900,11 @@ export default class Blockchain {
{
actions: [
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.JoinCooperative.actionName,
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteIncome.actionName,
authorization: [
{
actor: params.registrator,
actor: params.coopname,
permission: 'active',
},
],
@@ -860,38 +923,6 @@ export default class Blockchain {
console.log('Заявление на вступление отправлено в совет: ', params)
}
async createBoard(
params: SovietContract.Actions.Boards.CreateBoard.ICreateboard,
) {
await this.update_pass_instance()
await this.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Boards.CreateBoard.actionName,
authorization: [
{
actor: params.username,
permission: 'active',
},
],
data: {
...params,
},
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
console.log('Совет создан: ', params)
}
async createDraft(params: DraftContract.Actions.CreateDraft.ICreateDraft) {
await this.update_pass_instance()
@@ -919,7 +950,7 @@ export default class Blockchain {
},
)
console.log('Шаблон создан: ', params)
console.log('Шаблон создан: ', params.registry_id)
}
async createTranslation(
@@ -951,7 +982,7 @@ export default class Blockchain {
},
)
console.log('Перевод создан: ', params)
console.log('Перевод создан: ', params.registry_id)
}
async createProgram(
@@ -986,20 +1017,18 @@ export default class Blockchain {
console.log('Программа установлена: ', params)
}
async makeCoagreement(
params: SovietContract.Actions.Agreements.MakeCoagreement.IMakeCoagreement,
) {
async createBoard(params: SovietContract.Actions.Boards.CreateBoard.ICreateboard) {
await this.update_pass_instance()
await this.api.transact(
console.dir(params, { depth: null })
const result = await this.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Agreements.MakeCoagreement.actionName,
name: SovietContract.Actions.Boards.CreateBoard.actionName,
authorization: [
{
actor: params.administrator,
actor: params.coopname,
permission: 'active',
},
],
@@ -1015,6 +1044,38 @@ export default class Blockchain {
},
)
console.log('Программа установлена: ', params)
console.log('Создан совет: ', params)
return result
}
/**
* Выполняет транзакцию и автоматически выводит консоль логи через consoleIt
* @param actions - массив действий для транзакции
* @param options - опции транзакции (по умолчанию стандартные)
* @param options.blocksBehind - количество блоков позади для подтверждения
* @param options.expireSeconds - время жизни транзакции в секундах
* @returns результат транзакции
*/
async transactWithLogs(
actions: any[],
options: { blocksBehind?: number, expireSeconds?: number } = {},
): Promise<any> {
await this.update_pass_instance()
const defaultOptions = {
blocksBehind: 3,
expireSeconds: 30,
...options,
}
const result = await this.api.transact(
{ actions },
defaultOptions,
)
// Автоматически выводим консоль логи
consoleIt(result)
return result
}
}
+4 -4
View File
@@ -10,7 +10,7 @@ enable-stale-production = true
read-only-read-window-time-us = 120000
net-threads = 2
max-transaction-time=2000
max-transaction-time=10000
http-server-address = 0.0.0.0:8888
p2p-listen-endpoint = 0.0.0.0:9876
@@ -26,13 +26,13 @@ max-body-size = 10485760
abi-serializer-max-time-ms = 200000
contracts-console = true
max-block-cpu-usage-threshold-us = 5000
max-block-net-usage-threshold-bytes = 1024
max-block-cpu-usage-threshold-us = 200000
max-block-net-usage-threshold-bytes = 800000
verbose-http-errors = true
chain-state-history = true
trace-history = true
state-history-endpoint = 0.0.0.0:8080
state-history-endpoint = 0.0.0.0:8070
resource-monitor-space-threshold = 99
resource-monitor-not-shutdown-on-threshold-exceeded = true
wasm-runtime = eos-vm
+25
View File
@@ -65,6 +65,16 @@ export default [
path: path.resolve(process.cwd(), '../contracts/build/contracts/fund'),
target: 'fund',
},
{
name: 'ledger',
path: path.resolve(process.cwd(), '../contracts/build/contracts/ledger'),
target: 'ledger',
},
{
name: 'test',
path: path.resolve(process.cwd(), '../contracts/build/contracts/test'),
target: 'test',
},
{
name: 'contributor',
path: path.resolve(process.cwd(), '../contracts/build/contracts/contributor'),
@@ -75,4 +85,19 @@ export default [
path: path.resolve(process.cwd(), '../contracts/build/contracts/capital'),
target: 'capital',
},
{
name: 'wallet',
path: path.resolve(process.cwd(), '../contracts/build/contracts/wallet'),
target: 'wallet',
},
{
name: 'loan',
path: path.resolve(process.cwd(), '../contracts/build/contracts/loan'),
target: 'loan',
},
{
name: 'meet',
path: path.resolve(process.cwd(), '../contracts/build/contracts/meet'),
target: 'meet',
},
]
+2 -2
View File
@@ -9,8 +9,8 @@
"net_usage_leeway": 500,
"context_free_discount_net_usage_num": 20,
"context_free_discount_net_usage_den": 100,
"max_block_cpu_usage": 300000,
"target_block_cpu_usage_pct": 500,
"max_block_cpu_usage": 1000000,
"target_block_cpu_usage_pct": 1000,
"max_transaction_cpu_usage": 290000,
"min_transaction_cpu_usage": 100,
"max_transaction_lifetime": 3600,
+24 -4
View File
@@ -53,7 +53,7 @@ export default {
allocations: [
{
to: 'eosio',
quantity: `10000.0000 ${SYMBOL}`,
quantity: `100000.0000 ${SYMBOL}`,
},
],
accounts: [
@@ -87,6 +87,26 @@ export default {
name: 'capital',
code_permissions_to: ['capital'],
},
{
name: 'loan',
code_permissions_to: ['loan'],
},
{
name: 'ledger',
code_permissions_to: ['ledger'],
},
{
name: 'test',
code_permissions_to: ['test'],
},
{
name: 'meet',
code_permissions_to: ['meet'],
},
{
name: 'wallet',
code_permissions_to: ['wallet'],
},
{
name: 'soviet',
code_permissions_to: ['soviet'],
@@ -115,9 +135,9 @@ export default {
name: 'contributor',
code_permissions_to: ['contributor'],
},
{
name: provider_chairman,
},
// {
// name: provider_chairman,
// },
{
name: provider,
code_permissions_to: ['registrator'],
+21 -34
View File
@@ -1,38 +1,25 @@
import type { Container } from "dockerode"
import { findContainerByName } from "./find"
import { runContainer } from "./run"
import { exec } from 'node:child_process'
import { promisify } from 'node:util'
export async function execCommand(command: string[]): Promise<string> {
const container = await runContainer(true)
const execAsync = promisify(exec)
const exec = await container.exec({
Cmd: command,
AttachStdout: true,
AttachStderr: true,
})
export async function execCommand(command: string[]): Promise<void> {
const fullCmd = ['docker', 'compose', 'exec', 'node', ...command].join(' ')
return new Promise((resolve, reject) => {
exec.start({}, (err: any, stream: any) => {
if (err) {
return reject(err)
}
let output = ""
// eslint-disable-next-line node/prefer-global/buffer
stream.on("data", (data: Buffer) => {
output += data.toString()
})
stream.on("end", () => {
output = output.replace(/^\d+\s*/, "")
console.log(output)
resolve(output)
})
stream.on("error", (err: any) => {
reject(err)
})
})
})
try {
// eslint-disable-next-line node/prefer-global/process
const { stdout, stderr } = await execAsync(fullCmd, { env: process.env })
if (stdout)
console.log(stdout.trim())
if (stderr)
console.error(stderr.trim())
}
catch (err: any) {
if (err.stdout)
console.log(err.stdout.trim())
if (err.stderr)
console.error(err.stderr.trim())
// опционально:
// console.error(`Command failed with code ${err.code}`)
}
}
+11
View File
@@ -3,9 +3,18 @@ import * as path from 'node:path'
import mongoose from 'mongoose'
export async function clearDB(): Promise<void> {
// Очистка MongoDB
// eslint-disable-next-line node/prefer-global/process
await mongoose.connect(process.env.MONGO_URI as string)
try {
await mongoose.connection.collection('users').deleteMany({})
console.log('Все документы удалены из коллекции users')
}
catch (e) {
console.error('Ошибка при удалении:', e)
}
try {
await mongoose.connection.collection('sync').deleteMany({})
console.log('Все документы удалены из коллекции sync')
@@ -29,6 +38,8 @@ export async function clearDB(): Promise<void> {
catch (e) {
console.error('Ошибка при удалении:', e)
}
// PostgreSQL будет пересоздан полностью в скрипте extra_reboot.sh
}
export async function clearDirectory(dirPath: string): Promise<void> {
+119 -21
View File
@@ -6,7 +6,8 @@ import { config } from 'dotenv'
import { execCommand } from './docker/exec'
import { stopContainerByName } from './docker/stop'
import { runContainer } from './docker/run'
import { boot } from './init/booter'
import { boot, bootClean, bootExtra } from './init/booter'
import { startCoop } from './init/cooperative'
import { sleep } from './utils'
import { checkHealth } from './docker/health'
import { clearDB, clearDirectory, deleteFile } from './docker/purge'
@@ -31,17 +32,11 @@ program.version('0.1.0')
// Команда для запуска команды в контейнере
program
.command('cleos <cmd...>')
.command('cleos [cmd...]')
.description('Execute a cleos command in a Node container')
.allowUnknownOption()
.action(async (cmd: string[]) => {
try {
console.log(cmd)
await execCommand(['cleos', ...cmd])
}
catch (error) {
console.error('Command execution failed:', error)
}
.action(async (cmd: string[] = []) => {
await execCommand(['cleos', ...cmd])
})
// Команда для запуска команды в контейнере
@@ -54,14 +49,8 @@ program
.action(async (cmd: string, options: any) => {
try {
const { target, network } = options
await deleteFile(keosdPath)
await execCommand([
'cleos',
'wallet',
'unlock',
'--password',
process.env.PASSWORD!,
])
await deployCommand(cmd, target, network)
}
catch (error) {
@@ -126,15 +115,19 @@ program
.action(async () => {
try {
await stopContainerByName('node')
await clearDirectory(basePath)
await clearDB()
}
catch (e) {
console.log('Нет контейнера для остановки. Стартуем новый..')
}
try {
await runContainer()
await sleep(5000)
await checkHealth()
await boot()
console.log(`
Boot is
.d8888b. .d88888b. 888b d888 8888888b. 888 8888888888 88888888888 8888888888
d88P Y88b d88P" "Y88b 8888b d8888 888 Y88b 888 888 888 888
@@ -144,7 +137,7 @@ d88P Y88b d88P" "Y88b 8888b d8888 888 Y88b 888 888 888
888 888 888 888 888 Y8P 888 888 888 888 888 888
Y88b d88P Y88b. .d88P 888 " 888 888 888 888 888 888
"Y8888P" "Y88888P" 888 888 888 88888888 8888888888 888 8888888888
`)
process.exit(0)
}
@@ -154,6 +147,111 @@ Y88b d88P Y88b. .d88P 888 " 888 888 888 888 888
}
})
// Команда для получения списка контейнеров
program
.command('boot:clean')
.description('Boot infrastructure without initial data and cooperatives')
.action(async () => {
try {
await stopContainerByName('node')
}
catch (e) {
console.log('Нет контейнера для остановки. Стартуем новый..')
}
try {
await runContainer()
await sleep(5000)
await checkHealth()
await bootClean()
console.log(`
Boot Clean is
.d8888b. .d88888b. 888b d888 8888888b. 888 8888888888 88888888888 8888888888
d88P Y88b d88P" "Y88b 8888b d8888 888 Y88b 888 888 888 888
888 888 888 888 88888b.d88888 888 888 888 888 888 888
888 888 888 888Y88888P888 888 d88P 888 8888888 888 8888888
888 888 888 888 Y888P 888 8888888P" 888 888 888 888
888 888 888 888 888 Y8P 888 888 888 888 888 888
Y88b d88P Y88b. .d88P 888 " 888 888 888 888 888 888
"Y8888P" "Y88888P" 888 888 888 88888888 8888888888 888 8888888888
`)
process.exit(0)
}
catch (error) {
console.error('Failed to boot clean:', error)
process.exit(1)
}
})
// Команда для получения списка контейнеров
program
.command('boot:extra')
.description('Boot infrastructure with extended initial data and additional shareholders')
.action(async () => {
try {
await stopContainerByName('node')
}
catch (e) {
console.log('Нет контейнера для остановки. Стартуем новый..')
}
try {
await runContainer()
await sleep(5000)
await checkHealth()
await bootExtra()
const councilMembers = [
{ username: 'ant', fullName: 'Иванов Иван Иванович', email: 'ivanov@example.com' },
{ username: 'petr', fullName: 'Сидоров Петр Сергеевич', email: 'sidorov@example.com' },
{ username: 'anna', fullName: 'Петрова Анна Ивановна', email: 'petrova@example.com' },
{ username: 'mikhail', fullName: 'Кузнецов Михаил Андреевич', email: 'kuznetsov@example.com' },
{ username: 'olga', fullName: 'Соколова Ольга Викторовна', email: 'sokolova@example.com' },
]
console.log('\nЧлены совета (логин / ФИО / email):')
for (const member of councilMembers)
console.log(` - ${member.username}: ${member.fullName} / ${member.email}`)
console.log(`
Boot Extra is
.d8888b. .d88888b. 888b d888 8888888b. 888 8888888888 88888888888 8888888888
d88P Y88b d88P" "Y88b 8888b d8888 888 Y88b 888 888 888 888
888 888 888 888 88888b.d88888 888 888 888 888 888 888
888 888 888 888Y88888P888 888 d88P 888 8888888 888 8888888
888 888 888 888 Y888P 888 8888888P" 888 888 888 888
888 888 888 888 888 Y8P 888 888 888 888 888 888
Y88b d88P Y88b. .d88P 888 " 888 888 888 888 888 888
"Y8888P" "Y88888P" 888 888 888 88888888 8888888888 888 8888888888
`)
process.exit(0)
}
catch (error) {
console.error('Failed to boot extra:', error)
process.exit(1)
}
})
// Команда для создания кооператива
program
.command('create-coop')
.description('Create cooperative after clean boot')
.action(async () => {
try {
await startCoop()
console.log('Кооператив успешно создан')
process.exit(0)
}
catch (error) {
console.error('Failed to create cooperative:', error)
process.exit(1)
}
})
// Команда для получения списка контейнеров
program
.command('clear')
+31 -3
View File
@@ -1,7 +1,35 @@
import { startInfra } from "./infra"
import { startCoop } from "./cooperative"
import config from '../configs'
import { initExtensionsInPostgres, initSystemStatus } from '../postgres-init'
import { installExtraData, installInitialData, startInfra } from './infra'
import { CooperativeClass, startCoop } from './cooperative'
export async function boot() {
await startInfra()
const blockchain = await startInfra()
await installInitialData(blockchain, false) // Создать базовый совет
console.log('Инициализируем статус системы в PostgreSQL')
await initSystemStatus()
await startCoop()
}
export async function bootClean() {
const blockchain = await startInfra()
console.log('Создаём программы (Благорост и маркетплейс)')
const cooperative = new CooperativeClass(blockchain)
await cooperative.createPrograms(config.provider)
}
export async function bootExtra() {
const blockchain = await startInfra()
await installInitialData(blockchain, true) // Создать расширенный совет (устанавливает статус 'active' в MongoDB)
await installExtraData(blockchain) // Добавить дополнительных пайщиков
console.log('Инициализируем статус системы в PostgreSQL')
await initSystemStatus() // Устанавливает статус 'active' в PostgreSQL
console.log('Инициализируем extensions в PostgreSQL')
await initExtensionsInPostgres() // Инициализирует таблицу extensions с данными capital
}
+113 -117
View File
@@ -1,11 +1,9 @@
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 type { Cooperative } from 'cooptypes'
import type { Keys } from '../types'
import config, { GOVERN_SYMBOL, SYMBOL } from '../configs'
import Blockchain from '../blockchain'
import { sendPostToCoopbackWithSecret, sleep } from '../utils'
import { generateRandomSHA256 } from '../utils/randomHash'
import { processLastDecision } from '../tests/soviet/processLastDecision'
const test_hash
= '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777'
@@ -14,11 +12,23 @@ const test_sign
const test_pkey = 'EOS5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzxrLg3J'
const test_meta = JSON.stringify({})
const document = {
const document: Cooperative.Document.ISignedDocument2 = {
hash: test_hash,
signature: test_sign,
public_key: test_pkey,
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,
},
],
meta: test_meta,
version: '1.0.0',
doc_hash: test_hash,
meta_hash: test_hash,
}
export class CooperativeClass {
@@ -32,7 +42,7 @@ export class CooperativeClass {
await this.blockchain.createProgram({
coopname,
username: coopname,
type: 'cooplace',
type: 'marketplace',
title: 'КООПЛЕЙС',
announce: '',
description: '',
@@ -44,47 +54,37 @@ export class CooperativeClass {
meta: '',
is_can_coop_spend_share_contributions: false,
})
}
async createAgreements(coopname: string) {
// console.log('создаём кооперативное соглашение/положение по кошельку')
await this.blockchain.makeCoagreement({
await this.blockchain.createProgram({
coopname,
administrator: coopname,
type: 'wallet',
draft_id: TCooperative.Registry.WalletAgreement.registry_id,
program_id: 1,
username: coopname,
type: 'generator',
title: 'Целевая потребительская программа "Генератор"',
announce: '',
description: '',
preview: '',
images: '',
calculation_type: 'free',
fixed_membership_contribution: `${Number(0).toFixed(4)} ${GOVERN_SYMBOL}`,
membership_percent_fee: 0, // 10%
meta: '',
is_can_coop_spend_share_contributions: true,
})
await this.blockchain.makeCoagreement({
await this.blockchain.createProgram({
coopname,
administrator: coopname,
type: 'signature',
draft_id: TCooperative.Registry.RegulationElectronicSignature.registry_id,
program_id: 0,
})
await this.blockchain.makeCoagreement({
coopname,
administrator: coopname,
type: 'user',
draft_id: TCooperative.Registry.UserAgreement.registry_id,
program_id: 0,
})
await this.blockchain.makeCoagreement({
coopname,
administrator: coopname,
type: 'privacy',
draft_id: TCooperative.Registry.PrivacyPolicy.registry_id,
program_id: 0,
})
await this.blockchain.makeCoagreement({
coopname,
administrator: coopname,
type: 'coopenomics',
draft_id: TCooperative.Registry.CoopenomicsAgreement.registry_id,
program_id: 0,
username: coopname,
type: 'blagorost',
title: 'Целевая потребительская программа "Благорост"',
announce: '',
description: '',
preview: '',
images: '',
calculation_type: 'free',
fixed_membership_contribution: `${Number(0).toFixed(4)} ${GOVERN_SYMBOL}`,
membership_percent_fee: 0, // 10%
meta: '',
is_can_coop_spend_share_contributions: true,
})
}
@@ -96,8 +96,7 @@ export class CooperativeClass {
)
console.log('Регистрируем аккаунт')
await this.blockchain.registerAccount2({
registrator: config.provider,
await this.blockchain.createAccount({
coopname: config.provider,
referer: '',
username: account.username,
@@ -106,17 +105,68 @@ export class CooperativeClass {
})
console.log('Регистрируем аккаунт как пользователя')
const registration_hash = generateRandomSHA256()
await this.blockchain.registerUser({
registrator: config.provider,
coopname: config.provider,
braname: '',
username: account.username,
type: 'organization',
statement: document,
registration_hash,
})
console.log('Переводим аккаунт в организации')
console.log('Отправляем заявление на вступление')
await this.blockchain.registerOrganization({
await this.blockchain.ConfirmPayment({
coopname: config.provider,
income_hash: registration_hash,
})
await processLastDecision(this.blockchain, config.provider)
// console.log('Голосуем по решению в провайдере')
// await this.blockchain.votefor({
// version: '1.0.0',
// signed_at: '2025-05-14T12:22:26',
// signed_hash: test_hash,
// signature: test_sign,
// public_key: test_pkey,
// coopname: config.provider,
// username: config.provider_chairman,
// decision_id: 1,
// })
// console.log('Утверждаем решение в провайдере')
// await this.blockchain.authorize({
// coopname: config.provider,
// chairman: config.provider_chairman,
// decision_id: 1,
// document,
// })
// console.log('Исполняем решение в провайдере')
// await this.blockchain.exec({
// executer: config.provider_chairman,
// coopname: config.provider,
// decision_id: 1,
// })
console.log('Отправляем подписанное положение о ЦПП Кошелька оператору')
await this.blockchain.sendAgreement({
coopname: config.provider,
administrator: config.provider,
username: username!,
agreement_type: 'wallet',
document,
})
console.log('Переводим аккаунт в кооператив')
await this.blockchain.registerCooperative({
username: account.username,
coopname: account.username,
params: {
@@ -132,62 +182,8 @@ export class CooperativeClass {
document,
})
console.log('Отправляем заявление на вступление')
await this.blockchain.joinCoop({
registrator: config.provider_chairman,
coopname: config.provider,
username: account.username,
document,
braname: '',
})
console.log('Голосуем по решению в провайдере')
await this.blockchain.votefor({
coopname: config.provider,
member: config.provider_chairman,
decision_id: 1,
})
console.log('Утверждаем решение в провайдере')
await this.blockchain.authorize({
coopname: config.provider,
chairman: config.provider_chairman,
decision_id: 1,
document,
})
console.log('Исполняем решение в провайдере')
await this.blockchain.exec({
executer: config.provider_chairman,
coopname: config.provider,
decision_id: 1,
})
console.log('Отправляем подписанное положение о ЦПП Кошелька оператору')
await this.blockchain.sendAgreement({
coopname: config.provider,
administrator: config.provider,
username: username!,
agreement_type: 'wallet',
document: { // отправляем произвольный документ с валидной подписью
hash: '157192B276DA23CC84AB078FC8755C051C5F0430BF4802E55718221E6B76C777',
public_key: 'PUB_K1_5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzzEtUA4',
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
meta: '{}',
},
})
console.log('создаём программы и соглашения новому кооперативу')
// await this.createPrograms(username)
await this.createAgreements(username)
console.log('Переводим инициализационные токены')
await this.blockchain.transfer({ from: 'eosio', to: username, quantity: `100.0000 ${SYMBOL}`, memo: '' })
await this.blockchain.transfer({ from: 'eosio', to: account.username, quantity: `100.0000 ${SYMBOL}`, memo: '' })
console.log(`Арендуем ресурсы кооперативу`)
@@ -206,18 +202,18 @@ export async function startCoop() {
const blockchain = new Blockchain(config.network, config.private_keys)
const cooperative = new CooperativeClass(blockchain)
await cooperative.createCooperative('cooperative1', {
// eslint-disable-next-line node/prefer-global/process
privateKey: process.env.EOSIO_PRV_KEY!,
// eslint-disable-next-line node/prefer-global/process
publicKey: process.env.EOSIO_PUB_KEY!,
})
// await cooperative.createCooperative('cooperative1', {
// // eslint-disable-next-line node/prefer-global/process
// privateKey: process.env.EOSIO_PRV_KEY!,
// // eslint-disable-next-line node/prefer-global/process
// publicKey: process.env.EOSIO_PUB_KEY!,
// })
await blockchain.preInit({
coopname: 'cooperative1',
username: config.provider,
status: 'active',
})
// await blockchain.preInit({
// coopname: 'cooperative1',
// username: config.provider,
// status: 'active',
// })
console.log('Кооператив предварительно подготовлен к установке совета.')
}
+268 -36
View File
@@ -3,11 +3,12 @@ import axios from 'axios'
import { Generator, Registry } from '@coopenomics/factory'
import type { Cooperative } from 'cooptypes'
import { DraftContract } from 'cooptypes'
import mongoose from 'mongoose'
import mongoose, { Types } from 'mongoose'
import type { Account, Contract } from '../types'
import config from '../configs'
import Blockchain from '../blockchain'
import { sleep } from '../utils'
import { initUsersInPostgres, initVaultInPostgres } from '../postgres-init'
import { CooperativeClass } from './cooperative'
export async function startInfra() {
@@ -145,10 +146,32 @@ export async function startInfra() {
})
}
console.log(`Арендуем ресурсы провайдеру`)
await blockchain.powerup({
payer: 'eosio',
receiver: config.provider,
days: config.powerup.days,
payment: `10000.0000 ${config.token.symbol}`,
transfer: true,
})
await blockchain.transfer({
from: 'eosio',
to: config.provider,
quantity: `1000.0000 ${config.token.symbol}`,
memo: '',
})
console.log('Базовая инфраструктура установлена')
return blockchain
}
export async function installInitialData(blockchain: Blockchain, isExtended = false) {
const organizationData: Cooperative.Users.IOrganizationData = {
username: 'voskhod',
type: 'coop',
short_name: '"ПК Восход"',
short_name: 'ПК "Восход"',
full_name: 'Потребительский Кооператив "ВОСХОД"',
represented_by: {
first_name: 'Иван',
@@ -157,7 +180,7 @@ export async function startInfra() {
position: 'Председатель',
based_on: 'Решение общего собрания №1',
},
country: 'Russia',
country: 'Российская Федерация',
city: 'Москва',
fact_address: '117593 г. Москва, муниципальный округ Ясенево, проезд Соловьиный, дом 1, помещение 1/1',
full_address:
@@ -202,7 +225,7 @@ export async function startInfra() {
first_name: 'Иван',
last_name: 'Иванов',
middle_name: 'Иванович',
birthdate: '2023-04-01',
birthdate: '1990/04/01',
phone: '+71234567890',
email: 'ivanov@example.com',
full_address: 'Переулок Правды д. 1',
@@ -210,7 +233,7 @@ export async function startInfra() {
series: 7122,
number: 112233,
issued_by: 'отделом УФМС по г. Москва',
issued_at: '22.04.2010',
issued_at: '2010/05/10',
code: '111-232',
},
}
@@ -220,9 +243,9 @@ export async function startInfra() {
// добавляем переменные кооператива
const vars: Cooperative.Model.IVars = {
coopname: 'voskhod',
full_abbr: 'потребительский кооператив',
full_abbr_genitive: 'потребительского кооператива',
full_abbr_dative: 'потребительскому кооперативу',
full_abbr: 'Потребительский Кооператив',
full_abbr_genitive: 'Потребительского Кооператива',
full_abbr_dative: 'Потребительскому Кооперативу',
short_abbr: 'ПК',
website: 'цифровой-кооператив.рф',
name: 'Восход',
@@ -250,13 +273,55 @@ export async function startInfra() {
protocol_number: '10-04-2024',
protocol_day_month_year: '10 апреля 2024 г.',
},
generator_program: {
protocol_number: '1',
protocol_day_month_year: '09.02.2026 10:24',
},
generation_contract_template: {
protocol_number: '2',
protocol_day_month_year: '09.02.2026 10:24',
},
blagorost_program: {
protocol_number: '3',
protocol_day_month_year: '09.02.2026 10:24',
},
generator_offer_template: {
protocol_number: '4',
protocol_day_month_year: '09.02.2026 10:27',
},
blagorost_offer_template: {
protocol_number: '5',
protocol_day_month_year: '09.02.2026 10:27',
},
deleted: false,
block_num: 1,
}
await generator.save('vars', vars)
// Сохраняем vars с указанием конкретного _id и _created_at
// eslint-disable-next-line node/prefer-global/process
await mongoose.connect(process.env.MONGO_URI as string)
try {
await mongoose.connection.collection('vars').insertOne({
_id: new Types.ObjectId('69898c7d996550b4db4b1a36'),
_created_at: new Date('2026-02-08T13:29:12.423Z'),
...vars,
})
console.log('Vars сохранены с указанным _id и _created_at')
}
catch (e) {
console.log('Vars уже существуют, обновляем...')
await mongoose.connection.collection('vars').updateOne(
{ coopname: 'voskhod' },
{
$set: {
...vars,
_created_at: new Date('2026-02-08T13:29:12.423Z'),
},
},
)
}
try {
await mongoose.connection.collection('sync').deleteMany({})
console.log('Все документы удалены из коллекции sync')
@@ -281,17 +346,17 @@ export async function startInfra() {
console.error('Ошибка при удалении:', e)
}
// добавляем пользователя для подключений
try {
await mongoose.connection.collection('users').insertOne({
email: 'ivanov@example.com',
// Собираем пользователей для инициализации в PostgreSQL
const usersToInit = [
{
username: 'ant',
type: 'individual',
email: 'ivanov@example.com',
type: 'individual' as const,
role: 'chairman',
status: 'active',
is_registered: true,
})
}
catch (e) { console.log('user is exist') }
},
]
// имитируем установку
try {
@@ -312,28 +377,195 @@ export async function startInfra() {
})
}
catch (e) { console.log('vault is exist') }
console.log('Добавляем пайщика ant')
await blockchain.addUser({
coopname: 'voskhod',
referer: '',
username: 'ant',
type: 'individual',
created_at: '2025-01-15T10:00:00',
initial: '100.0000 RUB',
minimum: '200.0000 RUB',
spread_initial: true,
meta: 'Основатель кооператива ВОСХОД',
})
console.log('Устанавливаем дефолтный публичный ключ для ant')
await blockchain.changeKey({
coopname: 'voskhod',
changer: 'voskhod',
username: 'ant',
public_key: config.default_public_key,
})
// Если расширенный режим, сначала добавляем дополнительных пайщиков
if (isExtended) {
console.log('Добавляем дополнительных пайщиков для расширенного совета')
const extraUsers = [
{
username: 'petr',
first_name: 'Петр',
last_name: 'Сидоров',
middle_name: 'Сергеевич',
email: 'sidorov@example.com',
},
{
username: 'anna',
first_name: 'Анна',
last_name: 'Петрова',
middle_name: 'Ивановна',
email: 'petrova@example.com',
},
{
username: 'mikhail',
first_name: 'Михаил',
last_name: 'Кузнецов',
middle_name: 'Андреевич',
email: 'kuznetsov@example.com',
},
{
username: 'olga',
first_name: 'Ольга',
last_name: 'Соколова',
middle_name: 'Викторовна',
email: 'sokolova@example.com',
},
]
for (const user of extraUsers) {
// Добавляем в MongoDB
const userData: Cooperative.Users.IIndividualData = {
username: user.username,
first_name: user.first_name,
last_name: user.last_name,
middle_name: user.middle_name,
birthdate: '1990/04/01',
phone: '+71234567890',
email: user.email,
full_address: 'г. Москва, ул. Примерная д. 1',
passport: {
series: 7122,
number: Math.floor(Math.random() * 900000) + 100000,
issued_by: 'отделом УФМС по г. Москва',
issued_at: '2010/05/10',
code: '111-232',
},
}
await generator.save('individual', userData)
// Добавляем пользователя в список для PostgreSQL
usersToInit.push({
username: user.username,
email: user.email,
type: 'individual' as const,
role: 'member',
status: 'active',
is_registered: true,
})
console.log(`Добавляем пайщика ${user.username}`)
// Добавляем в блокчейн
await blockchain.addUser({
coopname: 'voskhod',
referer: user.username === 'petr' ? '' : 'petr',
username: user.username,
type: 'individual',
created_at: '2025-01-15T10:00:00',
initial: '100.0000 RUB',
minimum: '300.0000 RUB',
spread_initial: true,
meta: `Член совета кооператива ВОСХОД - ${user.first_name} ${user.middle_name} ${user.last_name}`,
})
console.log(`Устанавливаем дефолтный публичный ключ для ${user.username}`)
await blockchain.changeKey({
coopname: 'voskhod',
changer: 'voskhod',
username: user.username,
public_key: config.default_public_key,
})
}
}
console.log('Инициализируем пользователей в PostgreSQL')
await initUsersInPostgres(usersToInit)
console.log('Инициализируем vault в PostgreSQL')
await initVaultInPostgres()
console.log('Создаём совет')
const boardMembers: Array<{
username: string
is_voting: boolean
position_title: string
position: 'chairman' | 'member'
}> = [
{
username: 'ant',
is_voting: true,
position_title: 'Председатель совета',
position: 'chairman',
},
]
// Если расширенный режим, добавляем дополнительных членов
if (isExtended) {
boardMembers.push(
{
username: 'petr',
is_voting: true,
position_title: 'Член совета',
position: 'member',
},
{
username: 'anna',
is_voting: true,
position_title: 'Член совета',
position: 'member',
},
{
username: 'mikhail',
is_voting: true,
position_title: 'Член совета',
position: 'member',
},
{
username: 'olga',
is_voting: true,
position_title: 'Член совета',
position: 'member',
},
)
}
await blockchain.createBoard({
coopname: 'voskhod',
username: 'ant',
type: 'soviet',
members: boardMembers,
name: 'Совет',
description: isExtended ? 'Совет кооператива ВОСХОД (расширенный состав)' : 'Совет кооператива ВОСХОД',
})
console.log('Создаём программы и соглашения')
const cooperative = new CooperativeClass(blockchain)
await cooperative.createAgreements(config.provider)
await cooperative.createPrograms(config.provider)
console.log(`Арендуем ресурсы провайдеру`)
await blockchain.powerup({
payer: 'eosio',
receiver: config.provider,
days: config.powerup.days,
payment: `100.0000 ${config.token.symbol}`,
transfer: true,
})
await blockchain.transfer({
from: 'eosio',
to: config.provider,
quantity: `100.0000 ${config.token.symbol}`,
memo: '',
})
console.log('Базовая установка завершена')
console.log('Начальные данные установлены')
}
export async function installExtraData(blockchain: Blockchain) {
// В расширенном режиме пайщики уже добавлены в installInitialData
// Здесь можно добавить дополнительную логику инициализации если потребуется
console.log('Дополнительная инициализация для расширенного режима выполнена')
}
+102 -28
View File
@@ -6,29 +6,96 @@ import type { Account, Contract, Keys } from '../types'
import config, { GOVERN_SYMBOL, SYMBOL } from '../configs'
import Blockchain from '../blockchain'
import { sendPostToCoopbackWithSecret, sleep } from '../utils'
import { fakeDocument } from '../tests/shared/fakeDocument'
// const test_hash
// = '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777'
// const test_sign
// = 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8'
// const test_pkey = 'EOS5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzxrLg3J'
// const test_meta = JSON.stringify({})
// const document = {
// hash: test_hash,
// signature: test_sign,
// public_key: test_pkey,
// meta: test_meta,
// }
export class CooperativeClass {
export class ParticipantsClass {
public blockchain: Blockchain
constructor(blockchain: Blockchain) {
this.blockchain = blockchain
}
async createParticipant(username: string, keys?: Keys) {
async addUser2(coopname: string, username: string, keys?: Keys, referer?: string) {
const account = await this.blockchain.generateKeypair(
username,
keys,
'Аккаунт участника',
)
console.log('Регистрируем аккаунт')
const data: RegistratorContract.Actions.AddUser.IAddUser = {
coopname,
referer: referer || '',
username: account.username,
type: 'individual',
created_at: '2025-02-05T09:34:27',
initial: '100.0000 RUB',
minimum: '100.0000 RUB',
spread_initial: false,
meta: '',
}
await this.blockchain.api.transact({
actions: [
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.AddUser.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
}, {
blocksBehind: 3,
expireSeconds: 30,
})
console.log('Регистрируем аккаунт как пользователя')
const changeKey: RegistratorContract.Actions.ChangeKey.IChangeKey = {
coopname,
username,
public_key: account.publicKey,
changer: coopname,
}
await this.blockchain.api.transact({
actions: [
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.ChangeKey.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: changeKey,
},
],
}, {
blocksBehind: 3,
expireSeconds: 30,
})
// console.log('Отправляем подписанное положение о ЦПП Кошелька оператору')
// await this.blockchain.sendAgreement({
// coopname: config.provider,
// administrator: config.provider,
// username: username!,
// agreement_type: 'wallet',
// document: fakeDocument,
// })
// console.log('создаём кошелёк')
// console.log(`Арендуем ресурсы кооперативу`)
// await this.blockchain.powerup({
// payer: 'eosio',
// receiver: username,
// days: config.powerup.days,
// payment: `100.0000 ${config.token.symbol}`,
// transfer: true,
// })
}
async addUser(username: string, keys?: Keys, referer?: string) {
const account = await this.blockchain.generateKeypair(
username,
keys,
@@ -37,9 +104,8 @@ export class CooperativeClass {
console.log('Регистрируем аккаунт')
const data: RegistratorContract.Actions.AddUser.IAddUser = {
registrator: config.provider,
coopname: config.provider,
referer: '',
referer: referer || '',
username: account.username,
type: 'individual',
created_at: '2025-02-05T09:34:27',
@@ -93,12 +159,7 @@ export class CooperativeClass {
administrator: config.provider,
username: username!,
agreement_type: 'wallet',
document: { // отправляем произвольный документ с валидной подписью
hash: '157192B276DA23CC84AB078FC8755C051C5F0430BF4802E55718221E6B76C777',
public_key: 'PUB_K1_5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzzEtUA4',
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
meta: '{}',
},
document: fakeDocument,
})
console.log('создаём кошелёк')
@@ -115,15 +176,28 @@ export class CooperativeClass {
}
}
export async function createParticipant(username: string) {
export async function addUser(username: string, referer?: string) {
// инициализируем инстанс с ключами
const blockchain = new Blockchain(config.network, config.private_keys)
const cooperative = new CooperativeClass(blockchain)
const participants = new ParticipantsClass(blockchain)
await cooperative.createParticipant(username, {
await participants.addUser(username, {
// eslint-disable-next-line node/prefer-global/process
privateKey: process.env.EOSIO_PRV_KEY!,
// eslint-disable-next-line node/prefer-global/process
publicKey: process.env.EOSIO_PUB_KEY!,
})
}, referer)
}
export async function addUser2(coopname: string, username: string, referer?: string) {
// инициализируем инстанс с ключами
const blockchain = new Blockchain(config.network, config.private_keys)
const participants = new ParticipantsClass(blockchain)
await participants.addUser2(coopname, username, {
// eslint-disable-next-line node/prefer-global/process
privateKey: process.env.EOSIO_PRV_KEY!,
// eslint-disable-next-line node/prefer-global/process
publicKey: process.env.EOSIO_PUB_KEY!,
}, referer)
}
+294
View File
@@ -0,0 +1,294 @@
/* eslint-disable node/prefer-global/process */
import { Client } from 'pg'
import type { Cooperative } from 'cooptypes'
export async function initSystemStatus() {
console.log('Инициализация статуса системы для coopname: voskhod')
const client = new Client({
host: process.env.POSTGRES_HOST,
port: parseInt(process.env.POSTGRES_PORT || '5432'),
user: process.env.POSTGRES_USERNAME,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DATABASE,
})
try {
await client.connect()
console.log('Подключение к PostgreSQL установлено для initSystemStatus')
// Создаем enum тип для статуса системы
try {
await client.query(`
CREATE TYPE public.system_status_status_enum AS ENUM ('install', 'initialized', 'active', 'maintenance')
`)
}
catch (error) {
// Тип уже существует, продолжаем
console.log('Enum тип system_status_status_enum уже существует, пропускаем создание')
}
// Создаем таблицу system_status правильно, как в TypeORM entity
await client.query(`
CREATE TABLE IF NOT EXISTS public.system_status (
coopname varchar(12) NOT NULL,
install_code varchar(255) NULL,
install_code_expires_at timestamp NULL,
init_by_server bool NOT NULL DEFAULT false,
created_at timestamp NOT NULL DEFAULT now(),
updated_at timestamp NOT NULL DEFAULT now(),
status public.system_status_status_enum NOT NULL DEFAULT 'install'::system_status_status_enum,
CONSTRAINT system_status_pkey PRIMARY KEY (coopname)
)
`)
try {
// Устанавливаем начальный статус для voskhod (active - система готова к работе)
await client.query(`
INSERT INTO system_status (coopname, status)
VALUES ('voskhod', 'active')
ON CONFLICT (coopname) DO UPDATE SET
status = EXCLUDED.status,
updated_at = CURRENT_TIMESTAMP
`)
// Проверяем, что статус действительно установлен
const result = await client.query(`
SELECT status FROM system_status WHERE coopname = 'voskhod'
`)
console.log('Статус системы в PostgreSQL после установки:', result.rows[0]?.status)
}
catch (queryError) {
console.error('Ошибка при установке статуса системы в PostgreSQL:', queryError)
throw queryError
}
console.log('Статус системы инициализирован в PostgreSQL')
}
catch (error) {
console.error('Ошибка инициализации статуса системы в PostgreSQL:', error)
throw error
}
finally {
await client.end()
}
}
export async function initUsersInPostgres(
users: Array<{
username: string
email: string
type: 'individual' | 'entrepreneur' | 'organization'
role: string
status: string
is_registered: boolean
}>,
) {
const client = new Client({
host: process.env.POSTGRES_HOST,
port: parseInt(process.env.POSTGRES_PORT || '5432'),
user: process.env.POSTGRES_USERNAME,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DATABASE,
})
try {
await client.connect()
console.log('Подключение к PostgreSQL установлено для инициализации пользователей')
// Создаем таблицу users, если она не существует
await client.query(`
CREATE TABLE IF NOT EXISTS "users" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username VARCHAR(50) UNIQUE NOT NULL,
status VARCHAR(20) DEFAULT 'created',
message TEXT DEFAULT '',
is_registered BOOLEAN DEFAULT FALSE,
has_account BOOLEAN DEFAULT FALSE,
type VARCHAR(20) NOT NULL,
public_key TEXT DEFAULT '',
referer VARCHAR(100) DEFAULT '',
email VARCHAR(255),
role VARCHAR(20) DEFAULT 'user',
is_email_verified BOOLEAN DEFAULT FALSE,
subscriber_id VARCHAR(100) DEFAULT '',
subscriber_hash VARCHAR(255) DEFAULT '',
legacy_mongo_id VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`)
// Создаем пользователей
for (const user of users) {
// Проверяем, что username не null и не пустой
if (!user.username || user.username.trim() === '') {
console.warn(`Пропускаем пользователя с пустым username:`, user)
continue
}
await client.query(`
INSERT INTO "users" (
username, email, type, role, status, is_registered,
has_account, is_email_verified, created_at, updated_at
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (username) DO NOTHING
`, [
user.username,
user.email,
user.type,
user.role,
user.status,
user.is_registered,
false, // has_account
true, // is_email_verified
])
}
console.log(`Инициализировано ${users.length} пользователей в PostgreSQL`)
}
catch (error) {
console.error('Ошибка инициализации пользователей в PostgreSQL:', error)
throw error
}
finally {
await client.end()
}
}
export async function initVaultInPostgres() {
const client = new Client({
host: process.env.POSTGRES_HOST,
port: parseInt(process.env.POSTGRES_PORT || '5432'),
user: process.env.POSTGRES_USERNAME,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DATABASE,
})
try {
await client.connect()
console.log('Подключение к PostgreSQL установлено для инициализации vault')
// Создаем таблицу vaults, если она не существует
await client.query(`
CREATE TABLE IF NOT EXISTS "vaults" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username VARCHAR(50) NOT NULL,
permission VARCHAR(20) DEFAULT 'active',
wif TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(username, permission)
)
`)
// Создаем индексы
await client.query(`CREATE INDEX IF NOT EXISTS idx_vaults_username ON "vaults"(username)`)
await client.query(`CREATE INDEX IF NOT EXISTS idx_vaults_username_permission ON "vaults"(username, permission)`)
// Сохраняем зашифрованный ключ в vault
await client.query(`
INSERT INTO "vaults" (username, permission, wif, created_at, updated_at)
VALUES ($1, $2, $3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (username, permission) DO NOTHING
`, [
'voskhod',
'active',
'9d6479a9d77ead53fb0e5e54b3608a95:2046ee3c1577d48aecbee49e8f25c4c2df37ab02f15d73d0d1b6352f53a4b774cb9e71b6028fd7caf64568e195c7878dfbb5d2bf10a3766d90ba9e92ea724428',
])
console.log('Vault инициализирован в PostgreSQL')
}
catch (error) {
console.error('Ошибка инициализации vault в PostgreSQL:', error)
throw error
}
finally {
await client.end()
}
}
export async function initExtensionsInPostgres() {
const client = new Client({
host: process.env.POSTGRES_HOST,
port: parseInt(process.env.POSTGRES_PORT || '5432'),
user: process.env.POSTGRES_USERNAME,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DATABASE,
})
try {
await client.connect()
console.log('Подключение к PostgreSQL установлено для инициализации extensions')
// Создаем таблицу extensions по аналогии с ExtensionEntity
await client.query(`
CREATE TABLE IF NOT EXISTS "extensions" (
name VARCHAR(12) PRIMARY KEY,
enabled BOOLEAN DEFAULT true,
config JSONB DEFAULT '{}',
schema_version INTEGER DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`)
// Создаем индексы
await client.query(`CREATE INDEX IF NOT EXISTS idx_extensions_name ON "extensions"(name)`)
await client.query(`CREATE INDEX IF NOT EXISTS idx_extensions_enabled ON "extensions"(enabled)`)
// Вставляем запись для capital extension
const capitalConfig = {
level_depth_base: 100000000,
github_repository: "coopenomics/results-test",
onboarding_init_at: "2026-02-09T07:16:18.380Z",
expense_pool_percent: 100,
onboarding_expire_at: "2026-03-11T07:16:18.380Z",
voting_period_in_days: 1,
authors_voting_percent: 62.8,
creators_voting_percent: 62.8,
energy_gain_coefficient: 1,
level_growth_coefficient: 1.5,
coordinator_bonus_percent: 5,
energy_decay_rate_per_day: 0.02,
coordinator_invite_validity_days: 30,
onboarding_blagorost_provision_done: true,
onboarding_blagorost_provision_hash: "CDE57D987E3C945E79E108920CE02A4A80CFA7980CAA912949BB6C2111B7027A",
onboarding_blagorost_offer_template_done: true,
onboarding_blagorost_offer_template_hash: "5CA88BBD303E5CCDA01E565FFE47E51855515176EC6C957F0FBDBCA4C53DBFD2",
onboarding_generator_offer_template_done: true,
onboarding_generator_offer_template_hash: "8DA31574E8CC764C3A1FCAE1172726656A3DCDB1BB82AB0E567E2732070C3A44",
onboarding_generator_program_template_done: true,
onboarding_generator_program_template_hash: "E55564D8946C55C93490B5277968FC890FDCB10A049DB5B2E0FE9F67FDA80896",
onboarding_generation_contract_template_done: true,
onboarding_generation_contract_template_hash: "A4BD579D6130CCE2D8C34337DFA591807C1F028A148DD53689881B12AC2627E2"
}
await client.query(`
INSERT INTO "extensions" (name, enabled, config, schema_version, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (name) DO UPDATE SET
enabled = EXCLUDED.enabled,
config = EXCLUDED.config,
schema_version = EXCLUDED.schema_version,
updated_at = CURRENT_TIMESTAMP
`, [
'capital',
true,
JSON.stringify(capitalConfig),
1,
new Date('2026-02-09T02:13:06.620Z'),
new Date('2026-02-09T02:27:57.155Z')
])
console.log('Extensions инициализированы в PostgreSQL')
}
catch (error) {
console.error('Ошибка инициализации extensions в PostgreSQL:', error)
throw error
}
finally {
await client.end()
}
}
@@ -0,0 +1,454 @@
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { CapitalContract, SovietContract } from 'cooptypes'
import Blockchain from '../blockchain'
import config from '../configs'
import { getTotalRamUsage, globalRamStats } from '../utils/getTotalRamUsage'
import { addUser, addUser2 } from '../init/participant'
import { generateRandomUsername } from '../utils/randomUsername'
import { generateRandomSHA256 } from '../utils/randomHash'
import { sleep } from '../utils'
import { CooperativeClass } from '../init/cooperative'
import { getCirculationAccount, getCoopProgramWallet, getLedgerAccountById, getUserProgramWalletAmount } from './wallet/walletUtils'
import { registerContributor } from './capital/registerContributor'
import { signAppendix } from './capital/signAppendix'
import { capitalProgramId, circulationAccountId, sourceProgramId } from './capital/consts'
import { commitToResult } from './capital/commitToResult'
import { refreshSegment } from './capital/refreshSegment'
import { signWalletAgreement } from './wallet/signWalletAgreement'
import { fakeDocument } from './shared/fakeDocument'
import { depositToWallet } from './wallet/depositToWallet'
import { signCapitalAgreement } from './capital/signCapitalAgreement'
import { addAuthor } from './capital/addAuthor'
import { getSegment } from './capital/getSegment'
import { investInProject } from './capital/investInProject'
import { processDebt } from './capital/processDebt'
import { processCreateProjectProperty } from './capital/processCreateProjectProperty'
import { processCreateProgramProperty } from './capital/processCreateProgramProperty'
import { processStartVoting } from './capital/processStartVoting'
import { createVoteDistribution, submitVote } from './capital/submitVote'
import { processCompleteVoting } from './capital/processCompleteVoting'
import { processPushResult } from './capital/processPushResult'
import { processConvertSegment } from './capital/processConvertSegment'
import { processCalculateVotes } from './capital/processCalculateVotes'
import { processAddContributor } from './capital/processAddContributor'
import { processDeleteProject } from './capital/processDeleteProject'
import { getProject } from './capital/getProject'
import { processFundProgram } from './capital/processFundProgram'
import { processRefreshProg } from './capital/processRefreshProg'
import { processFundProject } from './capital/processFundProject'
import { processRefreshProj } from './capital/processRefreshProj'
// const CLI_PATH = 'src/index.ts'
const blockchain = new Blockchain(config.network, config.private_keys)
let metaProject: CapitalContract.Actions.CreateProject.ICreateProject
let componentProject: CapitalContract.Actions.CreateProject.ICreateProject
let newComponentProject: CapitalContract.Actions.CreateProject.ICreateProject
let testCoop: string
let chairman: string
let tester1: string
let tester2: string
let tester3: string
let tester4: string
let tester5: string
let investor1: string
let investor2: string
let investor3: string
// let result1: CapitalContract.Actions.CreateResult.ICreateResult
// let result2: CapitalContract.Actions.CreateResult.ICreateResult
// let result3: CapitalContract.Actions.CreateResult.ICreateResult
const commits: string[] = []
const tester1CommitHours = 10
const tester2CommitHours = 20
const tester3CommitHours = 100
const investAmount1 = 100000
const investAmount2 = 25000
const investAmount3 = 25000
const totalToCapitalConvertAmount = 0
const totalToProjectConvertAmount = 0
// Хранение хэшей долгов для каждого пользователя
let userDebtHashes: { [username: string]: string[] } = {}
beforeAll(async () => {
testCoop = generateRandomUsername()
chairman = generateRandomUsername()
await blockchain.update_pass_instance()
const coop = new CooperativeClass(blockchain)
await coop.createCooperative(testCoop, {
privateKey: process.env.EOSIO_PRV_KEY!,
publicKey: process.env.EOSIO_PUB_KEY!,
})
await blockchain.preInit({
coopname: testCoop,
username: config.provider,
status: 'active',
})
await addUser2(testCoop, chairman)
await blockchain.createBoard({
coopname: testCoop,
username: testCoop,
type: 'soviet',
members: [{
username: chairman,
is_voting: true,
position_title: 'Председатель совета',
position: 'chairman',
}],
name: 'Совет',
description: '',
})
console.log('testcoop: ', testCoop)
investor1 = generateRandomUsername()
console.log('investor1: ', investor1)
await addUser2(testCoop, investor1)
tester1 = generateRandomUsername()
console.log('tester1: ', tester1)
await addUser2(testCoop, tester1)
tester2 = generateRandomUsername()
console.log('tester2: ', tester2)
await addUser2(testCoop, tester2)
tester3 = generateRandomUsername()
console.log('tester3: ', tester3)
await addUser2(testCoop, tester3)
tester4 = generateRandomUsername()
console.log('tester4: ', tester4)
await addUser2(testCoop, tester4)
tester5 = generateRandomUsername()
console.log('tester5: ', tester5)
await addUser2(testCoop, tester5)
// Инициализируем массивы хэшей долгов для каждого пользователя
userDebtHashes = {
[tester1]: [],
[tester2]: [],
[tester3]: [],
[tester4]: [],
[tester5]: [],
[investor1]: [],
[investor2]: [],
[investor3]: [],
}
investor2 = generateRandomUsername()
console.log('investor2: ', investor2)
await addUser2(testCoop, investor2, tester4)
investor3 = generateRandomUsername()
console.log('investor3: ', investor3)
await addUser2(testCoop, investor3, tester5)
// const { stdout } = await execa('esno', [CLI_PATH, 'boot'], { stdio: 'inherit' })
// expect(stdout).toContain('Boot process completed')
}, 500_000)
afterAll(() => {
console.log('\n📊 **RAM USAGE SUMMARY** 📊')
let totalGlobalRam = 0
for (const [key, ramUsed] of Object.entries(globalRamStats)) {
const ramKb = (ramUsed / 1024).toFixed(2)
console.log(` ${key} = ${ramKb} kb`)
totalGlobalRam += ramUsed
}
console.log(`\n💾 **TOTAL RAM USED IN TESTS**: ${(totalGlobalRam / 1024).toFixed(2)} kb\n`)
console.log(`testCoop: ${testCoop}\ntester1: ${tester1}\ntester2: ${tester2}\ntester3: ${tester3}\ntester4: ${tester4}\ntester5: ${tester5}\ninvestor1: ${investor1}\ninvestor2: ${investor2}\ninvestor3: ${investor3}`)
})
describe('тест контракта CAPITAL', () => {
it('создаём программу генерации', async () => {
const program = await getCoopProgramWallet(blockchain, testCoop, sourceProgramId)
if (!program) {
const data: SovietContract.Actions.Programs.CreateProgram.ICreateProgram = {
coopname: testCoop,
is_can_coop_spend_share_contributions: true,
username: chairman,
title: 'Договор УХД',
announce: '',
description: '',
preview: '',
images: '',
calculation_type: 'free',
fixed_membership_contribution: '0.0000 RUB',
membership_percent_fee: '0',
meta: '',
type: 'generator',
}
const result = await blockchain.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Programs.CreateProgram.actionName,
authorization: [
{
actor: chairman,
permission: 'active',
},
],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
}
})
it('создаём программу капитализации', async () => {
const program = await getCoopProgramWallet(blockchain, testCoop, capitalProgramId)
if (!program) {
const data: SovietContract.Actions.Programs.CreateProgram.ICreateProgram = {
coopname: testCoop,
is_can_coop_spend_share_contributions: false,
username: chairman,
title: 'Капитализация',
announce: '',
description: '',
preview: '',
images: '',
calculation_type: 'free',
fixed_membership_contribution: '0.0000 RUB',
membership_percent_fee: '0',
meta: '',
type: 'blagorost',
}
const result = await blockchain.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Programs.CreateProgram.actionName,
authorization: [
{
actor: chairman,
permission: 'active',
},
],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
}
})
it('импорт вкладчика ДО установки конфига - должен работать', async () => {
// Создаем тестового пользователя для импорта
const importUsername = generateRandomUsername()
console.log('importUsername: ', importUsername)
await addUser2(testCoop, importUsername)
const contributorHash = generateRandomSHA256()
const contributionAmount = '10000.0000 RUB'
// Получаем состояние паевого счета ДО импорта
const shareFundBefore = await getCirculationAccount(blockchain, testCoop)
const shareFundBalanceBefore = parseFloat(shareFundBefore.available)
console.log('Share fund before import:', shareFundBalanceBefore)
// Импортируем вкладчика
const importData: CapitalContract.Actions.ImportContributor.IImportContributor = {
coopname: testCoop,
username: importUsername,
contributor_hash: contributorHash,
contribution_amount: contributionAmount,
memo: 'договор №123 от такого то числа',
}
const result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.ImportContributor.actionName,
authorization: [
{
actor: testCoop,
permission: 'active',
},
],
data: importData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
// Проверяем, что вкладчик был создан
const contributorTable = await blockchain.getTableRows(
CapitalContract.contractName.production,
testCoop,
'contributors',
)
console.log('contributorTable: ', contributorTable)
expect(contributorTable.length).toBe(1)
const contributor = contributorTable[0]
expect(contributor.username).toBe(importUsername)
expect(contributor.status).toBe('active')
// Проверяем, что кошелек программы был создан
const wallet = await getUserProgramWalletAmount(blockchain, testCoop, importUsername, capitalProgramId)
expect(parseFloat(wallet)).toBe(parseFloat(contributionAmount))
// Проверяем увеличение паевого счета
const shareFundAfter = await getCirculationAccount(blockchain, testCoop)
const shareFundBalanceAfter = parseFloat(shareFundAfter.available)
console.log('Share fund after import:', shareFundBalanceAfter)
expect(shareFundBalanceAfter).toBe(shareFundBalanceBefore + parseFloat(contributionAmount))
})
it('инициализируем контракт CAPITAL и сверяем конфиг', async () => {
const data: CapitalContract.Actions.SetConfig.ISetConfig = {
coopname: testCoop,
config: {
coordinator_bonus_percent: 4,
expense_pool_percent: 100,
coordinator_invite_validity_days: 30,
voting_period_in_days: 7,
authors_voting_percent: 38.2,
creators_voting_percent: 38.2,
energy_decay_rate_per_day: 0.11,
level_depth_base: 1000,
level_growth_coefficient: 1.5,
energy_gain_coefficient: 0.01,
},
}
const result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.SetConfig.actionName,
authorization: [
{
actor: testCoop,
permission: 'active',
},
],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
const state = (await blockchain.getTableRows(
CapitalContract.contractName.production,
CapitalContract.contractName.production,
'state',
1,
testCoop,
testCoop,
))[0]
console.log('state: ', state)
expect(state).toBeDefined()
expect(state.coopname).toBe(testCoop)
expect(parseFloat(state.config.coordinator_bonus_percent)).toBe(data.config.coordinator_bonus_percent)
expect(parseFloat(state.config.expense_pool_percent)).toBe(data.config.expense_pool_percent)
expect(parseFloat(state.config.coordinator_invite_validity_days)).toBe(data.config.coordinator_invite_validity_days)
expect(parseFloat(state.config.voting_period_in_days)).toBe(data.config.voting_period_in_days)
expect(parseFloat(state.config.authors_voting_percent)).toBe(data.config.authors_voting_percent)
expect(parseFloat(state.config.creators_voting_percent)).toBe(data.config.creators_voting_percent)
})
it('импорт вкладчика ПОСЛЕ установки конфига - должен провалиться', async () => {
// Создаем еще одного тестового пользователя для импорта
const importUsername2 = generateRandomUsername()
console.log('importUsername2: ', importUsername2)
await addUser(importUsername2)
const contributorHash2 = generateRandomSHA256()
const contributionAmount2 = '5000.0000 RUB'
// Пытаемся импортировать вкладчика после установки конфига
const importData2: CapitalContract.Actions.ImportContributor.IImportContributor = {
coopname: testCoop,
username: importUsername2,
contributor_hash: contributorHash2,
contribution_amount: contributionAmount2,
memo: 'договор №123 от такого то числа',
}
// Ожидаем, что транзакция провалится
await expect(
blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.ImportContributor.actionName,
authorization: [
{
actor: testCoop,
permission: 'active',
},
],
data: importData2,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
),
).rejects.toThrow() // Ожидаем ошибку
// Проверяем, что вкладчик НЕ был создан
const contributorTable = await blockchain.getTableRows(
CapitalContract.contractName.production,
testCoop,
'contributors',
)
expect(contributorTable.length).toBe(1)
// Проверяем, что кошелек программы НЕ был создан
const wallet = await getUserProgramWalletAmount(blockchain, testCoop, importUsername2, capitalProgramId)
expect(wallet).toBe('0.0000 RUB')
})
})
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,75 @@
import { CapitalContract } from 'cooptypes'
import { expect } from 'vitest'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
export async function addAuthor(blockchain: Blockchain, coopname: string, project_hash: string, author: string) {
const data: CapitalContract.Actions.AddAuthor.IAddAuthor = {
coopname,
project_hash,
author,
}
const result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.AddAuthor.actionName,
authorization: [
{
actor: coopname,
permission: 'active',
},
],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
const key = makeCombinedChecksum256NameIndexKey(project_hash, author)
const rows = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
CapitalContract.Tables.Segments.tableName,
1000,
key,
key,
3,
'i128',
))
const segment = rows[0]
console.log('segment: ', segment)
expect(segment).toBeDefined()
expect(segment.username).toBe(author)
expect(segment.project_hash).toBe(project_hash)
expect(segment.is_author).toBe(1)
const project = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
CapitalContract.Tables.Projects.tableName,
1000,
project_hash,
project_hash,
3,
'sha256',
))[0]
expect(project).toBeDefined()
return {
project,
segment,
}
}
@@ -1,34 +1,21 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { CapitalContract, SovietContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { fakeDocument } from '../soviet/fakeDocument'
import { ratePerHour } from './consts'
import { fakeDocument } from '../shared/fakeDocument'
import { processMasterApprove } from './processMasterApprove'
export async function commitToResult(
blockchain: any,
coopname: string,
resultHash: string,
projectHash: string,
creator: string,
spendHours: number,
) {
const commitHash = generateRandomSHA256()
const totalSpended = `${(spendHours * parseFloat(ratePerHour)).toFixed(4)} RUB`
console.log(`\n🔹 Начало коммита: ${commitHash}, часы: ${spendHours}, сумма: ${totalSpended}`)
// Получение текущих данных перед коммитом
const prevResult = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'results',
1,
resultHash,
resultHash,
2,
'sha256',
))[0] || { spend: '0.0000 RUB', commits_count: 0 }
console.log(`\n🔹 Начало коммита: ${commitHash}, проект: ${projectHash}, часы: ${spendHours}`)
// Получение текущих данных проекта перед коммитом
const prevProject = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
@@ -38,7 +25,7 @@ export async function commitToResult(
projectHash,
3,
'sha256',
))[0] || { spend: '0.0000 RUB' }
))[0] || { spended: '0.0000 RUB' }
const prevContributor = (await blockchain.getTableRows(
CapitalContract.contractName.production,
@@ -52,22 +39,34 @@ export async function commitToResult(
))[0] || { contributed_hours: 0, available: '0.0000 RUB' }
console.log('📊 Балансы до коммита:')
console.log('▶ Результат:', prevResult)
console.log('▶ Проект:', prevProject)
console.log('▶ Контрибьютор:', prevContributor)
// Обновим состояние проекта после возможной смены статуса
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
projectHash,
projectHash,
3,
'sha256',
))[0]
const prevTotalCommits = projectBefore?.counts?.total_commits ?? 0
const prevCreatorsHours = projectBefore?.fact?.creators_hours ?? 0
// Создание коммита
const commitData: CapitalContract.Actions.CreateCommit.ICommit = {
coopname,
application: coopname,
result_hash: resultHash,
creator,
username: creator,
project_hash: projectHash,
commit_hash: commitHash,
specification: fakeDocument,
contributed_hours: spendHours,
creator_hours: spendHours,
description: `Коммит ${commitHash}`,
meta: `{"hours": ${spendHours}, "creator": "${creator}"}`,
}
console.log(`\n🚀 Отправка транзакции CreateCommit для результата ${resultHash}`)
const createCommitResult = await blockchain.api.transact(
{
actions: [
@@ -89,7 +88,7 @@ export async function commitToResult(
expect(createCommitResult.transaction_id).toBeDefined()
// Проверка созданного коммита
let blockchainCommit = (await blockchain.getTableRows(
const blockchainCommit = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'commits',
@@ -103,41 +102,30 @@ export async function commitToResult(
console.log('🔍 Коммит после создания:', blockchainCommit)
expect(blockchainCommit).toBeDefined()
expect(blockchainCommit.commit_hash).toBe(commitHash)
expect(blockchainCommit.spend).toBe(totalSpended)
expect(blockchainCommit.project_hash).toBe(projectHash)
expect(blockchainCommit.status).toBe('created')
// Утверждение коммита
const approveCommitData: CapitalContract.Actions.ApproveCommit.IApproveCommit = {
// Получение данных проекта для определения мастера
const projectData = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
application: coopname,
commit_hash: commitHash,
approver: 'ant',
approved_specification: fakeDocument,
}
'projects',
1,
projectHash,
projectHash,
3,
'sha256',
))[0]
console.log(`\n✅ Подтверждение коммита ${commitHash}`)
const approveCommitResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.ApproveCommit.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: approveCommitData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
expect(projectData).toBeDefined()
expect(projectData.master).toBeDefined()
getTotalRamUsage(approveCommitResult)
expect(approveCommitResult.transaction_id).toBeDefined()
// Утверждение коммита мастером проекта
console.log(`\n✅ Подтверждение коммита ${commitHash} мастером ${projectData.master}`)
const approveCommitResult = await processMasterApprove(blockchain, coopname, commitHash, projectData.master)
// Проверка утвержденного коммита
blockchainCommit = (await blockchain.getTableRows(
const blockchainEmptyCommit = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'commits',
@@ -148,23 +136,10 @@ export async function commitToResult(
'sha256',
))[0]
console.log('🔍 Коммит после утверждения:', blockchainCommit)
expect(blockchainCommit).toBeDefined()
expect(blockchainCommit.commit_hash).toBe(commitHash)
expect(blockchainCommit.status).toBe('approved')
// Проверка результата после утверждения коммита
const finalResult = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'results',
1,
resultHash,
resultHash,
2,
'sha256',
))[0]
// Коммит удаляем после утверждения
expect(blockchainEmptyCommit).toBeUndefined()
// Проверка проекта после утверждения коммита
const finalProject = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
@@ -188,23 +163,19 @@ export async function commitToResult(
))[0]
console.log('\n📊 Балансы после утверждения коммита:')
console.log('▶ Результат:', finalResult)
console.log('▶ Проект:', finalProject)
console.log('▶ Контрибьютор:', finalContributor)
expect(parseFloat(finalResult.spend)).toBe(parseFloat(prevResult.spend) + parseFloat(totalSpended))
expect(parseFloat(finalProject.spend)).toBe(parseFloat(prevProject.spend))
// Проверка, что у контрибьютора увеличились contributed_hours и available
expect(parseFloat(finalContributor.contributed_hours)).toBe(parseFloat(prevContributor.contributed_hours) + spendHours)
expect(parseFloat(finalContributor.available)).toBe(parseFloat(prevContributor.available) + parseFloat(totalSpended))
// Проверки по проекту
expect(finalProject.counts.total_commits).toBe(prevTotalCommits + 1)
expect(parseInt(finalProject.fact.creators_hours)).toBe(prevCreatorsHours + spendHours)
console.log(`\n✅ Коммит ${commitHash} завершен успешно!`)
return {
commitHash,
commit: blockchainCommit,
transactionId: approveCommitResult.transaction_id,
finalResult,
transaction: approveCommitResult,
finalProject,
finalContributor,
}
+5 -1
View File
@@ -1,3 +1,7 @@
export const capitalProgramId = 3
export const sourceProgramId = 3
export const capitalProgramId = 4
export const walletProgramId = 1
export const ratePerHour = '1000.0000 RUB'
export const sourceProgramName = 'generator'
export const capitalProgramName = 'blagorost'
export const circulationAccountId = 80
@@ -0,0 +1,27 @@
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
/**
* Получает кошелек капитализации пользователя
* @param blockchain экземпляр блокчейна
* @param coopname название кооператива
* @param username имя пользователя
* @returns кошелек капитализации или null если не найден
*/
export async function getCapitalWallet(
blockchain: Blockchain,
coopname: string,
username: string,
) {
const capitalWallets = await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'capwallets',
100,
username,
username,
2, // by_username index
)
return capitalWallets[0] || null
}
@@ -0,0 +1,18 @@
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
export async function getProject(blockchain: Blockchain, coopname: string, project_hash: string): Promise<CapitalContract.Tables.Projects.IProject> {
const rows = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
CapitalContract.Tables.Projects.tableName,
1000,
project_hash,
project_hash,
3,
'sha256',
))
return rows[0]
}
@@ -0,0 +1,30 @@
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
/**
* Получает кошелек проекта пользователя
* @param blockchain экземпляр блокчейна
* @param coopname название кооператива
* @param projectHash хеш проекта
* @param username имя пользователя
* @returns кошелек проекта или null если не найден
*/
export async function getProjectWallet(
blockchain: Blockchain,
coopname: string,
projectHash: string,
username: string,
) {
const projectWallets = await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projwallets',
100,
username,
username,
3, // by_username index
)
// Ищем кошелек для конкретного проекта
return projectWallets.find((wallet: any) => wallet.project_hash === projectHash) || null
}
@@ -0,0 +1,19 @@
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
export async function getSegment(blockchain: Blockchain, coopname: string, project_hash: string, author: string): Promise<CapitalContract.Tables.Segments.ISegment> {
const key = makeCombinedChecksum256NameIndexKey(project_hash, author)
const rows = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
CapitalContract.Tables.Segments.tableName,
1000,
key,
key,
3,
'i128',
))
return rows[0]
}
@@ -4,7 +4,10 @@ import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { getCoopProgramWallet, getUserProgramWallet } from '../wallet/walletUtils'
import { processDecision } from '../soviet/processDecision'
import { capitalProgramId } from './consts'
import { consoleIt } from '../shared'
import { processApprove } from './processApprove'
import { getSegment } from './getSegment'
import { capitalProgramId, walletProgramId } from './consts'
export async function investInProject(
blockchain: any,
@@ -17,18 +20,6 @@ export async function investInProject(
const investHash = generateRandomSHA256()
console.log(`\n🔹 Начало инвестиции: ${investHash}`)
// Получение текущих данных перед инвестицией
const prevContributor = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'contributors',
1,
investor,
investor,
2,
'i64',
))[0] || { invested: '0.0000 RUB' }
const prevProject = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
@@ -40,19 +31,19 @@ export async function investInProject(
'sha256',
))[0] || { invested: '0.0000 RUB', available: '0.0000 RUB' }
const prevWalletWallet = await getUserProgramWallet(blockchain, coopname, investor, walletProgramId) || { blocked: '0.0000 RUB' }
const prevUserWallet = await getUserProgramWallet(blockchain, coopname, investor, capitalProgramId) || { blocked: '0.0000 RUB' }
const prevProgramWallet = await getCoopProgramWallet(blockchain, coopname, capitalProgramId) || { blocked: '0.0000 RUB', share_contributions: '0.0000 RUB' }
console.log('📊 Балансы до инвестиции:')
console.log('▶ Контрибьютор:', prevContributor)
console.log('▶ Проект:', prevProject)
console.log('▶ Кошелек пользователя:', prevUserWallet)
console.log('▶ Кошелек программы:', prevProgramWallet)
console.log('▶ Главный кошелек пользователя:', prevWalletWallet)
console.log('▶ Кошелек пользователя (благорост):', prevUserWallet)
console.log('▶ Кошелек программы (благорост):', prevProgramWallet)
console.log('▶ Сумма инвестиции: ', investAmount)
// Создание инвестиции
const createInvestData: CapitalContract.Actions.CreateInvest.ICreateInvest = {
const createInvestData: CapitalContract.Actions.CreateProjectInvest.ICreateInvest = {
coopname,
application: coopname,
project_hash: projectHash,
username: investor,
invest_hash: investHash,
@@ -66,7 +57,7 @@ export async function investInProject(
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CreateInvest.actionName,
name: CapitalContract.Actions.CreateProjectInvest.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: createInvestData,
},
@@ -77,56 +68,31 @@ export async function investInProject(
expireSeconds: 30,
},
)
consoleIt(createInvestResult)
getTotalRamUsage(createInvestResult)
expect(createInvestResult.transaction_id).toBeDefined()
let blockchainInvest = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'invests',
1,
investHash,
investHash,
2,
'sha256',
))[0]
// const blockchainInvest = (await blockchain.getTableRows(
// CapitalContract.contractName.production,
// coopname,
// 'invests',
// 1,
// investHash,
// investHash,
// 2,
// 'sha256',
// ))[0]
console.log('🔍 Инвестиция в блокчейне:', blockchainInvest)
expect(blockchainInvest).toBeDefined()
expect(blockchainInvest.status).toBe('created')
// console.log('🔍 Инвестиция в блокчейне:', blockchainInvest)
// expect(blockchainInvest).toBeDefined()
// expect(blockchainInvest.status).toBe('created')
// Утверждение инвестиции
const approveInvestData: CapitalContract.Actions.ApproveInvest.IApproveInvest = {
coopname,
application: coopname,
approver: 'ant',
invest_hash: investHash,
approved_statement: fakeDocument,
}
// console.log(`\n✅ Подтверждение инвестиции ${investHash}`)
// const approveInvestResult = await processApprove(blockchain, coopname, investHash)
console.log(`\n✅ Подтверждение инвестиции ${investHash}`)
const approveInvestResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.ApproveInvest.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: approveInvestData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(approveInvestResult)
expect(approveInvestResult.transaction_id).toBeDefined()
blockchainInvest = (await blockchain.getTableRows(
// Проверка утвержденной инвестиции
const blockchainEmptyInvest = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'invests',
@@ -137,49 +103,10 @@ export async function investInProject(
'sha256',
))[0]
console.log('🔍 Инвестиция после утверждения:', blockchainInvest)
expect(blockchainInvest).toBeDefined()
expect(blockchainInvest.status).toBe('approved')
// Инвестиция удаляется после утверждения
expect(blockchainEmptyInvest).toBeUndefined()
// Получение всех решений и выполнение последнего
const decisions = await blockchain.getTableRows(
SovietContract.contractName.production,
coopname,
'decisions',
1000,
)
const lastDecision = decisions[decisions.length - 1]
console.log(`\n📜 Выполнение последнего решения: ${lastDecision.id}`)
await processDecision(blockchain, lastDecision.id)
blockchainInvest = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'invests',
1,
investHash,
investHash,
2,
'sha256',
))[0]
console.log('📌 Инвестиция после завершения:', blockchainInvest)
expect(blockchainInvest).toBeUndefined()
// Получение новых данных после инвестиции
const contributor = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'contributors',
1,
investor,
investor,
2,
'i64',
))[0]
const project = (await blockchain.getTableRows(
const finalProject = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
@@ -190,24 +117,30 @@ export async function investInProject(
'sha256',
))[0]
const userWallet = await getUserProgramWallet(blockchain, coopname, investor, capitalProgramId)
const programWallet = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
const finalUserWallet = await getUserProgramWallet(blockchain, coopname, investor, capitalProgramId)
const finalProgramWallet = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
// Получение сегмента инвестора для данного проекта
const segment = await getSegment(blockchain, coopname, projectHash, investor)
console.log('\n📊 Балансы после инвестиции:')
console.log('▶ Контрибьютор:', contributor)
console.log('▶ Проект:', project)
console.log('▶ Кошелек пользователя:', userWallet)
console.log('▶ Кошелек программы:', programWallet)
console.log('▶ Проект:', finalProject)
console.log('▶ Сегмент:', segment)
console.log('▶ Кошелек пользователя (благорост):', finalUserWallet)
console.log('▶ Кошелек программы (благорост):', finalProgramWallet)
// Проверка изменения балансов
expect(parseFloat(contributor.invested)).toBe(parseFloat(prevContributor.invested) + parseFloat(investAmount))
expect(parseFloat(project.invested)).toBe(parseFloat(prevProject.invested) + parseFloat(investAmount))
expect(parseFloat(project.available)).toBe(parseFloat(prevProject.available) + parseFloat(investAmount))
expect(parseFloat(userWallet.blocked)).toBe(parseFloat(prevUserWallet.blocked) + parseFloat(investAmount))
expect(parseFloat(programWallet.blocked)).toBe(parseFloat(prevProgramWallet.blocked) + parseFloat(investAmount))
expect(parseFloat(programWallet.share_contributions)).toBe(parseFloat(prevProgramWallet.share_contributions) + parseFloat(investAmount))
expect(parseFloat(finalUserWallet.blocked)).toBeCloseTo(parseFloat(prevUserWallet.blocked) + parseFloat(investAmount), 1)
expect(parseFloat(finalProgramWallet.blocked)).toBeCloseTo(parseFloat(prevProgramWallet.blocked) + parseFloat(investAmount), 1)
console.log(`\n✅ Инвестирование на ${investAmount} завершено успешно!`)
return { investHash, transactionId: approveInvestResult.transaction_id }
return {
investHash,
// invest: blockchainInvest,
transactionId: createInvestResult.transaction_id,
prevProject,
project: finalProject,
segment,
}
}
@@ -0,0 +1,53 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { getSegment } from './getSegment'
export async function processAddContributor(
blockchain: Blockchain,
coopname: string,
project_hash: string,
username: string,
) {
// Состояние сегмента до добавления (может не существовать)
let segmentBefore: any = null
try {
segmentBefore = await getSegment(blockchain, coopname, project_hash, username)
}
catch (error) {
// Сегмент может не существовать - это нормально
}
const data: CapitalContract.Actions.RegisterShare.IRegisterShare = {
coopname,
project_hash,
username,
}
// Выполняем добавление участника в проект
const txAdd = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.RegisterShare.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txAdd)
expect(txAdd.transaction_id).toBeDefined()
// Состояние сегмента после добавления
const segmentAfter = await getSegment(blockchain, coopname, project_hash, username)
return {
transactionId: txAdd.transaction_id,
segmentBefore,
segmentAfter,
}
}
@@ -0,0 +1,37 @@
import { expect } from 'vitest'
import { SovietContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { fakeDocument } from '../shared/fakeDocument'
export async function processApprove(blockchain: any, coopname: string, approvalHash: string) {
fakeDocument.signatures[0].signer = 'ant'
const data: SovietContract.Actions.Approves.ConfirmApprove.IConfirmApprove = {
coopname,
username: 'ant',
approval_hash: approvalHash,
approved_document: fakeDocument,
}
const result = await blockchain.api.transact(
{
actions: [
{
account: SovietContract.contractName.production,
name: SovietContract.Actions.Approves.ConfirmApprove.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
return result
}
@@ -0,0 +1,111 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
/**
* Функция для расчета голосов участника по методу Водянова
* @param blockchain экземпляр блокчейна
* @param data данные для расчета голосов
*/
export async function processCalculateVotes(
blockchain: Blockchain,
data: CapitalContract.Actions.CalculateVotes.IFinalVoting,
) {
const { coopname, username, project_hash } = data
console.log(`\n🧮 Начало расчета голосов для участника ${username} в проекте ${project_hash}`)
// Состояние до: данные проекта
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
// Состояние до: данные сегмента участника
const segmentKey = makeCombinedChecksum256NameIndexKey(project_hash, username)
const segmentBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'segments',
1,
segmentKey,
segmentKey,
3,
'i128',
))[0]
// console.log('📊 Состояние до расчета голосов:')
// console.log('▶ Статус проекта:', projectBefore.status)
// console.log('▶ Голосов получено:', projectBefore.voting.votes_received)
// console.log('▶ Всего участников голосования:', projectBefore.voting.total_voters)
// console.log(`▶ Сегмент участника ${username}:`)
// console.log(' - voting_bonus:', segmentBefore.voting_bonus)
// console.log(' - equal_author_bonus:', segmentBefore.equal_author_bonus)
// console.log(' - direct_creator_bonus:', segmentBefore.direct_creator_bonus)
// console.log(' - total_segment_cost:', segmentBefore.total_segment_cost)
// Отправляем транзакцию расчета голосов
const txResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CalculateVotes.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txResult)
expect(txResult.transaction_id).toBeDefined()
// Состояние после: данные проекта
const projectAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
// Состояние после: данные сегмента участника
const segmentAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'segments',
1,
segmentKey,
segmentKey,
3,
'i128',
))[0]
console.log('📊 Состояние после расчета голосов:')
console.log('▶ Статус проекта:', projectAfter.status)
console.log(`▶ Сегмент участника ${username}:`)
console.log(' - voting_bonus:', segmentAfter.voting_bonus)
console.log(' - total_voting_pool:', projectAfter.voting.amounts.total_voting_pool)
console.log(' - active_voting_amount:', projectAfter.voting.amounts.active_voting_amount)
console.log(' - equal_voting_amount:', projectAfter.voting.amounts.equal_voting_amount)
return {
txId: txResult.transaction_id,
projectBefore,
projectAfter,
segmentBefore,
segmentAfter,
}
}
@@ -0,0 +1,82 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
/**
* Функция для завершения голосования по проекту
* @param blockchain экземпляр блокчейна
* @param data данные для завершения голосования
*/
export async function processCompleteVoting(
blockchain: Blockchain,
data: CapitalContract.Actions.CompleteVoting.ICompleteVoting,
) {
const { coopname, project_hash } = data
console.log(`\n✅ Начало завершения голосования для проекта ${project_hash}`)
// Состояние до: данные проекта
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
console.log('📊 Состояние до завершения голосования:')
console.log('▶ Статус проекта:', projectBefore.status)
console.log('▶ Голосов получено:', projectBefore.voting.votes_received)
console.log('▶ Всего участников голосования:', projectBefore.voting.total_voters)
console.log('▶ Дедлайн голосования:', projectBefore.voting.voting_deadline)
// Отправляем транзакцию завершения голосования
const txResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CompleteVoting.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txResult)
expect(txResult.transaction_id).toBeDefined()
// Состояние после: данные проекта
const projectAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
console.log('📊 Состояние после завершения голосования:')
console.log('▶ Статус проекта:', projectAfter.status)
console.log('▶ Голосов получено:', projectAfter.voting.votes_received)
console.log('▶ Всего участников голосования:', projectAfter.voting.total_voters)
// Проверяем что статус изменился на result
expect(projectAfter.status).toBe('result')
console.log(`\n✅ Голосование для проекта ${project_hash} успешно завершено!`)
return {
projectHash: project_hash,
txId: txResult.transaction_id,
projectBefore,
projectAfter,
}
}
@@ -0,0 +1,176 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { fakeDocument } from '../shared/fakeDocument'
import type Blockchain from '../../blockchain'
import { getCoopProgramWallet, getUserProgramWallet } from '../wallet/walletUtils'
import { getSegment } from './getSegment'
import { getProjectWallet } from './getProjectWallet'
import { capitalProgramId, walletProgramId } from './consts'
import { getProject } from './getProject'
/**
* Функция для конвертации сегмента участника
* @param blockchain экземпляр блокчейна
* @param coopname название кооператива
* @param username имя пользователя
* @param projectHash хеш проекта
* @param walletAmount сумма для конвертации в главный кошелек
* @param capitalAmount сумма для конвертации в капитализацию
* @param projectAmount сумма для конвертации в кошелек проекта
*/
export async function processConvertSegment(
blockchain: Blockchain,
coopname: string,
username: string,
projectHash: string,
walletAmount: string,
capitalAmount: string,
projectAmount: string,
) {
const convertHash = generateRandomSHA256()
console.log(`\n🔹 Начало процесса конвертации сегмента: ${convertHash}`)
// Получаем сегмент до конвертации
const segmentBefore = await getSegment(blockchain, coopname, projectHash, username)
console.log('📊 Сегмент до конвертации:', segmentBefore)
// Получаем кошельки до конвертации
const mainWalletBefore = await getCoopProgramWallet(blockchain, coopname, walletProgramId)
const capitalWalletBefore = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
const userCapitalWalletBefore = await getUserProgramWallet(blockchain, coopname, username, capitalProgramId)
const projectWalletBefore = await getProjectWallet(blockchain, coopname, projectHash, username)
const projectBefore = await getProject(blockchain, coopname, projectHash)
console.log('💰 Кошельки до конвертации:')
console.log(' ▶ Главный кошелек программы:', mainWalletBefore)
console.log(' ▶ Глобальный кошелек программы капитализации:', capitalWalletBefore)
console.log(' ▶ Кошелек пользователя в программе капитализации:', userCapitalWalletBefore)
console.log(' ▶ Кошелек проекта:', projectWalletBefore)
console.log(' ▶ Проект до конвертации: ', projectBefore)
// 1. Конвертируем сегмент
const convertSegmentData: CapitalContract.Actions.ConvertSegment.IConvertSegment = {
coopname,
username,
project_hash: projectHash,
convert_hash: convertHash,
wallet_amount: walletAmount,
capital_amount: capitalAmount,
convert_statement: fakeDocument,
}
console.log(`\n🚀 Отправка транзакции ConvertSegment для ${username}`)
console.log(` В главный кошелек: ${walletAmount}`)
console.log(` В капитализацию: ${capitalAmount}`)
console.log(` В проект: ${projectAmount}`)
const convertResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.ConvertSegment.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: convertSegmentData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(convertResult)
expect(convertResult.transaction_id).toBeDefined()
console.log('✅ Сегмент конвертирован:', convertHash)
// Получаем сегмент после конвертации (должен быть удален)
const segmentAfter = await getSegment(blockchain, coopname, projectHash, username)
// Получаем кошельки после конвертации
const mainWalletAfter = await getCoopProgramWallet(blockchain, coopname, walletProgramId)
const capitalWalletAfter = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
const userCapitalWalletAfter = await getUserProgramWallet(blockchain, coopname, username, capitalProgramId)
const projectWalletAfter = await getProjectWallet(blockchain, coopname, projectHash, username)
console.log('💰 Кошельки после конвертации:')
console.log(' ▶ Главный кошелек программы:', mainWalletAfter)
console.log(' ▶ Глобальный кошелек программы капитализации:', capitalWalletAfter)
console.log(' ▶ Кошелек пользователя в программе капитализации:', userCapitalWalletAfter)
console.log(' ▶ Кошелек проекта:', projectWalletAfter)
// Проверяем что сегмент удален после конвертации
expect(segmentAfter).toBeUndefined()
console.log('🔍 Сегмент после конвертации: ', segmentAfter)
// Проверяем изменения в кошельках
console.log('\n🔍 Проверка изменений в кошельках:')
// Проверяем главный кошелек программы (wallet_amount)
if (walletAmount !== '0.0000 RUB') {
const walletAmountValue = parseFloat(walletAmount.split(' ')[0])
const beforeAvailable = mainWalletBefore ? parseFloat(mainWalletBefore.available.split(' ')[0]) : 0
const afterAvailable = mainWalletAfter ? parseFloat(mainWalletAfter.available.split(' ')[0]) : 0
const expectedIncrease = beforeAvailable + walletAmountValue
console.log(`✅ Главный кошелек программы: ${beforeAvailable}${afterAvailable} (+${walletAmountValue})`)
expect(afterAvailable).toBeCloseTo(expectedIncrease, 1)
}
// Проверяем глобальный кошелек программы капитализации (capital_amount)
// investor_base уже заблокирован в _capital_program при инвестировании (createinvest)
// В capitalAmount теперь передается только чистая дельта интеллектуального вклада
if (capitalAmount !== '0.0000 RUB') {
const capitalAmountValue = parseFloat(capitalAmount.split(' ')[0])
const actualCapitalIncrease = capitalAmountValue
const beforeBlocked = capitalWalletBefore ? parseFloat(capitalWalletBefore.blocked.split(' ')[0]) : 0
const afterBlocked = capitalWalletAfter ? parseFloat(capitalWalletAfter.blocked.split(' ')[0]) : 0
const expectedIncrease = beforeBlocked + actualCapitalIncrease
console.log(`✅ Глобальный кошелек программы капитализации: ${beforeBlocked}${afterBlocked} (+${actualCapitalIncrease})`)
expect(afterBlocked).toBeCloseTo(expectedIncrease, 1)
}
// Проверяем кошелек пользователя в программе капитализации (capital_amount)
if (capitalAmount !== '0.0000 RUB') {
const capitalAmountValue = parseFloat(capitalAmount.split(' ')[0])
const actualCapitalIncrease = capitalAmountValue
const beforeBlocked = userCapitalWalletBefore ? parseFloat(userCapitalWalletBefore.blocked.split(' ')[0]) : 0
const afterBlocked = userCapitalWalletAfter ? parseFloat(userCapitalWalletAfter.blocked.split(' ')[0]) : 0
const expectedIncrease = beforeBlocked + actualCapitalIncrease
console.log(`✅ Кошелек пользователя в программе капитализации: ${beforeBlocked}${afterBlocked} (+${actualCapitalIncrease})`)
expect(afterBlocked).toBeCloseTo(expectedIncrease, 1)
}
// Проверяем кошелек проекта (project_amount)
if (projectAmount !== '0.0000 RUB' && projectBefore.parent_hash.includes('0000000000')) {
const projectAmountValue = parseFloat(projectAmount.split(' ')[0])
const beforeShares = projectWalletBefore ? parseFloat(projectWalletBefore.shares.split(' ')[0]) : 0
const afterShares = projectWalletAfter ? parseFloat(projectWalletAfter.shares.split(' ')[0]) : 0
const expectedIncrease = beforeShares + projectAmountValue
console.log(`✅ Кошелек проекта: ${beforeShares}${afterShares} (+${projectAmountValue})`)
expect(afterShares).toBeCloseTo(expectedIncrease, 1)
}
console.log('\n📊 Результаты после конвертации сегмента:')
console.log('▶ Сегмент до:', segmentBefore)
console.log('▶ Сегмент после удалён: ', segmentAfter)
console.log(`\n✅ Процесс конвертации сегмента завершен успешно!`)
return {
convertHash,
segmentBefore,
segmentAfter,
mainWalletBefore,
mainWalletAfter,
capitalWalletBefore,
capitalWalletAfter,
userCapitalWalletBefore,
userCapitalWalletAfter,
projectWalletBefore,
projectWalletAfter,
transactionId: convertResult.transaction_id,
}
}
@@ -0,0 +1,109 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { getCoopProgramWallet, getLedgerAccountById } from '../wallet/walletUtils'
import { processLastDecision } from '../soviet/processLastDecision'
import { processApprove } from './processApprove'
import { capitalProgramId, circulationAccountId } from './consts'
export async function processCreateProgramProperty(
blockchain: Blockchain,
data: CapitalContract.Actions.CreateProgramProperty.ICreateProgramProperty,
fakeDocument: any,
) {
const { coopname, username, property_hash } = data
// Состояния до: кошелек программы капитализации и паевой счет в бухгалтерии
const programWalletBefore = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
const ledgerShareBefore = await getLedgerAccountById(blockchain, coopname, circulationAccountId)
// 1) Создаём программное предложение имущественного взноса
const txCreate = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CreateProgramProperty.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txCreate)
expect(txCreate.transaction_id).toBeDefined()
// 2) Подтверждение одобрения председателем (через soviet)
await processApprove(blockchain as any, coopname, property_hash as unknown as string)
// 3) Обработка решения совета (AUTHORIZE_PROGRAM_PROPERTY)
await processLastDecision(blockchain, coopname)
// 4) Акт 1 — подписывает участник-вноситель (username)
fakeDocument.signatures[0].signer = username
const act1Data = {
coopname,
username,
property_hash,
act: fakeDocument,
} as CapitalContract.Actions.Act1ProgramProperty.IAct1ProgramProperty
const txAct1 = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.Act1ProgramProperty.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: act1Data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txAct1)
expect(txAct1.transaction_id).toBeDefined()
// 5) Акт 2 — подписывает председатель совета кооператива ('ant')
fakeDocument.signatures[0].signer = 'ant'
const act2Data = {
coopname,
property_hash,
username: 'ant',
act: fakeDocument,
} as CapitalContract.Actions.Act2ProgramProperty.IAct2ProgramProperty
const txAct2 = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.Act2ProgramProperty.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: act2Data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txAct2)
expect(txAct2.transaction_id).toBeDefined()
// Состояния после
const programWalletAfter = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
const ledgerShareAfter = await getLedgerAccountById(blockchain, coopname, circulationAccountId)
return {
propertyHash: property_hash,
txCreateId: txCreate.transaction_id,
txAct1Id: txAct1.transaction_id,
txAct2Id: txAct2.transaction_id,
programWalletBefore,
programWalletAfter,
ledgerShareBefore,
ledgerShareAfter,
}
}
@@ -0,0 +1,69 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { processLastDecision } from '../soviet/processLastDecision'
import { processApprove } from './processApprove'
import { getSegment } from './getSegment'
export async function processCreateProjectProperty(
blockchain: Blockchain,
data: CapitalContract.Actions.CreateProjectProperty.ICreateProjectProperty,
) {
const { coopname, username, project_hash, property_hash } = data
// Состояния до: сегмент проекта и данные проекта
const segmentBefore = await getSegment(blockchain, coopname, project_hash, username)
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
// 1) Создаём проектное предложение имущественного взноса
const txCreate = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CreateProjectProperty.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txCreate)
expect(txCreate.transaction_id).toBeDefined()
// 2) Подтверждение одобрения председателем (через soviet)
await processApprove(blockchain as any, coopname, property_hash as unknown as string)
// Состояния после: сегмент проекта и данные проекта
const segmentAfter = await getSegment(blockchain, coopname, project_hash, username)
const projectAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
return {
propertyHash: property_hash,
txCreateId: txCreate.transaction_id,
segmentBefore,
segmentAfter,
projectBefore,
projectAfter,
}
}
@@ -0,0 +1,179 @@
import { expect } from 'vitest'
import { CapitalContract, GatewayContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { processLastDecision } from '../soviet/processLastDecision'
import type Blockchain from '../../blockchain'
import { formatDateToISOString } from '../shared/formatDateToISOString'
import { processApprove } from './processApprove'
import { getSegment } from './getSegment'
export async function processDebt(
blockchain: Blockchain,
coopname: string,
username: string,
projectHash: string,
debtAmount: string,
fakeDocument: any,
repaidAtDate: Date,
) {
const debtHash = generateRandomSHA256()
console.log(`\n🔹 Начало процесса создания долга: ${debtHash}`)
// Дата возврата через месяц
const repaidAt = formatDateToISOString(repaidAtDate)
// Получаем сегмент до создания долга
const segmentBefore = await getSegment(blockchain, coopname, projectHash, username)
console.log('📊 Сегмент до долга:', segmentBefore)
// Получаем контрибьютора до создания долга
const contributorsBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'contributors',
1,
username,
username,
2,
'i64',
))
const contributorBefore = contributorsBefore[0]
console.log('📊 Контрибьютор до долга:', contributorBefore)
fakeDocument.signatures[0].signer = username
// 1. Создаем долг
const createDebtData: CapitalContract.Actions.CreateDebt.ICreateDebt = {
coopname,
username,
debt_hash: debtHash,
project_hash: projectHash,
amount: debtAmount,
repaid_at: repaidAt,
statement: fakeDocument,
}
console.log(`\n🚀 Отправка транзакции CreateDebt для ${username} на сумму ${debtAmount}`)
const createDebtResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CreateDebt.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: createDebtData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(createDebtResult)
expect(createDebtResult.transaction_id).toBeDefined()
console.log('✅ Долг создан:', debtHash)
// Проверяем что долг создался
const debts = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'debts',
1,
debtHash,
debtHash,
3,
'sha256',
))
const createdDebt = debts[0]
console.log('🔍 Долг в блокчейне:', createdDebt)
expect(createdDebt).toBeDefined()
expect(createdDebt.status).toBe('created')
// 2. Одобряем долг через processApprove (soviet контракт)
console.log(`\n✅ Подтверждение долга ${debtHash} через soviet`)
await processApprove(blockchain, coopname, debtHash)
console.log('✅ Долг одобрен председателем (создана agenda)')
// 3. Процессим решение совета (agenda для авторизации долга)
await processLastDecision(blockchain, coopname)
console.log('✅ Решение совета принято (долг авторизован, outcome создан)')
// 4. Подтверждаем завершение вывода (gateway сам вызовет callback на capital)
const confirmOutcomeData: GatewayContract.Actions.CompleteOutcome.ICompleteOutcome = {
coopname,
outcome_hash: debtHash,
}
console.log(`\n✅ Подтверждение завершения вывода`)
const confirmResult = await blockchain.api.transact(
{
actions: [
{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteOutcome.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: confirmOutcomeData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(confirmResult)
expect(confirmResult.transaction_id).toBeDefined()
console.log('✅ Оплата долга подтверждена')
// Получаем финальные состояния
const segmentAfter = await getSegment(blockchain, coopname, projectHash, username)
const contributorsAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'contributors',
1,
username,
username,
2,
'i64',
))
const contributorAfter = contributorsAfter[0]
// Получаем долг после подтверждения оплаты
const debtsAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'debts',
1,
debtHash,
debtHash,
3,
'sha256',
))
const debtAfter = debtsAfter[0]
console.log('\n📊 Результаты после обработки долга:')
console.log('▶ Сегмент до:', segmentBefore)
console.log('▶ Сегмент после:', segmentAfter)
console.log('▶ Контрибьютор до:', contributorBefore)
console.log('▶ Контрибьютор после:', contributorAfter)
console.log('▶ Долг после оплаты:', debtAfter)
console.log(`\n✅ Процесс долга на ${debtAmount} завершен успешно!`)
return {
debtHash,
debt: createdDebt,
debtAfter,
segmentBefore,
segmentAfter,
contributorBefore,
contributorAfter,
transactionId: confirmResult.transaction_id,
}
}
@@ -0,0 +1,62 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
export async function processDeleteProject(
blockchain: Blockchain,
coopname: string,
project_hash: string,
) {
// Состояние проекта до удаления
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
const data: CapitalContract.Actions.DeleteProject.IDeleteProject = {
coopname,
project_hash,
}
// Выполняем удаление проекта
const txDelete = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.DeleteProject.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txDelete)
expect(txDelete.transaction_id).toBeDefined()
// Проверяем что проект удален
const projectsAfter = await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
)
return {
transactionId: txDelete.transaction_id,
projectBefore,
projectAfter: projectsAfter[0] || null, // null если удален
}
}
@@ -0,0 +1,61 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { getCapitalWallet } from './getCapitalWallet'
export async function processFundProgram(
blockchain: any,
coopname: string,
amount: string,
memo: string = 'Тестовое финансирование программы',
) {
console.log(`\n🔹 Начало финансирования программы капитализации: сумма=${amount}, memo=${memo}`)
// Получение данных ДО финансирования
const programWalletBefore = await getCapitalWallet(blockchain, coopname, coopname)
console.log('📊 Состояние программы ДО финансирования:')
console.log('▶ Кошелек программы:', programWalletBefore)
// Финансирование программы капитализации
const fundData: CapitalContract.Actions.FundProgram.IFundProgram = {
coopname,
amount,
memo,
}
const fundResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.FundProgram.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: fundData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(fundResult)
expect(fundResult.transaction_id).toBeDefined()
// Получение данных ПОСЛЕ финансирования
const programWalletAfter = await getCapitalWallet(blockchain, coopname, coopname)
console.log('📊 Состояние программы ПОСЛЕ финансирования:')
console.log('▶ Кошелек программы:', programWalletAfter)
console.log(`\n✅ Финансирование программы капитализации завершено успешно!`)
return {
transactionId: fundResult.transaction_id,
transaction: fundResult,
programWalletBefore,
programWalletAfter,
}
}
@@ -0,0 +1,63 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { getProjectWallet } from './getProjectWallet'
export async function processFundProject(
blockchain: any,
coopname: string,
projectHash: string,
amount: string,
memo: string = 'Тестовое финансирование проекта',
) {
console.log(`\n🔹 Начало финансирования проекта: проект=${projectHash}, сумма=${amount}, memo=${memo}`)
// Получение данных ДО финансирования
const projectWalletBefore = await getProjectWallet(blockchain, coopname, projectHash, coopname)
console.log('📊 Состояние проекта ДО финансирования:')
console.log('▶ Кошелек проекта:', projectWalletBefore)
// Финансирование проекта
const fundData: CapitalContract.Actions.FundProject.IFundProject = {
coopname,
project_hash: projectHash,
amount,
memo,
}
const fundResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.FundProject.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: fundData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(fundResult)
expect(fundResult.transaction_id).toBeDefined()
// Получение данных ПОСЛЕ финансирования
const projectWalletAfter = await getProjectWallet(blockchain, coopname, projectHash, coopname)
console.log('📊 Состояние проекта ПОСЛЕ финансирования:')
console.log('▶ Кошелек проекта:', projectWalletAfter)
console.log(`\n✅ Финансирование проекта завершено успешно!`)
return {
transactionId: fundResult.transaction_id,
transaction: fundResult,
projectWalletBefore,
projectWalletAfter,
}
}
@@ -0,0 +1,30 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
export async function processMasterApprove(
blockchain: any,
coopname: string,
commitHash: string,
master: string,
) {
const data: CapitalContract.Actions.CommitApprove.ICommitApprove = {
coopname,
master,
commit_hash: commitHash,
}
const result = await blockchain.transactWithLogs([
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CommitApprove.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
])
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
return result
}
@@ -0,0 +1,180 @@
import { expect } from 'vitest'
import { CapitalContract, SovietContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { setDocumentSignatures } from '../../utils/setDocumentSignatures'
import { processLastDecision } from '../soviet/processLastDecision'
import { fakeDocument } from '../shared/fakeDocument'
import type Blockchain from '../../blockchain'
import { getSegment } from './getSegment'
import { processApprove } from './processApprove'
/**
* Функция для внесения результата в кооператив
* @param blockchain экземпляр блокчейна
* @param coopname название кооператива
* @param username имя пользователя
* @param projectHash хеш проекта
* @param contributionAmount сумма взноса
* @param debtAmount сумма долга к погашению
* @param debtHashes вектор хэшей долгов для погашения
*/
export async function processPushResult(
blockchain: Blockchain,
coopname: string,
username: string,
projectHash: string,
contributionAmount: string,
debtAmount: string,
debtHashes: string[] = [],
) {
const resultHash = generateRandomSHA256()
console.log(`\n🔹 Начало процесса внесения результата: ${resultHash}`)
// Получаем сегмент до внесения результата
const segmentBefore = await getSegment(blockchain, coopname, projectHash, username)
console.log('📊 Сегмент до внесения результата:', segmentBefore)
// Устанавливаем подписи документа
const documentWithSignatures = setDocumentSignatures([username])
// 1. Вносим результат
const pushResultData: CapitalContract.Actions.PushResult.IPushResult = {
coopname,
username,
project_hash: projectHash,
result_hash: resultHash,
contribution_amount: contributionAmount,
debt_amount: debtAmount,
statement: documentWithSignatures,
debt_hashes: debtHashes,
}
console.log(`\n🚀 Отправка транзакции PushResult для ${username} на сумму ${contributionAmount}`)
const pushResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.PushResult.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: pushResultData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(pushResult)
expect(pushResult.transaction_id).toBeDefined()
console.log('✅ Результат внесен:', resultHash)
// Проверяем что результат создался
const results = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'results',
1,
resultHash,
resultHash,
3,
'sha256',
))
const createdResult = results[0]
console.log('🔍 Результат в блокчейне:', createdResult)
expect(createdResult).toBeDefined()
expect(createdResult.status).toBe('created')
// 2. Одобряем результат через processApprove (soviet контракт)
console.log(`\n✅ Одобрение результата ${resultHash} через soviet`)
await processApprove(blockchain, coopname, resultHash)
console.log('✅ Результат одобрен советом')
// 3. Процессим решение совета
await processLastDecision(blockchain, coopname)
console.log('✅ Решение совета принято')
console.log('🔍 Подписываем акт №1 вкладчиком', documentWithSignatures)
// 4. Подписываем акт №1 вкладчиком
const signAct1Data: CapitalContract.Actions.SignAct1.ISignAct1 = {
coopname,
username,
result_hash: resultHash,
act: documentWithSignatures,
}
console.log(`\n✅ Подписание акта №1 вкладчиком ${username}`)
const signAct1Result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.SignAct1.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: signAct1Data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(signAct1Result)
expect(signAct1Result.transaction_id).toBeDefined()
console.log('✅ Акт №1 подписан вкладчиком')
// Создаем документ с подписями вкладчика и председателя
const documentWithChairmanSignature = setDocumentSignatures([username, 'ant'])
// 5. Подписываем акт №2 председателем
const signAct2Data: CapitalContract.Actions.SignAct2.ISignAct2 = {
coopname,
result_hash: resultHash,
act: documentWithChairmanSignature,
chairman: 'ant',
}
console.log(`\n✅ Подписание акта №2 председателем`, documentWithChairmanSignature)
const signAct2Result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.SignAct2.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: signAct2Data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(signAct2Result)
expect(signAct2Result.transaction_id).toBeDefined()
console.log('✅ Акт №2 подписан председателем')
// Получаем финальные состояния
const segmentAfter = await getSegment(blockchain, coopname, projectHash, username)
console.log('\n📊 Результаты после внесения результата:')
console.log('▶ Сегмент до:', segmentBefore)
console.log('▶ Сегмент после:', segmentAfter)
console.log(`\n✅ Процесс внесения результата на ${contributionAmount} завершен успешно!`)
return {
resultHash,
result: createdResult,
segmentBefore,
segmentAfter,
transactionId: signAct2Result.transaction_id,
}
}
@@ -0,0 +1,59 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { getCapitalWallet } from './getCapitalWallet'
export async function processRefreshProg(
blockchain: any,
coopname: string,
username: string,
) {
console.log(`\n🔹 Начало обновления CRPS пайщика в программе капитализации: пользователь=${username}`)
// Получение данных ДО обновления
const programWalletBefore = await getCapitalWallet(blockchain, coopname, username)
console.log('📊 Состояние пайщика ДО обновления CRPS:')
console.log('▶ Кошелек пайщика:', programWalletBefore)
// Обновление CRPS пайщика в программе капитализации
const refreshData: CapitalContract.Actions.RefreshProgram.IRefreshProgram = {
coopname,
username,
}
const refreshResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.RefreshProgram.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: refreshData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(refreshResult)
expect(refreshResult.transaction_id).toBeDefined()
// Получение данных ПОСЛЕ обновления
const programWalletAfter = await getCapitalWallet(blockchain, coopname, username)
console.log('📊 Состояние пайщика ПОСЛЕ обновления CRPS:')
console.log('▶ Кошелек пайщика:', programWalletAfter)
console.log(`\n✅ Обновление CRPS пайщика в программе капитализации завершено успешно!`)
return {
transactionId: refreshResult.transaction_id,
transaction: refreshResult,
programWalletBefore,
programWalletAfter,
}
}
@@ -0,0 +1,61 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { getProjectWallet } from './getProjectWallet'
export async function processRefreshProj(
blockchain: any,
coopname: string,
projectHash: string,
username: string,
) {
console.log(`\n🔹 Начало обновления CRPS пайщика в проекте: проект=${projectHash}, пользователь=${username}`)
// Получение данных ДО обновления
const projectWalletBefore = await getProjectWallet(blockchain, coopname, projectHash, username)
console.log('📊 Состояние кошелька проекта ДО обновления CRPS:')
console.log('▶ Кошелек проекта:', projectWalletBefore)
// Обновление CRPS пайщика в проекте
const refreshData: CapitalContract.Actions.RefreshProject.IRefreshProject = {
coopname,
project_hash: projectHash,
username,
}
const refreshResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.RefreshProject.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: refreshData,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(refreshResult)
expect(refreshResult.transaction_id).toBeDefined()
// Получение данных ПОСЛЕ обновления
const projectWalletAfter = await getProjectWallet(blockchain, coopname, projectHash, username)
console.log('📊 Состояние кошелька проекта ПОСЛЕ обновления CRPS:')
console.log('▶ Кошелек проекта:', projectWalletAfter)
console.log(`\n✅ Обновление CRPS пайщика в проекте завершено успешно!`)
return {
transactionId: refreshResult.transaction_id,
transaction: refreshResult,
projectWalletBefore,
projectWalletAfter,
}
}
@@ -0,0 +1,59 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
export async function processStartVoting(
blockchain: Blockchain,
data: CapitalContract.Actions.StartVoting.IStartVoting,
) {
const { coopname, project_hash } = data
// Состояние до: данные проекта
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
console.log('projectBefore: ', projectBefore)
// 1) Запускаем голосование по проекту
const txStart = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.StartVoting.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txStart)
expect(txStart.transaction_id).toBeDefined()
// Состояние после: данные проекта
const projectAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
project_hash,
project_hash,
3,
'sha256',
))[0]
console.log('projectAfter: ', projectAfter)
return {
projectHash: project_hash,
txStartId: txStart.transaction_id,
projectBefore,
projectAfter,
}
}
@@ -0,0 +1,61 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import type Blockchain from '../../blockchain'
export async function refreshSegment(
blockchain: Blockchain,
coopname: string,
projectHash: string,
username: string,
) {
console.log(`\n🔄 Обновляем сегмент CRPS для ${username} в проекте ${projectHash}`)
// Получение данных сегмента перед обновлением
const prevSegment = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
CapitalContract.Tables.Segments.tableName,
1000,
)).filter((s: any) => s.project_hash === projectHash && s.username === username)[0]
// console.log('📊 Сегмент до обновления:', prevSegment)
// Обновление сегмента через rfrshsegment
const refreshSegmentData: CapitalContract.Actions.RefreshSegment.IRefreshSegment = {
coopname,
project_hash: projectHash,
username,
}
const refreshResult = await blockchain.transactWithLogs(
[
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.RefreshSegment.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: refreshSegmentData,
},
],
)
getTotalRamUsage(refreshResult)
expect(refreshResult.transaction_id).toBeDefined()
// Получение обновленного сегмента
const updatedSegment = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
CapitalContract.Tables.Segments.tableName,
1000,
)).filter((s: any) => s.project_hash === projectHash && s.username === username)[0]
// console.log('📊 Сегмент после обновления:', updatedSegment)
console.log(`✅ Сегмент для ${username} обновлен успешно!`)
return {
transactionId: refreshResult.transaction_id,
prevSegment,
updatedSegment,
}
}
@@ -1,3 +1,69 @@
export function registerContributor() {
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { fakeDocument } from '../shared/fakeDocument'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { processApprove } from './processApprove'
export async function registerContributor(
blockchain: any,
coopname: string,
username: string,
contributorHash: string,
ratePerHour: string,
hoursPerDay: number = 8,
) {
const contract = fakeDocument
contract.signatures[0].signer = username
const data: CapitalContract.Actions.RegisterContributor.IRegisterContributor = {
coopname,
username,
contributor_hash: contributorHash,
rate_per_hour: ratePerHour,
hours_per_day: hoursPerDay,
is_external_contract: false,
contract,
storage_agreement: fakeDocument,
generator_agreement: fakeDocument,
blagorost_agreement: fakeDocument,
}
const result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.RegisterContributor.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
// Аппрув председателя (через совет) по хэшу договора УХД
await processApprove(blockchain, coopname, contributorHash)
// Проверяем, что контрибьютор активирован и договор сохранён
const contributor = (
await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'contributors',
1,
username,
username,
2,
'i64',
)
)[0]
expect(contributor).toBeDefined()
expect(contributor.status).toBe('active')
expect(contributor.contract).toBeDefined()
}
@@ -174,26 +174,24 @@ export async function registerExpense(
const blockchainWithdraw = (await blockchain.getTableRows(
GatewayContract.contractName.production,
coopname,
GatewayContract.Tables.Withdraws.tableName,
GatewayContract.Tables.Outcomes.tableName,
1,
expenseHash,
expenseHash,
3,
2,
'sha256',
))[0]
console.log('🔍 Вывод средств:', blockchainWithdraw)
expect(blockchainWithdraw).toBeDefined()
expect(blockchainWithdraw.withdraw_hash).toBe(expenseHash)
expect(blockchainWithdraw.outcome_hash).toBe(expenseHash)
expect(blockchainWithdraw.quantity).toBe(expenseAmount)
expect(blockchainWithdraw.status).toBe('authorized')
expect(blockchainWithdraw.status).toBe('pending')
// Завершение вывода
const completeWithdrawData: GatewayContract.Actions.CompleteWithdraw.ICompleteWithdraw = {
const completeWithdrawData: GatewayContract.Actions.CompleteOutcome.ICompleteOutcome = {
coopname,
application: '',
memo: '',
withdraw_hash: expenseHash,
outcome_hash: expenseHash,
}
console.log(`\n✅ Завершение вывода ${expenseHash}`)
@@ -202,7 +200,7 @@ export async function registerExpense(
actions: [
{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteWithdraw.actionName,
name: GatewayContract.Actions.CompleteOutcome.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: completeWithdrawData,
},
@@ -0,0 +1,62 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import { fakeDocument } from '../shared/fakeDocument'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { processApprove } from './processApprove'
export async function signAppendix(
blockchain: any,
coopname: string,
username: string,
projectHash: string,
appendixHash: string,
) {
// Подписание приложения пайщиком (через кооператив, как и в контракте)
const data: CapitalContract.Actions.GetClearance.IGetClearance = {
coopname,
username,
project_hash: projectHash,
appendix_hash: appendixHash,
document: fakeDocument,
}
const result = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.GetClearance.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{
blocksBehind: 3,
expireSeconds: 30,
},
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
// Аппрув председателя (через совет) по хэшу приложения
await processApprove(blockchain, coopname, appendixHash)
// Проверяем, что приложение применилось к контрибьютору, а запись удалена
const contributor = (
await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'contributors',
1,
username,
username,
2,
'i64',
)
)[0]
expect(contributor).toBeDefined()
expect(contributor.appendixes).toContain(projectHash)
}
@@ -1,7 +1,7 @@
import { expect } from 'vitest'
import { signAgreement } from '../soviet/signAgreement'
import { getCoopProgramWallet, getUserProgramWallet } from '../wallet/walletUtils'
import { capitalProgramId } from './consts'
import { capitalProgramName, sourceProgramId } from './consts'
export async function signCapitalAgreement(
blockchain: any,
@@ -9,20 +9,20 @@ export async function signCapitalAgreement(
username: string,
fakeDocument: any,
) {
const txId = await signAgreement(blockchain, coopname, username, 'cofund', fakeDocument)
const txId = await signAgreement(blockchain, coopname, username, capitalProgramName, fakeDocument)
const wallet = await getUserProgramWallet(blockchain, coopname, username, capitalProgramId)
const wallet = await getUserProgramWallet(blockchain, coopname, username, sourceProgramId)
expect(wallet).toEqual(expect.objectContaining({
coopname,
username,
available: expect.any(String),
blocked: expect.any(String),
program_id: capitalProgramId,
program_id: sourceProgramId,
membership_contribution: expect.any(String),
}))
const program = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
const program = await getCoopProgramWallet(blockchain, coopname, sourceProgramId)
return { wallet, program, txId }
}
@@ -0,0 +1,28 @@
import { expect } from 'vitest'
import { signAgreement } from '../soviet/signAgreement'
import { getCoopProgramWallet, getUserProgramWallet } from '../wallet/walletUtils'
import { sourceProgramId, sourceProgramName } from './consts'
export async function signGenerationContract(
blockchain: any,
coopname: string,
username: string,
fakeDocument: any,
) {
const txId = await signAgreement(blockchain, coopname, username, sourceProgramName, fakeDocument)
const wallet = await getUserProgramWallet(blockchain, coopname, username, sourceProgramId)
expect(wallet).toEqual(expect.objectContaining({
coopname,
username,
available: expect.any(String),
blocked: expect.any(String),
program_id: sourceProgramId,
membership_contribution: expect.any(String),
}))
const program = await getCoopProgramWallet(blockchain, coopname, sourceProgramId)
return { wallet, program, txId }
}
@@ -0,0 +1,166 @@
import { expect } from 'vitest'
import { CapitalContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
/**
* Интерфейс для распределения голосов
*/
export interface VoteInput {
recipient: string
amount: string
}
/**
* Создает равномерное распределение голосующей суммы между участниками (кроме самого голосующего)
* @param voters список всех участников голосования
* @param voter участник который голосует (исключается из распределения)
* @param votingAmount общая голосующая сумма для распределения
* @returns массив распределения голосов
*/
export function createVoteDistribution(
voters: string[],
voter: string,
votingAmount: string,
): VoteInput[] {
const otherVoters = voters.filter(v => v !== voter)
const votingAmountFloat = parseFloat(votingAmount.split(' ')[0])
const currency = votingAmount.split(' ')[1]
// Равномерно распределяем сумму между остальными участниками
const amountPerVoter = votingAmountFloat / otherVoters.length
return otherVoters.map(recipient => ({
recipient,
amount: `${amountPerVoter.toFixed(4)} ${currency}`,
}))
}
/**
* Функция для голосования по методу Водянова
* @param blockchain экземпляр блокчейна
* @param coopname имя кооператива
* @param voter участник который голосует
* @param projectHash хэш проекта
* @param voteDistribution массив голосов для распределения
*/
export async function submitVote(
blockchain: Blockchain,
coopname: string,
voter: string,
projectHash: string,
voteDistribution: VoteInput[],
) {
console.log(`\n🗳️ Начало голосования участника ${voter}`)
// Состояние до: данные проекта
const projectBefore = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
projectHash,
projectHash,
3,
'sha256',
))[0]
// Получаем все голоса по проекту до голосования
const votesBefore = await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'votes',
100,
projectHash,
projectHash,
2,
'sha256',
)
// Получаем голоса конкретного участника до голосования
const voterVotesBefore = votesBefore.filter((vote: any) =>
vote.project_hash === projectHash && vote.voter === voter,
)
console.log('📊 Состояние до голосования:')
console.log('▶ Проект votes_received:', projectBefore.voting.votes_received)
console.log('▶ Всего голосов в таблице:', votesBefore.filter((v: any) => v.project_hash === projectHash).length)
console.log('▶ Голосов от участника:', voterVotesBefore.length)
// Формируем данные для транзакции
const votes = voteDistribution.map(v => ({
recipient: v.recipient,
amount: v.amount,
}))
const data: any = {
coopname,
voter,
project_hash: projectHash,
votes,
}
console.log('📝 Данные голосования:', JSON.stringify(data, null, 2))
// Отправляем транзакцию голосования
const txResult = await blockchain.api.transact(
{
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.SubmitVote.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
},
{ blocksBehind: 3, expireSeconds: 30 },
)
getTotalRamUsage(txResult)
expect(txResult.transaction_id).toBeDefined()
// Состояние после: данные проекта
const projectAfter = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'projects',
1,
projectHash,
projectHash,
3,
'sha256',
))[0]
// Получаем все голоса по проекту после голосования
const votesAfter = await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'votes',
100,
projectHash,
projectHash,
2,
'sha256',
)
// Получаем голоса конкретного участника после голосования
const voterVotesAfter = votesAfter.filter((vote: any) =>
vote.project_hash === projectHash && vote.voter === voter,
)
console.log('📊 Состояние после голосования:')
console.log('▶ Проект votes_received:', projectAfter.voting.votes_received)
console.log('▶ Всего голосов в таблице:', votesAfter.filter((v: any) => v.project_hash === projectHash).length)
console.log('▶ Голосов от участника:', voterVotesAfter.length)
return {
txId: txResult.transaction_id,
projectBefore,
projectAfter,
votesBefore: votesBefore.filter((v: any) => v.project_hash === projectHash),
votesAfter: votesAfter.filter((v: any) => v.project_hash === projectHash),
voterVotesBefore,
voterVotesAfter,
voteInput: voteDistribution,
}
}
@@ -44,15 +44,14 @@ export async function withdrawContribution(
console.log('▶ Проект:', prevProject)
// Создание заявления на возврат
const createWithdrawData: CapitalContract.Actions.CreateWithdraw1.ICreateWithdraw = {
const createWithdrawData: CapitalContract.Actions.WithdrawResult.IWithdrawFromResult = {
coopname,
application: coopname,
username,
result_hash: resultHash,
withdraw_hash: withdrawHash,
commit_hashes: commitHashes,
contribution_statement: fakeDocument,
return_statement: fakeDocument,
amount: withdrawAmount,
}
console.log(`\n🚀 Отправка транзакции CreateWithdraw для ${username} на сумму ${withdrawAmount}`)
@@ -61,7 +60,7 @@ export async function withdrawContribution(
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.CreateWithdraw1.actionName,
name: CapitalContract.Actions.WithdrawResult.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: createWithdrawData,
},
@@ -79,7 +78,7 @@ export async function withdrawContribution(
let blockchainWithdraw = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'withdraws',
CapitalContract.Tables.ResultWithdraws.tableName,
1,
withdrawHash,
withdrawHash,
@@ -93,12 +92,11 @@ export async function withdrawContribution(
expect(blockchainWithdraw.amount).toBe(withdrawAmount)
// Подтверждение возврата
const approveWithdrawData: CapitalContract.Actions.ApproveWithdraw.IApproveWithdraw = {
const approveWithdrawData: CapitalContract.Actions.ApproveWithdrawResult.IApproveWithdrawResult = {
coopname,
application: coopname,
approver: 'ant',
withdraw_hash: withdrawHash,
approved_contribution_statement: fakeDocument,
approved_return_statement: fakeDocument,
}
@@ -108,7 +106,7 @@ export async function withdrawContribution(
actions: [
{
account: CapitalContract.contractName.production,
name: CapitalContract.Actions.ApproveWithdraw.actionName,
name: CapitalContract.Actions.ApproveWithdrawResult.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: approveWithdrawData,
},
@@ -126,7 +124,7 @@ export async function withdrawContribution(
blockchainWithdraw = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'withdraws',
CapitalContract.Tables.ResultWithdraws.tableName,
1,
withdrawHash,
withdrawHash,
@@ -146,17 +144,15 @@ export async function withdrawContribution(
1000,
)
const lastDecision = decisions[decisions.length - 1]
const prevLastDecision = decisions[decisions.length - 2]
console.log(`\n📜 Выполнение решений: ${prevLastDecision.id}, ${lastDecision.id}`)
console.log(`\n📜 Выполнение решения: ${lastDecision.id}`)
await processDecision(blockchain, prevLastDecision.id)
await processDecision(blockchain, lastDecision.id)
blockchainWithdraw = (await blockchain.getTableRows(
CapitalContract.contractName.production,
coopname,
'withdraws',
CapitalContract.Tables.ResultWithdraws.tableName,
1,
withdrawHash,
withdrawHash,
@@ -203,7 +199,9 @@ export async function withdrawContribution(
expect(parseFloat(programWallet.blocked)).toBe(parseFloat(prevProgramWallet.blocked))
// Проверка изменения spend в проекте
expect(parseFloat(finalProject.spend)).toBe(parseFloat(prevProject.spend) + parseFloat(withdrawAmount))
expect(parseFloat(finalProject.withdrawed)).toBe(parseFloat(prevProject.withdrawed) + parseFloat(withdrawAmount))
expect(parseFloat(finalProject.spend)).toBe(parseFloat(prevProject.spend))
console.log(`\n✅ Возврат на ${withdrawAmount} завершен успешно!`)
@@ -0,0 +1,57 @@
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { GatewayContract, RegistratorContract, SovietContract } from 'cooptypes'
import { sha256 } from 'eosjs/dist/eosjs-key-conversions'
import Blockchain from '../blockchain'
import config from '../configs'
import { getTotalRamUsage, globalRamStats } from '../utils/getTotalRamUsage'
import { addUser } from '../init/participant'
import { generateRandomUsername } from '../utils/randomUsername'
import { generateRandomSHA256 } from '../utils/randomHash'
import { signWalletAgreement } from './wallet/signWalletAgreement'
import { depositToWallet } from './wallet/depositToWallet'
import { processDecision } from './soviet/processDecision'
import { getParticipant } from './registrator/getParticipant'
import { registerUser } from './registrator/registerUser'
const blockchain = new Blockchain(config.network, config.private_keys)
let tester1: string
const walletProgramStates1: any[] = []
const fakeDocument = {
hash: '157192B276DA23CC84AB078FC8755C051C5F0430BF4802E55718221E6B76C777',
public_key: 'PUB_K1_5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzzEtUA4',
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
meta: '{}',
}
beforeAll(async () => {
await blockchain.update_pass_instance()
}, 500_000)
afterAll(() => {
console.log('\n📊 **RAM USAGE SUMMARY** 📊')
let totalGlobalRam = 0
for (const [key, ramUsed] of Object.entries(globalRamStats)) {
console.log(` ${key} = ${(ramUsed / 1024).toFixed(2)} kb`)
totalGlobalRam += ramUsed
}
console.log(`\n💾 **TOTAL RAM USED IN TESTS**: ${(totalGlobalRam / 1024).toFixed(2)} kb\n`)
})
describe('тест Registrator', () => {
it('регистируем пайщика 1', async () => {
const coopname = 'voskhod'
const tester1 = generateRandomUsername()
await registerUser(blockchain, coopname, tester1)
})
it('регистируем пайщика 2', async () => {
const coopname = 'voskhod'
const tester2 = generateRandomUsername()
await registerUser(blockchain, coopname, tester2)
})
})
@@ -0,0 +1,19 @@
import { SovietContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
export async function getParticipant(
blockchain: Blockchain,
coopname: string,
username: string,
) {
const participant = (await blockchain.getTableRows(
SovietContract.contractName.production,
'voskhod',
'participants',
1,
username,
username,
))[0]
return participant
}
@@ -0,0 +1,123 @@
import { GatewayContract, RegistratorContract, SovietContract } from 'cooptypes'
import { expect } from 'vitest'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { processDecision } from '../soviet/processDecision'
import { getParticipant } from '../registrator/getParticipant'
import { fakeDocument } from '../soviet/fakeDocument'
import { compareTokenAmounts, getCoopWallet } from '../wallet/walletUtils'
export async function registerUser(blockchain: any, coopname: string, username: string) {
const registration_hash = generateRandomSHA256()
const account = await blockchain.generateKeypair(username, undefined, 'Аккаунт кооператива')
// 🔹 Получаем балансы до регистрации
const prevCoopWallet = await getCoopWallet(blockchain, coopname)
const prevAvailable = prevCoopWallet?.circulating_account?.available || '0.0000 RUB'
const prevInitial = prevCoopWallet?.initial_account?.available || '0.0000 RUB'
console.log('📦 CoopWallet BEFORE:', prevCoopWallet)
// 🔹 1. Создание аккаунта
{
const data: RegistratorContract.Actions.CreateAccount.ICreateAccount = {
coopname,
referer: '',
username,
public_key: account.publicKey,
meta: '',
}
const result = await blockchain.api.transact({
actions: [{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.CreateAccount.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
}],
}, { blocksBehind: 3, expireSeconds: 30 })
getTotalRamUsage(result)
console.log('✅ Аккаунт создан')
}
// 🔹 2. Регистрация пайщика
{
const data: RegistratorContract.Actions.RegisterUser.IRegisterUser = {
coopname,
braname: '',
username,
type: 'individual',
statement: fakeDocument,
registration_hash,
}
const result = await blockchain.api.transact({
actions: [{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.RegisterUser.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
}],
}, { blocksBehind: 3, expireSeconds: 30 })
getTotalRamUsage(result)
console.log('✅ Пайщик зарегистрирован')
}
// 🔹 3. Подтверждение прихода
{
const data: GatewayContract.Actions.CompleteIncome.ICompleteIncome = {
coopname,
income_hash: registration_hash,
}
const result = await blockchain.api.transact({
actions: [{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteIncome.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
}],
}, { blocksBehind: 3, expireSeconds: 30 })
getTotalRamUsage(result)
console.log('✅ Приход подтвержден')
}
// 🔹 4. Исполнение решения
const decisions = await blockchain.getTableRows(
SovietContract.contractName.production,
coopname,
'decisions',
1000,
)
const lastDecision = decisions.at(-1)
console.log('📜 Последнее решение:', lastDecision)
await processDecision(blockchain, lastDecision.id)
console.log('✅ Решение выполнено')
// 🔹 5. Проверка участника
const participant = await getParticipant(blockchain, coopname, username)
console.log('🙋 Участник:', participant)
expect(participant).toBeDefined()
expect(participant.has_vote).toBe(1)
expect(participant.status).toBe('accepted')
expect(participant.username).toBe(username)
// 🔹 6. Проверка балансов после регистрации
const coopWalletAfter = await getCoopWallet(blockchain, coopname)
const newAvailable = coopWalletAfter?.circulating_account?.available || '0.0000 RUB'
const newInitial = coopWalletAfter?.initial_account?.available || '0.0000 RUB'
console.log('💰 CoopWallet AFTER:', coopWalletAfter)
const minAmount = parseFloat(participant.minimum_amount.split(' ')[0])
const initAmount = parseFloat(participant.initial_amount.split(' ')[0])
compareTokenAmounts(prevAvailable, newAvailable, minAmount)
compareTokenAmounts(prevInitial, newInitial, initAmount)
return participant
}
@@ -0,0 +1,57 @@
// Комбинирование индекса (project_hash + username) как в C++ combine_checksum_ids
// Возвращает десятичную строку uint128 для использования с key_type 'i128'
function charToSymbol(c: string): number {
if (c === '.') return 0
if (c >= '1' && c <= '5') return c.charCodeAt(0) - '1'.charCodeAt(0) + 1
if (c >= 'a' && c <= 'z') return c.charCodeAt(0) - 'a'.charCodeAt(0) + 6
return 0
}
function nameToUint64(name: string): bigint {
let value = 0n
let i = 0
for (; i < 12 && i < name.length; i++) {
value <<= 5n
value |= BigInt(charToSymbol(name[i]))
}
value <<= 4n + 5n * BigInt(12 - i)
if (i < name.length) {
value |= BigInt(charToSymbol(name[i])) & 0x0fn
}
return value
}
function hexToBytes(hex: string): Uint8Array {
const clean = hex.startsWith('0x') ? hex.slice(2) : hex
if (clean.length !== 64) throw new Error('Invalid sha256 hex')
const out = new Uint8Array(32)
for (let i = 0; i < 32; i++) {
out[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16)
}
return out
}
function readBigUInt64LE(bytes: Uint8Array, offset = 0): bigint {
let result = 0n
for (let i = 0; i < 8; i++) {
result |= BigInt(bytes[offset + i]) << (8n * BigInt(i))
}
return result
}
export function makeCombinedChecksum256NameIndexKey(projectHashHex: string, username: string): string {
const bytes = hexToBytes(projectHashHex)
const truncatedHashLE = readBigUInt64LE(bytes, 0)
const nameValue = nameToUint64(username)
const key128 = (truncatedHashLE << 64n) | nameValue
return key128.toString()
}
export function makeCombinedChecksum256Uint64IndexKey(projectHashHex: string, id: number): string {
const bytes = hexToBytes(projectHashHex)
const truncatedHashLE = readBigUInt64LE(bytes, 0)
const idValue = BigInt(id)
const key128 = (truncatedHashLE << 64n) | idValue
return key128.toString()
}
@@ -0,0 +1,84 @@
interface ActionTrace {
act?: {
account: string
name: string
authorization?: { actor: string }[]
}
console?: string
inline_traces?: ActionTrace[]
}
interface TransactionResult {
processed?: {
action_traces?: ActionTrace[]
}
}
/**
* Извлекает и печатает все консоль логи из результата транзакции
* @param transaction - результат транзакции blockchain.api.transact
*/
export function consoleIt(transaction: TransactionResult): void {
if (!transaction?.processed?.action_traces) {
console.log('❌ Нет данных о транзакции или action traces')
return
}
console.log('\n📋 КОНСОЛЬ ЛОГИ ТРАНЗАКЦИИ:')
console.log('='.repeat(50))
let hasConsoleLogs = false
function processActionTrace(trace: ActionTrace, depth = 0): void {
const indent = ' '.repeat(depth)
// Получаем информацию о действии
const account = trace.act?.account || 'unknown'
const action = trace.act?.name || 'unknown'
const actor = trace.act?.authorization?.[0]?.actor || 'unknown'
// Печатаем заголовок действия
console.log(`${indent}🔧 ${account}:${action} (actor: ${actor})`)
// Если есть консоль логи, печатаем их
if (trace.console) {
hasConsoleLogs = true
const logs = trace.console.split('\n').filter(log => log.trim())
if (logs.length > 0) {
console.log(`${indent}📝 Console output:`)
logs.forEach((log) => {
console.log(`${indent} ${log}`)
})
}
}
else {
console.log(`${indent} (нет консоль логов)`)
}
// Обрабатываем inline traces рекурсивно
if (trace.inline_traces?.length) {
console.log(`${indent} ↳ Inline actions:`)
trace.inline_traces.forEach((inlineTrace) => {
processActionTrace(inlineTrace, depth + 1)
})
}
if (depth === 0) {
console.log('') // Пустая строка между действиями верхнего уровня
}
}
// Обрабатываем все action traces
transaction.processed.action_traces.forEach((trace, index) => {
console.log(`\n📍 Action ${index + 1}:`)
processActionTrace(trace, 0)
})
if (!hasConsoleLogs) {
console.log('ℹ️ В транзакции не найдено консоль логов')
}
console.log('='.repeat(50))
console.log('🏁 Конец логов транзакции\n')
}
@@ -0,0 +1,16 @@
export const fakeDocument = {
version: '1.0.0',
hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
doc_hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
meta_hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
meta: '{}',
signatures: [{
id: 1,
signed_hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
signer: 'cooperative1',
public_key: 'EOS5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzxrLg3J',
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
signed_at: '2025-05-14T12:22:26',
meta: '{}',
}],
}
@@ -0,0 +1,10 @@
export const fakeVote = {
version: '1.0.0',
coopname: 'voskhod',
username: 'ant',
decision_id: '1',
signed_at: '2025-05-14T12:22:26.000',
signed_hash: '157192B276DA23CC84AB078FC8755C051C5F0430BF4802E55718221E6B76C777',
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
public_key: 'PUB_K1_5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzzEtUA4',
}
@@ -0,0 +1,8 @@
/**
* Форматирует дату в строку формата 'YYYY-MM-DDTHH:mm:ss'
* @param date - Дата для форматирования
* @returns Строка в формате 'YYYY-MM-DDTHH:mm:ss'
*/
export function formatDateToISOString(date: Date): string {
return date.toISOString().slice(0, 19);
}
@@ -0,0 +1,5 @@
export * from './combinedKeys'
export * from './consoleIt'
export * from './fakeDocument'
export * from './fakeVote'
export * from './formatDateToISOString'
@@ -1,14 +1,12 @@
import { SovietContract } from 'cooptypes'
import { expect } from 'vitest'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { fakeDocument } from './fakeDocument'
import { fakeDocument } from '../shared/fakeDocument'
import type Blockchain from '../../blockchain'
import { fakeVote } from '../shared/fakeVote'
export async function processDecision(blockchain: any, decisionId: number) {
const voteData: SovietContract.Actions.Decisions.VoteFor.IVoteForDecision = {
coopname: 'voskhod',
member: 'ant',
decision_id: decisionId,
}
export async function processDecision(blockchain: Blockchain, decisionId: number) {
const voteData: SovietContract.Actions.Decisions.VoteFor.IVoteForDecision = fakeVote
voteData.decision_id = decisionId
const authData: SovietContract.Actions.Decisions.Authorize.IAuthorize = {
coopname: 'voskhod',
@@ -53,5 +51,4 @@ export async function processDecision(blockchain: any, decisionId: number) {
)
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
}
@@ -0,0 +1,19 @@
import { SovietContract } from 'cooptypes'
import type Blockchain from '../../blockchain'
import { processDecision } from './processDecision'
export async function processLastDecision(blockchain: Blockchain, coopname: string) {
// Получаем последнее решение для голосования
const decisions = await blockchain.getTableRows(
SovietContract.contractName.production,
coopname,
SovietContract.Tables.Decisions.tableName,
100, // берем больше, чтобы гарантированно получить все
)
console.log('Активные вопросы на повестке: ', decisions)
// Берем ПОСЛЕДНИЙ элемент массива (самый свежий)
const lastDecision = decisions[decisions.length - 1]
// 3. Голосуем за решение совета
await processDecision(blockchain, lastDecision.id)
}
+2 -2
View File
@@ -2,7 +2,7 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import Blockchain from '../blockchain'
import config from '../configs'
import { getTotalRamUsage, globalRamStats } from '../utils/getTotalRamUsage'
import { createParticipant } from '../init/participant'
import { addUser } from '../init/participant'
import { generateRandomUsername } from '../utils/randomUsername'
import { signWalletAgreement } from './wallet/signWalletAgreement'
import { depositToWallet } from './wallet/depositToWallet'
@@ -23,7 +23,7 @@ beforeAll(async () => {
tester1 = generateRandomUsername()
console.log('tester1: ', tester1)
await createParticipant(tester1)
await addUser(tester1)
}, 500_000)
afterAll(() => {
@@ -1,17 +1,17 @@
import { randomInt } from 'node:crypto'
import { GatewayContract } from 'cooptypes'
import { GatewayContract, WalletContract } from 'cooptypes'
import { expect } from 'vitest'
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
import { compareTokenAmounts, getCoopProgramWallet, getCoopWallet, getDeposit, getUserProgramWallet } from './walletUtils'
import { generateRandomSHA256 } from '../../utils/randomHash'
import { compareTokenAmounts, getCirculationAccount, getCoopProgramWallet, getDeposit, getUserProgramWallet } from './walletUtils'
export async function depositToWallet(blockchain: any, coopname: string, username: string, amount: number) {
const depositId = randomInt(100000)
const data: GatewayContract.Actions.CreateDeposit.ICreateDeposit = {
const data: WalletContract.Actions.CreateDeposit.ICreateDeposit = {
coopname,
username,
deposit_id: depositId,
type: 'deposit',
deposit_hash: generateRandomSHA256(),
quantity: `${amount.toFixed(4)} RUB`,
}
@@ -19,9 +19,9 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
{
actions: [
{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CreateDeposit.actionName,
authorization: [{ actor: username, permission: 'active' }],
account: WalletContract.contractName.production,
name: WalletContract.Actions.CreateDeposit.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data,
},
],
@@ -35,28 +35,24 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
getTotalRamUsage(result)
expect(result.transaction_id).toBeDefined()
const deposit = await getDeposit(blockchain, coopname, depositId)
const deposit = await getDeposit(blockchain, coopname, data.deposit_hash)
expect(deposit).toEqual(expect.objectContaining({
id: depositId,
expect(deposit).toMatchObject({
deposit_hash: data.deposit_hash,
username,
coopname,
type: 'deposit',
quantity: `${amount.toFixed(4)} RUB`,
status: 'pending',
}))
})
const prevUserWallet = await getUserProgramWallet(blockchain, coopname, username, 1)
const prevUserWalletAvailable = prevUserWallet?.available || '0.0000 RUB'
const prevCoopWallet = await getCoopWallet(blockchain, coopname)
const prevCoopWalletAvailable = prevCoopWallet?.circulating_account?.available || '0.0000 RUB'
const prevCoopWalletAvailable = (await getCirculationAccount(blockchain, coopname)).available
const data2: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit = {
const data2: GatewayContract.Actions.CompleteIncome.ICompleteIncome = {
coopname,
admin: coopname,
deposit_id: depositId,
memo: '',
income_hash: data.deposit_hash,
}
const result2 = await blockchain.api.transact(
@@ -64,7 +60,7 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
actions: [
{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteDeposit.actionName,
name: GatewayContract.Actions.CompleteIncome.actionName,
authorization: [{ actor: coopname, permission: 'active' }],
data: data2,
},
@@ -77,16 +73,18 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
)
getTotalRamUsage(result2)
const deposit2 = await getDeposit(blockchain, coopname, depositId)
expect(deposit2.status).toBe('completed')
const deposit2 = await getDeposit(blockchain, coopname, data.deposit_hash)
expect(deposit2).toBeUndefined()
const program = await getCoopProgramWallet(blockchain, coopname, 1)
const userWallet = await getUserProgramWallet(blockchain, coopname, username, 1)
console.log('▶ Кошелёк пользователя после пополнения: ', userWallet)
// Проверяем изменение балансов
compareTokenAmounts(prevUserWalletAvailable, userWallet.available, amount)
compareTokenAmounts(prevCoopWalletAvailable, (await getCoopWallet(blockchain, coopname)).circulating_account.available, amount)
compareTokenAmounts(prevCoopWalletAvailable, (await getCirculationAccount(blockchain, coopname)).available, amount)
return { depositId, program, userWallet }
}
@@ -1,5 +1,7 @@
import { FundContract, GatewayContract, SovietContract } from 'cooptypes'
import { FundContract, LedgerContract, SovietContract, WalletContract } from 'cooptypes'
import { expect } from 'vitest'
import type Blockchain from '../../blockchain'
import { circulationAccountId } from '../capital/consts'
export async function getUserProgramWallet(blockchain: any, coopname: string, username: string, program_id: number) {
const wallets = await blockchain.getTableRows(
@@ -15,6 +17,11 @@ export async function getUserProgramWallet(blockchain: any, coopname: string, us
return wallets.find((el: any) => el.program_id === program_id)
}
export async function getUserProgramWalletAmount(blockchain: any, coopname: string, username: string, program_id: number) {
const wallet = await getUserProgramWallet(blockchain, coopname, username, program_id)
return wallet ? `${(parseFloat(wallet.available) + parseFloat(wallet.blocked)).toFixed(4)} RUB` : '0.0000 RUB'
}
export async function getCoopProgramWallet(blockchain: any, coopname: string, program_id: number) {
const program = await blockchain.getTableRows(
SovietContract.contractName.production,
@@ -24,10 +31,29 @@ export async function getCoopProgramWallet(blockchain: any, coopname: string, pr
program_id.toString(),
program_id.toString(),
)
console.log('PROGRAMS:', program)
return program[0]
}
export async function getLedgerAccounts(blockchain: Blockchain, coopname: string) {
return (await blockchain.getTableRows(
LedgerContract.contractName.production,
coopname,
LedgerContract.Tables.Laccount.tableName,
100,
))
}
export async function getLedgerAccountById(blockchain: Blockchain, coopname: string, accountId: number) {
const accounts = await getLedgerAccounts(blockchain, coopname)
return accounts.find((account: any) => account.id === accountId)
}
export async function getCirculationAccount(blockchain: Blockchain, coopname: string) {
const accounts = await getLedgerAccounts(blockchain, coopname)
return accounts.find((account: any) => account.id === circulationAccountId) || { available: '0.0000 RUB' }
}
export async function getCoopWallet(blockchain: any, coopname: string) {
return (await blockchain.getTableRows(
FundContract.contractName.production,
@@ -37,14 +63,16 @@ export async function getCoopWallet(blockchain: any, coopname: string) {
))[0]
}
export async function getDeposit(blockchain: any, coopname: string, deposit_id: number) {
export async function getDeposit(blockchain: Blockchain, coopname: string, deposit_hash: string) {
return (await blockchain.getTableRows(
GatewayContract.contractName.production,
WalletContract.contractName.production,
coopname,
'deposits',
WalletContract.Tables.Deposits.tableName,
1,
deposit_id.toString(),
deposit_id.toString(),
deposit_hash,
deposit_hash,
2,
'sha256',
))[0]
}
@@ -53,5 +81,5 @@ export function compareTokenAmounts(prevAmount: string, currentAmount: string, e
const current = parseFloat(currentAmount.split(' ')[0])
const expected = prev + expectedIncrease
expect(current).toBe(expected)
expect(current).toBeCloseTo(expected, 4)
}
+2
View File
@@ -16,3 +16,5 @@ export async function sendPostToCoopbackWithSecret(url: string, data: any) {
},
})
}
export * from './randomData'

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