project & component initialization & InitProjectStatement & InitProjectDecision

This commit is contained in:
Alex Ant
2026-01-19 17:39:20 +05:00
parent 2b3b662a9a
commit dff4579f28
18 changed files with 542 additions and 33 deletions
@@ -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);
@@ -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: Проект создан, требуется инициализация
</div>
\endhtmlonly
@@ -49,13 +41,11 @@
Система проверяет уникальность хеша проекта и валидирует родительский проект,
затем создает запись проекта.
Дочерние проекты (компоненты) автоматически авторизованы при создании на авторизованном родительском проекте.
Корневые проекты (без родителя) создаются без авторизации и требуют отдельной инициализации.
Все проекты (корневые и дочерние) создаются без авторизации и требуют отдельной инициализации.
@post создается запись проекта в таблице projects
@post статус проекта устанавливается в PENDING
@post для дочерних проектов: проект автоматически авторизован и готов к планированию
@post для корневых проектов: проект создан и ожидает инициализации
@post проект создан и ожидает инициализации
## Документы процесса
@include{doc} project-creation-docs.dox
@@ -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, "Проект уже инициализирован");
@@ -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: Инициализация проекта
<b>Действие:</b> @ref capital::initprj "инициализация проекта"
Кооператив отправляет корневой проект на рассмотрение совета кооператива с предоставленным документом.
Система проверяет что проект существует, является корневым и еще не инициализирован,
Кооператив отправляет проект на рассмотрение совета кооператива с предоставленным документом.
Система проверяет что проект существует и еще не инициализирован,
затем отправляет проект на авторизацию в совет.
@post проект отправлен на рассмотрение совета
@@ -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);
}
@@ -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
@@ -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 = `<div class="digital-document"><div style="text-align: center"><h1>ЗАЯВЛЕНИЕ</h1><h2>на инициализацию проекта</h2></div><p style="text-align: right">{{ meta.created_at }}, {{ coop.city }}</p><p>{% 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' %}:</p><p>{% 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 %}</p><h2>{% trans 'details_and_signatures_of_parties' %}</h2><p><strong>{% trans 'society' %}/{{ vars.full_abbr }} "{{ vars.name }}"/:</strong></p><p>ИНН {{ coop.details.inn }}, КПП {{ coop.details.kpp }}, ОГРН {{ coop.details.ogrn }}</p><p>{% trans 'legal_address' %}: {{ coop.full_address }}</p><p>{% trans 'contact_phone' %}: {{ coop.phone }}</p><p>{% trans 'email' %}: {{ coop.email }}</p><p>{% trans 'bank_account' %}: {{ coop.defaultBankAccount.account_number }}</p><p>{% trans 'bank_name' %}: {{ coop.defaultBankAccount.bank_name }}</p><p>{% trans 'bik' %}: {{ coop.defaultBankAccount.details.bik }}</p><p>{% trans 'correspondent_account' %}: {{ coop.defaultBankAccount.details.corr }}</p><p>{% trans 'chairman' %} {{ vars.full_abbr_genitive }} "{{ vars.name }}"</p><p>{{ coop.chairman.last_name }} {{ coop.chairman.first_name }} {{ coop.chairman.middle_name }}</p><p>{% trans 'signed_by_digital_signature' %}</p><p><strong>{% trans 'participant' %}:</strong></p><p>{{ user.full_name_or_short_name }}</p><p>{% trans 'contact_phone' %}: {{ user.phone }}</p><p>{% trans 'email' %}: {{ user.email }}</p><p>{% trans 'signed_by_digital_signature' %}</p></div><style>.digital-document {padding: 20px;white-space: pre-wrap;}</style>`
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: 'Москва',
},
}
@@ -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 = '<style> \nh1 {\nmargin: 0px; \ntext-align:center;\n}\nh3{\nmargin: 0px;\npadding-top: 15px;\n}\n.about {\npadding: 20px;\n}\n.about p{\nmargin: 0px;\n}\n.signature {\npadding-top: 20px;\n}\n.digital-document {\npadding: 20px;\nwhite-space: pre-wrap;\n}\n.subheader {\npadding-bottom: 20px; \n}\ntable {\n width: 100%;\n border-collapse: collapse;\n}\nth, td {\n border: 1px solid #ccc;\n padding: 8px;\n text-align: left;\n word-wrap: break-word; \n overflow-wrap: break-word; \n}\nth {\n background-color: #f4f4f4;\n width: 30%;\n}\n</style>\n\n<div class="digital-document"><h1 class="header">{% trans \'protocol_number\', decision.id %}</h1>\n<p style="text-align:center" class="subheader">{% trans \'council_meeting_name\' %} {{vars.full_abbr_genitive}} "{{vars.name}}"</p>\n<p style="text-align: right"> {{ meta.created_at }}, {{ coop.city }}</p>\n<table class="about">\n<tbody>\n<tr>\n<th>{% trans \'meeting_format\' %}</th>\n<td>{% trans \'meeting_format_value\' %}</td>\n</tr>\n<tr>\n<th>{% trans \'meeting_place\' %}</th>\n<td>{{ coop.full_address }}</td>\n</tr>\n<tr>\n<th>{% trans \'meeting_date\' %}</th>\n<td>{{ decision.date }}</td>\n</tr>\n<tr>\n<th>{% trans \'opening_time\' %}</th>\n<td>{{ decision.time }}</td>\n</tr>\n</tbody>\n</table>\n<h3>{% trans \'council_members\' %}</h3>\n<table>\n<tbody>\n{% for member in coop.members %}\n<tr>\n<th>{% if member.is_chairman %}{% trans \'chairman_of_the_council\' %}{% else %}{% trans \'member_of_the_council\' %}{% endif %}</th>\n<td>{{ member.last_name }} {{ member.first_name }} {{ member.middle_name }}</td>\n</tr>\n{% endfor %}\n</tbody>\n</table>\n<h3>{% trans \'meeting_legality\' %} </h3>\n<p>{% 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 %}.</p>\n<h3>{% trans \'agenda\' %}</h3>\n<table>\n<tbody>\n<tr>\n<th>№</th>\n<td>{% trans \'question\' %}</td>\n</tr>\n<tr>\n<th>1</th>\n<td>{% 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 %}</td>\n</tr>\n</tbody>\n</table>\n<h3>{% trans \'voting\' %}</h3>\n<p>{% trans \'vote_results\' %} </p><table>\n<tbody>\n<tr>\n<th>{% trans \'votes_for\' %}</th>\n<td>{{ decision.votes_for }}</td>\n</tr>\n<tr>\n<th>{% trans \'votes_against\' %}</th>\n<td>{{ decision.votes_against }}</td>\n</tr>\n<tr>\n<th>{% trans \'votes_abstained\' %}</th>\n<td>{{ decision.votes_abstained }}</td>\n</tr>\n</tbody>\n</table>\n<h3>{% trans \'decision_made\' %}</h3>\n<table>\n<tbody>\n<tr>\n<th>№</th>\n<td>{% trans \'decision\' %}</td>\n</tr>\n<tr>\n<th>1</th>\n<td>{% 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 %}</td>\n</tr>\n</tbody>\n</table>\n<hr>\n<p>{% trans \'closing_time\', decision.time %}</p>\n<div class="signature"><p>{% trans \'signature\' %}</p><p>{% trans \'chairman\' %} {{ coop.chairman.last_name }} {{ coop.chairman.first_name }} {{ coop.chairman.middle_name }}</p></div></div>\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: 'ВОСХОД',
},
}
@@ -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'
@@ -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<InitProjectStatement.Action> {
constructor(storage: MongoDBConnector) {
super(storage)
}
async generateDocument(data: InitProjectStatement.Action, options?: IGenerationOptions): Promise<IGeneratedDocument> {
let template: ITemplate<InitProjectStatement.Model>
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
}
}
@@ -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<InitProjectDecision.Action> {
constructor(storage: MongoDBConnector) {
super(storage)
}
async generateDocument(
data: InitProjectDecision.Action,
options?: IGenerationOptions,
): Promise<IGeneratedDocument> {
let template: ITemplate<InitProjectDecision.Model>
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
}
}
+2
View File
@@ -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'
@@ -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<Model> = {
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<Model> = {
title: Cooperative.Registry.InitProjectStatement.title,
description: Cooperative.Registry.InitProjectStatement.description,
model: Schema,
context: Cooperative.Registry.InitProjectStatement.context,
translations: Cooperative.Registry.InitProjectStatement.translations,
}
@@ -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<Model> = {
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<Model> = {
title: Cooperative.Registry.InitProjectDecision.title,
description: Cooperative.Registry.InitProjectDecision.description,
model: Schema,
context: Cooperative.Registry.InitProjectDecision.context,
translations: Cooperative.Registry.InitProjectDecision.translations,
}
@@ -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'
@@ -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,
+2
View File
@@ -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
@@ -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({