diff --git a/components/contracts/cpp/capital/app/project_managment/create_project/createproj.cpp b/components/contracts/cpp/capital/app/project_managment/create_project/createproj.cpp index c2658ca0941..1057a439c16 100644 --- a/components/contracts/cpp/capital/app/project_managment/create_project/createproj.cpp +++ b/components/contracts/cpp/capital/app/project_managment/create_project/createproj.cpp @@ -17,7 +17,7 @@ * @ingroup public_capital_actions * @note Авторизация требуется от аккаунта: @p coopname - * @note Для корневых проектов требуется отдельная инициализация через @ref capital::initprj + * @note Для всех проектов требуется отдельная инициализация через @ref capital::initprj */ void capital::createproj ( eosio::name coopname, @@ -42,11 +42,10 @@ void capital::createproj ( // Валидируем parent_hash согласно правилам проектов Capital::Projects::validate_parent_hash(coopname, parent_hash); - // Если указан родительский проект, проверяем что он авторизован + // Если указан родительский проект, проверяем что он существует if (parent_hash != checksum256()) { auto parent_project = Capital::Projects::get_project(coopname, parent_hash); eosio::check(parent_project.has_value(), "Родительский проект не найден"); - eosio::check(parent_project -> is_authorized, "Родительский проект не авторизован советом"); } Capital::Projects::create_project(coopname, project_hash, parent_hash, title, description, invite, meta, data, can_convert_to_project); diff --git a/components/contracts/cpp/capital/app/project_managment/create_project/project-creation-process.dox b/components/contracts/cpp/capital/app/project_managment/create_project/project-creation-process.dox index a292a16396c..dc8bf211c31 100644 --- a/components/contracts/cpp/capital/app/project_managment/create_project/project-creation-process.dox +++ b/components/contracts/cpp/capital/app/project_managment/create_project/project-creation-process.dox @@ -8,8 +8,7 @@ реализации и генерации интеллектуального результата. Процесс вызывается тогда и только тогда, когда проект готов идти в планирование. До этого идея проекта хранится в банке идей за пределами контракта. - Дочерние проекты (компоненты мета-проекта) автоматически авторизованы при создании на авторизованном родительском проекте. - Корневые проекты (без родителя) требуют отдельной инициализации через процесс @ref public_capital_project_initialization_process. + Все проекты (корневые и дочерние) требуют отдельной инициализации через процесс @ref public_capital_project_initialization_process. \htmlonly @@ -22,18 +21,11 @@ Note over БИ: Идея прошла отбор БИ->>К: Идея готова к реализации - alt Корневой проект - К->>C: Создание проекта - Note over C: Проверка уникальности хеша - Note over C: Валидация родительского проекта - C->>C: Создание записи проекта - Note over C: Проект создан, требуется инициализация - else Дочерний проект - К->>C: Создание компонента - Note over C: Проверка авторизации родителя - C->>C: Создание записи проекта - Note over C: Компонент автоматически авторизован - end + К->>C: Создание проекта + Note over C: Проверка уникальности хеша + Note over C: Валидация родительского проекта + C->>C: Создание записи проекта + Note over C: Проект создан, требуется инициализация \endhtmlonly @@ -49,13 +41,11 @@ Система проверяет уникальность хеша проекта и валидирует родительский проект, затем создает запись проекта. -Дочерние проекты (компоненты) автоматически авторизованы при создании на авторизованном родительском проекте. -Корневые проекты (без родителя) создаются без авторизации и требуют отдельной инициализации. +Все проекты (корневые и дочерние) создаются без авторизации и требуют отдельной инициализации. @post создается запись проекта в таблице projects @post статус проекта устанавливается в PENDING -@post для дочерних проектов: проект автоматически авторизован и готов к планированию -@post для корневых проектов: проект создан и ожидает инициализации +@post проект создан и ожидает инициализации ## Документы процесса @include{doc} project-creation-docs.dox diff --git a/components/contracts/cpp/capital/app/project_managment/init_project/initprj.cpp b/components/contracts/cpp/capital/app/project_managment/init_project/initprj.cpp index 5ee1fb03aeb..5be5cb2c1f7 100644 --- a/components/contracts/cpp/capital/app/project_managment/init_project/initprj.cpp +++ b/components/contracts/cpp/capital/app/project_managment/init_project/initprj.cpp @@ -1,7 +1,7 @@ /** * @brief Инициализирует проект для авторизации советом - * Отправляет корневой проект на рассмотрение совета кооператива: - * - Проверяет что проект существует и является корневым + * Отправляет проект на рассмотрение совета кооператива: + * - Проверяет что проект существует * - Проверяет что проект еще не инициализирован * - Отправляет проект на авторизацию с предоставленным документом * @param coopname Наименование кооператива @@ -11,7 +11,7 @@ * @ingroup public_capital_actions * @note Авторизация требуется от аккаунта: @p coopname - * @note Доступно только для корневых проектов (без родителя) + * @note Доступно для всех проектов (корневых и дочерних) */ void capital::initprj(eosio::name coopname, checksum256 project_hash, document2 document) { require_auth(coopname); @@ -22,9 +22,6 @@ void capital::initprj(eosio::name coopname, checksum256 project_hash, document2 // Получаем проект для проверки auto project = Capital::Projects::get_project_or_fail(coopname, project_hash); - // Проверяем что это корневой проект (без родителя) - eosio::check(project->parent_hash == checksum256(), "Инициализация требуется только для корневых проектов"); - // Проверяем что проект еще не инициализирован eosio::check(!project->is_authorized, "Проект уже инициализирован"); diff --git a/components/contracts/cpp/capital/app/project_managment/init_project/project-init-process.dox b/components/contracts/cpp/capital/app/project_managment/init_project/project-init-process.dox index c2173caaff6..852254efbfc 100644 --- a/components/contracts/cpp/capital/app/project_managment/init_project/project-init-process.dox +++ b/components/contracts/cpp/capital/app/project_managment/init_project/project-init-process.dox @@ -3,9 +3,8 @@ @defgroup public_capital_project_initialization_process Инициализировать проект @ingroup public_capital_processes @pre Проект создан через @ref public_capital_project_creation_process - @pre Проект является корневым (без родителя) - Процесс инициализации корневого проекта для получения авторизации совета кооператива. + Процесс инициализации проекта для получения авторизации совета кооператива. Процесс включает отправку проекта на рассмотрение совета с предоставленным документом, рассмотрение советом и принятие решения об авторизации или отклонении проекта. @@ -18,7 +17,7 @@ participant С as Совет К->>C: Инициализация проекта - Note over C: Проверка что проект корневой + Note over C: Проверка что проект существует Note over C: Проверка что проект не инициализирован C->>С: Отправка на авторизацию с документом Note over С: Рассмотрение проекта советом @@ -37,8 +36,8 @@ ### Шаг 1: Инициализация проекта Действие: @ref capital::initprj "инициализация проекта" -Кооператив отправляет корневой проект на рассмотрение совета кооператива с предоставленным документом. -Система проверяет что проект существует, является корневым и еще не инициализирован, +Кооператив отправляет проект на рассмотрение совета кооператива с предоставленным документом. +Система проверяет что проект существует и еще не инициализирован, затем отправляет проект на авторизацию в совет. @post проект отправлен на рассмотрение совета diff --git a/components/contracts/cpp/capital/app/project_managment/stop_project/stopproject.cpp b/components/contracts/cpp/capital/app/project_managment/stop_project/stopproject.cpp index d5864bb24a7..a5136e4b5d5 100644 --- a/components/contracts/cpp/capital/app/project_managment/stop_project/stopproject.cpp +++ b/components/contracts/cpp/capital/app/project_managment/stop_project/stopproject.cpp @@ -4,6 +4,7 @@ * - Проверяет существование проекта * - Валидирует что проект в статусе active * - Обновляет статус проекта на pending + * - Сбрасывает авторизацию проекта (требуется повторная инициализация) * @param coopname Наименование кооператива * @param project_hash Хеш проекта для остановки * @ingroup public_actions @@ -25,4 +26,7 @@ void capital::stopproject(name coopname, checksum256 project_hash) { // Обновляем статус проекта на "pending" Capital::Projects::update_status(coopname, project_hash, Capital::Projects::Status::PENDING); + + // Сбрасываем авторизацию проекта (требуется повторная инициализация) + Capital::Projects::revoke_authorization(coopname, project_hash); } diff --git a/components/contracts/cpp/capital/domain/entities/projects.hpp b/components/contracts/cpp/capital/domain/entities/projects.hpp index 601bbe52954..b9effbe357d 100644 --- a/components/contracts/cpp/capital/domain/entities/projects.hpp +++ b/components/contracts/cpp/capital/domain/entities/projects.hpp @@ -171,7 +171,7 @@ namespace Capital::Projects { row.data = data; row.is_planed = false; // Изначально проект не запланирован row.can_convert_to_project = can_convert_to_project; // Разрешена ли конвертация в кошелек проекта - row.is_authorized = (parent_hash != checksum256()); // Дочерние проекты сразу авторизованы, корневые - нет + row.is_authorized = false; // Все проекты требуют инициализации row.authorization = document2(); // Пустой документ авторизации }); } @@ -845,5 +845,24 @@ namespace Capital::Projects { }); } + /** + * @brief Сбрасывает авторизацию проекта + * Устанавливает флаг авторизации в false и очищает документ авторизации + * @param coopname Наименование кооператива + * @param project_hash Хеш проекта + */ + inline void revoke_authorization(eosio::name coopname, const checksum256 &project_hash) { + project_index projects(_capital, coopname.value); + auto project_hash_index = projects.get_index<"byhash"_n>(); + + auto project_itr = project_hash_index.find(project_hash); + eosio::check(project_itr != project_hash_index.end(), "Проект не найден"); + + project_hash_index.modify(project_itr, coopname, [&](auto& row) { + row.is_authorized = false; + row.authorization = document2(); // Пустой документ авторизации + }); + } + }// namespace Project \ No newline at end of file diff --git a/components/cooptypes/src/cooperative/registry/1005.InitProjectStatement/index.ts b/components/cooptypes/src/cooperative/registry/1005.InitProjectStatement/index.ts new file mode 100644 index 00000000000..359ddf55d5a --- /dev/null +++ b/components/cooptypes/src/cooperative/registry/1005.InitProjectStatement/index.ts @@ -0,0 +1,113 @@ +import type { ICommonUser, ICooperativeData, IVars } from '../../model' +import type { IGenerate, IMetaDocument } from '../../document' + +export const registry_id = 1005 + +// Модель действия для генерации +export interface Action extends IGenerate { + registry_id: number + project_name: string + project_id: string + component_name: string + component_id: string + is_component: boolean +} + +export type Meta = IMetaDocument & Action + +// Модель данных документа +export interface Model { + meta: IMetaDocument + coop: ICooperativeData + vars: IVars + user: ICommonUser + project_name: string + project_id: string + component_name: string + component_id: string + is_component: boolean +} + +export const title = 'Заявление на инициализацию проекта' +export const description = 'Заявление на инициализацию проекта или компонента' + +export const context = `

ЗАЯВЛЕНИЕ

на инициализацию проекта

{{ meta.created_at }}, {{ coop.city }}

{% trans 'parties_intro' %} "{{ vars.name }}" {% trans 'in_face_of_chairman' %} {{ coop.chairman.last_name }} {{ coop.chairman.first_name }} {{ coop.chairman.middle_name }}, {% trans 'acting_on_basis_of_charter' %}, {% trans 'hereinafter_referred_to_as' %} "{% trans 'society' %}", {% trans 'and_participant' %} {{ user.full_name_or_short_name }}, {% trans 'hereinafter_referred_to_as' %} "{% trans 'participant' %}", {% trans 'jointly_referred_to_as' %} "{% trans 'parties' %}", {% trans 'have_concluded_this_statement' %} {% trans 'hereinafter_referred_to_as' %} "{% trans 'statement' %}" {% trans 'of_the_following' %}:

{% if is_component %}{% trans 'participant_requests_component_init' %} "{{ component_name }}" (№{{ component_id }}) {% trans 'as_part_of_project' %} "{{ project_name }}" (№{{ project_id }}).{% else %}{% trans 'participant_requests_project_init' %} "{{ project_name }}" (№{{ project_id }}).{% endif %}

{% trans 'details_and_signatures_of_parties' %}

{% trans 'society' %}/{{ vars.full_abbr }} "{{ vars.name }}"/:

ИНН {{ coop.details.inn }}, КПП {{ coop.details.kpp }}, ОГРН {{ coop.details.ogrn }}

{% trans 'legal_address' %}: {{ coop.full_address }}

{% trans 'contact_phone' %}: {{ coop.phone }}

{% trans 'email' %}: {{ coop.email }}

{% trans 'bank_account' %}: {{ coop.defaultBankAccount.account_number }}

{% trans 'bank_name' %}: {{ coop.defaultBankAccount.bank_name }}

{% trans 'bik' %}: {{ coop.defaultBankAccount.details.bik }}

{% trans 'correspondent_account' %}: {{ coop.defaultBankAccount.details.corr }}

{% trans 'chairman' %} {{ vars.full_abbr_genitive }} "{{ vars.name }}"

{{ coop.chairman.last_name }} {{ coop.chairman.first_name }} {{ coop.chairman.middle_name }}

{% trans 'signed_by_digital_signature' %}

{% trans 'participant' %}:

{{ user.full_name_or_short_name }}

{% trans 'contact_phone' %}: {{ user.phone }}

{% trans 'email' %}: {{ user.email }}

{% trans 'signed_by_digital_signature' %}

` + +export const translations = { + ru: { + parties_intro: 'Потребительский Кооператив', + in_face_of_chairman: 'в лице Председателя Совета', + acting_on_basis_of_charter: 'действующего на основании Устава', + hereinafter_referred_to_as: 'далее именуемый(-ая)', + society: 'Общество', + and_participant: 'и', + participant: 'Пайщик', + jointly_referred_to_as: 'совместно именуемые', + parties: 'Стороны', + have_concluded_this_statement: 'составили настоящее Заявление', + statement: 'Заявление', + of_the_following: 'о нижеследующем', + participant_requests_component_init: 'Пайщик просит инициализировать Компонент', + participant_requests_project_init: 'Пайщик просит инициализировать Проект', + as_part_of_project: 'как составную часть Проекта', + details_and_signatures_of_parties: 'РЕКВИЗИТЫ И ПОДПИСИ СТОРОН', + legal_address: 'Юр. адрес', + contact_phone: 'Контактный тел.', + email: 'Электронная почта', + bank_account: 'Р/с', + bank_name: 'Банк', + bik: 'БИК', + correspondent_account: 'Корр/счет', + chairman: 'Председатель Совета', + signed_by_digital_signature: 'Подписано электронной подписью', + }, +} + +export const exampleData = { + meta: { + created_at: '11.04.2024 12:00', + }, + project_name: 'Проект цифровой платформы', + project_id: 'B2C3D4E5F6789ABC', + component_name: 'Компонент разработки', + component_id: 'A1B2C3D4E5F6789A', + is_component: true, + vars: { + name: 'ВОСХОД', + full_abbr_genitive: 'Потребительского Кооператива', + full_abbr: 'Потребительский Кооператив', + }, + user: { + full_name_or_short_name: 'Иванов Иван Иванович', + abbr_full_name: 'Иванов И.И.', + email: 'ivanov@example.com', + phone: '+7 999 123-45-67', + }, + coop: { + short_name: 'ПК "ВОСХОД"', + details: { + inn: '9728130611', + kpp: '772801001', + ogrn: '1247700283346', + }, + full_address: '117593, г. МОСКВА, ВН.ТЕР.Г. МУНИЦИПАЛЬНЫЙ ОКРУГ ЯСЕНЕВО, ПРОЕЗД СОЛОВЬИНЫЙ, Д. 1, ПОМЕЩ. 1/1', + phone: '+7 902 895-33-75', + email: 'chairman.voskhod@gmail.com', + defaultBankAccount: { + currency: 'RUB', + bank_name: 'ПАО Сбербанк', + account_number: '40703810038000110117', + details: { + bik: '044525225', + corr: '30101810400000000225', + kpp: '772801001', + }, + }, + chairman: { + first_name: 'Алексей', + last_name: 'Муравьев', + middle_name: 'Николаевич', + }, + city: 'Москва', + }, +} diff --git a/components/cooptypes/src/cooperative/registry/1006.InitProjectDecision/index.ts b/components/cooptypes/src/cooperative/registry/1006.InitProjectDecision/index.ts new file mode 100644 index 00000000000..8e8773d3aef --- /dev/null +++ b/components/cooptypes/src/cooperative/registry/1006.InitProjectDecision/index.ts @@ -0,0 +1,104 @@ +import type { IDecisionData, IGenerate, IMetaDocument } from '../../document' +import type { ICooperativeData, IVars } from '../../model' + +export const registry_id = 1006 + +/** + * Интерфейс генерации решения совета по инициализации проекта + */ +export interface Action extends IGenerate { + decision_id: number + project_name: string + project_id: string + component_name: string + component_id: string + is_component: boolean +} + +export type Meta = IMetaDocument & Action + +// Модель данных +export interface Model { + coop: ICooperativeData + meta: IMetaDocument + decision: IDecisionData + vars: IVars + project_name: string + project_id: string + component_name: string + component_id: string + is_component: boolean +} + +export const title = 'Протокол решения совета об инициализации проекта' +export const description = 'Форма протокола решения совета об инициализации проекта или компонента' +export const context = '\n\n

{% trans \'protocol_number\', decision.id %}

\n

{% trans \'council_meeting_name\' %} {{vars.full_abbr_genitive}} "{{vars.name}}"

\n

{{ meta.created_at }}, {{ coop.city }}

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
{% trans \'meeting_format\' %}{% trans \'meeting_format_value\' %}
{% trans \'meeting_place\' %}{{ coop.full_address }}
{% trans \'meeting_date\' %}{{ decision.date }}
{% trans \'opening_time\' %}{{ decision.time }}
\n

{% trans \'council_members\' %}

\n\n\n{% for member in coop.members %}\n\n\n\n\n{% endfor %}\n\n
{% if member.is_chairman %}{% trans \'chairman_of_the_council\' %}{% else %}{% trans \'member_of_the_council\' %}{% endif %}{{ member.last_name }} {{ member.first_name }} {{ member.middle_name }}
\n

{% trans \'meeting_legality\' %}

\n

{% trans \'voting_results\', decision.voters_percent %} {% trans \'quorum\' %} {% trans \'chairman_of_the_meeting\', coop.chairman.last_name, coop.chairman.first_name, coop.chairman.middle_name %}.

\n

{% trans \'agenda\' %}

\n\n\n\n\n\n\n\n\n\n\n\n
{% trans \'question\' %}
1{% if is_component %}{% trans \'init_component_question\', component_name, component_id, project_name, project_id %}{% else %}{% trans \'init_project_question\', project_name, project_id %}{% endif %}
\n

{% trans \'voting\' %}

\n

{% trans \'vote_results\' %}

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
{% trans \'votes_for\' %}{{ decision.votes_for }}
{% trans \'votes_against\' %}{{ decision.votes_against }}
{% trans \'votes_abstained\' %}{{ decision.votes_abstained }}
\n

{% trans \'decision_made\' %}

\n\n\n\n\n\n\n\n\n\n\n\n
{% trans \'decision\' %}
1{% if is_component %}{% trans \'init_component_decision\', component_name, component_id, project_name, project_id %}{% else %}{% trans \'init_project_decision\', project_name, project_id %}{% endif %}
\n
\n

{% trans \'closing_time\', decision.time %}

\n

{% trans \'signature\' %}

{% trans \'chairman\' %} {{ coop.chairman.last_name }} {{ coop.chairman.first_name }} {{ coop.chairman.middle_name }}

\n' + +export const translations = { + ru: { + meeting_format: 'Форма', + meeting_date: 'Дата', + meeting_place: 'Место', + opening_time: 'Время открытия', + council_members: 'ЧЛЕНЫ СОВЕТА', + voting_results: 'Количество голосов составляет {0}% от общего числа членов Совета.', + meeting_legality: 'СОБРАНИЕ ПРАВОМОЧНО', + chairman_of_the_meeting: 'Председатель собрания совета: {0} {1} {2}', + agenda: 'ПОВЕСТКА ДНЯ', + vote_results: 'По первому вопросу повестки дня проголосовали:', + decision_made: 'РЕШИЛИ', + closing_time: 'Время закрытия собрания совета: {0}.', + protocol_number: 'ПРОТОКОЛ № {0}', + council_meeting_name: 'Собрания Совета', + chairman_of_the_council: 'Председатель совета', + signature: 'Документ подписан электронной подписью.', + chairman: 'Председатель', + quorum: 'Кворум для решения поставленных на повестку дня вопросов имеется.', + voting: 'ГОЛОСОВАНИЕ', + meeting_format_value: 'Заочная', + member_of_the_council: 'Член совета', + question: 'Вопрос', + votes_for: 'ЗА', + votes_against: 'ПРОТИВ', + votes_abstained: 'ВОЗДЕРЖАЛСЯ', + decision: 'Решение', + init_component_question: 'Об инициализации компонента "{0}" (№{1}) как составной части проекта "{2}" (№{3})', + init_project_question: 'Об инициализации проекта "{0}" (№{1})', + init_component_decision: 'Инициализировать компонент "{0}" (№{1}) как составную часть проекта "{2}" (№{3})', + init_project_decision: 'Инициализировать проект "{0}" (№{1})', + }, + // ... другие переводы +} + +export const exampleData = { + meta: { + created_at: '12.02.2024 00:01', + }, + project_name: 'Проект цифровой платформы', + project_id: 'B2C3D4E5F6789ABC', + component_name: 'Компонент разработки', + component_id: 'A1B2C3D4E5F6789A', + is_component: true, + coop: { + city: 'Москва', + full_address: 'Смольная 3-84', + chairman: { + last_name: 'Муравьев', + first_name: 'Алексей', + middle_name: 'Николаевич', + }, + }, + decision: { + time: '00:01', + date: '12.02.2024', + votes_for: '3', + votes_against: '0', + votes_abstained: '0', + voters_percent: '100', + id: '1', + }, + vars: { + full_abbr_genitive: 'потребительского кооператива', + name: 'ВОСХОД', + }, +} diff --git a/components/cooptypes/src/cooperative/registry/index.ts b/components/cooptypes/src/cooperative/registry/index.ts index 9c22f7c3021..67ceafca42c 100644 --- a/components/cooptypes/src/cooperative/registry/index.ts +++ b/components/cooptypes/src/cooperative/registry/index.ts @@ -29,6 +29,8 @@ export * as BlagorostOfferTemplate from './999.BlagorostOfferTemplate' export * as GenerationAgreementTemplate from './997.GenerationAgreementTemplate' export * as GenerationAgreement from './1001.GenerationAgreement' export * as AppendixGenerationAgreement from './1002.AppendixGenerationAgreement' +export * as InitProjectStatement from './1005.InitProjectStatement' +export * as InitProjectDecision from './1006.InitProjectDecision' export * as ExpenseStatement from './1010.ExpenseStatement' export * as ExpenseDecision from './1011.ExpenseDecision' diff --git a/components/factory/src/Actions/1005.InitProjectStatement.ts b/components/factory/src/Actions/1005.InitProjectStatement.ts new file mode 100644 index 00000000000..a06815a60ba --- /dev/null +++ b/components/factory/src/Actions/1005.InitProjectStatement.ts @@ -0,0 +1,50 @@ +import { DraftContract } from 'cooptypes' +import { InitProjectStatement } from '../Templates' +import { DocFactory } from '../Factory' +import type { IGeneratedDocument, IGenerationOptions, IMetaDocument, ITemplate } from '../Interfaces' +import type { MongoDBConnector } from '../Services/Databazor' + +export { InitProjectStatement as Template } from '../Templates' + +export class Factory extends DocFactory { + constructor(storage: MongoDBConnector) { + super(storage) + } + + async generateDocument(data: InitProjectStatement.Action, options?: IGenerationOptions): Promise { + let template: ITemplate + + if (process.env.SOURCE === 'local') { + template = InitProjectStatement.Template + } + else { + template = await this.getTemplate(DraftContract.contractName.production, InitProjectStatement.registry_id, data.block_num) + } + + const meta: IMetaDocument = await this.getMeta({ title: template.title, ...data }) + const coop = await super.getCooperative(data.coopname, data.block_num) + const vars = await super.getVars(data.coopname, data.block_num) + + const userData = await super.getUser(data.username, data.block_num) + const user = super.getCommonUser(userData) + + const combinedData: InitProjectStatement.Model = { + meta, + coop, + vars, + user, + project_name: data.project_name, + project_id: data.project_id, + component_name: data.component_name, + component_id: data.component_id, + is_component: data.is_component, + } + + await this.validate(combinedData, template.model) + + const translation = template.translations[meta.lang] + const document: IGeneratedDocument = await this.generatePDF('', template.context, combinedData, translation, meta, options?.skip_save) + + return document + } +} diff --git a/components/factory/src/Actions/1006.InitProjectDecision.ts b/components/factory/src/Actions/1006.InitProjectDecision.ts new file mode 100644 index 00000000000..b149c2abbee --- /dev/null +++ b/components/factory/src/Actions/1006.InitProjectDecision.ts @@ -0,0 +1,88 @@ +import { type Cooperative, DraftContract } from 'cooptypes' +import { DocFactory } from '../Factory' +import type { + IGeneratedDocument, + IGenerationOptions, + IMetaDocument, + ITemplate, +} from '../Interfaces' +import type { MongoDBConnector } from '../Services/Databazor' + +import { InitProjectDecision } from '../Templates' + +export { InitProjectDecision as Template } from '../Templates' + +export class Factory extends DocFactory { + constructor(storage: MongoDBConnector) { + super(storage) + } + + async generateDocument( + data: InitProjectDecision.Action, + options?: IGenerationOptions, + ): Promise { + let template: ITemplate + + if (process.env.SOURCE === 'local') { + template = InitProjectDecision.Template + } + else { + template = await this.getTemplate(DraftContract.contractName.production, InitProjectDecision.registry_id, data.block_num) + } + + const user = await this.getUser(data.username, data.block_num) + + const userData = { + [user.type]: user.data, + } + + const coop = await this.getCooperative(data.coopname, data.block_num) + + // TODO необходимо строго типизировать мета-данные документов друг под друга! + const meta: IMetaDocument = await this.getMeta({ + title: template.title, + ...data, + }) // Генерируем мета-данные + + const decision: Cooperative.Document.IDecisionData = await this.getDecision( + coop, + data.coopname, + data.decision_id, + meta.created_at, + ) + + const vars = await super.getVars(data.coopname, data.block_num) + + const combinedData: InitProjectDecision.Model = { + ...userData, + meta, + coop, + decision, + project_name: data.project_name, + project_id: data.project_id, + component_name: data.component_name, + component_id: data.component_id, + is_component: data.is_component, + vars, + } + + // валидируем скомбинированные данные + await this.validate(combinedData, template.model) + + // получаем комплекс перевода + const translation = template.translations[meta.lang] + + // генерируем документ + const document: IGeneratedDocument = await this.generatePDF( + '', + template.context, + combinedData, + translation, + meta, + options?.skip_save, + ) + + // возвращаем + return document + } +} diff --git a/components/factory/src/Actions/index.ts b/components/factory/src/Actions/index.ts index b11969e6239..6f973c1a689 100644 --- a/components/factory/src/Actions/index.ts +++ b/components/factory/src/Actions/index.ts @@ -25,6 +25,8 @@ export * as ReturnByMoneyDecision from './901.ReturnByMoneyDecision' export * as BlagorostOffer from './1000.CapitalizationAgreement' export * as GenerationAgreement from './1001.GenerationAgreement' export * as AppendixGenerationAgreement from './1002.AppendixGenerationAgreement' +export * as InitProjectStatement from './1005.InitProjectStatement' +export * as InitProjectDecision from './1006.InitProjectDecision' export * as ExpenseStatement from './1010.ExpenseStatement' export * as ExpenseDecision from './1011.ExpenseDecision' diff --git a/components/factory/src/Templates/1005.InitProjectStatement.ts b/components/factory/src/Templates/1005.InitProjectStatement.ts new file mode 100644 index 00000000000..c70bd4817c1 --- /dev/null +++ b/components/factory/src/Templates/1005.InitProjectStatement.ts @@ -0,0 +1,41 @@ +import type { JSONSchemaType } from 'ajv' +import { Cooperative } from 'cooptypes' +import type { ITemplate } from '../Interfaces' +import { CooperativeSchema } from '../Schema' +import { IMetaJSONSchema } from '../Schema/MetaSchema' +import { VarsSchema } from '../Schema/VarsSchema' +import { CommonUserSchema } from '../Schema/CommonUserSchema' + +export const registry_id = Cooperative.Registry.InitProjectStatement.registry_id + +// Модель действия для генерации +export type Action = Cooperative.Registry.InitProjectStatement.Action + +// Модель данных +export type Model = Cooperative.Registry.InitProjectStatement.Model + +// Схема для сверки +export const Schema: JSONSchemaType = { + type: 'object', + properties: { + meta: IMetaJSONSchema, + coop: CooperativeSchema, + vars: VarsSchema, + user: CommonUserSchema, + project_name: { type: 'string' }, + project_id: { type: 'string' }, + component_name: { type: 'string' }, + component_id: { type: 'string' }, + is_component: { type: 'boolean' }, + }, + required: ['meta', 'coop', 'vars', 'user', 'project_name', 'project_id', 'component_name', 'component_id', 'is_component'], + additionalProperties: true, +} + +export const Template: ITemplate = { + title: Cooperative.Registry.InitProjectStatement.title, + description: Cooperative.Registry.InitProjectStatement.description, + model: Schema, + context: Cooperative.Registry.InitProjectStatement.context, + translations: Cooperative.Registry.InitProjectStatement.translations, +} diff --git a/components/factory/src/Templates/1006.InitProjectDecision.ts b/components/factory/src/Templates/1006.InitProjectDecision.ts new file mode 100644 index 00000000000..c2d4b303e4b --- /dev/null +++ b/components/factory/src/Templates/1006.InitProjectDecision.ts @@ -0,0 +1,64 @@ +import type { JSONSchemaType } from 'ajv' + +import { Cooperative } from 'cooptypes' +import type { ITemplate } from '../Interfaces' +import { IMetaJSONSchema } from '../Schema/MetaSchema' +import { CooperativeSchema } from '../Schema/CooperativeSchema' +import { decisionSchema } from '../Schema/DecisionSchema' +import { VarsSchema } from '../Schema' + +export const registry_id = Cooperative.Registry.InitProjectDecision.registry_id + +/** + * Интерфейс генерации решения совета по инициализации проекта + */ +export type Action = Cooperative.Registry.InitProjectDecision.Action + +export type Model = Cooperative.Registry.InitProjectDecision.Model + +// Схема для сверки +export const Schema: JSONSchemaType = { + type: 'object', + properties: { + coop: { + type: 'object', + properties: { + ...CooperativeSchema.properties, + }, + required: [...CooperativeSchema.required], + additionalProperties: true, + }, + decision: { + type: 'object', + properties: { + ...decisionSchema.properties, + }, + required: [...decisionSchema.required], + additionalProperties: true, + }, + meta: { + type: 'object', + properties: { + ...IMetaJSONSchema.properties, + }, + required: [...IMetaJSONSchema.required], + additionalProperties: true, + }, + vars: VarsSchema, + project_name: { type: 'string' }, + project_id: { type: 'string' }, + component_name: { type: 'string' }, + component_id: { type: 'string' }, + is_component: { type: 'boolean' }, + }, + required: ['meta', 'coop', 'decision', 'project_name', 'project_id', 'component_name', 'component_id', 'is_component'], + additionalProperties: true, +} + +export const Template: ITemplate = { + title: Cooperative.Registry.InitProjectDecision.title, + description: Cooperative.Registry.InitProjectDecision.description, + model: Schema, + context: Cooperative.Registry.InitProjectDecision.context, + translations: Cooperative.Registry.InitProjectDecision.translations, +} diff --git a/components/factory/src/Templates/index.ts b/components/factory/src/Templates/index.ts index 2f388a89c4f..888ae97aad4 100644 --- a/components/factory/src/Templates/index.ts +++ b/components/factory/src/Templates/index.ts @@ -27,6 +27,8 @@ export * as BlagorostOfferTemplate from './999.BlagorostOfferTemplate' export * as BlagorostOffer from './1000.CapitalizationAgreement' export * as GenerationAgreement from './1001.GenerationAgreement' export * as AppendixGenerationAgreement from './1002.AppendixGenerationAgreement' +export * as InitProjectStatement from './1005.InitProjectStatement' +export * as InitProjectDecision from './1006.InitProjectDecision' export * as ExpenseStatement from './1010.ExpenseStatement' export * as ExpenseDecision from './1011.ExpenseDecision' diff --git a/components/factory/src/Templates/registry.ts b/components/factory/src/Templates/registry.ts index a2b9837389d..9db0e258b45 100644 --- a/components/factory/src/Templates/registry.ts +++ b/components/factory/src/Templates/registry.ts @@ -20,6 +20,8 @@ import * as ReturnByMoney from './900.returnByMoney' import * as ReturnByMoneyDecision from './901.ReturnByMoneyDecision' import * as GenerationAgreement from './1001.GenerationAgreement' import * as AppendixGenerationAgreement from './1002.AppendixGenerationAgreement' +import * as InitProjectStatement from './1005.InitProjectStatement' +import * as InitProjectDecision from './1006.InitProjectDecision' import * as ExpenseStatement from './1010.ExpenseStatement' import * as ExpenseDecision from './1011.ExpenseDecision' @@ -95,6 +97,8 @@ export const Registry = { 1000: BlagorostOffer, 1001: GenerationAgreement, 1002: AppendixGenerationAgreement, + 1005: InitProjectStatement, + 1006: InitProjectDecision, 1010: ExpenseStatement, 1011: ExpenseDecision, 1020: GenerationMoneyInvestStatement, diff --git a/components/factory/src/index.ts b/components/factory/src/index.ts index 62ccccbe70e..39176d66e79 100644 --- a/components/factory/src/index.ts +++ b/components/factory/src/index.ts @@ -110,6 +110,8 @@ export class Generator implements IGenerator { [Actions.AssetContributionAct.Template.registry_id]: new Actions.AssetContributionAct.Factory(this.storage), // 702 [Actions.GenerationAgreement.Template.registry_id]: new Actions.GenerationAgreement.Factory(this.storage), // 1001 [Actions.AppendixGenerationAgreement.Template.registry_id]: new Actions.AppendixGenerationAgreement.Factory(this.storage), // 1002 + [Actions.InitProjectStatement.Template.registry_id]: new Actions.InitProjectStatement.Factory(this.storage), // 1005 + [Actions.InitProjectDecision.Template.registry_id]: new Actions.InitProjectDecision.Factory(this.storage), // 1006 [Actions.ExpenseStatement.Template.registry_id]: new Actions.ExpenseStatement.Factory(this.storage), // 1010 [Actions.ExpenseDecision.Template.registry_id]: new Actions.ExpenseDecision.Factory(this.storage), // 1011 diff --git a/components/factory/test/documents-1000-plus.test.ts b/components/factory/test/documents-1000-plus.test.ts index 1d6a2895906..4ee518a44fb 100644 --- a/components/factory/test/documents-1000-plus.test.ts +++ b/components/factory/test/documents-1000-plus.test.ts @@ -55,6 +55,35 @@ describe('тест генератора документов с registry_id >= 1 }) }) + it('генерируем заявление на инициализацию проекта', async () => { + await testDocumentGeneration({ + registry_id: 1005, + coopname: 'voskhod', + username: 'ant', + project_name: 'Проект цифровой платформы', + project_id: 'B2C3D4E5F6789ABC', + component_name: 'Компонент разработки', + component_id: 'A1B2C3D4E5F6789A', + is_component: true, + lang: 'ru', + }) + }) + + it('генерируем решение об инициализации проекта', async () => { + await testDocumentGeneration({ + registry_id: 1006, + coopname: 'voskhod', + username: 'ant', + decision_id: 1, + project_name: 'Проект цифровой платформы', + project_id: 'B2C3D4E5F6789ABC', + component_name: 'Компонент разработки', + component_id: 'A1B2C3D4E5F6789A', + is_component: true, + lang: 'ru', + }) + }) + // Документы расходов it('генерируем заявление о расходах', async () => { await testDocumentGeneration({