Правка сообщений с ошибками на фронте, расширение шаблонов общих собраний на повторяемость.
This commit is contained in:
@@ -1,3 +1,36 @@
|
||||
# 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
|
||||
|
||||
В этом релизе реализован новый стандарт передачи и хранения документов по блокчейну с поддержкой неограниченного количества подписей и их валидацией. Также внесены улучшения в интерфейс и исправлены ошибки.
|
||||
|
||||
@@ -244,6 +244,9 @@ input AnnualGeneralMeetingAgendaGenerateDocumentInput {
|
||||
"""Имя генератора, использованного для создания документа"""
|
||||
generator: String
|
||||
|
||||
"""Флаг повторного собрания"""
|
||||
is_repeated: Boolean!
|
||||
|
||||
"""Язык документа"""
|
||||
lang: String
|
||||
|
||||
@@ -298,6 +301,9 @@ input AnnualGeneralMeetingAgendaSignedMetaDocumentInput {
|
||||
"""Имя генератора, использованного для создания документа"""
|
||||
generator: String!
|
||||
|
||||
"""Флаг повторного собрания"""
|
||||
is_repeated: Boolean!
|
||||
|
||||
"""Язык документа"""
|
||||
lang: String!
|
||||
|
||||
@@ -524,6 +530,9 @@ input AnnualGeneralMeetingSovietDecisionGenerateDocumentInput {
|
||||
"""Имя генератора, использованного для создания документа"""
|
||||
generator: String
|
||||
|
||||
"""Флаг повторного собрания"""
|
||||
is_repeated: Boolean!
|
||||
|
||||
"""Язык документа"""
|
||||
lang: String
|
||||
|
||||
@@ -2342,6 +2351,9 @@ type Meet {
|
||||
"""Сертификат инициатора собрания"""
|
||||
initiator_certificate: UserCertificateUnion
|
||||
|
||||
"""Уровень собрания"""
|
||||
level: String!
|
||||
|
||||
"""Список пользователей, которые подписали уведомление"""
|
||||
notified_users: [String!]!
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ export class BranchDomainInteractor {
|
||||
|
||||
async selectBranch(data: SelectBranchInputDomainInterface): Promise<boolean> {
|
||||
// TODO move it to separate document domain service for validate
|
||||
const document = await this.documentRepository.findByHash(data.document.hash);
|
||||
const document = await this.documentRepository.findByHash(data.document.doc_hash);
|
||||
if (!document) throw new BadRequestException('Документ не найден');
|
||||
|
||||
if (data.document.meta.registry_id != Cooperative.Registry.SelectBranchStatement.registry_id)
|
||||
|
||||
+6
-1
@@ -5,7 +5,7 @@ import { MetaDocumentInputDTO } from '~/modules/document/dto/meta-document-input
|
||||
import { SignedDigitalDocumentInputDTO } from '~/modules/document/dto/signed-digital-document-input.dto';
|
||||
import type { ExcludeCommonProps } from '~/modules/document/types';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsArray, IsObject, ValidateNested } from 'class-validator';
|
||||
import { IsArray, IsBoolean, IsNotEmpty, IsObject, ValidateNested } from 'class-validator';
|
||||
import { AgendaMeetDTO } from '~/modules/meet/dto/agenda-meet.dto';
|
||||
import { AgendaGeneralMeetQuestionDTO } from '~/modules/meet/dto/agenda-general-meet-question.dto';
|
||||
|
||||
@@ -25,6 +25,11 @@ class BaseAnnualGeneralMeetingAgendaMetaDocumentInputDTO implements ExcludeCommo
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => AgendaGeneralMeetQuestionDTO)
|
||||
questions!: AgendaGeneralMeetQuestionDTO[];
|
||||
|
||||
@Field(() => Boolean, { description: 'Флаг повторного собрания' })
|
||||
@IsBoolean()
|
||||
@IsNotEmpty()
|
||||
is_repeated!: boolean;
|
||||
}
|
||||
|
||||
@InputType(`AnnualGeneralMeetingAgendaGenerateDocumentInput`)
|
||||
|
||||
+6
-11
@@ -4,7 +4,7 @@ import { GenerateMetaDocumentInputDTO } from '~/modules/document/dto/generate-me
|
||||
import { MetaDocumentInputDTO } from '~/modules/document/dto/meta-document-input.dto';
|
||||
import { SignedDigitalDocumentInputDTO } from '~/modules/document/dto/signed-digital-document-input.dto';
|
||||
import type { ExcludeCommonProps } from '~/modules/document/types';
|
||||
import { IsNotEmpty, IsNumber, IsString } from 'class-validator';
|
||||
import { IsBoolean, IsNotEmpty, IsNumber, IsString } from 'class-validator';
|
||||
|
||||
// интерфейс параметров для генерации
|
||||
type action = Cooperative.Registry.AnnualGeneralMeetingSovietDecision.Action;
|
||||
@@ -20,6 +20,11 @@ class BaseAnnualGeneralMeetingSovietDecisionDocumentInputDTO implements ExcludeC
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
meet_hash!: string;
|
||||
|
||||
@Field(() => Boolean, { description: 'Флаг повторного собрания' })
|
||||
@IsBoolean()
|
||||
@IsNotEmpty()
|
||||
is_repeated!: boolean;
|
||||
}
|
||||
|
||||
@InputType(`AnnualGeneralMeetingSovietDecisionGenerateDocumentInput`)
|
||||
@@ -31,16 +36,6 @@ export class AnnualGeneralMeetingSovietDecisionGenerateDocumentInputDTO
|
||||
implements action
|
||||
{
|
||||
registry_id!: number;
|
||||
|
||||
@Field(() => Number, { description: 'ID решения совета' })
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
decision_id!: number;
|
||||
|
||||
@Field(() => String, { description: 'Хеш собрания' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
meet_hash!: string;
|
||||
}
|
||||
|
||||
@InputType(`AnnualGeneralMeetingSovietDecisionSignedMetaDocumentInput`)
|
||||
|
||||
+5
-1
@@ -23,6 +23,7 @@ interface IAgendaQuestion {
|
||||
export interface Action extends IGenerate {
|
||||
meet: IAgendaMeet
|
||||
questions: IAgendaQuestion[]
|
||||
is_repeated: boolean
|
||||
}
|
||||
|
||||
export type Meta = IMetaDocument & Action
|
||||
@@ -33,19 +34,21 @@ export interface Model {
|
||||
coop: ICooperativeData
|
||||
meet: IAgendaMeet
|
||||
questions: IAgendaQuestion[]
|
||||
is_repeated: boolean
|
||||
user: ICommonUser
|
||||
vars: IVars
|
||||
}
|
||||
|
||||
export const title = 'Предложение повестки дня общего собрания пайщиков'
|
||||
export const description = 'Форма предложения повестки дня очередного/внеочередного общего собрания пайщиков потребительского кооператива'
|
||||
export const context = `<style> h1 {margin: 0px; text-align:center;}h3{margin: 0px;padding-top: 15px;text-align: center;}.about {padding: 20px;}.digital-document {padding: 20px;white-space: pre-wrap;}.subheader {padding-bottom: 20px; }table {width: 100%;border-collapse: collapse;}th, td {border: 1px solid #ccc;padding: 8px;text-align: left;word-wrap: break-word; overflow-wrap: break-word; }th {background-color: #f4f4f4;width: 30% !important;max-width: 30% !important;}</style><div class="digital-document"><div style="padding-bottom: 30px;"><h1 style="text-align:center">{% trans 'AGENDA_PROPOSAL_TITLE' %}</h1><p style="text-align:center">{% if meet.type == 'regular' %}{% trans 'ANNUAL_REGULAR' %}{% else %}{% trans 'ANNUAL_extra' %}{% endif %} {% trans 'GENERAL_MEETING_SHAREHOLDERS' %}</p><p style="text-align:center">{{vars.full_abbr_genitive}} «{{vars.name}}»</p><p style="text-align: right; padding-top: 20px">{{ coop.city }}, {{ meta.created_at }}</p></div><table class="about"><tbody><tr><th>{% trans 'MEETING_DATETIME_LABEL' %}</th><td>{{ meet.open_at_datetime }}</td></tr><tr><th>{% trans 'MEETING_FORMAT_LABEL' %}</th><td>{% trans 'MEETING_FORMAT_VALUE' %}</td></tr><tr><th>{% trans 'REGISTRATION_DATETIME' %}</th><td>{{ meet.open_at_datetime }}</td></tr><tr><th>{% trans 'VOTING_DEADLINE' %}</th><td>{% trans 'NO_LATER_THAN' %} {{ meet.close_at_datetime }}</td></tr></tbody></table><h3 style="padding-top: 30px; padding-bottom: 10px;">{% trans 'AGENDA_QUESTIONS' %}</h3><table><tbody>{% for question in questions %}<tr><th>{{ question.number }}.</th><td>{{ question.title }}</td></tr>{% if question.context %}<tr><th></th><td><em>{{ question.context }}</em></td></tr>{% endif %}{% endfor %}</tbody></table><h3 style="padding-top: 30px; padding-bottom: 10px;">{% trans 'PROJECT_DECISIONS' %}</h3><table><tbody>{% for question in questions %}<tr><th>{% trans 'PROJECT_DECISION_LABEL' %} {{ question.number }}</th><td>{{ question.decision }}</td></tr>{% endfor %}</tbody></table><div class="signature"><div style="padding-top: 20px;"><p>{{ user.full_name_or_short_name }}</p><p>{% trans 'SIGNED_DIGITALLY' %}</p></div></div></div>`
|
||||
export const context = `<style> h1 {margin: 0px; text-align:center;}h3{margin: 0px;padding-top: 15px;text-align: center;}.about {padding: 20px;}.digital-document {padding: 20px;white-space: pre-wrap;}.subheader {padding-bottom: 20px; }table {width: 100%;border-collapse: collapse;}th, td {border: 1px solid #ccc;padding: 8px;text-align: left;word-wrap: break-word; overflow-wrap: break-word; }th {background-color: #f4f4f4;width: 30% !important;max-width: 30% !important;}</style><div class="digital-document"><div style="padding-bottom: 30px;"><h1 style="text-align:center">{% trans 'AGENDA_PROPOSAL_TITLE' %}</h1><p style="text-align:center">{% if is_repeated %}{% trans 'REPEATED' %} {% endif %}{% if meet.type == 'regular' %}{% trans 'ANNUAL_REGULAR' %}{% else %}{% trans 'ANNUAL_extra' %}{% endif %} {% trans 'GENERAL_MEETING_SHAREHOLDERS' %}</p><p style="text-align:center">{{vars.full_abbr_genitive}} «{{vars.name}}»</p><p style="text-align: right; padding-top: 20px">{{ coop.city }}, {{ meta.created_at }}</p></div><table class="about"><tbody><tr><th>{% trans 'MEETING_DATETIME_LABEL' %}</th><td>{{ meet.open_at_datetime }}</td></tr><tr><th>{% trans 'MEETING_FORMAT_LABEL' %}</th><td>{% trans 'MEETING_FORMAT_VALUE' %}</td></tr><tr><th>{% trans 'REGISTRATION_DATETIME' %}</th><td>{{ meet.open_at_datetime }}</td></tr><tr><th>{% trans 'VOTING_DEADLINE' %}</th><td>{% trans 'NO_LATER_THAN' %} {{ meet.close_at_datetime }}</td></tr></tbody></table><h3 style="padding-top: 30px; padding-bottom: 10px;">{% trans 'AGENDA_QUESTIONS' %}</h3><table><tbody>{% for question in questions %}<tr><th>{{ question.number }}.</th><td>{{ question.title }}</td></tr>{% if question.context %}<tr><th></th><td><em>{{ question.context }}</em></td></tr>{% endif %}{% endfor %}</tbody></table><h3 style="padding-top: 30px; padding-bottom: 10px;">{% trans 'PROJECT_DECISIONS' %}</h3><table><tbody>{% for question in questions %}<tr><th>{% trans 'PROJECT_DECISION_LABEL' %} {{ question.number }}</th><td>{{ question.decision }}</td></tr>{% endfor %}</tbody></table><div class="signature"><div style="padding-top: 20px;"><p>{{ user.full_name_or_short_name }}</p><p>{% trans 'SIGNED_DIGITALLY' %}</p></div></div></div>`
|
||||
|
||||
export const translations = {
|
||||
ru: {
|
||||
AGENDA_PROPOSAL_TITLE: 'ПРЕДЛОЖЕНИЕ ПОВЕСТКИ',
|
||||
ANNUAL_REGULAR: 'ОЧЕРЕДНОГО',
|
||||
ANNUAL_extra: 'ВНЕОЧЕРЕДНОГО',
|
||||
REPEATED: 'ПОВТОРНОГО',
|
||||
GENERAL_MEETING_SHAREHOLDERS: 'СОБРАНИЯ ПАЙЩИКОВ',
|
||||
MEETING_DATETIME_LABEL: 'Дата и время проведения Собрания',
|
||||
MEETING_FORMAT_LABEL: 'Форма проведения собрания',
|
||||
@@ -94,4 +97,5 @@ export const exampleData = {
|
||||
full_abbr_genitive: 'Потребительского Кооператива',
|
||||
name: 'ВОСХОД',
|
||||
},
|
||||
is_repeated: false,
|
||||
}
|
||||
|
||||
+11
-5
@@ -10,6 +10,7 @@ export const registry_id = 301
|
||||
export interface Action extends IGenerate {
|
||||
decision_id: number
|
||||
meet_hash: string
|
||||
is_repeated: boolean
|
||||
}
|
||||
|
||||
export type Meta = IMetaDocument & Action
|
||||
@@ -20,13 +21,14 @@ export interface Model {
|
||||
coop: ICooperativeData
|
||||
decision: IDecisionData
|
||||
meet: IMeetExtended
|
||||
is_repeated: boolean
|
||||
questions: IMeetPoint[]
|
||||
vars: IVars
|
||||
}
|
||||
|
||||
export const title = 'Решение совета о созыве общего собрания пайщиков'
|
||||
export const description = 'Форма протокола собрания совета о созыве очередного/внеочередного общего собрания пайщиков потребительского кооператива'
|
||||
export const context = `<style> h1 {margin: 0px; text-align:center;}h3{margin: 0px;padding-top: 15px;text-align: center;}.about {padding: 20px;}.digital-document {padding: 20px;white-space: pre-wrap;}.subheader {padding-bottom: 20px; }table {width: 100%;border-collapse: collapse;}th, td {border: 1px solid #ccc;padding: 8px;text-align: left;word-wrap: break-word; overflow-wrap: break-word; }th {background-color: #f4f4f4;width: 30% !important;max-width: 30% !important;}</style><div class="digital-document"><h1 class="header">{% trans 'protocol_number', decision.id %}</h1><p style="text-align:center" class="subheader">{% trans 'council_meeting_name' %}</p><p style="text-align:center">{{ vars.full_abbr_genitive }} «{{ vars.name }}»</p><p style="text-align: right; padding-top: 20px;"> {{ coop.city }}, {{ meta.created_at }}</p><table class="about"><tbody><tr><th>{% trans 'meeting_format' %}</th><td>{% trans 'meeting_format_value' %}</td></tr><tr><th>{% trans 'meeting_date' %}</th><td>{{ decision.date }}</td></tr><tr><th>{% trans 'opening_time' %}</th><td>{{ decision.time }}</td></tr></tbody></table><h3 style="padding-top: 30px; padding-bottom: 10px;">{% trans 'council_members' %}</h3><table><tbody>{% for member in coop.members %}<tr><th>{% if member.is_chairman %}{% trans 'chairman_of_the_council' %}{% else %}{% trans 'member_of_the_council' %}{% endif %}</th><td>{{ member.last_name }} {{ member.first_name }} {{ member.middle_name }}{% if member.is_chairman %} ({% trans 'chairman_council_label' %}){% endif %}</td></tr>{% endfor %}</tbody></table><p>{% trans 'quorum_description', decision.voters_percent %}. {% trans 'quorum_available' %}. {% trans 'meeting_legal' %}.</p><h3 style="padding-top: 30px; padding-bottom: 10px;">{% trans 'agenda' %}</h3><p>{% trans 'agenda_item' %}. {% trans 'convene_meeting_description' %} {% if meet.type == 'regular' %}{% trans 'annual_regular' %}{% else %}{% trans 'annual_extra' %}{% endif %} {% if coop.is_branched %}{% trans 'general_meeting_form_branched' %}{% else %}{% trans 'general_meeting_form' %}{% endif %} {{ meet.close_at_datetime }} {% trans 'and_agenda_namely' %}:</p><table><tbody><tr><th>№</th><td>{% trans 'agenda_points' %}</td></tr>{% for question in questions %}<tr><th>{{ question.number }}.</th><td>{{ question.title }}</td></tr>{% if question.context %}<tr><th></th><td><em>{{ question.context }}</em></td></tr>{% endif %}{% endfor %}</tbody></table><p><strong>{% trans 'voting_results_label' %}</strong>: {% trans 'votes_for_label' %} – {{ decision.votes_for }}; {% trans 'votes_against_label' %} - {{ decision.votes_against }}; {% trans 'votes_abstained_label' %} - {{ decision.votes_abstained }}.</p><h3 style="padding-top: 30px; padding-bottom: 10px;">{% trans 'decision_made' %}</h3><p>{% trans 'convene_meeting_decision' %} {% if meet.type == 'regular' %}{% trans 'annual_regular' %}{% else %}{% trans 'annual_extra' %}{% endif %} {% if coop.is_branched %}{% trans 'general_meeting_form_branched' %}{% else %}{% trans 'general_meeting_form' %}{% endif %} {{ meet.close_at_datetime }} {% trans 'with_agenda' %}:</p><table><tbody><tr><th>№</th><td>{% trans 'agenda_points' %}</td></tr>{% for question in questions %}<tr><th>{{ question.number }}.</th><td>{{ question.title }}</td></tr>{% if question.context %}<tr><th></th><td><em>{{ question.context }}</em></td></tr>{% endif %}{% endfor %}</tbody></table><hr><p>{% trans 'closing_time', decision.time %}</p><div class="signature"><p>{% trans 'chairman_council_signature' %} {{ coop.chairman.last_name }} {{ coop.chairman.first_name }} {{ coop.chairman.middle_name }}</p><p>{% trans 'signed_digitally' %}</p></div></div>`
|
||||
export const context = `<style> h1 {margin: 0px; text-align:center;}h3{margin: 0px;padding-top: 15px;text-align: center;}.about {padding: 20px;}.digital-document {padding: 20px;white-space: pre-wrap;}.subheader {padding-bottom: 20px; }table {width: 100%;border-collapse: collapse;}th, td {border: 1px solid #ccc;padding: 8px;text-align: left;word-wrap: break-word; overflow-wrap: break-word; }th {background-color: #f4f4f4;width: 30% !important;max-width: 30% !important;}</style><div class="digital-document"><h1 class="header">{% trans 'protocol_number', decision.id %}</h1><p style="text-align:center" class="subheader">{% trans 'council_meeting_name' %}</p><p style="text-align:center">{{ vars.full_abbr_genitive }} «{{ vars.name }}»</p><p style="text-align: right; padding-top: 20px;"> {{ coop.city }}, {{ meta.created_at }}</p><table class="about"><tbody><tr><th>{% trans 'meeting_format' %}</th><td>{% trans 'meeting_format_value' %}</td></tr><tr><th>{% trans 'meeting_date' %}</th><td>{{ decision.date }}</td></tr><tr><th>{% trans 'opening_time' %}</th><td>{{ decision.time }}</td></tr></tbody></table><h3 style="padding-top: 30px; padding-bottom: 10px;">{% trans 'council_members' %}</h3><table><tbody>{% for member in coop.members %}<tr><th>{% if member.is_chairman %}{% trans 'chairman_of_the_council' %}{% else %}{% trans 'member_of_the_council' %}{% endif %}</th><td>{{ member.last_name }} {{ member.first_name }} {{ member.middle_name }}{% if member.is_chairman %} ({% trans 'chairman_council_label' %}){% endif %}</td></tr>{% endfor %}</tbody></table><p>{% trans 'quorum_description', decision.voters_percent %}. {% trans 'quorum_available' %}. {% trans 'meeting_legal' %}.</p><h3 style="padding-top: 30px; padding-bottom: 10px;">{% trans 'agenda' %}</h3><p>{% trans 'agenda_item' %}. {% if is_repeated %}{% trans 'repeat_convene_meeting_description' %}{% else %}{% trans 'convene_meeting_description' %}{% endif %} {% if meet.type == 'regular' %}{% trans 'annual_regular' %}{% else %}{% trans 'annual_extra' %}{% endif %} {% if coop.is_branched %}{% trans 'general_meeting_form_branched' %}{% else %}{% trans 'general_meeting_form' %}{% endif %} {{ meet.close_at_datetime }} {% if is_repeated %}{% trans 'and_previous_agenda' %}{% else %}{% trans 'and_agenda_namely' %}{% endif %}:</p><table><tbody><tr><th>№</th><td>{% trans 'agenda_points' %}</td></tr>{% for question in questions %}<tr><th>{{ question.number }}.</th><td>{{ question.title }}</td></tr>{% if question.context %}<tr><th></th><td><em>{{ question.context }}</em></td></tr>{% endif %}{% endfor %}</tbody></table><p><strong>{% trans 'voting_results_label' %}</strong>: {% trans 'votes_for_label' %} – {{ decision.votes_for }}; {% trans 'votes_against_label' %} - {{ decision.votes_against }}; {% trans 'votes_abstained_label' %} - {{ decision.votes_abstained }}.</p><h3 style="padding-top: 30px; padding-bottom: 10px;">{% trans 'decision_made' %}</h3><p>{% if is_repeated %}{% trans 'repeat_convene_meeting_decision' %}{% else %}{% trans 'convene_meeting_decision' %}{% endif %} {% if meet.type == 'regular' %}{% trans 'annual_regular' %}{% else %}{% trans 'annual_extra' %}{% endif %} {% if coop.is_branched %}{% trans 'general_meeting_form_branched' %}{% else %}{% trans 'general_meeting_form' %}{% endif %} {{ meet.close_at_datetime }} {% trans 'with_agenda' %}:</p><table><tbody><tr><th>№</th><td>{% trans 'agenda_points' %}</td></tr>{% for question in questions %}<tr><th>{{ question.number }}.</th><td>{{ question.title }}</td></tr>{% if question.context %}<tr><th></th><td><em>{{ question.context }}</em></td></tr>{% endif %}{% endfor %}</tbody></table><hr><p>{% trans 'closing_time', decision.time %}</p><div class="signature"><p>{% trans 'chairman_council_signature' %} {{ coop.chairman.last_name }} {{ coop.chairman.first_name }} {{ coop.chairman.middle_name }}</p><p>{% trans 'signed_digitally' %}</p></div></div>`
|
||||
|
||||
export const translations = {
|
||||
ru: {
|
||||
@@ -47,18 +49,21 @@ export const translations = {
|
||||
agenda_item: '1',
|
||||
agenda_points: 'Вопросы повестки дня',
|
||||
convene_meeting_description: 'О созыве',
|
||||
annual_regular: 'очередное',
|
||||
annual_extra: 'внеочередное',
|
||||
general_meeting_form: 'общее собрание пайщиков в заочной форме с датой завершения голосования',
|
||||
general_meeting_form_branched: 'общее собрание уполномоченных в заочной форме с датой завершения голосования',
|
||||
repeat_convene_meeting_description: 'О повторном созыве',
|
||||
annual_regular: 'очередного',
|
||||
annual_extra: 'внеочередного',
|
||||
general_meeting_form: 'общего собрания пайщиков в заочной форме с датой завершения голосования',
|
||||
general_meeting_form_branched: 'общего собрания уполномоченных в заочной форме с датой завершения голосования',
|
||||
voting_results_label: 'Голосовали',
|
||||
votes_for_label: '«За»',
|
||||
votes_against_label: '«Против»',
|
||||
votes_abstained_label: '«Воздержался»',
|
||||
decision_made: 'Решили',
|
||||
convene_meeting_decision: 'Созвать',
|
||||
repeat_convene_meeting_decision: 'Созвать повторно',
|
||||
with_agenda: 'со следующей повесткой дня',
|
||||
and_agenda_namely: 'и его повестки дня, а именно',
|
||||
and_previous_agenda: 'и его прежней повестке дня, а именно',
|
||||
closing_time: 'Время закрытия Собрания Совета: {0}',
|
||||
chairman_council_signature: 'Председатель Совета',
|
||||
signed_digitally: 'подписано электронной подписью',
|
||||
@@ -113,4 +118,5 @@ export const exampleData = {
|
||||
first_name: '',
|
||||
middle_name: '',
|
||||
},
|
||||
is_repeated: false,
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ export const useSessionStore = defineStore('session', (): ISessionStore => {
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
FailAlert(e.message);
|
||||
FailAlert(e);
|
||||
close()
|
||||
globalStore.logout()
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ form(@submit.prevent="submit").full-width
|
||||
|
||||
} catch (e: any) {
|
||||
loading.value = false
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ const next = async () => {
|
||||
|
||||
SuccessAlert('Установка произведена успешно')
|
||||
} catch(e: any){
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface ICreateMeetWithAgendaInput {
|
||||
open_at: string
|
||||
close_at: string
|
||||
username: string
|
||||
type: 'regular' | 'extra'
|
||||
agenda_points: {
|
||||
title: string
|
||||
context: string
|
||||
@@ -81,11 +82,12 @@ export async function createMeetWithAgenda(data: ICreateMeetWithAgendaInput): Pr
|
||||
coopname: data.coopname,
|
||||
username: data.username,
|
||||
meet: {
|
||||
type: 'regular', // По умолчанию очередное собрание
|
||||
type: data.type,
|
||||
open_at_datetime: openAtFormatted,
|
||||
close_at_datetime: closeAtFormatted
|
||||
},
|
||||
questions: questions
|
||||
questions: questions,
|
||||
is_repeated: false
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -5,9 +5,11 @@ div
|
||||
label="Создать общее собрание"
|
||||
@click="showCreateMeetDialog = true"
|
||||
)
|
||||
|
||||
CreateMeetForm(
|
||||
v-model="showCreateMeetDialog"
|
||||
:loading="isCreating"
|
||||
:is-chairman="isChairman"
|
||||
@create="handleCreate"
|
||||
)
|
||||
</template>
|
||||
@@ -22,6 +24,11 @@ import { useRoute } from 'vue-router'
|
||||
import { Notify } from 'quasar'
|
||||
import { FailAlert } from 'src/shared/api'
|
||||
|
||||
// Определяем пропсы
|
||||
defineProps<{
|
||||
isChairman: boolean
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const sessionStore = useSessionStore()
|
||||
const meetStore = useMeetStore()
|
||||
@@ -40,6 +47,7 @@ const handleCreate = async (formData: any) => {
|
||||
open_at: formData.open_at,
|
||||
close_at: formData.close_at,
|
||||
username: sessionStore.username,
|
||||
type: formData.type,
|
||||
agenda_points: formData.agenda_points
|
||||
})
|
||||
|
||||
|
||||
@@ -2,12 +2,23 @@
|
||||
q-dialog(:model-value="modelValue" @update:model-value="$emit('update:modelValue', $event)" persistent)
|
||||
q-card(style="min-width: 500px")
|
||||
q-card-section.row.items-center
|
||||
div.text-h6 Создать общее собрание
|
||||
div.text-h6 Создать общее собрание {{ isChairman ? '(Председатель)' : '(Член совета)' }}
|
||||
q-space
|
||||
q-btn(icon="close" flat round dense v-close-popup @click="$emit('update:modelValue', false)")
|
||||
|
||||
q-card-section
|
||||
q-form(@submit="handleSubmit")
|
||||
// Выбор типа собрания
|
||||
q-select(
|
||||
v-model="formData.type"
|
||||
:options="meetTypeOptions"
|
||||
label="Тип собрания"
|
||||
emit-value
|
||||
map-options
|
||||
:rules="[val => !!val || 'Обязательное поле']"
|
||||
dense
|
||||
)
|
||||
|
||||
q-input(
|
||||
v-model="formData.presider"
|
||||
label="Имя аккаунта председателя собрания"
|
||||
@@ -88,18 +99,25 @@ q-dialog(:model-value="modelValue" @update:model-value="$emit('update:modelValue
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import { reactive, computed, onMounted } from 'vue'
|
||||
import { useAgendaPoints } from 'src/shared/hooks/useAgendaPoints'
|
||||
import { getCurrentLocalDateForForm, convertLocalDateToUTC, getTimezoneLabel, getFutureDateForForm } from 'src/shared/lib/utils/dates/timezone'
|
||||
import { env } from 'src/shared/config/Environment'
|
||||
import { useSessionStore } from 'src/entities/Session';
|
||||
import { useSystemStore } from 'src/entities/System/model';
|
||||
|
||||
defineProps<{
|
||||
// Определяем пропсы один раз
|
||||
const props = defineProps<{
|
||||
modelValue: boolean,
|
||||
loading?: boolean
|
||||
loading?: boolean,
|
||||
isChairman: boolean
|
||||
}>()
|
||||
|
||||
// Отладочная информация при монтировании
|
||||
onMounted(() => {
|
||||
console.log('CreateMeetForm mounted, isChairman:', props.isChairman)
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'create', data: any): void
|
||||
@@ -110,6 +128,30 @@ const timezoneLabel = getTimezoneLabel()
|
||||
const session = useSessionStore()
|
||||
const system = useSystemStore()
|
||||
|
||||
// Делаем isChairman доступным в шаблоне напрямую для отладки
|
||||
const isChairman = computed(() => {
|
||||
console.log('Вычисляемое свойство isChairman:', props.isChairman)
|
||||
return props.isChairman
|
||||
})
|
||||
|
||||
// Опции для выбора типа собрания в зависимости от роли
|
||||
const meetTypeOptions = computed(() => {
|
||||
// Выведем отладочную информацию, чтобы увидеть значение флага
|
||||
console.log('isChairman в форме для опций:', props.isChairman)
|
||||
|
||||
// Председатель может выбирать любой тип
|
||||
if (props.isChairman) {
|
||||
return [
|
||||
{ label: 'Очередное собрание', value: 'regular' },
|
||||
{ label: 'Внеочередное собрание', value: 'extra' }
|
||||
]
|
||||
}
|
||||
// Члены совета могут создавать только внеочередное
|
||||
return [
|
||||
{ label: 'Внеочередное собрание', value: 'extra' }
|
||||
]
|
||||
})
|
||||
|
||||
// Форма для создания собрания
|
||||
const formData = reactive(
|
||||
env.NODE_ENV === 'development'
|
||||
@@ -121,6 +163,7 @@ const formData = reactive(
|
||||
open_at: getCurrentLocalDateForForm(0.17), // ~10 секунд от текущего времени
|
||||
close_at: getCurrentLocalDateForForm(2), // 2 минуты от текущего времени
|
||||
username: session.username,
|
||||
type: props.isChairman ? 'regular' : 'extra', // Тип по умолчанию в зависимости от роли
|
||||
agenda_points: [
|
||||
{
|
||||
title: 'Тестовый вопрос',
|
||||
@@ -137,6 +180,7 @@ const formData = reactive(
|
||||
open_at: getFutureDateForForm(15, 6, 0), // через 15 дней, 6:00
|
||||
close_at: getFutureDateForForm(18, 12, 0), // через 18 дней, 12:00
|
||||
username: session.username,
|
||||
type: props.isChairman ? 'regular' : 'extra', // Тип по умолчанию в зависимости от роли
|
||||
agenda_points: [
|
||||
// пустой массив, либо можно добавить пустой объект для вёрстки
|
||||
],
|
||||
|
||||
@@ -36,7 +36,7 @@ export function useGenerateSovietDecisionOnAnnualMeet() {
|
||||
if (!data.meet_hash) {
|
||||
throw new Error('Необходимо указать хеш собрания (meet_hash)')
|
||||
}
|
||||
|
||||
console.log('data', data)
|
||||
const { [Mutations.Meet.GenerateSovietDecisionOnAnnualMeetDocument.name]: result } = await client.Mutation(
|
||||
Mutations.Meet.GenerateSovietDecisionOnAnnualMeetDocument.mutation,
|
||||
{
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface IRestartMeetWithProposalInput {
|
||||
context: string
|
||||
decision: string
|
||||
}[]
|
||||
type: string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,11 +82,12 @@ export async function restartMeetWithProposal(data: IRestartMeetWithProposalInpu
|
||||
coopname: data.coopname,
|
||||
username: data.username,
|
||||
meet: {
|
||||
type: 'regular', // По умолчанию очередное собрание
|
||||
type: data.type,
|
||||
open_at_datetime: openAtFormatted,
|
||||
close_at_datetime: closeAtFormatted
|
||||
},
|
||||
questions: questions
|
||||
questions: questions,
|
||||
is_repeated: true
|
||||
})
|
||||
|
||||
// Подписываем документ
|
||||
@@ -145,13 +147,24 @@ export const useRestartMeet = (
|
||||
if (!meetStore.currentMeet) return false
|
||||
processingRef.value = true
|
||||
try {
|
||||
// Выводим полную структуру для отладки
|
||||
console.log('Полные данные о собрании:', meetStore.currentMeet)
|
||||
|
||||
// Получаем тип собрания из текущего объекта meet
|
||||
// По умолчанию используем 'regular' если тип не найден
|
||||
const originalMeetType = (meetStore.currentMeet.processing?.meet.proposal?.document.meta as any).meet.type
|
||||
|
||||
// Выводим для отладки
|
||||
console.log('Тип собрания для перезапуска:', originalMeetType)
|
||||
|
||||
const result = await restartMeetWithProposal({
|
||||
coopname: info.coopname,
|
||||
hash: meetStore.currentMeet.hash,
|
||||
username: sessionStore.username,
|
||||
new_open_at: data.new_open_at,
|
||||
new_close_at: data.new_close_at,
|
||||
agenda_points: data.agenda_points
|
||||
agenda_points: data.agenda_points,
|
||||
type: originalMeetType // Передаем оригинальный тип собрания
|
||||
})
|
||||
|
||||
await meetStore.loadMeet({ coopname: info.coopname, hash: result.processing?.hash as string})
|
||||
|
||||
+1
-1
@@ -159,7 +159,7 @@ const handlerSubmit = async (): Promise<void> => {
|
||||
} catch (e: any) {
|
||||
showDialog.value = false
|
||||
isSubmitting.value = false
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ const handlerSubmit = async (): Promise<void> => {
|
||||
} catch (e: any) {
|
||||
showDialog.value = false
|
||||
isSubmitting.value = false
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ const accept = async () => {
|
||||
|
||||
SuccessAlert('Заявка принята')
|
||||
} catch (e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -30,7 +30,7 @@ q-btn(color="red" @click="cancel") отменить поставку
|
||||
} as ICancelRequest)
|
||||
SuccessAlert('Заявка отменена')
|
||||
} catch (e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ q-btn(color="green" @click="complete") завершить поставку
|
||||
|
||||
SuccessAlert('Подтверждение принято')
|
||||
} catch (e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ q-btn(color="green" @click="confirmRecieve") выдал имущество
|
||||
|
||||
SuccessAlert('Выдача имущества подтверждена')
|
||||
} catch (e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ q-btn(color="green" @click="supply") Подписать акт передачи
|
||||
|
||||
SuccessAlert('Имущество принято в кооператив')
|
||||
} catch (e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@
|
||||
SuccessAlert('Заказ НЕ создан')
|
||||
router.push({ name: 'marketplace-user-supplies' }) //TODO роутинг
|
||||
} catch (e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ const handlerSubmit = async () => {
|
||||
SuccessAlert('Объявление успешно создано')
|
||||
} catch (e: any) {
|
||||
console.log(e)
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ q-btn(color="green" @click="delivered") Подтвердить доставку
|
||||
|
||||
SuccessAlert('Подтверждение принято')
|
||||
} catch (e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ q-btn(color="red" @click="dispute") вернуть по гарантии
|
||||
|
||||
SuccessAlert('Спор открыт')
|
||||
} catch (e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -30,7 +30,7 @@ q-btn(@click="publish") опубликовать
|
||||
})
|
||||
SuccessAlert('Заявка опубликована')
|
||||
} catch (e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ q-btn(color="green" @click="recieve") получил имущество
|
||||
|
||||
SuccessAlert('Подтверждение принято')
|
||||
} catch (e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ q-btn(color="green" @click="supply") Подписать акт приёма
|
||||
} as ISupplyOnRequest)
|
||||
SuccessAlert('Ваша подпись принята')
|
||||
} catch (e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ q-btn(@click="unpublish") снять с публикации
|
||||
})
|
||||
SuccessAlert('Заявка снята с публикации')
|
||||
} catch (e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -83,7 +83,7 @@ const addNow = async () => {
|
||||
|
||||
clear()
|
||||
} catch(e: any){
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ const submit = async () => {
|
||||
} catch (e: any) {
|
||||
console.error(e)
|
||||
loading.value = false
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ const handlerSubmit = async (): Promise<void> => {
|
||||
} catch (e: any) {
|
||||
console.log('e.message', e.message)
|
||||
isSubmitting.value = false
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ const handlerSubmit = async (): Promise<void> => {
|
||||
} catch (e: any) {
|
||||
// console.log('e.message', e.message)
|
||||
isSubmitting.value = false
|
||||
// FailAlert(e.message)
|
||||
// FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ div
|
||||
div.row.justify-center
|
||||
div.col-12
|
||||
div.row.q-pa-md(v-if="canCreateMeet")
|
||||
CreateMeet
|
||||
CreateMeet(:is-chairman="isChairman")
|
||||
MeetCardsList(
|
||||
:meets="meets"
|
||||
:loading="loading"
|
||||
|
||||
@@ -77,7 +77,7 @@ const setAccount = async () => {
|
||||
} catch (e: any) {
|
||||
store.goTo('SetUserData')
|
||||
console.error(e)
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -70,7 +70,7 @@ const loadStatement = async (): Promise<void> => {
|
||||
isLoading.value = false
|
||||
} catch (e: any) {
|
||||
isLoading.value = false
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
const back = () => {
|
||||
|
||||
@@ -91,8 +91,6 @@ watch(
|
||||
() => {
|
||||
if (store.step >= steps.GenerateAccount && store.step < steps.WaitingRegistration) {
|
||||
useInitWalletProcess().run()
|
||||
// currentUser.loadProfile(username.value, info.coopname)
|
||||
// wallet.loadUserWallet({coopname: info.coopname, username: username.value})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
+2
-2
@@ -99,7 +99,7 @@
|
||||
loadCooperatives()
|
||||
SuccessAlert('Кооператив активирован')
|
||||
} catch(e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
loadCooperatives()
|
||||
SuccessAlert('Кооператив заблокирован')
|
||||
} catch(e: any) {
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,10 +135,15 @@ export function useDecisionProcessor() {
|
||||
}
|
||||
else if (registry_id === Cooperative.Registry.AnnualGeneralMeetingSovietDecision.registry_id) {
|
||||
const { generateSovietDecisionOnAnnualMeet } = useGenerateSovietDecisionOnAnnualMeet()
|
||||
const unparsedDocumentMeta = row.table.statement.meta == '' ? '{}' : row.table.statement.meta
|
||||
const parsedDocumentMeta = JSON.parse(unparsedDocumentMeta) as Cooperative.Registry.AnnualGeneralMeetingAgenda.Action
|
||||
const is_repeated = parsedDocumentMeta.is_repeated || false
|
||||
|
||||
document = await generateSovietDecisionOnAnnualMeet({
|
||||
username: username,
|
||||
decision_id: decision_id,
|
||||
meet_hash: row.table.hash as string
|
||||
meet_hash: row.table.hash as string,
|
||||
is_repeated
|
||||
})
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useSelectBranch } from 'src/features/Branch/SelectBranch'
|
||||
import { useSystemStore } from 'src/entities/System/model'
|
||||
import { useSessionStore } from 'src/entities/Session'
|
||||
@@ -24,9 +24,16 @@ export function useSelectBranchProcess() {
|
||||
|
||||
const branches = computed(() => branchStore.publicBranches)
|
||||
|
||||
if (session.isAuth) {
|
||||
branchStore.loadPublicBranches({ coopname: system.info.coopname })
|
||||
}
|
||||
// Вотчер на авторизацию
|
||||
watch(
|
||||
() => session.isAuth,
|
||||
(isAuth, wasAuth) => {
|
||||
if (isAuth && !wasAuth) {
|
||||
branchStore.loadPublicBranches({ coopname: system.info.coopname })
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const next = async () => {
|
||||
isLoading.value = true
|
||||
|
||||
@@ -15,13 +15,14 @@ export function createBlockchainGetError(name: string) {
|
||||
|
||||
export function handleException(e: unknown): void {
|
||||
if (e instanceof Error) {
|
||||
FailAlert(e.message);
|
||||
FailAlert(e);
|
||||
} else {
|
||||
FailAlert('Произошла неизвестная ошибка');
|
||||
}
|
||||
}
|
||||
|
||||
export function extractGraphQLErrorMessages(error: unknown): string {
|
||||
if (typeof error === 'string') return error;
|
||||
if (!error || typeof error !== 'object') return 'Unknown error';
|
||||
|
||||
// Проверяем, если ошибка уже является массивом
|
||||
|
||||
@@ -16,7 +16,7 @@ const loadFunds = async () => {
|
||||
try {
|
||||
await coop.loadFunds(info.coopname)
|
||||
} catch(e: any){
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ const delFund = async(fund: FundContract.Tables.AccumulatedFunds.IAccumulatedFun
|
||||
SuccessAlert('Фонд успешно удалён')
|
||||
|
||||
} catch (e: any){
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ const saveFund = async (fund: FundContract.Tables.AccumulatedFunds.IAccumulatedF
|
||||
|
||||
SuccessAlert('Фонд успешно обновлён')
|
||||
} catch(e: any){
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
await coop.loadFunds(info.coopname)
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ const addFund = async() => {
|
||||
SuccessAlert('Фонд успешно создан')
|
||||
} catch(e: any){
|
||||
localShowAdd.value = false
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ const addFund = async() => {
|
||||
SuccessAlert('Фонд успешно создан')
|
||||
} catch(e: any){
|
||||
localShowAdd.value = false
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ const loadFunds = async () => {
|
||||
try {
|
||||
await coop.loadFunds(info.coopname)
|
||||
} catch(e: any){
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ const delFund = async(fund: FundContract.Tables.AccumulatedFunds.IAccumulatedFun
|
||||
SuccessAlert('Фонд успешно удалён')
|
||||
|
||||
} catch (e: any){
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ const saveFund = async (fund: FundContract.Tables.AccumulatedFunds.IAccumulatedF
|
||||
|
||||
SuccessAlert('Фонд успешно обновлён')
|
||||
} catch(e: any){
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
await coop.loadFunds(info.coopname)
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ const submit = async () => {
|
||||
loading.value = false
|
||||
} catch (e: any) {
|
||||
loading.value = false
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ const finish = async () => {
|
||||
|
||||
} catch (e: any) {
|
||||
loading.value = false
|
||||
FailAlert(e.message)
|
||||
FailAlert(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ export class Factory extends DocFactory<AnnualGeneralMeetingAgenda.Action> {
|
||||
meet,
|
||||
questions,
|
||||
user,
|
||||
is_repeated: data.is_repeated,
|
||||
}
|
||||
|
||||
await super.validate(combinedData, template.model)
|
||||
|
||||
@@ -21,7 +21,7 @@ export class Factory extends DocFactory<AnnualGeneralMeetingSovietDecision.Actio
|
||||
else {
|
||||
template = await this.getTemplate(DraftContract.contractName.production, AnnualGeneralMeetingSovietDecision.registry_id, data.block_num)
|
||||
}
|
||||
|
||||
console.log('data on gerate soviet decision', data)
|
||||
const meta: IMetaDocument = await super.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)
|
||||
@@ -43,6 +43,7 @@ export class Factory extends DocFactory<AnnualGeneralMeetingSovietDecision.Actio
|
||||
decision,
|
||||
meet,
|
||||
questions,
|
||||
is_repeated: data.is_repeated,
|
||||
}
|
||||
|
||||
await super.validate(combinedData, template.model)
|
||||
|
||||
@@ -25,8 +25,9 @@ export const Schema: JSONSchemaType<Model> = {
|
||||
type: 'array',
|
||||
items: AgendaQuestionSchema,
|
||||
},
|
||||
is_repeated: { type: 'boolean' },
|
||||
},
|
||||
required: ['meta', 'coop', 'vars', 'meet', 'user', 'questions'],
|
||||
required: ['meta', 'coop', 'vars', 'meet', 'user', 'questions', 'is_repeated'],
|
||||
additionalProperties: true,
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,9 @@ export const Schema: JSONSchemaType<Model> = {
|
||||
type: 'array',
|
||||
items: MeetPointSchema,
|
||||
},
|
||||
is_repeated: { type: 'boolean' },
|
||||
},
|
||||
required: ['meta', 'coop', 'vars', 'decision', 'meet', 'questions'],
|
||||
required: ['meta', 'coop', 'vars', 'decision', 'meet', 'questions', 'is_repeated'],
|
||||
additionalProperties: true,
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,18 @@ describe('тест генератора документов общих собр
|
||||
username: 'individual',
|
||||
meet: testMeetData,
|
||||
questions: testQuestions,
|
||||
is_repeated: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('генерируем предложение повестки дня повторного собрания (300 с is_repeated)', async () => {
|
||||
await testDocumentGeneration<Cooperative.Registry.AnnualGeneralMeetingAgenda.Action>({
|
||||
registry_id: Cooperative.Registry.AnnualGeneralMeetingAgenda.registry_id,
|
||||
coopname: 'voskhod',
|
||||
username: 'individual',
|
||||
meet: testMeetData,
|
||||
questions: testQuestions,
|
||||
is_repeated: true,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -57,6 +69,18 @@ describe('тест генератора документов общих собр
|
||||
username: 'ant',
|
||||
decision_id: _testDecisionId,
|
||||
meet_hash: _testMeetHash,
|
||||
is_repeated: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('генерируем решение совета о повторном созыве собрания (301 с is_repeated)', async () => {
|
||||
await testDocumentGeneration<Cooperative.Registry.AnnualGeneralMeetingSovietDecision.Action>({
|
||||
registry_id: Cooperative.Registry.AnnualGeneralMeetingSovietDecision.registry_id,
|
||||
coopname: 'voskhod',
|
||||
username: 'ant',
|
||||
decision_id: _testDecisionId,
|
||||
meet_hash: _testMeetHash,
|
||||
is_repeated: true,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -506,7 +506,7 @@ export async function preLoading() {
|
||||
await generator.save('paymentMethod', paymentData4)
|
||||
|
||||
// Добавляем данные для тестирования собраний
|
||||
const testMeetHash = 'test_meet_hash_12345'
|
||||
const testMeetHash = ('test_meet_hash_12345').toUpperCase()
|
||||
const testMeetId = '1'
|
||||
|
||||
// Добавляем данные собрания в таблицу meets
|
||||
@@ -529,7 +529,7 @@ export async function preLoading() {
|
||||
created_at: '2024-12-15T00:00:00.000',
|
||||
open_at: '2024-12-20T10:00:00.000',
|
||||
close_at: '2024-12-21T18:00:00.000',
|
||||
quorum_percent: '51',
|
||||
quorum_percent: 51,
|
||||
signed_ballots: '0',
|
||||
current_quorum_percent: '0',
|
||||
cycle: '1',
|
||||
|
||||
@@ -10,14 +10,17 @@ export async function testDocumentGeneration<T extends IGenerate = IGenerate>(
|
||||
) {
|
||||
const document: IGeneratedDocument = await generator.generate(params)
|
||||
|
||||
const filename1 = `${document.meta.title}-${document.meta.username}.pdf`
|
||||
// Генерируем уникальный суффикс для имен файлов
|
||||
const uniqueSuffix = Date.now().toString(36) + Math.random().toString(36).substring(2, 7)
|
||||
|
||||
const filename1 = `${document.meta.title}-${document.meta.username}-${uniqueSuffix}.pdf`
|
||||
await saveBufferToDisk(document.binary, filename1)
|
||||
|
||||
const regenerated_document: IGeneratedDocument = await generator.generate({
|
||||
...document.meta,
|
||||
})
|
||||
|
||||
const filename2 = `${document.meta.title}-${document.meta.username}-regenerated.pdf`
|
||||
const filename2 = `${document.meta.title}-${document.meta.username}-regenerated-${uniqueSuffix}.pdf`
|
||||
await saveBufferToDisk(regenerated_document.binary, filename2)
|
||||
|
||||
expect(document.meta).toEqual(regenerated_document.meta)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"local": "028_edit_selected_draft.ts",
|
||||
"local": "031_edit_selected_draft.ts",
|
||||
"test": "029_create_general_meet_drafts.ts",
|
||||
"prod": "029_create_general_meet_drafts.ts"
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Cooperative, DraftContract, SovietContract } from 'cooptypes';
|
||||
import { Registry } from '@coopenomics/factory';
|
||||
import type { Migration } from '../src/migration_interface';
|
||||
import { api } from '../src/eos';
|
||||
import { editDraft } from '../src/utils/editDraft';
|
||||
import { editTranslation } from '../src/utils/editTranslation';
|
||||
|
||||
export class InitialMigration implements Migration {
|
||||
async run(): Promise<void> {
|
||||
const ids = [300, 301];
|
||||
|
||||
for (const id of ids) {
|
||||
try {
|
||||
const drafts = await api.rpc.get_table_rows({
|
||||
json: true,
|
||||
code: 'draft',
|
||||
scope: 'draft',
|
||||
table: 'drafts',
|
||||
});
|
||||
|
||||
const target = drafts.rows.find((draft) => draft.registry_id === id);
|
||||
if (!target) {
|
||||
console.warn(`Draft with registry_id ${id} not found.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const template = Registry[id as unknown as keyof typeof Registry];
|
||||
if (!template) {
|
||||
console.warn(`Template for registry_id ${id} not found in Registry.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await editDraft({
|
||||
scope: DraftContract.contractName.production,
|
||||
username: 'eosio',
|
||||
registry_id: id,
|
||||
title: template.Template.title,
|
||||
description: template.Template.description,
|
||||
context: template.Template.context,
|
||||
model: JSON.stringify(template.Template.model),
|
||||
});
|
||||
|
||||
await editTranslation({
|
||||
scope: DraftContract.contractName.production,
|
||||
username: 'eosio',
|
||||
translate_id: target.default_translation_id,
|
||||
data: JSON.stringify(template.Template.translations.ru),
|
||||
});
|
||||
|
||||
console.log(`Update for registry_id ${id} successful`);
|
||||
} catch (error) {
|
||||
console.error(`Error during migration for registry_id ${id}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
import { Cooperative, DraftContract, SovietContract } from 'cooptypes';
|
||||
import { Registry } from '@coopenomics/factory';
|
||||
import type { Migration } from '../src/migration_interface';
|
||||
import { api } from '../src/eos';
|
||||
import { createDraft } from '../src/utils/createDraft';
|
||||
import type { Migration } from '../../src/migration_interface';
|
||||
import { api } from '../../src/eos';
|
||||
import { createDraft } from '../../src/utils/createDraft';
|
||||
|
||||
export class InitialMigration implements Migration {
|
||||
async run(): Promise<void> {
|
||||
@@ -46,6 +46,7 @@ const rawMeetSelector = {
|
||||
decision1: rawDocumentAggregateSelector,
|
||||
decision2: rawDocumentAggregateSelector,
|
||||
notified_users: true,
|
||||
level: true,
|
||||
}
|
||||
|
||||
// Селектор для MeetProcessing
|
||||
|
||||
@@ -1084,6 +1084,7 @@ export const ReturnTypes: Record<string,any> = {
|
||||
id:"Float",
|
||||
initiator:"String",
|
||||
initiator_certificate:"UserCertificateUnion",
|
||||
level:"String",
|
||||
notified_users:"String",
|
||||
open_at:"DateTime",
|
||||
presider:"String",
|
||||
|
||||
@@ -1096,6 +1096,8 @@ export type ValueTypes = {
|
||||
created_at?: string | undefined | null | Variable<any, string>,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator?: string | undefined | null | Variable<any, string>,
|
||||
/** Флаг повторного собрания */
|
||||
is_repeated: boolean | Variable<any, string>,
|
||||
/** Язык документа */
|
||||
lang?: string | undefined | null | Variable<any, string>,
|
||||
/** Ссылки, связанные с документом */
|
||||
@@ -1134,6 +1136,8 @@ export type ValueTypes = {
|
||||
created_at: string | Variable<any, string>,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator: string | Variable<any, string>,
|
||||
/** Флаг повторного собрания */
|
||||
is_repeated: boolean | Variable<any, string>,
|
||||
/** Язык документа */
|
||||
lang: string | Variable<any, string>,
|
||||
/** Ссылки, связанные с документом */
|
||||
@@ -1290,6 +1294,8 @@ export type ValueTypes = {
|
||||
decision_id: number | Variable<any, string>,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator?: string | undefined | null | Variable<any, string>,
|
||||
/** Флаг повторного собрания */
|
||||
is_repeated: boolean | Variable<any, string>,
|
||||
/** Язык документа */
|
||||
lang?: string | undefined | null | Variable<any, string>,
|
||||
/** Ссылки, связанные с документом */
|
||||
@@ -2561,6 +2567,8 @@ export type ValueTypes = {
|
||||
initiator?:boolean | `@${string}`,
|
||||
/** Сертификат инициатора собрания */
|
||||
initiator_certificate?:ValueTypes["UserCertificateUnion"],
|
||||
/** Уровень собрания */
|
||||
level?:boolean | `@${string}`,
|
||||
/** Список пользователей, которые подписали уведомление */
|
||||
notified_users?:boolean | `@${string}`,
|
||||
/** Дата открытия собрания */
|
||||
@@ -4298,6 +4306,8 @@ export type ResolverInputTypes = {
|
||||
created_at?: string | undefined | null,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator?: string | undefined | null,
|
||||
/** Флаг повторного собрания */
|
||||
is_repeated: boolean,
|
||||
/** Язык документа */
|
||||
lang?: string | undefined | null,
|
||||
/** Ссылки, связанные с документом */
|
||||
@@ -4336,6 +4346,8 @@ export type ResolverInputTypes = {
|
||||
created_at: string,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator: string,
|
||||
/** Флаг повторного собрания */
|
||||
is_repeated: boolean,
|
||||
/** Язык документа */
|
||||
lang: string,
|
||||
/** Ссылки, связанные с документом */
|
||||
@@ -4492,6 +4504,8 @@ export type ResolverInputTypes = {
|
||||
decision_id: number,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator?: string | undefined | null,
|
||||
/** Флаг повторного собрания */
|
||||
is_repeated: boolean,
|
||||
/** Язык документа */
|
||||
lang?: string | undefined | null,
|
||||
/** Ссылки, связанные с документом */
|
||||
@@ -5763,6 +5777,8 @@ export type ResolverInputTypes = {
|
||||
initiator?:boolean | `@${string}`,
|
||||
/** Сертификат инициатора собрания */
|
||||
initiator_certificate?:ResolverInputTypes["UserCertificateUnion"],
|
||||
/** Уровень собрания */
|
||||
level?:boolean | `@${string}`,
|
||||
/** Список пользователей, которые подписали уведомление */
|
||||
notified_users?:boolean | `@${string}`,
|
||||
/** Дата открытия собрания */
|
||||
@@ -7496,6 +7512,8 @@ export type ModelTypes = {
|
||||
created_at?: string | undefined | null,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator?: string | undefined | null,
|
||||
/** Флаг повторного собрания */
|
||||
is_repeated: boolean,
|
||||
/** Язык документа */
|
||||
lang?: string | undefined | null,
|
||||
/** Ссылки, связанные с документом */
|
||||
@@ -7534,6 +7552,8 @@ export type ModelTypes = {
|
||||
created_at: string,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator: string,
|
||||
/** Флаг повторного собрания */
|
||||
is_repeated: boolean,
|
||||
/** Язык документа */
|
||||
lang: string,
|
||||
/** Ссылки, связанные с документом */
|
||||
@@ -7690,6 +7710,8 @@ export type ModelTypes = {
|
||||
decision_id: number,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator?: string | undefined | null,
|
||||
/** Флаг повторного собрания */
|
||||
is_repeated: boolean,
|
||||
/** Язык документа */
|
||||
lang?: string | undefined | null,
|
||||
/** Ссылки, связанные с документом */
|
||||
@@ -8931,6 +8953,8 @@ export type ModelTypes = {
|
||||
initiator: string,
|
||||
/** Сертификат инициатора собрания */
|
||||
initiator_certificate?: ModelTypes["UserCertificateUnion"] | undefined | null,
|
||||
/** Уровень собрания */
|
||||
level: string,
|
||||
/** Список пользователей, которые подписали уведомление */
|
||||
notified_users: Array<string>,
|
||||
/** Дата открытия собрания */
|
||||
@@ -10701,6 +10725,8 @@ export type GraphQLTypes = {
|
||||
created_at?: string | undefined | null,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator?: string | undefined | null,
|
||||
/** Флаг повторного собрания */
|
||||
is_repeated: boolean,
|
||||
/** Язык документа */
|
||||
lang?: string | undefined | null,
|
||||
/** Ссылки, связанные с документом */
|
||||
@@ -10739,6 +10765,8 @@ export type GraphQLTypes = {
|
||||
created_at: string,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator: string,
|
||||
/** Флаг повторного собрания */
|
||||
is_repeated: boolean,
|
||||
/** Язык документа */
|
||||
lang: string,
|
||||
/** Ссылки, связанные с документом */
|
||||
@@ -10895,6 +10923,8 @@ export type GraphQLTypes = {
|
||||
decision_id: number,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator?: string | undefined | null,
|
||||
/** Флаг повторного собрания */
|
||||
is_repeated: boolean,
|
||||
/** Язык документа */
|
||||
lang?: string | undefined | null,
|
||||
/** Ссылки, связанные с документом */
|
||||
@@ -12167,6 +12197,8 @@ export type GraphQLTypes = {
|
||||
initiator: string,
|
||||
/** Сертификат инициатора собрания */
|
||||
initiator_certificate?: GraphQLTypes["UserCertificateUnion"] | undefined | null,
|
||||
/** Уровень собрания */
|
||||
level: string,
|
||||
/** Список пользователей, которые подписали уведомление */
|
||||
notified_users: Array<string>,
|
||||
/** Дата открытия собрания */
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
console.log(JSON.stringify(meetStore.currentMeet, null, 2))
|
||||
+79
-44
@@ -1,7 +1,7 @@
|
||||
Сгенерируй changelog для версии v2025.5.14.
|
||||
Сгенерируй changelog для версии v2025.6.14.
|
||||
|
||||
Правила:
|
||||
- Начинай changelog заголовком: "# v2025.5.14"
|
||||
- Начинай changelog заголовком: "# v2025.6.14"
|
||||
- Затем добавь краткое текстовое описание релиза (1–3 строки), основанное на комментарии разработчика. Не копируй комментарий дословно. Используй его только как ориентир для формулировки описания.
|
||||
- Не добавляй ничего от себя, не используй мотивационные или маркетинговые фразы.
|
||||
- После описания сгруппируй изменения по категориям:
|
||||
@@ -14,10 +14,85 @@
|
||||
- В конце поставь тег: #releases
|
||||
|
||||
Комментарий разработчика для описания релиза:
|
||||
Система переведена на новый стандарт передачи документов по блокчейну. Новый стандарт обеспечивает возможность нанесенения неограниченного количества подписей на один документ с валидацией подписей по блокчейну.
|
||||
Реализован модуль проведения очередных общих собраний пайщиков.
|
||||
|
||||
Ниже список закрытых issues, начиная с 2025-05-03:
|
||||
Ниже список закрытых issues, начиная с 2025-05-14:
|
||||
|
||||
#274: Настроить рассылку оповещений на почту при получении решения о проведении собрания
|
||||
|
||||
|
||||
|
||||
---
|
||||
#273: Встроить реальные шаблоны документов общего собрания
|
||||
|
||||
* [ ] Бюллетень * [ ] Оповещение * [ ] Решение о проведении []Решение общего собрания
|
||||
|
||||
---
|
||||
#272: Подписанные уведомления складируются в реестр и извлекаются из реестра по Graph-QL
|
||||
|
||||
|
||||
|
||||
---
|
||||
#271: Посчитать количество пайщиков в каждом кооперативе и вести подсчёт при добавлении / удалении (soviet)
|
||||
|
||||
|
||||
|
||||
---
|
||||
#270: Ссылка с оповещения ведет на страницу собрания, где всплывает документ-уведомление на подпись
|
||||
|
||||
|
||||
|
||||
---
|
||||
#269: Рассылать уведомления перед началом собрания
|
||||
|
||||
|
||||
|
||||
---
|
||||
#268: Сверстать страницу результатов общего собрания
|
||||
|
||||
|
||||
|
||||
---
|
||||
#267: Сверстать страницу просмотра/скачивания бюллетеней и уведомлений по собранию
|
||||
|
||||
|
||||
|
||||
---
|
||||
#266: Протестировать все процессы общего собрания
|
||||
|
||||
|
||||
|
||||
---
|
||||
#265: Отладить процесс общего собрания пайщиков
|
||||
|
||||
* [x] текущий кворум - явка * [x] ввести простые сертификаты пользователей * [x] отобразить информацию о секретаре и председателе собрания пофамильно * [x] решить что делать с отображением протокола и прочих документов а именно с тем, что сейчас агрегат всегда содержит полные приватные данные того кто подписал документ. Наверное, необходимо сузить полную информацию о подписанте до поверхностной. Где? в DTO * [x] посчитать и отобразить статус собрания на бэкенде нормально * [x] перехватить транзакцию закрытого собрания и сохранить её в реестре * [x] извлекать информацию о закрытом собрании через DTO * [x] отобразить информацию о результатах собрания * [ ] ~~сохранить транзакцией количество пайщиков которые принимали участие в собрании и кворуме~~ * [x] сохранить протокол решения общего собрания транзакцией processed * [x] отобразить протокол решения общего собрания * [x] отладить вёрстку * [x] скрывать голосование если голосовал * [x] заменить таблицу на листе на что то иное * [x] отобразить состояние участия в собрании (голосовал - не голосовал) * [x] последовательная подпись документов закрытия собрания секретарем и председателем * [x] подписать уведомление * [x] разобраться с часовыми поясами при создании и демонстрации собрания * [x] отобразить подписанные бюллетени в папке документов * [x] отобразить подписанные уведомления в папке документов * [x] поправить тип переменной кворума на double * [x] поправить верстку документов * [x] в шаблонах * [x] на рабочем столе * [x] подложить реальные документы * [x] предложение повестки * [x] уведомление * [x] бюллетень * [x] протокол решения общего собрания * [x] протокол решения собрания совета * [x] extendedAction убрать user * [x] отобразить кто голосовал за решение собрания совета * [ ] ~~страница членов совета для мониторинга?~~ * [x] посчитать фактическое количество пайщиков в кооперативе и изменять его при регистрации / выходе / блокировке * [x] обновлять meet_hash при перезапуске * [x] предусмотреть что контракты могут использоваться для собраний пайщиков на КУ * [x] level * [x] разобраться с идентификацией номеров протоколов * [x] скрыть кнопку создания собрания для пайщиков * [x] Баг перезапуска собрания после изменения хэша * [x] Баг: расхождение часовых поясов в документах * [x] в шапке и в тексте * [x] Баг: почему-то голос ЗА в заявлении отображается как “Воздержался” * [x] рассылать уведомления перед началом и о начале * [x] расширить env в playbook * [x] убрать решетку из уведомлений, но сохранить совместимость с SPA * [x] установка новых шаблонов на тестнет * [x] деплой ПО на тестнет * [x] добавить контракт meet в список деплоя плейбука! * [x] подсчет текущего факта количества пайщиков на контрактах для всех кооперативов * [x] тест
|
||||
|
||||
---
|
||||
#264: Разработать смарт-контракт общего собрания пайщиков (meet)
|
||||
|
||||
|
||||
|
||||
---
|
||||
#263: Модуль оповещений на электронные почты по жизненному циклу общего собрания пайщиков
|
||||
|
||||
|
||||
|
||||
---
|
||||
#262: Баг: рабочий стол совета включается, однако, страница всегда открывается со стола пайщика
|
||||
|
||||
|
||||
|
||||
---
|
||||
#261: Баг: первая загрузка переадресует на главную страницу всегда - прямой переход на собрание становится недоступен.
|
||||
|
||||
|
||||
|
||||
---
|
||||
#260: Ввести счетчик количества пайщиков в кооперативе на контракте регистратора
|
||||
|
||||
|
||||
|
||||
---
|
||||
#259: Поправить отступы в мобильной карточке пайщика на странице пайщиков
|
||||
|
||||
|
||||
@@ -27,11 +102,6 @@
|
||||
|
||||
|
||||
|
||||
---
|
||||
#257: Сохранять выбранную светлую / темную тему в локалсторадж и извлекать при следующей загрузке страницы
|
||||
|
||||
|
||||
|
||||
---
|
||||
#256: Отобразить статус членства каждого пайщика в разделе Пайщики
|
||||
|
||||
@@ -42,39 +112,4 @@
|
||||
|
||||
|
||||
|
||||
---
|
||||
#254: Снимать вопросы с повестки автоматически через 3 дня
|
||||
|
||||
|
||||
|
||||
---
|
||||
#253: Устранить избыточную точность суммы оплаты с 0.0000 RUB до 0.00 RUB в заявлении на вступлении
|
||||
|
||||
|
||||
|
||||
---
|
||||
#252: Ввести обновленный стандарт хранения и передачи документа по блокчейну
|
||||
|
||||
* [x] разработать стандарт document2 * [x] разработать мигратор данных в controller * [ ] ~~вкачать agreements из блокчейна в базу данных~~ * [ ] ~~встроить agreements в accountDTO~~ * [ ] ~~перевести фронтенд на использование структуры agreements из accountDTO~~ * [ ] ~~снести таблицу agreements из бч~~ * [x] мигрировать данные из orgs → coops * [x] мигрировать данные из agreements → agreements2 * [x] мигрировать шаблон 100 * [x] заменить стандарты в блокчейне для всех таблиц * [x] заменить имена таблиц в cooptypes * [x] заменить тип IDocument в cooptypes на новый * [x] заменить все вхождения document → document2 во всех контрактах, кроме legacy таблиц * [x] адаптировать все инпуты всех документов к новому формату DTO * [x] сверить что там с решениями в Восходе - убрать висяки * [x] заменить фиксаторы реестра newsubmitted и т.д. на новые типобезопасные версии * [x] обновить агрегатор с учетом новой версий фиксаторов * [x] добавить хэш signed_hash в каждую подпись для прозрачности стандарта * [x] отладить контроллер * [x] отладить десктоп * [x] восстановить отображение подписей на фронте * [x] восстановить загрузку соглашений на фронте * [x] написать мигратор соглашений * [x] обновить шаблон 100 * [x] замена модели генерации (опциональная подпись) * [x] округлить нули в суммах через новые параметры * [x] голосование через подпись номера решения
|
||||
|
||||
---
|
||||
#251: Разработать версионированный мигратор данных для контроллера кооператива
|
||||
|
||||
|
||||
|
||||
---
|
||||
#250: Вынести автоматическую сборку и публикацию пакетов в пайплайн гитхаба
|
||||
|
||||
|
||||
|
||||
---
|
||||
#249: Баг: спутанная маршрутизация между рабочими столами кооперативов
|
||||
|
||||
|
||||
|
||||
---
|
||||
#244: Обновить стандарт сборки документов
|
||||
|
||||
* [x] Ввести использование хэш-идентификаторов для сборки документов вместо внутреннего decision_id * [x] смарт-контракты * [x] агрегатор * [x] Сгружать соглашения в общий реестр подписанных документов как документы без решений
|
||||
|
||||
---
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
# v2025.5.14
|
||||
# v2025.6.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): Обновление стандарта сборки документов и переход на хэш-идентификаторы
|
||||
- [#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): Ссылка в оповещении ведет на страницу собрания с документом-уведомлением для подписи
|
||||
|
||||
🐛 Исправления ошибок
|
||||
- [#249](https://github.com/coopenomics/mono/issues/249): Исправлена спутанная маршрутизация между рабочими столами кооперативов
|
||||
- [#253](https://github.com/coopenomics/mono/issues/253): Исправлена избыточная точность суммы оплаты в заявлении на вступление
|
||||
- [#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-эффект на документе и пайщике в таблице
|
||||
|
||||
🔧 Улучшения
|
||||
- [#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): Отображение ФИО/Наименования плательщика в разделе "Платежи"
|
||||
- [#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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION: v2025.5.14
|
||||
FROM DATE: 2025-05-03
|
||||
VERSION: v2025.6.14
|
||||
FROM DATE: 2025-05-14 (10:00 - это для нового релиза, TODO: учесть время)
|
||||
COMMENT:
|
||||
Система переведена на новый стандарт передачи документов по блокчейну. Новый стандарт обеспечивает возможность нанесенения неограниченного количества подписей на один документ с валидацией подписей по блокчейну.
|
||||
Реализован модуль проведения очередных общих собраний пайщиков.
|
||||
|
||||
Reference in New Issue
Block a user