controller meets v2
This commit is contained in:
@@ -1,3 +1,75 @@
|
||||
# v2026.4.2-2
|
||||
|
||||
В этом релизе — стабилизация Благороста для вывода в продуктивную работу с результатами интеллектуальной деятельности. Отдельно заложена основа под отчётность в ФНС и ФСС и прототип поиска по документам.
|
||||
|
||||
**Благорост и проекты**
|
||||
|
||||
- Быстрые действия на странице программы: создать проект, компонент, задачу или требование.
|
||||
- Требования к компонентам в репозитории: поддержка **Mermaid**, **Draw.io** и **BPMN**.
|
||||
- Синхронизация проектов, компонентов, требований и задач с Git-репозиторием результатов.
|
||||
- Встраивание полноразмерного видео (iframe) на страницах проектов.
|
||||
- Скачивание пакета подписанных документов одной кнопкой.
|
||||
- Комнаты проектов в кооперативном мессенджере.
|
||||
|
||||
**Мессенджер и звонки**
|
||||
|
||||
- Автосекретарь: запись синхронных звонков, текстовых и голосовых сообщений в проектных комнатах.
|
||||
|
||||
**Отчётность и инфраструктура**
|
||||
|
||||
- Прототип фабрики отчётов ФНС/ФСС: выгрузка в XML для дальнейшей отправки.
|
||||
- Прототип поисковой системы по документам.
|
||||
- Установщик для развёртывания на своих серверах (разработка или эксплуатация).
|
||||
- Рефакторинг в сторону чистой архитектуры на бэкенде.
|
||||
- Ускорение сборки фронтенда за счёт перехода на **Vite 8**.
|
||||
|
||||
**Исправления**
|
||||
|
||||
- Повторное общее собрание больше не мешало завершить онбординг кооператива.
|
||||
- Центр уведомлений не блокировал загрузку рабочего стола при отключённом провайдере оповещений.
|
||||
- Уведомления о собрании совета по свободным вопросам снова доходят до членов совета.
|
||||
- В интерфейсе восстановлено отображение контактов кооператива.
|
||||
|
||||
#releases
|
||||
|
||||
---
|
||||
|
||||
# v2026.4.2-2
|
||||
|
||||
В этом релизе — стабилизация Благороста для вывода в продуктивную работу с результатами интеллектуальной деятельности. Отдельно заложена основа под отчётность в ФНС и ФСС и прототип поиска по документам.
|
||||
|
||||
**Благорост и проекты**
|
||||
|
||||
- Быстрые действия на странице программы: создать проект, компонент, задачу или требование.
|
||||
- Требования к компонентам в репозитории: поддержка **Mermaid**, **Draw.io** и **BPMN**.
|
||||
- Синхронизация проектов, компонентов, требований и задач с Git-репозиторием результатов.
|
||||
- Встраивание полноразмерного видео (iframe) на страницах проектов.
|
||||
- Скачивание пакета подписанных документов одной кнопкой.
|
||||
- Комнаты проектов в кооперативном мессенджере.
|
||||
|
||||
**Мессенджер и звонки**
|
||||
|
||||
- Автосекретарь: запись синхронных звонков, текстовых и голосовых сообщений в проектных комнатах.
|
||||
|
||||
**Отчётность и инфраструктура**
|
||||
|
||||
- Прототип фабрики отчётов ФНС/ФСС: выгрузка в XML для дальнейшей отправки.
|
||||
- Прототип поисковой системы по документам.
|
||||
- Установщик для развёртывания на своих серверах (разработка или эксплуатация).
|
||||
- Рефакторинг в сторону чистой архитектуры на бэкенде.
|
||||
- Ускорение сборки фронтенда за счёт перехода на **Vite 8**.
|
||||
|
||||
**Исправления**
|
||||
|
||||
- Повторное общее собрание больше не мешало завершить онбординг кооператива.
|
||||
- Центр уведомлений не блокировал загрузку рабочего стола при отключённом провайдере оповещений.
|
||||
- Уведомления о собрании совета по свободным вопросам снова доходят до членов совета.
|
||||
- В интерфейсе восстановлено отображение контактов кооператива.
|
||||
|
||||
#releases
|
||||
|
||||
---
|
||||
|
||||
# v2025.12.28-8
|
||||
|
||||
В этой версии представлен прототип трекера результатов интеллектуальной деятельности, реализован мост в 1С, обновлен интерфейс и существенно повышена стабильность системы.
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"max_transaction_lifetime": 3600,
|
||||
"deferred_trx_expiration_window": 600,
|
||||
"max_transaction_delay": 3888000,
|
||||
"max_inline_action_size": 4096,
|
||||
"max_inline_action_size": 65536,
|
||||
"max_inline_action_depth": 10,
|
||||
"max_authority_depth": 6
|
||||
},
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator';
|
||||
import { IsArray, IsNotEmpty, IsOptional, IsString, MaxLength, ValidateNested } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import type { CreateAnnualGeneralMeetInputDomainInterface } from '~/domain/meet/interfaces/create-annual-meet-input-domain.interface';
|
||||
import { AgendaGeneralMeetPointInputDTO } from './agenda-meet-point-input.dto';
|
||||
import { AnnualGeneralMeetingAgendaSignedDocumentInputDTO } from '~/application/document/documents-dto/annual-general-meeting-agenda-document.dto';
|
||||
|
||||
/** Согласовано с лимитом на desktop (повестка / форма собрания) */
|
||||
const MEET_DETAILS_MAX_LEN = 10_000;
|
||||
|
||||
@InputType('CreateAnnualGeneralMeetInput')
|
||||
export class CreateAnnualGeneralMeetInputDTO implements CreateAnnualGeneralMeetInputDomainInterface {
|
||||
@Field(() => String, { description: 'Имя аккаунта кооператива' })
|
||||
@@ -43,4 +46,13 @@ export class CreateAnnualGeneralMeetInputDTO implements CreateAnnualGeneralMeetI
|
||||
|
||||
@Field(() => AnnualGeneralMeetingAgendaSignedDocumentInputDTO, { description: 'Предложение повестки собрания' })
|
||||
proposal!: AnnualGeneralMeetingAgendaSignedDocumentInputDTO;
|
||||
|
||||
@Field(() => String, {
|
||||
nullable: true,
|
||||
description: 'Дополнительная информация о формате собрания (ссылка, как участвовать и т.д.)',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(MEET_DETAILS_MAX_LEN)
|
||||
details?: string | null;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,13 @@ export class MeetPreProcessingDTO implements MeetPreProcessingDomainInterface {
|
||||
@IsOptional()
|
||||
proposal?: DocumentAggregateDTO;
|
||||
|
||||
@Field(() => String, {
|
||||
nullable: true,
|
||||
description: 'Дополнительная информация о формате собрания',
|
||||
})
|
||||
@IsOptional()
|
||||
details?: string | null;
|
||||
|
||||
constructor(
|
||||
data: MeetPreProcessingDomainInterface,
|
||||
presiderCertificate?: UserCertificateDomainInterface | null,
|
||||
@@ -80,6 +87,7 @@ export class MeetPreProcessingDTO implements MeetPreProcessingDomainInterface {
|
||||
this.open_at = data.open_at;
|
||||
this.close_at = data.close_at;
|
||||
this.proposal = data.proposal ? new DocumentAggregateDTO(data.proposal) : undefined;
|
||||
this.details = data.details ?? null;
|
||||
|
||||
// Обрабатываем сертификат инициатора
|
||||
if (initiatorCertificate) {
|
||||
|
||||
+13
-1
@@ -1,9 +1,11 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsString, IsDate, ValidateNested } from 'class-validator';
|
||||
import { IsString, IsDate, IsOptional, MaxLength, ValidateNested } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { AnnualGeneralMeetingAgendaSignedDocumentInputDTO } from '~/application/document/documents-dto/annual-general-meeting-agenda-document.dto';
|
||||
import { RestartAnnualGeneralMeetInputDomainInterface } from '~/domain/meet/interfaces/restart-annual-general-meet-input-domain.interface';
|
||||
|
||||
const MEET_DETAILS_MAX_LEN = 10_000;
|
||||
|
||||
@InputType('RestartAnnualGeneralMeetInput', { description: 'DTO для перезапуска ежегодного общего собрания кооператива' })
|
||||
export class RestartAnnualGeneralMeetInputDTO implements RestartAnnualGeneralMeetInputDomainInterface {
|
||||
@Field(() => String, { description: 'Имя аккаунта кооператива' })
|
||||
@@ -29,4 +31,14 @@ export class RestartAnnualGeneralMeetInputDTO implements RestartAnnualGeneralMee
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
new_close_at!: Date;
|
||||
|
||||
@Field(() => String, {
|
||||
nullable: true,
|
||||
description:
|
||||
'Доп. информация для нового слота; передайте явно, если нужна в повестке и уведомлениях. Поле не передано — у нового собрания details пустой',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(MEET_DETAILS_MAX_LEN)
|
||||
details?: string | null;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import { ProcessMeetDecisionInputDomainInterface } from '~/domain/meet/interface
|
||||
import { MeetProcessedDomainEntity } from '~/domain/meet/entities/meet-processed-domain.entity';
|
||||
import { NotifyOnAnnualGeneralMeetInputDomainInterface } from '~/domain/meet/interfaces/notify-on-annual-general-meet-input-domain.interface';
|
||||
import { generateUniqueHash } from '~/utils/generate-hash.util';
|
||||
import { HttpApiError } from '~/utils/httpApiError';
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
@Injectable()
|
||||
export class MeetInteractor {
|
||||
@@ -48,10 +50,13 @@ export class MeetInteractor {
|
||||
// Генерируем уникальный хэш для собрания
|
||||
const hash = generateUniqueHash();
|
||||
|
||||
const detailsNormalized = MeetInteractor.normalizeMeetDetailsField(data.details);
|
||||
|
||||
const preProcessing = new MeetPreProcessingDomainEntity({
|
||||
...data,
|
||||
hash,
|
||||
proposal: proposalAggregate,
|
||||
details: detailsNormalized,
|
||||
});
|
||||
|
||||
// Сохраняем данные в репозиторий
|
||||
@@ -60,6 +65,8 @@ export class MeetInteractor {
|
||||
// Вызов блокчейн порта для создания собрания
|
||||
await this.meetBlockchainPort.createMeet({ ...data, hash });
|
||||
|
||||
// Задержка синхронизации на 1 секунду, чтобы избежать ошибки "row not found"
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
// Получаем обновленные данные из блокчейна
|
||||
const processingData = await this.meetBlockchainPort.getMeet({
|
||||
coopname: data.coopname,
|
||||
@@ -173,22 +180,59 @@ export class MeetInteractor {
|
||||
}
|
||||
|
||||
async restartMeet(data: RestartAnnualGeneralMeetInputDomainInterface): Promise<MeetAggregate> {
|
||||
// Вызов блокчейн порта для перезапуска собрания
|
||||
const oldPre = await this.meetPreRepository.findByHash(data.hash);
|
||||
|
||||
const new_hash = await this.meetBlockchainPort.restartMeet(data);
|
||||
|
||||
// Получаем обновленные данные из обоих источников
|
||||
const processingData = await this.meetBlockchainPort.getMeet({
|
||||
coopname: data.coopname,
|
||||
hash: new_hash,
|
||||
});
|
||||
|
||||
if (!processingData) {
|
||||
throw new HttpApiError(httpStatus.NOT_FOUND, 'Собрание после перезапуска не найдено в блокчейне');
|
||||
}
|
||||
|
||||
const proposalAggregate = await this.documentAggregator.buildDocumentAggregate(data.newproposal);
|
||||
|
||||
const meetRow = processingData.meet;
|
||||
const agendaFromChain = processingData.questions.map((q) => ({
|
||||
context: q.context,
|
||||
title: q.title,
|
||||
decision: q.decision,
|
||||
}));
|
||||
|
||||
const detailsForNewPre = MeetInteractor.normalizeMeetDetailsField(data.details);
|
||||
|
||||
const preProcessing = new MeetPreProcessingDomainEntity({
|
||||
hash: new_hash,
|
||||
coopname: meetRow.coopname,
|
||||
initiator: oldPre?.initiator ?? meetRow.initiator,
|
||||
presider: oldPre?.presider ?? meetRow.presider,
|
||||
secretary: oldPre?.secretary ?? meetRow.secretary,
|
||||
agenda: agendaFromChain,
|
||||
open_at: meetRow.open_at,
|
||||
close_at: meetRow.close_at,
|
||||
proposal: proposalAggregate ?? undefined,
|
||||
details: detailsForNewPre,
|
||||
});
|
||||
|
||||
await this.meetPreRepository.create(preProcessing);
|
||||
|
||||
const preMeet = await this.meetPreRepository.findByHash(new_hash);
|
||||
const processedMeetEntity = await this.meetProcessedRepository.findByHash(new_hash);
|
||||
|
||||
// Создаем агрегат из обоих источников данных
|
||||
return new MeetAggregate(preMeet, processingData, processedMeetEntity);
|
||||
}
|
||||
|
||||
/** Пустая строка → отсутствие значения в PG */
|
||||
private static normalizeMeetDetailsField(value: string | null | undefined): string | null | undefined {
|
||||
if (value === undefined) return undefined;
|
||||
if (value === null) return null;
|
||||
const t = value.trim();
|
||||
return t.length > 0 ? t : null;
|
||||
}
|
||||
|
||||
async signBySecretaryOnAnnualGeneralMeet(
|
||||
data: SignBySecretaryOnAnnualGeneralMeetInputDomainInterface
|
||||
): Promise<MeetAggregate> {
|
||||
|
||||
@@ -11,6 +11,7 @@ export class MeetPreProcessingDomainEntity implements MeetPreProcessingDomainInt
|
||||
public readonly open_at!: Date;
|
||||
public readonly close_at!: Date;
|
||||
public readonly proposal?: DocumentAggregateDomainInterface;
|
||||
public readonly details?: string | null;
|
||||
|
||||
constructor(data: MeetPreProcessingDomainInterface) {
|
||||
Object.assign(this, data);
|
||||
|
||||
+1
@@ -9,4 +9,5 @@ export type CreateAnnualGeneralMeetInputDomainInterface = Omit<
|
||||
open_at: Date;
|
||||
close_at: Date;
|
||||
hash?: string;
|
||||
details?: string | null;
|
||||
};
|
||||
|
||||
@@ -21,6 +21,8 @@ export interface MeetPreProcessingDomainInterface {
|
||||
open_at: Date;
|
||||
close_at: Date;
|
||||
proposal?: DocumentAggregateDomainInterface;
|
||||
/** Текст уровня собрания (ссылка на трансляцию, формат участия и т.п.), только в PG */
|
||||
details?: string | null;
|
||||
}
|
||||
|
||||
assertType<AssertKeysMatch<MeetContract.Actions.CreateMeet.IInput, MeetPreProcessingDomainInterface>>();
|
||||
|
||||
+2
@@ -9,4 +9,6 @@ export interface RestartAnnualGeneralMeetInputDomainInterface {
|
||||
newproposal: ISignedDocumentDomainInterface;
|
||||
new_open_at: Date;
|
||||
new_close_at: Date;
|
||||
/** Если передано — пишем в meet_pre нового hash; если в мутации не передано — details у нового pre пустой (старый pre не подмешиваем) */
|
||||
details?: string | null;
|
||||
}
|
||||
|
||||
+15
-1
@@ -312,7 +312,21 @@ export class GenerationService {
|
||||
return [];
|
||||
}
|
||||
|
||||
const rooms = await this.projectCommArtifacts.listCommunicationRoomsForProject(anchorProjectHash);
|
||||
const projectForStory = await this.projectRepository.findByHash(anchorProjectHash);
|
||||
if (!projectForStory) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Компоненты не имеют отдельных записей чатов в реестре: комната Matrix у корневого проекта (parent_hash).
|
||||
let matrixRoomProjectHash = anchorProjectHash.trim().toLowerCase();
|
||||
if (projectForStory.isComponent()) {
|
||||
const parentHash = projectForStory.parent_hash?.trim().toLowerCase() ?? '';
|
||||
if (parentHash) {
|
||||
matrixRoomProjectHash = parentHash;
|
||||
}
|
||||
}
|
||||
|
||||
const rooms = await this.projectCommArtifacts.listCommunicationRoomsForProject(matrixRoomProjectHash);
|
||||
if (rooms.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
+21
@@ -1,4 +1,5 @@
|
||||
import { Injectable, Inject, OnModuleInit } from '@nestjs/common';
|
||||
import { MEET_REPOSITORY, MeetPreProcessingRepository } from '~/domain/meet/repositories/meet-pre.repository';
|
||||
import { WinstonLoggerService } from '~/application/logger/logger-app.service';
|
||||
import { NovuWorkflowAdapter } from '~/infrastructure/novu/novu-workflow.adapter';
|
||||
import { NOVU_WORKFLOW_PORT } from '~/domain/notification/interfaces/novu-workflow.port';
|
||||
@@ -20,6 +21,8 @@ export class MeetWorkflowNotificationService implements OnModuleInit {
|
||||
private readonly novuWorkflowAdapter: NovuWorkflowAdapter,
|
||||
@Inject(ACCOUNT_DATA_PORT)
|
||||
private readonly accountPort: AccountDataPort,
|
||||
@Inject(MEET_REPOSITORY)
|
||||
private readonly meetPreRepository: MeetPreProcessingRepository,
|
||||
private readonly logger: WinstonLoggerService
|
||||
) {
|
||||
this.logger.setContext(MeetWorkflowNotificationService.name);
|
||||
@@ -56,6 +59,16 @@ export class MeetWorkflowNotificationService implements OnModuleInit {
|
||||
return config.timezone === 'Europe/Moscow' ? 'МСК' : config.timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Текст из meet_pre только для workflow «до/у начала»: meet-initial, meet-reminder-start, meet-started.
|
||||
* В reminder-end, restart, ended поле details в схемах Novu нет — не передаём, иначе ошибка валидации.
|
||||
*/
|
||||
private async meetDetailsPayloadPart(hash: string): Promise<{ details?: string }> {
|
||||
const pre = await this.meetPreRepository.findByHash(hash);
|
||||
const trimmed = pre?.details?.trim();
|
||||
return trimmed ? { details: trimmed } : {};
|
||||
}
|
||||
|
||||
// Получение всех email-адресов пользователей
|
||||
private async getAllUserEmails(): Promise<Array<{ username: string; email: string }>> {
|
||||
try {
|
||||
@@ -113,6 +126,7 @@ export class MeetWorkflowNotificationService implements OnModuleInit {
|
||||
const timezone = this.getTimezoneDisplay();
|
||||
const meetUrl = this.getNotificationUrl(meet);
|
||||
|
||||
const detailsPart = await this.meetDetailsPayloadPart(meet.hash);
|
||||
const payload: Workflows.MeetInitial.IPayload = {
|
||||
coopShortName,
|
||||
meetId: meet.id,
|
||||
@@ -122,6 +136,7 @@ export class MeetWorkflowNotificationService implements OnModuleInit {
|
||||
meetEndTime,
|
||||
timezone,
|
||||
meetUrl,
|
||||
...detailsPart,
|
||||
};
|
||||
|
||||
// Отправляем уведомления каждому пользователю в цикле
|
||||
@@ -166,6 +181,7 @@ export class MeetWorkflowNotificationService implements OnModuleInit {
|
||||
|
||||
const meetUrl = this.getNotificationUrl(meet);
|
||||
|
||||
const detailsPart = await this.meetDetailsPayloadPart(meet.hash);
|
||||
const payload: Workflows.MeetReminderStart.IPayload = {
|
||||
coopShortName,
|
||||
meetId: meet.id,
|
||||
@@ -173,6 +189,7 @@ export class MeetWorkflowNotificationService implements OnModuleInit {
|
||||
meetTime,
|
||||
timeDescription,
|
||||
meetUrl,
|
||||
...detailsPart,
|
||||
};
|
||||
|
||||
// Отправляем уведомления каждому пользователю в цикле
|
||||
@@ -211,6 +228,7 @@ export class MeetWorkflowNotificationService implements OnModuleInit {
|
||||
const timezone = this.getTimezoneDisplay();
|
||||
const meetUrl = this.getNotificationUrl(meet);
|
||||
|
||||
const detailsPart = await this.meetDetailsPayloadPart(meet.hash);
|
||||
const payload: Workflows.MeetStarted.IPayload = {
|
||||
coopShortName,
|
||||
meetId: meet.id,
|
||||
@@ -218,6 +236,7 @@ export class MeetWorkflowNotificationService implements OnModuleInit {
|
||||
meetEndTime,
|
||||
timezone,
|
||||
meetUrl,
|
||||
...detailsPart,
|
||||
};
|
||||
|
||||
// Отправляем уведомления каждому пользователю в цикле
|
||||
@@ -263,6 +282,7 @@ export class MeetWorkflowNotificationService implements OnModuleInit {
|
||||
const timezone = this.getTimezoneDisplay();
|
||||
const meetUrl = this.getNotificationUrl(meet);
|
||||
|
||||
const detailsPart = await this.meetDetailsPayloadPart(meet.hash);
|
||||
const payload: Workflows.MeetReminderEnd.IPayload = {
|
||||
coopShortName,
|
||||
meetId: meet.id,
|
||||
@@ -271,6 +291,7 @@ export class MeetWorkflowNotificationService implements OnModuleInit {
|
||||
timeDescription,
|
||||
timezone,
|
||||
meetUrl,
|
||||
...detailsPart,
|
||||
};
|
||||
|
||||
// Отправляем уведомления каждому пользователю в цикле
|
||||
|
||||
+4
-2
@@ -181,9 +181,11 @@ export class MeetBlockchainAdapter implements MeetBlockchainPort {
|
||||
|
||||
this.blockchainService.initialize(data.coopname, wif);
|
||||
|
||||
// Преобразуем доменный объект в инфраструктурный тип
|
||||
const { details: _details, ...meetPayload } = data;
|
||||
|
||||
// Преобразуем доменный объект в инфраструктурный тип (поле details только в PG, не в цепи)
|
||||
const blockchainData: MeetContract.Actions.CreateMeet.IInput = {
|
||||
...data,
|
||||
...meetPayload,
|
||||
hash: data.hash || generateUniqueHash(),
|
||||
proposal: this.domainToBlockchainUtils.convertSignedDocumentToBlockchainFormat(data.proposal),
|
||||
open_at: this.domainToBlockchainUtils.convertDateToBlockchainFormat(data.open_at),
|
||||
|
||||
@@ -34,6 +34,9 @@ export class MeetPreEntity implements MeetPreProcessingDomainInterface {
|
||||
@Column('jsonb', { nullable: true })
|
||||
proposal?: DocumentAggregateDomainInterface;
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
details?: string | null;
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at!: Date;
|
||||
|
||||
@@ -48,6 +51,7 @@ export class MeetPreEntity implements MeetPreProcessingDomainInterface {
|
||||
this.open_at = data.open_at;
|
||||
this.close_at = data.close_at;
|
||||
this.proposal = data.proposal;
|
||||
this.details = data.details ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +67,7 @@ export class MeetPreEntity implements MeetPreProcessingDomainInterface {
|
||||
open_at: this.open_at,
|
||||
close_at: this.close_at,
|
||||
proposal: this.proposal,
|
||||
details: this.details ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export const meetInitialPayloadSchema = z.object({
|
||||
meetEndTime: z.string(),
|
||||
timezone: z.string(),
|
||||
meetUrl: z.string(),
|
||||
details: z.string().optional(),
|
||||
});
|
||||
|
||||
export type IPayload = z.infer<typeof meetInitialPayloadSchema>;
|
||||
@@ -36,12 +37,12 @@ export const workflow: WorkflowDefinition<IWorkflow> = WorkflowBuilder
|
||||
createEmailStep(
|
||||
'meet-initial-email',
|
||||
'Уведомление о общем собрании пайщиков №{{payload.meetId}} в {{payload.coopShortName}}',
|
||||
'Уважаемый пайщик!<br><br>В кооперативе объявлено новое общее собрание №{{payload.meetId}}.<br><br>Дата и время начала: {{payload.meetDate}} в {{payload.meetTime}} ({{payload.timezone}})<br>Дата и время завершения: {{payload.meetEndDate}} в {{payload.meetEndTime}} ({{payload.timezone}})<br><br>Для ознакомления с повесткой, пожалуйста, перейдите по ссылке:<br><a href="{{payload.meetUrl}}">{{payload.meetUrl}}</a><br><br>С уважением, Совет {{payload.coopShortName}}.'
|
||||
'Уважаемый пайщик!<br><br>В кооперативе объявлено новое общее собрание №{{payload.meetId}}.<br><br>Дата и время начала: {{payload.meetDate}} в {{payload.meetTime}} ({{payload.timezone}})<br>Дата и время завершения: {{payload.meetEndDate}} в {{payload.meetEndTime}} ({{payload.timezone}})<br><br>Для ознакомления с повесткой, пожалуйста, перейдите по ссылке:<br><a href="{{payload.meetUrl}}">{{payload.meetUrl}}</a>{{#if payload.details}}<br><br>Дополнительная информация:<div style="white-space:pre-wrap;">{{payload.details}}</div>{{/if}}<br><br>С уважением, Совет {{payload.coopShortName}}.'
|
||||
),
|
||||
createInAppStep(
|
||||
'meet-initial-notification',
|
||||
'Новое общее собрание №{{payload.meetId}}',
|
||||
'Назначено собрание на {{payload.meetDate}} в {{payload.meetTime}}'
|
||||
'Назначено собрание на {{payload.meetDate}} в {{payload.meetTime}}{{#if payload.details}}. {{payload.details}}{{/if}}'
|
||||
),
|
||||
createPushStep(
|
||||
'meet-initial-push',
|
||||
|
||||
@@ -14,6 +14,7 @@ export const meetReminderStartPayloadSchema = z.object({
|
||||
meetTime: z.string(),
|
||||
timeDescription: z.string(),
|
||||
meetUrl: z.string(),
|
||||
details: z.string().optional(),
|
||||
});
|
||||
|
||||
export type IPayload = z.infer<typeof meetReminderStartPayloadSchema>;
|
||||
@@ -34,12 +35,12 @@ export const workflow: WorkflowDefinition<IWorkflow> = WorkflowBuilder
|
||||
createEmailStep(
|
||||
'meet-reminder-start-email',
|
||||
'Напоминание о предстоящем общем собрании №{{payload.meetId}} в {{payload.coopShortName}}',
|
||||
'Уважаемый пайщик!<br><br>Напоминаем, что {{payload.timeDescription}} состоится общее собрание пайщиков №{{payload.meetId}} ({{payload.meetDate}} в {{payload.meetTime}}).<br><br>Для ознакомления с повесткой и подписи уведомления, пожалуйста, перейдите по ссылке:<br><a href="{{payload.meetUrl}}">{{payload.meetUrl}}</a><br><br>С уважением, Совет {{payload.coopShortName}}.'
|
||||
'Уважаемый пайщик!<br><br>Напоминаем, что {{payload.timeDescription}} состоится общее собрание пайщиков №{{payload.meetId}} ({{payload.meetDate}} в {{payload.meetTime}}).<br><br>Для ознакомления с повесткой и подписи уведомления, пожалуйста, перейдите по ссылке:<br><a href="{{payload.meetUrl}}">{{payload.meetUrl}}</a>{{#if payload.details}}<br><br>Дополнительная информация:<div style="white-space:pre-wrap;">{{payload.details}}</div>{{/if}}<br><br>С уважением, Совет {{payload.coopShortName}}.'
|
||||
),
|
||||
createInAppStep(
|
||||
'meet-reminder-start-notification',
|
||||
'Напоминание о собрании №{{payload.meetId}}',
|
||||
'Собрание начнется {{payload.timeDescription}}'
|
||||
'Собрание начнется {{payload.timeDescription}}{{#if payload.details}}. {{payload.details}}{{/if}}'
|
||||
),
|
||||
createPushStep(
|
||||
'meet-reminder-start-push',
|
||||
|
||||
@@ -14,6 +14,7 @@ export const meetStartedPayloadSchema = z.object({
|
||||
meetEndTime: z.string(),
|
||||
timezone: z.string(),
|
||||
meetUrl: z.string(),
|
||||
details: z.string().optional(),
|
||||
});
|
||||
|
||||
export type IPayload = z.infer<typeof meetStartedPayloadSchema>;
|
||||
@@ -34,12 +35,12 @@ export const workflow: WorkflowDefinition<IWorkflow> = WorkflowBuilder
|
||||
createEmailStep(
|
||||
'meet-started-email',
|
||||
'Собрание пайщиков №{{payload.meetId}} в {{payload.coopShortName}} началось',
|
||||
'Уважаемый пайщик!<br><br>Сегодня началось общее собрание пайщиков №{{payload.meetId}}.<br>Собрание будет проходить до {{payload.meetEndDate}} {{payload.meetEndTime}} ({{payload.timezone}}).<br><br>Просим принять участие в голосовании по вопросам повестки дня.<br>Для голосования перейдите по ссылке:<br><a href="{{payload.meetUrl}}">{{payload.meetUrl}}</a><br><br>С уважением, Совет {{payload.coopShortName}}.'
|
||||
'Уважаемый пайщик!<br><br>Сегодня началось общее собрание пайщиков №{{payload.meetId}}.<br>Собрание будет проходить до {{payload.meetEndDate}} {{payload.meetEndTime}} ({{payload.timezone}}).<br><br>Просим принять участие в голосовании по вопросам повестки дня.<br>Для голосования перейдите по ссылке:<br><a href="{{payload.meetUrl}}">{{payload.meetUrl}}</a>{{#if payload.details}}<br><br>Дополнительная информация:<div style="white-space:pre-wrap;">{{payload.details}}</div>{{/if}}<br><br>С уважением, Совет {{payload.coopShortName}}.'
|
||||
),
|
||||
createInAppStep(
|
||||
'meet-started-notification',
|
||||
'Собрание №{{payload.meetId}} началось',
|
||||
'Примите участие в голосовании'
|
||||
'Примите участие в голосовании{{#if payload.details}}. {{payload.details}}{{/if}}'
|
||||
),
|
||||
createPushStep(
|
||||
'meet-started-push',
|
||||
|
||||
@@ -1,39 +1,33 @@
|
||||
# v2025.12.28-8
|
||||
# v2026.4.2-2
|
||||
|
||||
В этой версии представлен прототип трекера результатов интеллектуальной деятельности, реализован мост в 1С, обновлен интерфейс и существенно повышена стабильность системы.
|
||||
В этом релизе — стабилизация Благороста для вывода в продуктивную работу с результатами интеллектуальной деятельности. Отдельно заложена основа под отчётность в ФНС и ФСС и прототип поиска по документам.
|
||||
|
||||
---
|
||||
**Благорост и проекты**
|
||||
|
||||
### ✨ Новые функции
|
||||
- [#332](https://github.com/coopenomics/mono/issues/332): Прототип конструктора требований дополнительных документов при регистрации
|
||||
- [#330](https://github.com/coopenomics/mono/issues/330): Прототип моста в 1С:Бухгалтерию для передачи документов и проводок
|
||||
- [#329](https://github.com/coopenomics/mono/issues/329): Интеграция и тестирование LMS TUTOR для образовательных задач
|
||||
- [#328](https://github.com/coopenomics/mono/issues/328): Размещение прототипов мульти-лендингов на цифровой-кооператив.рф и coopenomics.world
|
||||
- [#326](https://github.com/coopenomics/mono/issues/326): Минимальный интерфейс трекера результатов интеллектуальной деятельности
|
||||
- [#324](https://github.com/coopenomics/mono/issues/324): Смарт-контракт генерации и капитализации результатов интеллектуальной деятельности ("Благорост")
|
||||
- [#322](https://github.com/coopenomics/mono/issues/322): Поставка обновлений ПО с нулевым даунтаймом по blue-green стратегии
|
||||
- [#321](https://github.com/coopenomics/mono/issues/321): Внедрение системы проводок по фондам для контрактов
|
||||
- [#319](https://github.com/coopenomics/mono/issues/319): Палитра команд и быстрый доступ к страницам рабочих столов (cmk+k)
|
||||
- [#316](https://github.com/coopenomics/mono/issues/316): Переход рабочего стола на GraphQL SDK
|
||||
- [#314](https://github.com/coopenomics/mono/issues/314): Развёртывание GlitchTip для мониторинга ошибок
|
||||
- [#306](https://github.com/coopenomics/mono/issues/306): Модуль запросов и мутаций для контракта капитализации
|
||||
- Быстрые действия на странице программы: создать проект, компонент, задачу или требование.
|
||||
- Требования к компонентам в репозитории: поддержка **Mermaid**, **Draw.io** и **BPMN**.
|
||||
- Синхронизация проектов, компонентов, требований и задач с Git-репозиторием результатов.
|
||||
- Встраивание полноразмерного видео (iframe) на страницах проектов.
|
||||
- Скачивание пакета подписанных документов одной кнопкой.
|
||||
- Комнаты проектов в кооперативном мессенджере.
|
||||
|
||||
### 🐛 Исправления ошибок
|
||||
- [#312](https://github.com/coopenomics/mono/issues/312): Исправление подписки на изменение статуса коммитов
|
||||
- [#309](https://github.com/coopenomics/mono/issues/309): Исправление отображения чужих билетов времени в трекере
|
||||
**Мессенджер и звонки**
|
||||
|
||||
### 🔧 Улучшения
|
||||
- [#331](https://github.com/coopenomics/mono/issues/331): Настройка системы мониторинга сбоев и ошибок на базе GlitchTIP, Loki, Prometheus
|
||||
- [#327](https://github.com/coopenomics/mono/issues/327): Пользовательская документация по интерфейсам цифрового кооператива
|
||||
- [#325](https://github.com/coopenomics/mono/issues/325): Документирование смарт-контракта программы "Благорост"
|
||||
- [#308](https://github.com/coopenomics/mono/issues/308): Улучшение отображения рабочих столов в магазине приложений
|
||||
- [#307](https://github.com/coopenomics/mono/issues/307): Объединение настроек контракта с нативными настройками приложения
|
||||
- [#305](https://github.com/coopenomics/mono/issues/305): Объединение полей title и description в проекте
|
||||
- [#304](https://github.com/coopenomics/mono/issues/304): Доменная модель контракта капитализации на бэкенде
|
||||
- [#303](https://github.com/coopenomics/mono/issues/303): Пересмотр архитектуры парсера и формирования локальной истории
|
||||
- [#302](https://github.com/coopenomics/mono/issues/302): Рефакторинг архитектуры, внедрение двухконтурной шины данных и обработки микрофорков
|
||||
- [#301](https://github.com/coopenomics/mono/issues/301): Доработка и отладка контракта "Капитализация РИД" v0.2
|
||||
- [#222](https://github.com/coopenomics/mono/issues/222): Внедрение метода Водянова для распределения пула премий по программе "Благорост"
|
||||
- [#212](https://github.com/coopenomics/mono/issues/212): Снижение точности валютных значений до двух знаков после запятой в документах
|
||||
- Автосекретарь: запись синхронных звонков, текстовых и голосовых сообщений в проектных комнатах.
|
||||
|
||||
**Отчётность и инфраструктура**
|
||||
|
||||
- Прототип фабрики отчётов ФНС/ФСС: выгрузка в XML для дальнейшей отправки.
|
||||
- Прототип поисковой системы по документам.
|
||||
- Установщик для развёртывания на своих серверах (разработка или эксплуатация).
|
||||
- Рефакторинг в сторону чистой архитектуры на бэкенде.
|
||||
- Ускорение сборки фронтенда за счёт перехода на **Vite 8**.
|
||||
|
||||
**Исправления**
|
||||
|
||||
- Повторное общее собрание больше не мешало завершить онбординг кооператива.
|
||||
- Центр уведомлений не блокировал загрузку рабочего стола при отключённом провайдере оповещений.
|
||||
- Уведомления о собрании совета по свободным вопросам снова доходят до членов совета.
|
||||
- В интерфейсе восстановлено отображение контактов кооператива.
|
||||
|
||||
#releases
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION: v2025.12.28-8
|
||||
FROM DATE: 2025-07-2T10:00:00+03:00
|
||||
VERSION: v2026.4.2-2
|
||||
FROM DATE: 2025-12-28T10:00:00+03:00
|
||||
COMMENT:
|
||||
Релиз содержит прототип трекера результатов интеллектуальной деятельности, прототип моста в 1С, обновленный интерфейс и значительно повышенную стабильность.
|
||||
|
||||
Reference in New Issue
Block a user