document names refactoring & context component
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
// Enable the ESlint flat config support
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnSave": false,
|
||||
// Auto fix
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
|
||||
@@ -3,7 +3,7 @@ import { signAgreement } from '../soviet/signAgreement'
|
||||
import { getCoopProgramWallet, getUserProgramWallet } from '../wallet/walletUtils'
|
||||
import { sourceProgramId, sourceProgramName } from './consts'
|
||||
|
||||
export async function signGenerationAgreement(
|
||||
export async function signGenerationContract(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
username: string,
|
||||
|
||||
+2
-2
@@ -51,8 +51,8 @@ import { client } from 'src/shared/api/client';
|
||||
import { Mutations } from '@coopenomics/sdk';
|
||||
|
||||
async function generateDocument(data: IGenerateDocumentInput): Promise<IGeneratedDocumentOutput> {
|
||||
const { [Mutations.Capital.GenerateGenerationAgreement.name]: result } =
|
||||
await client.Mutation(Mutations.Capital.GenerateGenerationAgreement.mutation, {
|
||||
const { [Mutations.Capital.GenerateGenerationContract.name]: result } =
|
||||
await client.Mutation(Mutations.Capital.GenerateGenerationContract.mutation, {
|
||||
variables: {
|
||||
data: {
|
||||
coopname: 'coopname',
|
||||
@@ -2291,12 +2291,12 @@ input CapitalLogFilterInput {
|
||||
type CapitalOnboardingState {
|
||||
blagorost_offer_template_done: Boolean!
|
||||
blagorost_provision_done: Boolean!
|
||||
generation_agreement_template_done: Boolean!
|
||||
generation_contract_template_done: Boolean!
|
||||
generator_program_template_done: Boolean!
|
||||
onboarding_blagorost_offer_template_hash: String
|
||||
onboarding_blagorost_provision_hash: String
|
||||
onboarding_expire_at: String!
|
||||
onboarding_generation_agreement_template_hash: String
|
||||
onboarding_generation_contract_template_hash: String
|
||||
onboarding_generator_program_template_hash: String
|
||||
onboarding_init_at: String!
|
||||
}
|
||||
@@ -2304,7 +2304,7 @@ type CapitalOnboardingState {
|
||||
enum CapitalOnboardingStep {
|
||||
blagorost_offer_template
|
||||
blagorost_program
|
||||
generation_agreement_template
|
||||
generation_contract_template
|
||||
generator_program_template
|
||||
}
|
||||
|
||||
@@ -3581,7 +3581,7 @@ input CompleteVotingInput {
|
||||
project_hash: String!
|
||||
}
|
||||
|
||||
input ComponentGenerationAgreementGenerateDocumentInput {
|
||||
input ComponentGenerationContractGenerateDocumentInput {
|
||||
"""Номер блока, на котором был создан документ"""
|
||||
block_num: Int
|
||||
|
||||
@@ -5536,7 +5536,7 @@ type GeneratedRegistrationDocument {
|
||||
title: String!
|
||||
}
|
||||
|
||||
input GenerationAgreementGenerateDocumentInput {
|
||||
input GenerationContractGenerateDocumentInput {
|
||||
"""Номер блока, на котором был создан документ"""
|
||||
block_num: Int
|
||||
|
||||
@@ -5571,7 +5571,7 @@ input GenerationAgreementGenerateDocumentInput {
|
||||
version: String
|
||||
}
|
||||
|
||||
input GenerationAgreementSignedDocumentInput {
|
||||
input GenerationContractSignedDocumentInput {
|
||||
"""Хэш содержимого документа"""
|
||||
doc_hash: String!
|
||||
|
||||
@@ -5581,7 +5581,7 @@ input GenerationAgreementSignedDocumentInput {
|
||||
"""
|
||||
Метаинформация для документа договора участия в хозяйственной деятельности
|
||||
"""
|
||||
meta: GenerationAgreementSignedMetaDocumentInput!
|
||||
meta: GenerationContractSignedMetaDocumentInput!
|
||||
|
||||
"""Хэш мета-данных"""
|
||||
meta_hash: String!
|
||||
@@ -5593,7 +5593,7 @@ input GenerationAgreementSignedDocumentInput {
|
||||
version: String!
|
||||
}
|
||||
|
||||
input GenerationAgreementSignedMetaDocumentInput {
|
||||
input GenerationContractSignedMetaDocumentInput {
|
||||
"""Номер блока, на котором был создан документ"""
|
||||
block_num: Int!
|
||||
|
||||
@@ -6551,7 +6551,7 @@ type Mutation {
|
||||
capitalGenerateCapitalizationToMainWalletConvertStatement(data: GenerateDocumentInput!, options: GenerateDocumentOptionsInput): GeneratedDocument!
|
||||
|
||||
"""Сгенерировать документ дополнения к приложению для компонента"""
|
||||
capitalGenerateComponentGenerationAgreement(data: ComponentGenerationAgreementGenerateDocumentInput!, options: GenerateDocumentOptionsInput): GeneratedDocument!
|
||||
capitalGenerateComponentGenerationContract(data: ComponentGenerationContractGenerateDocumentInput!, options: GenerateDocumentOptionsInput): GeneratedDocument!
|
||||
|
||||
"""Сгенерировать решение о расходе"""
|
||||
capitalGenerateExpenseDecision(data: GenerateDocumentInput!, options: GenerateDocumentOptionsInput): GeneratedDocument!
|
||||
@@ -6560,7 +6560,7 @@ type Mutation {
|
||||
capitalGenerateExpenseStatement(data: GenerateDocumentInput!, options: GenerateDocumentOptionsInput): GeneratedDocument!
|
||||
|
||||
"""Сгенерировать генерационное соглашение"""
|
||||
capitalGenerateGenerationAgreement(data: GenerationAgreementGenerateDocumentInput!, options: GenerateDocumentOptionsInput): GeneratedDocument!
|
||||
capitalGenerateGenerationContract(data: GenerationContractGenerateDocumentInput!, options: GenerateDocumentOptionsInput): GeneratedDocument!
|
||||
|
||||
"""Сгенерировать заявление об инвестировании в генерацию"""
|
||||
capitalGenerateGenerationMoneyInvestStatement(data: GenerateDocumentInput!, options: GenerateDocumentOptionsInput): GeneratedDocument!
|
||||
@@ -6593,7 +6593,7 @@ type Mutation {
|
||||
capitalGenerateGetLoanStatement(data: GenerateDocumentInput!, options: GenerateDocumentOptionsInput): GeneratedDocument!
|
||||
|
||||
"""Сгенерировать документ приложения к договору участия для проекта"""
|
||||
capitalGenerateProjectGenerationAgreement(data: ProjectGenerationAgreementGenerateDocumentInput!, options: GenerateDocumentOptionsInput): GeneratedDocument!
|
||||
capitalGenerateProjectGenerationContract(data: ProjectGenerationContractGenerateDocumentInput!, options: GenerateDocumentOptionsInput): GeneratedDocument!
|
||||
|
||||
"""Сгенерировать акт о вкладе результатов"""
|
||||
capitalGenerateResultContributionAct(data: GenerateDocumentInput!, options: GenerateDocumentOptionsInput): GeneratedDocument!
|
||||
@@ -7872,6 +7872,12 @@ enum ProgramInvestStatus {
|
||||
UNDEFINED
|
||||
}
|
||||
|
||||
"""Ключ выбранной программы регистрации"""
|
||||
enum ProgramKey {
|
||||
CAPITALIZATION
|
||||
GENERATION
|
||||
}
|
||||
|
||||
input ProhibitRequestInput {
|
||||
"""Имя аккаунта кооператива"""
|
||||
coopname: String!
|
||||
@@ -7979,7 +7985,7 @@ input ProjectFreeDecisionSignedMetaDocumentInput {
|
||||
version: String!
|
||||
}
|
||||
|
||||
input ProjectGenerationAgreementGenerateDocumentInput {
|
||||
input ProjectGenerationContractGenerateDocumentInput {
|
||||
"""Номер блока, на котором был создан документ"""
|
||||
block_num: Int
|
||||
|
||||
@@ -8510,7 +8516,7 @@ input RegisterContributorInput {
|
||||
about: String
|
||||
|
||||
"""Документ контракта"""
|
||||
contract: GenerationAgreementSignedDocumentInput!
|
||||
contract: GenerationContractSignedDocumentInput!
|
||||
|
||||
"""Хэш участника для верификации документа"""
|
||||
contributor_hash: String!
|
||||
@@ -8529,17 +8535,25 @@ input RegisterContributorInput {
|
||||
}
|
||||
|
||||
input RegisterParticipantInput {
|
||||
"""Имя кооперативного участка"""
|
||||
braname: String
|
||||
|
||||
"""
|
||||
Подписанный документ соглашения по капитализации (опционально, только если требуется)
|
||||
"""
|
||||
blagorost_offer: SignedDigitalDocumentInput
|
||||
|
||||
"""Имя кооперативного участка"""
|
||||
braname: String
|
||||
|
||||
"""
|
||||
Подписанный документ оферты по программе "Генератор" (опционально, только для программы generation)
|
||||
"""
|
||||
generator_offer: SignedDigitalDocumentInput
|
||||
|
||||
"""Подписанный документ политики конфиденциальности от пайщика"""
|
||||
privacy_agreement: SignedDigitalDocumentInput!
|
||||
|
||||
"""Ключ выбранной программы регистрации"""
|
||||
program_key: ProgramKey
|
||||
|
||||
"""Подписанный документ положения о цифровой подписи от пайщика"""
|
||||
signature_agreement: SignedDigitalDocumentInput!
|
||||
|
||||
|
||||
+13
-13
@@ -9,10 +9,10 @@ import { SignedDigitalDocumentInputDTO } from '~/application/document/dto/signed
|
||||
type ExcludeCommonProps<T> = Omit<T, 'coopname' | 'username' | 'registry_id' | 'block_num' | 'lang'>;
|
||||
|
||||
// интерфейс параметров для генерации
|
||||
type action = Cooperative.Registry.ComponentGenerationAgreement.Action;
|
||||
type action = Cooperative.Registry.ComponentGenerationContract.Action;
|
||||
|
||||
@InputType(`BaseComponentGenerationAgreementMetaDocumentInput`)
|
||||
class BaseComponentGenerationAgreementMetaDocumentInputDTO implements ExcludeCommonProps<action> {
|
||||
@InputType(`BaseComponentGenerationContractMetaDocumentInput`)
|
||||
class BaseComponentGenerationContractMetaDocumentInputDTO implements ExcludeCommonProps<action> {
|
||||
@Field({ description: 'Хэш компонента (проекта)' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@@ -24,17 +24,17 @@ class BaseComponentGenerationAgreementMetaDocumentInputDTO implements ExcludeCom
|
||||
parent_project_hash!: string;
|
||||
}
|
||||
|
||||
@InputType(`ComponentGenerationAgreementGenerateDocumentInput`)
|
||||
export class ComponentGenerationAgreementGenerateDocumentInputDTO extends IntersectionType(
|
||||
BaseComponentGenerationAgreementMetaDocumentInputDTO,
|
||||
@InputType(`ComponentGenerationContractGenerateDocumentInput`)
|
||||
export class ComponentGenerationContractGenerateDocumentInputDTO extends IntersectionType(
|
||||
BaseComponentGenerationContractMetaDocumentInputDTO,
|
||||
OmitType(GenerateMetaDocumentInputDTO, ['registry_id'] as const)
|
||||
) {
|
||||
registry_id!: number;
|
||||
}
|
||||
|
||||
@InputType(`ComponentGenerationAgreementSignedMetaDocumentInput`)
|
||||
export class ComponentGenerationAgreementSignedMetaDocumentInputDTO extends IntersectionType(
|
||||
BaseComponentGenerationAgreementMetaDocumentInputDTO,
|
||||
@InputType(`ComponentGenerationContractSignedMetaDocumentInput`)
|
||||
export class ComponentGenerationContractSignedMetaDocumentInputDTO extends IntersectionType(
|
||||
BaseComponentGenerationContractMetaDocumentInputDTO,
|
||||
MetaDocumentInputDTO
|
||||
) {
|
||||
@Field({ description: 'Хэш дополнения к приложению (для компонента)' })
|
||||
@@ -70,10 +70,10 @@ export class ComponentGenerationAgreementSignedMetaDocumentInputDTO extends Inte
|
||||
project_id!: string;
|
||||
}
|
||||
|
||||
@InputType(`ComponentGenerationAgreementSignedDocumentInput`)
|
||||
export class ComponentGenerationAgreementSignedDocumentInputDTO extends SignedDigitalDocumentInputDTO {
|
||||
@Field(() => ComponentGenerationAgreementSignedMetaDocumentInputDTO, {
|
||||
@InputType(`ComponentGenerationContractSignedDocumentInput`)
|
||||
export class ComponentGenerationContractSignedDocumentInputDTO extends SignedDigitalDocumentInputDTO {
|
||||
@Field(() => ComponentGenerationContractSignedMetaDocumentInputDTO, {
|
||||
description: 'Метаинформация для документа дополнения к приложению договора участия в хозяйственной деятельности для компонентов',
|
||||
})
|
||||
public readonly meta!: ComponentGenerationAgreementSignedMetaDocumentInputDTO;
|
||||
public readonly meta!: ComponentGenerationContractSignedMetaDocumentInputDTO;
|
||||
}
|
||||
|
||||
+13
-13
@@ -9,18 +9,18 @@ import { SignedDigitalDocumentInputDTO } from '~/application/document/dto/signed
|
||||
type ExcludeCommonProps<T> = Omit<T, 'coopname' | 'username' | 'registry_id'>;
|
||||
|
||||
// интерфейс параметров для генерации
|
||||
type action = Cooperative.Registry.GenerationAgreement.Action;
|
||||
type action = Cooperative.Registry.GenerationContract.Action;
|
||||
|
||||
@InputType(`BaseGenerationAgreementMetaDocumentInput`)
|
||||
class BaseGenerationAgreementMetaDocumentInputDTO implements ExcludeCommonProps<action> {
|
||||
@InputType(`BaseGenerationContractMetaDocumentInput`)
|
||||
class BaseGenerationContractMetaDocumentInputDTO implements ExcludeCommonProps<action> {
|
||||
@Field({ description: 'Хэш участника для генерации соглашения' })
|
||||
@IsString()
|
||||
contributor_hash!: string;
|
||||
}
|
||||
|
||||
@InputType(`GenerationAgreementGenerateDocumentInput`)
|
||||
export class GenerationAgreementGenerateDocumentInputDTO extends IntersectionType(
|
||||
BaseGenerationAgreementMetaDocumentInputDTO,
|
||||
@InputType(`GenerationContractGenerateDocumentInput`)
|
||||
export class GenerationContractGenerateDocumentInputDTO extends IntersectionType(
|
||||
BaseGenerationContractMetaDocumentInputDTO,
|
||||
OmitType(GenerateMetaDocumentInputDTO, ['registry_id'] as const)
|
||||
) {
|
||||
registry_id!: number;
|
||||
@@ -30,15 +30,15 @@ export class GenerationAgreementGenerateDocumentInputDTO extends IntersectionTyp
|
||||
}
|
||||
}
|
||||
|
||||
@InputType(`GenerationAgreementSignedMetaDocumentInput`)
|
||||
export class GenerationAgreementSignedMetaDocumentInputDTO
|
||||
extends IntersectionType(BaseGenerationAgreementMetaDocumentInputDTO, MetaDocumentInputDTO)
|
||||
@InputType(`GenerationContractSignedMetaDocumentInput`)
|
||||
export class GenerationContractSignedMetaDocumentInputDTO
|
||||
extends IntersectionType(BaseGenerationContractMetaDocumentInputDTO, MetaDocumentInputDTO)
|
||||
implements action {}
|
||||
|
||||
@InputType(`GenerationAgreementSignedDocumentInput`)
|
||||
export class GenerationAgreementSignedDocumentInputDTO extends SignedDigitalDocumentInputDTO {
|
||||
@Field(() => GenerationAgreementSignedMetaDocumentInputDTO, {
|
||||
@InputType(`GenerationContractSignedDocumentInput`)
|
||||
export class GenerationContractSignedDocumentInputDTO extends SignedDigitalDocumentInputDTO {
|
||||
@Field(() => GenerationContractSignedMetaDocumentInputDTO, {
|
||||
description: 'Метаинформация для документа договора участия в хозяйственной деятельности',
|
||||
})
|
||||
public readonly meta!: GenerationAgreementSignedMetaDocumentInputDTO;
|
||||
public readonly meta!: GenerationContractSignedMetaDocumentInputDTO;
|
||||
}
|
||||
|
||||
+13
-13
@@ -9,27 +9,27 @@ import { SignedDigitalDocumentInputDTO } from '~/application/document/dto/signed
|
||||
type ExcludeCommonProps<T> = Omit<T, 'coopname' | 'username' | 'registry_id' | 'block_num' | 'lang'>;
|
||||
|
||||
// интерфейс параметров для генерации
|
||||
type action = Cooperative.Registry.ProjectGenerationAgreement.Action;
|
||||
type action = Cooperative.Registry.ProjectGenerationContract.Action;
|
||||
|
||||
@InputType(`BaseProjectGenerationAgreementMetaDocumentInput`)
|
||||
class BaseProjectGenerationAgreementMetaDocumentInputDTO implements ExcludeCommonProps<action> {
|
||||
@InputType(`BaseProjectGenerationContractMetaDocumentInput`)
|
||||
class BaseProjectGenerationContractMetaDocumentInputDTO implements ExcludeCommonProps<action> {
|
||||
@Field({ description: 'Хэш проекта' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
project_hash!: string;
|
||||
}
|
||||
|
||||
@InputType(`ProjectGenerationAgreementGenerateDocumentInput`)
|
||||
export class ProjectGenerationAgreementGenerateDocumentInputDTO extends IntersectionType(
|
||||
BaseProjectGenerationAgreementMetaDocumentInputDTO,
|
||||
@InputType(`ProjectGenerationContractGenerateDocumentInput`)
|
||||
export class ProjectGenerationContractGenerateDocumentInputDTO extends IntersectionType(
|
||||
BaseProjectGenerationContractMetaDocumentInputDTO,
|
||||
OmitType(GenerateMetaDocumentInputDTO, ['registry_id'] as const)
|
||||
) {
|
||||
registry_id!: number;
|
||||
}
|
||||
|
||||
@InputType(`ProjectGenerationAgreementSignedMetaDocumentInput`)
|
||||
export class ProjectGenerationAgreementSignedMetaDocumentInputDTO extends IntersectionType(
|
||||
BaseProjectGenerationAgreementMetaDocumentInputDTO,
|
||||
@InputType(`ProjectGenerationContractSignedMetaDocumentInput`)
|
||||
export class ProjectGenerationContractSignedMetaDocumentInputDTO extends IntersectionType(
|
||||
BaseProjectGenerationContractMetaDocumentInputDTO,
|
||||
MetaDocumentInputDTO
|
||||
) {
|
||||
@Field({ description: 'Хэш приложения' })
|
||||
@@ -53,10 +53,10 @@ export class ProjectGenerationAgreementSignedMetaDocumentInputDTO extends Inters
|
||||
project_id!: string;
|
||||
}
|
||||
|
||||
@InputType(`ProjectGenerationAgreementSignedDocumentInput`)
|
||||
export class ProjectGenerationAgreementSignedDocumentInputDTO extends SignedDigitalDocumentInputDTO {
|
||||
@Field(() => ProjectGenerationAgreementSignedMetaDocumentInputDTO, {
|
||||
@InputType(`ProjectGenerationContractSignedDocumentInput`)
|
||||
export class ProjectGenerationContractSignedDocumentInputDTO extends SignedDigitalDocumentInputDTO {
|
||||
@Field(() => ProjectGenerationContractSignedMetaDocumentInputDTO, {
|
||||
description: 'Метаинформация для документа приложения к договору участия в хозяйственной деятельности для проектов',
|
||||
})
|
||||
public readonly meta!: ProjectGenerationAgreementSignedMetaDocumentInputDTO;
|
||||
public readonly meta!: ProjectGenerationContractSignedMetaDocumentInputDTO;
|
||||
}
|
||||
|
||||
+3
-3
@@ -25,9 +25,9 @@ export class CooperativeConfigService {
|
||||
* Получить дополнительные соглашения по умолчанию для кооператива
|
||||
*/
|
||||
getDefaultAdditionalAgreements(coopname: string, accountType: AccountType): AgreementId[] {
|
||||
if (coopname === 'voskhod' && accountType === AccountType.individual) {
|
||||
return [AgreementId.BLAGOROST_OFFER];
|
||||
}
|
||||
// if (coopname === 'voskhod' && accountType === AccountType.individual) {
|
||||
// return [AgreementId.BLAGOROST_OFFER];
|
||||
// }
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { IsEnum, IsNotEmpty, IsOptional, IsString, MaxLength } from 'class-valid
|
||||
|
||||
export enum CapitalOnboardingStepEnum {
|
||||
generator_program_template = 'generator_program_template',
|
||||
generation_agreement_template = 'generation_agreement_template',
|
||||
generation_contract_template = 'generation_contract_template',
|
||||
blagorost_program = 'blagorost_program',
|
||||
blagorost_offer_template = 'blagorost_offer_template',
|
||||
}
|
||||
@@ -44,10 +44,10 @@ export class CapitalOnboardingStateDTO {
|
||||
onboarding_generator_program_template_hash?: string | null;
|
||||
|
||||
@Field(() => Boolean)
|
||||
generation_agreement_template_done!: boolean;
|
||||
generation_contract_template_done!: boolean;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
onboarding_generation_agreement_template_hash?: string | null;
|
||||
onboarding_generation_contract_template_hash?: string | null;
|
||||
|
||||
@Field(() => Boolean)
|
||||
blagorost_provision_done!: boolean;
|
||||
|
||||
+4
-4
@@ -2,7 +2,7 @@ import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import type { RegisterContributorDomainInput } from '../../../domain/actions/register-contributor-domain-input.interface';
|
||||
import { GenerationAgreementSignedDocumentInputDTO } from '~/application/document/documents-dto/generation-agreement-document.dto';
|
||||
import { GenerationContractSignedDocumentInputDTO } from '~/application/document/documents-dto/generation-agreement-document.dto';
|
||||
|
||||
/**
|
||||
* GraphQL DTO для регистрации участника CAPITAL контракта
|
||||
@@ -40,7 +40,7 @@ export class RegisterContributorInputDTO implements RegisterContributorDomainInp
|
||||
@Type(() => Number)
|
||||
hours_per_day?: number;
|
||||
|
||||
@Field(() => GenerationAgreementSignedDocumentInputDTO, { description: 'Документ контракта' })
|
||||
@Type(() => GenerationAgreementSignedDocumentInputDTO)
|
||||
contract!: GenerationAgreementSignedDocumentInputDTO;
|
||||
@Field(() => GenerationContractSignedDocumentInputDTO, { description: 'Документ контракта' })
|
||||
@Type(() => GenerationContractSignedDocumentInputDTO)
|
||||
contract!: GenerationContractSignedDocumentInputDTO;
|
||||
}
|
||||
|
||||
+18
-18
@@ -16,9 +16,9 @@ import { GetContributorInputDTO } from '../dto/participation_management/get-cont
|
||||
import { createPaginationResult, PaginationInputDTO, PaginationResult } from '~/application/common/dto/pagination.dto';
|
||||
import { GeneratedDocumentDTO } from '~/application/document/dto/generated-document.dto';
|
||||
import { GenerateDocumentOptionsInputDTO } from '~/application/document/dto/generate-document-options-input.dto';
|
||||
import { GenerationAgreementGenerateDocumentInputDTO } from '~/application/document/documents-dto/generation-agreement-document.dto';
|
||||
import { ProjectGenerationAgreementGenerateDocumentInputDTO } from '~/application/document/documents-dto/project-generation-agreement-document.dto';
|
||||
import { ComponentGenerationAgreementGenerateDocumentInputDTO } from '~/application/document/documents-dto/component-generation-agreement-document.dto';
|
||||
import { GenerationContractGenerateDocumentInputDTO } from '~/application/document/documents-dto/generation-agreement-document.dto';
|
||||
import { ProjectGenerationContractGenerateDocumentInputDTO } from '~/application/document/documents-dto/project-generation-agreement-document.dto';
|
||||
import { ComponentGenerationContractGenerateDocumentInputDTO } from '~/application/document/documents-dto/component-generation-agreement-document.dto';
|
||||
import { GenerateDocumentInputDTO } from '~/application/document/dto/generate-document-input.dto';
|
||||
|
||||
/**
|
||||
@@ -155,56 +155,56 @@ export class ParticipationManagementResolver {
|
||||
* Мутация для генерации генерационного соглашения
|
||||
*/
|
||||
@Mutation(() => GeneratedDocumentDTO, {
|
||||
name: 'capitalGenerateGenerationAgreement',
|
||||
name: 'capitalGenerateGenerationContract',
|
||||
description: 'Сгенерировать генерационное соглашение',
|
||||
})
|
||||
@Throttle({ default: { limit: 3, ttl: 60000 } })
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
@AuthRoles(['chairman', 'member'])
|
||||
async generateGenerationAgreement(
|
||||
@Args('data', { type: () => GenerationAgreementGenerateDocumentInputDTO })
|
||||
data: GenerationAgreementGenerateDocumentInputDTO,
|
||||
async generateGenerationContract(
|
||||
@Args('data', { type: () => GenerationContractGenerateDocumentInputDTO })
|
||||
data: GenerationContractGenerateDocumentInputDTO,
|
||||
@Args('options', { type: () => GenerateDocumentOptionsInputDTO, nullable: true })
|
||||
options: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
return this.participationManagementService.generateGenerationAgreement(data, options);
|
||||
return this.participationManagementService.generateGenerationContract(data, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Мутация для генерации документа приложения к договору участия для проекта (1002)
|
||||
*/
|
||||
@Mutation(() => GeneratedDocumentDTO, {
|
||||
name: 'capitalGenerateProjectGenerationAgreement',
|
||||
name: 'capitalGenerateProjectGenerationContract',
|
||||
description: 'Сгенерировать документ приложения к договору участия для проекта',
|
||||
})
|
||||
@Throttle({ default: { limit: 3, ttl: 60000 } })
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
@AuthRoles(['chairman', 'member'])
|
||||
async generateProjectGenerationAgreement(
|
||||
@Args('data', { type: () => ProjectGenerationAgreementGenerateDocumentInputDTO })
|
||||
data: ProjectGenerationAgreementGenerateDocumentInputDTO,
|
||||
async generateProjectGenerationContract(
|
||||
@Args('data', { type: () => ProjectGenerationContractGenerateDocumentInputDTO })
|
||||
data: ProjectGenerationContractGenerateDocumentInputDTO,
|
||||
@Args('options', { type: () => GenerateDocumentOptionsInputDTO, nullable: true })
|
||||
options: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
return this.participationManagementService.generateProjectGenerationAgreement(data, options);
|
||||
return this.participationManagementService.generateProjectGenerationContract(data, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Мутация для генерации документа дополнения к приложению для компонента (1003)
|
||||
*/
|
||||
@Mutation(() => GeneratedDocumentDTO, {
|
||||
name: 'capitalGenerateComponentGenerationAgreement',
|
||||
name: 'capitalGenerateComponentGenerationContract',
|
||||
description: 'Сгенерировать документ дополнения к приложению для компонента',
|
||||
})
|
||||
@Throttle({ default: { limit: 3, ttl: 60000 } })
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
@AuthRoles(['chairman', 'member'])
|
||||
async generateComponentGenerationAgreement(
|
||||
@Args('data', { type: () => ComponentGenerationAgreementGenerateDocumentInputDTO })
|
||||
data: ComponentGenerationAgreementGenerateDocumentInputDTO,
|
||||
async generateComponentGenerationContract(
|
||||
@Args('data', { type: () => ComponentGenerationContractGenerateDocumentInputDTO })
|
||||
data: ComponentGenerationContractGenerateDocumentInputDTO,
|
||||
@Args('options', { type: () => GenerateDocumentOptionsInputDTO, nullable: true })
|
||||
options: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
return this.participationManagementService.generateComponentGenerationAgreement(data, options);
|
||||
return this.participationManagementService.generateComponentGenerationContract(data, options);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ export class CapitalOnboardingEventsService {
|
||||
private mapStepToFlag(step: string): keyof IConfig | null {
|
||||
const mapping: Record<string, keyof IConfig> = {
|
||||
generator_program_template: 'onboarding_generator_program_template_done',
|
||||
generation_agreement_template: 'onboarding_generation_agreement_template_done',
|
||||
generation_contract_template: 'onboarding_generation_contract_template_done',
|
||||
blagorost_program: 'onboarding_blagorost_provision_done',
|
||||
blagorost_offer_template: 'onboarding_blagorost_offer_template_done',
|
||||
};
|
||||
|
||||
+10
-10
@@ -16,13 +16,13 @@ import { DecisionEventType } from '~/domain/decision-tracking/interfaces/trackin
|
||||
|
||||
type OnboardingFlagKey =
|
||||
| 'onboarding_generator_program_template_done'
|
||||
| 'onboarding_generation_agreement_template_done'
|
||||
| 'onboarding_generation_contract_template_done'
|
||||
| 'onboarding_blagorost_provision_done'
|
||||
| 'onboarding_blagorost_offer_template_done';
|
||||
|
||||
type OnboardingHashKey =
|
||||
| 'onboarding_generator_program_template_hash'
|
||||
| 'onboarding_generation_agreement_template_hash'
|
||||
| 'onboarding_generation_contract_template_hash'
|
||||
| 'onboarding_blagorost_provision_hash'
|
||||
| 'onboarding_blagorost_offer_template_hash';
|
||||
|
||||
@@ -38,8 +38,8 @@ export class CapitalOnboardingService {
|
||||
switch (step) {
|
||||
case CapitalOnboardingStepEnum.generator_program_template:
|
||||
return 'onboarding_generator_program_template_done';
|
||||
case CapitalOnboardingStepEnum.generation_agreement_template:
|
||||
return 'onboarding_generation_agreement_template_done';
|
||||
case CapitalOnboardingStepEnum.generation_contract_template:
|
||||
return 'onboarding_generation_contract_template_done';
|
||||
case CapitalOnboardingStepEnum.blagorost_program:
|
||||
return 'onboarding_blagorost_provision_done';
|
||||
case CapitalOnboardingStepEnum.blagorost_offer_template:
|
||||
@@ -53,8 +53,8 @@ export class CapitalOnboardingService {
|
||||
switch (step) {
|
||||
case CapitalOnboardingStepEnum.generator_program_template:
|
||||
return 'onboarding_generator_program_template_hash';
|
||||
case CapitalOnboardingStepEnum.generation_agreement_template:
|
||||
return 'onboarding_generation_agreement_template_hash';
|
||||
case CapitalOnboardingStepEnum.generation_contract_template:
|
||||
return 'onboarding_generation_contract_template_hash';
|
||||
case CapitalOnboardingStepEnum.blagorost_program:
|
||||
return 'onboarding_blagorost_provision_hash';
|
||||
case CapitalOnboardingStepEnum.blagorost_offer_template:
|
||||
@@ -68,8 +68,8 @@ export class CapitalOnboardingService {
|
||||
switch (step) {
|
||||
case CapitalOnboardingStepEnum.generator_program_template:
|
||||
return 'generator_program';
|
||||
case CapitalOnboardingStepEnum.generation_agreement_template:
|
||||
return 'generation_agreement_template';
|
||||
case CapitalOnboardingStepEnum.generation_contract_template:
|
||||
return 'generation_contract_template';
|
||||
case CapitalOnboardingStepEnum.blagorost_program:
|
||||
return 'blagorost_program';
|
||||
case CapitalOnboardingStepEnum.blagorost_offer_template:
|
||||
@@ -109,8 +109,8 @@ export class CapitalOnboardingService {
|
||||
return {
|
||||
generator_program_template_done: !!pluginConfig.onboarding_generator_program_template_done,
|
||||
onboarding_generator_program_template_hash: pluginConfig.onboarding_generator_program_template_hash || null,
|
||||
generation_agreement_template_done: !!pluginConfig.onboarding_generation_agreement_template_done,
|
||||
onboarding_generation_agreement_template_hash: pluginConfig.onboarding_generation_agreement_template_hash || null,
|
||||
generation_contract_template_done: !!pluginConfig.onboarding_generation_contract_template_done,
|
||||
onboarding_generation_contract_template_hash: pluginConfig.onboarding_generation_contract_template_hash || null,
|
||||
blagorost_provision_done: !!pluginConfig.onboarding_blagorost_provision_done,
|
||||
onboarding_blagorost_provision_hash: pluginConfig.onboarding_blagorost_provision_hash || null,
|
||||
blagorost_offer_template_done: !!pluginConfig.onboarding_blagorost_offer_template_done,
|
||||
|
||||
+12
-12
@@ -17,9 +17,9 @@ import { DocumentInteractor } from '~/application/document/interactors/document.
|
||||
import { ContributorMapperService } from './contributor-mapper.service';
|
||||
import { ContributorSyncService } from '../syncers/contributor-sync.service';
|
||||
import { Cooperative } from 'cooptypes';
|
||||
import { GenerationAgreementGenerateDocumentInputDTO } from '~/application/document/documents-dto/generation-agreement-document.dto';
|
||||
import { ProjectGenerationAgreementGenerateDocumentInputDTO } from '~/application/document/documents-dto/project-generation-agreement-document.dto';
|
||||
import { ComponentGenerationAgreementGenerateDocumentInputDTO } from '~/application/document/documents-dto/component-generation-agreement-document.dto';
|
||||
import { GenerationContractGenerateDocumentInputDTO } from '~/application/document/documents-dto/generation-agreement-document.dto';
|
||||
import { ProjectGenerationContractGenerateDocumentInputDTO } from '~/application/document/documents-dto/project-generation-agreement-document.dto';
|
||||
import { ComponentGenerationContractGenerateDocumentInputDTO } from '~/application/document/documents-dto/component-generation-agreement-document.dto';
|
||||
|
||||
/**
|
||||
* Сервис уровня приложения для управления участием в CAPITAL
|
||||
@@ -53,21 +53,21 @@ export class ParticipationManagementService {
|
||||
/**
|
||||
* Генерация документа приложения к договору участия для проекта (1002)
|
||||
*/
|
||||
async generateProjectGenerationAgreement(
|
||||
data: ProjectGenerationAgreementGenerateDocumentInputDTO,
|
||||
async generateProjectGenerationContract(
|
||||
data: ProjectGenerationContractGenerateDocumentInputDTO,
|
||||
options?: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
return await this.participationManagementInteractor.generateProjectGenerationAgreement(data, options);
|
||||
return await this.participationManagementInteractor.generateProjectGenerationContract(data, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерация документа дополнения к приложению для компонента (1003)
|
||||
*/
|
||||
async generateComponentGenerationAgreement(
|
||||
data: ComponentGenerationAgreementGenerateDocumentInputDTO,
|
||||
async generateComponentGenerationContract(
|
||||
data: ComponentGenerationContractGenerateDocumentInputDTO,
|
||||
options?: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
return await this.participationManagementInteractor.generateComponentGenerationAgreement(data, options);
|
||||
return await this.participationManagementInteractor.generateComponentGenerationContract(data, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,14 +171,14 @@ export class ParticipationManagementService {
|
||||
/**
|
||||
* Генерация генерационного соглашения
|
||||
*/
|
||||
async generateGenerationAgreement(
|
||||
data: GenerationAgreementGenerateDocumentInputDTO,
|
||||
async generateGenerationContract(
|
||||
data: GenerationContractGenerateDocumentInputDTO,
|
||||
options: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
const document = await this.documentInteractor.generateDocument({
|
||||
data: {
|
||||
...data,
|
||||
registry_id: Cooperative.Registry.GenerationAgreement.registry_id,
|
||||
registry_id: Cooperative.Registry.GenerationContract.registry_id,
|
||||
},
|
||||
options,
|
||||
});
|
||||
|
||||
+10
-10
@@ -27,8 +27,8 @@ import type {
|
||||
PaginationInputDomainInterface,
|
||||
PaginationResultDomainInterface,
|
||||
} from '~/domain/common/interfaces/pagination.interface';
|
||||
import type { ProjectGenerationAgreementGenerateDocumentInputDTO } from '~/application/document/documents-dto/project-generation-agreement-document.dto';
|
||||
import type { ComponentGenerationAgreementGenerateDocumentInputDTO } from '~/application/document/documents-dto/component-generation-agreement-document.dto';
|
||||
import type { ProjectGenerationContractGenerateDocumentInputDTO } from '~/application/document/documents-dto/project-generation-agreement-document.dto';
|
||||
import type { ComponentGenerationContractGenerateDocumentInputDTO } from '~/application/document/documents-dto/component-generation-agreement-document.dto';
|
||||
import type { GenerateDocumentOptionsInputDTO } from '~/application/document/dto/generate-document-options-input.dto';
|
||||
import type { GeneratedDocumentDTO } from '~/application/document/dto/generated-document.dto';
|
||||
import { DomainToBlockchainUtils } from '~/shared/utils/domain-to-blockchain.utils';
|
||||
@@ -362,8 +362,8 @@ export class ParticipationManagementInteractor {
|
||||
/**
|
||||
* Генерация документа приложения к договору участия для проекта (1002)
|
||||
*/
|
||||
async generateProjectGenerationAgreement(
|
||||
data: ProjectGenerationAgreementGenerateDocumentInputDTO,
|
||||
async generateProjectGenerationContract(
|
||||
data: ProjectGenerationContractGenerateDocumentInputDTO,
|
||||
options?: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
// 1. Получаем данные участника
|
||||
@@ -398,7 +398,7 @@ export class ParticipationManagementInteractor {
|
||||
if (isComponent) {
|
||||
throw new HttpApiError(
|
||||
httpStatus.BAD_REQUEST,
|
||||
`Проект ${data.project_hash} является компонентом. Используйте generateComponentGenerationAgreement`
|
||||
`Проект ${data.project_hash} является компонентом. Используйте generateComponentGenerationContract`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -410,7 +410,7 @@ export class ParticipationManagementInteractor {
|
||||
coopname: data.coopname,
|
||||
username: data.username,
|
||||
lang: data.lang || 'ru',
|
||||
registry_id: Cooperative.Registry.ProjectGenerationAgreement.registry_id,
|
||||
registry_id: Cooperative.Registry.ProjectGenerationContract.registry_id,
|
||||
appendix_hash,
|
||||
contributor_hash: contributor.contributor_hash,
|
||||
contributor_created_at: contributor.created_at,
|
||||
@@ -430,8 +430,8 @@ export class ParticipationManagementInteractor {
|
||||
/**
|
||||
* Генерация документа дополнения к приложению для компонента (1003)
|
||||
*/
|
||||
async generateComponentGenerationAgreement(
|
||||
data: ComponentGenerationAgreementGenerateDocumentInputDTO,
|
||||
async generateComponentGenerationContract(
|
||||
data: ComponentGenerationContractGenerateDocumentInputDTO,
|
||||
options?: GenerateDocumentOptionsInputDTO
|
||||
): Promise<GeneratedDocumentDTO> {
|
||||
// 1. Получаем данные участника
|
||||
@@ -466,7 +466,7 @@ export class ParticipationManagementInteractor {
|
||||
if (!isComponent) {
|
||||
throw new HttpApiError(
|
||||
httpStatus.BAD_REQUEST,
|
||||
`Проект ${data.component_hash} не является компонентом. Используйте generateProjectGenerationAgreement`
|
||||
`Проект ${data.component_hash} не является компонентом. Используйте generateProjectGenerationContract`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -511,7 +511,7 @@ export class ParticipationManagementInteractor {
|
||||
coopname: data.coopname,
|
||||
username: data.username,
|
||||
lang: data.lang || 'ru',
|
||||
registry_id: Cooperative.Registry.ComponentGenerationAgreement.registry_id,
|
||||
registry_id: Cooperative.Registry.ComponentGenerationContract.registry_id,
|
||||
component_appendix_hash,
|
||||
parent_appendix_hash: parentAppendix.appendix_hash,
|
||||
contributor_hash: contributor.contributor_hash,
|
||||
|
||||
@@ -35,7 +35,7 @@ export const defaultConfig = {
|
||||
energy_gain_coefficient: 1.0,
|
||||
// Онбординг флаги
|
||||
onboarding_generator_program_template_done: false,
|
||||
onboarding_generation_agreement_template_done: false,
|
||||
onboarding_generation_contract_template_done: false,
|
||||
onboarding_blagorost_provision_done: false,
|
||||
onboarding_blagorost_offer_template_done: false,
|
||||
} as const;
|
||||
@@ -159,9 +159,9 @@ export const Schema = z.object({
|
||||
.boolean()
|
||||
.default(defaultConfig.onboarding_generator_program_template_done)
|
||||
.describe(describeField({ label: 'Шаг положения о программе ГЕНЕРАТОР выполнен', visible: false })),
|
||||
onboarding_generation_agreement_template_done: z
|
||||
onboarding_generation_contract_template_done: z
|
||||
.boolean()
|
||||
.default(defaultConfig.onboarding_generation_agreement_template_done)
|
||||
.default(defaultConfig.onboarding_generation_contract_template_done)
|
||||
.describe(describeField({ label: 'Шаг соглашения о генерации выполнен', visible: false })),
|
||||
onboarding_blagorost_provision_done: z
|
||||
.boolean()
|
||||
|
||||
@@ -89,7 +89,7 @@ export interface IVars {
|
||||
protocol_number: string
|
||||
protocol_day_month_year: string
|
||||
}
|
||||
generation_agreement_template?: { // Шаблон договора участия в хозяйственной деятельности
|
||||
generation_contract_template?: { // Шаблон договора участия в хозяйственной деятельности
|
||||
protocol_number: string
|
||||
protocol_day_month_year: string
|
||||
}
|
||||
|
||||
+3
-3
File diff suppressed because one or more lines are too long
+2
-2
@@ -33,7 +33,7 @@ export interface Model {
|
||||
export const title = 'Приложение к договору участия в проекте'
|
||||
export const description = 'Приложение к договору участия в хозяйственной деятельности для проектов'
|
||||
|
||||
export const context = `<div class="digital-document"><div style="text-align: center"><h1>ПРИЛОЖЕНИЕ № {{ short_appendix_hash }}</h1><h2>к Договору об участии в хозяйственной деятельности № УХД-{{ short_contributor_hash }}</h2></div><p style="text-align: right">{{ meta.created_at }}, {{ coop.city }}</p><p>{% trans 'parties_intro' %} "{{ vars.name }}" {% trans 'in_face_of_chairman' %} {{ coop.chairman.last_name }} {{ coop.chairman.first_name }} {{ coop.chairman.middle_name }}, {% trans 'acting_on_basis_of_charter' %}, {% trans 'hereinafter_referred_to_as' %} "{% trans 'society' %}", {% trans 'and_participant' %} {{ user.full_name_or_short_name }}, {% trans 'hereinafter_referred_to_as' %} "{% trans 'participant' %}", {% trans 'jointly_referred_to_as' %} "{% trans 'parties' %}", {% trans 'have_concluded_this_appendix' %} {% trans 'hereinafter_referred_to_as' %} "{% trans 'appendix' %}" {% trans 'to_agreement' %} УХД-{{ short_contributor_hash }} {% trans 'from_date' %} {{ contributor_created_at }} {% trans 'of_the_following' %}:</p><p>{% trans 'according_to_regulations' %} "{{ vars.generation_agreement_template.protocol_number }}" {% trans 'from_date' %} {{ vars.generation_agreement_template.protocol_day_month_year }}), {% trans 'participant_commits_project' %} "{{ project_name }}" (№{{ project_id }}).</p><h2>{% trans 'details_and_signatures_of_parties' %}</h2><p><strong>{% trans 'society' %}/{{ vars.full_abbr }} "{{ vars.name }}"/:</strong></p><p>ИНН {{ coop.details.inn }}, КПП {{ coop.details.kpp }}, ОГРН {{ coop.details.ogrn }}</p><p>{% trans 'legal_address' %}: {{ coop.full_address }}</p><p>{% trans 'contact_phone' %}: {{ coop.phone }}</p><p>{% trans 'email' %}: {{ coop.email }}</p><p>{% trans 'bank_account' %}: {{ coop.defaultBankAccount.account_number }}</p><p>{% trans 'bank_name' %}: {{ coop.defaultBankAccount.bank_name }}</p><p>{% trans 'bik' %}: {{ coop.defaultBankAccount.details.bik }}</p><p>{% trans 'correspondent_account' %}: {{ coop.defaultBankAccount.details.corr }}</p><p>{% trans 'chairman' %} {{ vars.full_abbr_genitive }} "{{ vars.name }}"</p><p>{{ coop.chairman.last_name }} {{ coop.chairman.first_name }} {{ coop.chairman.middle_name }}</p><p>{% trans 'signed_by_digital_signature' %}</p><p><strong>{% trans 'participant' %}:</strong></p><p>{{ user.full_name_or_short_name }}</p><p>{% trans 'contact_phone' %}: {{ user.phone }}</p><p>{% trans 'email' %}: {{ user.email }}</p><p>{% trans 'signed_by_digital_signature' %}</p></div><style>.digital-document {padding: 20px;white-space: pre-wrap;}</style>`
|
||||
export const context = `<div class="digital-document"><div style="text-align: center"><h1>ПРИЛОЖЕНИЕ № {{ short_appendix_hash }}</h1><h2>к Договору об участии в хозяйственной деятельности № УХД-{{ short_contributor_hash }}</h2></div><p style="text-align: right">{{ meta.created_at }}, {{ coop.city }}</p><p>{% trans 'parties_intro' %} "{{ vars.name }}" {% trans 'in_face_of_chairman' %} {{ coop.chairman.last_name }} {{ coop.chairman.first_name }} {{ coop.chairman.middle_name }}, {% trans 'acting_on_basis_of_charter' %}, {% trans 'hereinafter_referred_to_as' %} "{% trans 'society' %}", {% trans 'and_participant' %} {{ user.full_name_or_short_name }}, {% trans 'hereinafter_referred_to_as' %} "{% trans 'participant' %}", {% trans 'jointly_referred_to_as' %} "{% trans 'parties' %}", {% trans 'have_concluded_this_appendix' %} {% trans 'hereinafter_referred_to_as' %} "{% trans 'appendix' %}" {% trans 'to_agreement' %} УХД-{{ short_contributor_hash }} {% trans 'from_date' %} {{ contributor_created_at }} {% trans 'of_the_following' %}:</p><p>{% trans 'according_to_regulations' %} "{{ vars.generation_contract_template.protocol_number }}" {% trans 'from_date' %} {{ vars.generation_contract_template.protocol_day_month_year }}), {% trans 'participant_commits_project' %} "{{ project_name }}" (№{{ project_id }}).</p><h2>{% trans 'details_and_signatures_of_parties' %}</h2><p><strong>{% trans 'society' %}/{{ vars.full_abbr }} "{{ vars.name }}"/:</strong></p><p>ИНН {{ coop.details.inn }}, КПП {{ coop.details.kpp }}, ОГРН {{ coop.details.ogrn }}</p><p>{% trans 'legal_address' %}: {{ coop.full_address }}</p><p>{% trans 'contact_phone' %}: {{ coop.phone }}</p><p>{% trans 'email' %}: {{ coop.email }}</p><p>{% trans 'bank_account' %}: {{ coop.defaultBankAccount.account_number }}</p><p>{% trans 'bank_name' %}: {{ coop.defaultBankAccount.bank_name }}</p><p>{% trans 'bik' %}: {{ coop.defaultBankAccount.details.bik }}</p><p>{% trans 'correspondent_account' %}: {{ coop.defaultBankAccount.details.corr }}</p><p>{% trans 'chairman' %} {{ vars.full_abbr_genitive }} "{{ vars.name }}"</p><p>{{ coop.chairman.last_name }} {{ coop.chairman.first_name }} {{ coop.chairman.middle_name }}</p><p>{% trans 'signed_by_digital_signature' %}</p><p><strong>{% trans 'participant' %}:</strong></p><p>{{ user.full_name_or_short_name }}</p><p>{% trans 'contact_phone' %}: {{ user.phone }}</p><p>{% trans 'email' %}: {{ user.email }}</p><p>{% trans 'signed_by_digital_signature' %}</p></div><style>.digital-document {padding: 20px;white-space: pre-wrap;}</style>`
|
||||
|
||||
export const translations = {
|
||||
ru: {
|
||||
@@ -78,7 +78,7 @@ export const exampleData = {
|
||||
project_name: 'Проект цифровой платформы',
|
||||
project_id: 'B2C3D4E5F6789ABC',
|
||||
vars: {
|
||||
generation_agreement_template: {
|
||||
generation_contract_template: {
|
||||
protocol_number: 'СС-11-04-24',
|
||||
protocol_day_month_year: '11 апреля 2024 г.',
|
||||
},
|
||||
+1
-1
@@ -92,7 +92,7 @@ export const exampleData = {
|
||||
project_name: 'Проект цифровой платформы',
|
||||
project_id: 'B2C3D4E5F6789ABC',
|
||||
vars: {
|
||||
generation_agreement_template: {
|
||||
generation_contract_template: {
|
||||
protocol_number: 'СС-11-04-24',
|
||||
protocol_day_month_year: '11 апреля 2024 г.',
|
||||
},
|
||||
+2
-2
File diff suppressed because one or more lines are too long
@@ -29,10 +29,10 @@ export * as GeneratorOffer from './996.GeneratorOffer'
|
||||
export * as BlagorostOffer from './1000.BlagorostOffer'
|
||||
export * as BlagorostProgramTemplate from './998.BlagorostProgramTemplate'
|
||||
export * as BlagorostOfferTemplate from './999.BlagorostOfferTemplate'
|
||||
export * as GenerationAgreementTemplate from './997.GenerationAgreementTemplate'
|
||||
export * as GenerationAgreement from './1001.GenerationAgreement'
|
||||
export * as ProjectGenerationAgreement from './1002.ProjectGenerationAgreement'
|
||||
export * as ComponentGenerationAgreement from './1003.ComponentGenerationAgreement'
|
||||
export * as GenerationContractTemplate from './997.GenerationContractTemplate'
|
||||
export * as GenerationContract from './1001.GenerationContract'
|
||||
export * as ProjectGenerationContract from './1002.ProjectGenerationContract'
|
||||
export * as ComponentGenerationContract from './1003.ComponentGenerationContract'
|
||||
export * as InitProjectStatement from './1005.InitProjectStatement'
|
||||
export * as InitProjectDecision from './1006.InitProjectDecision'
|
||||
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
---
|
||||
description: Правило реализации диалогов подтверждения для действий
|
||||
alwaysApply: false
|
||||
---
|
||||
## Правило реализации диалогов подтверждения
|
||||
|
||||
### Принцип
|
||||
Диалоги подтверждения должны реализовываться в UI компонентах кнопок, а не в composables функций моделей. Composable отвечает только за бизнес-логику выполнения действия.
|
||||
|
||||
### Структура реализации
|
||||
|
||||
```vue
|
||||
<template lang="pug">
|
||||
q-btn(
|
||||
@click='showDialog = true',
|
||||
:loading='isSubmitting'
|
||||
)
|
||||
// иконка действия
|
||||
|
||||
q-dialog(v-model='showDialog', @hide='close')
|
||||
ModalBase(title='Заголовок действия')
|
||||
Form.q-pa-sm(
|
||||
:handler-submit='handleAction',
|
||||
:is-submitting='isSubmitting',
|
||||
:button-cancel-txt='"Отменить"',
|
||||
:button-submit-txt='"Выполнить"',
|
||||
@cancel='close'
|
||||
)
|
||||
div(style='max-width: 400px')
|
||||
p Подтверждающее сообщение
|
||||
// дополнительные поля ввода если нужны
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// Импорты
|
||||
import { FailAlert, SuccessAlert } from 'src/shared/api';
|
||||
import { useModelFunction } from '../model';
|
||||
import { ref } from 'vue';
|
||||
import { ModalBase } from 'src/shared/ui/ModalBase';
|
||||
import { Form } from 'src/shared/ui/Form';
|
||||
|
||||
// Props, emits
|
||||
const emit = defineEmits(['actionCompleted', 'close']);
|
||||
|
||||
// Состояние
|
||||
const { actionFunction } = useModelFunction();
|
||||
const isSubmitting = ref(false);
|
||||
const showDialog = ref(false);
|
||||
|
||||
// Важно: composable useModelFunction НЕ содержит try-catch!
|
||||
// Он просто выполняет действие и возвращает результат.
|
||||
// Вся обработка ошибок происходит здесь, в UI компоненте.
|
||||
|
||||
// Функции
|
||||
const close = () => {
|
||||
showDialog.value = false;
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const handleAction = async () => {
|
||||
isSubmitting.value = true;
|
||||
try {
|
||||
await actionFunction(params);
|
||||
SuccessAlert('Действие выполнено');
|
||||
emit('actionCompleted');
|
||||
close();
|
||||
} catch (e: any) {
|
||||
FailAlert(`Возникла ошибка: ${e.message}`);
|
||||
close();
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
### Ключевые моменты
|
||||
- **Диалог в компоненте**: Не в composable, а в UI компоненте
|
||||
- **Состояние в компоненте**: `showDialog`, `isSubmitting` управляются локально
|
||||
- **Эмиссия событий**: Для внешней обработки результатов действия
|
||||
- **Composable без try-catch**: Функции модели НЕ содержат обработку ошибок, только выполнение действия
|
||||
- **Уведомления**: Использовать `FailAlert`/`SuccessAlert` вместо прямых вызовов Notify
|
||||
- **Валидация**: Проверять данные перед отправкой в composable
|
||||
- **Очистка состояния**: Сбрасывать поля при закрытии диалога
|
||||
- **Нет window.location.reload()**: Обновление данных через store или emits
|
||||
|
||||
### Примеры применения
|
||||
- Удаление объектов (DeleteStoryButton.vue)
|
||||
- Подтверждение действий (ConfirmApprovalButton.vue)
|
||||
- Отклонение с причиной (DeclineApprovalButton.vue)
|
||||
@@ -1,253 +0,0 @@
|
||||
---
|
||||
globs: ["**/entities/**/*.ts", "**/entities/**/*.vue"]
|
||||
alwaysApply: false
|
||||
---
|
||||
## Архитектура сущностей (Entities Layer)
|
||||
|
||||
### Общее устройство
|
||||
|
||||
Слой сущностей содержит бизнес-сущности приложения и их модели данных. Каждая сущность имеет строгую структуру и отвечает только за хранение и управление данными.
|
||||
|
||||
**Запрещено**: Размещать методы действий (мутаций) в сущностях. Действия должны быть в фичах (features).
|
||||
|
||||
### Структура папок сущности
|
||||
|
||||
```
|
||||
entities/
|
||||
EntityName/
|
||||
index.ts # Экспорт модели и API
|
||||
api/
|
||||
index.ts # Функции для работы с GraphQL API
|
||||
model/
|
||||
index.ts # Экспорт типов и store
|
||||
types.ts # Типы сущностей на основе SDK
|
||||
store.ts # Pinia store для реактивного состояния
|
||||
```
|
||||
|
||||
### Организация типов (model/types.ts)
|
||||
|
||||
#### Импорты
|
||||
```typescript
|
||||
import type { Queries, Mutations, Zeus } from '@coopenomics/sdk';
|
||||
```
|
||||
|
||||
#### Паттерны типизации
|
||||
|
||||
**Для простых сущностей:**
|
||||
```typescript
|
||||
// Тип пагинированного списка
|
||||
export type IEntitiesPagination =
|
||||
Queries.Extension.GetEntities.IOutput[typeof Queries.Extension.GetEntities.name];
|
||||
|
||||
// Тип одной сущности
|
||||
export type IEntity = IEntitiesPagination['items'][0];
|
||||
|
||||
// Типы входных параметров
|
||||
export type IGetEntitiesInput = Queries.Extension.GetEntities.IInput;
|
||||
export type IGetEntityInput = Queries.Extension.GetEntity.IInput['data'];
|
||||
|
||||
// Типы выходных данных мутаций
|
||||
export type ICreateEntityInput = Mutations.Extension.CreateEntity.IInput['data'];
|
||||
export type ICreateEntityOutput =
|
||||
Mutations.Extension.CreateEntity.IOutput[typeof Mutations.Extension.CreateEntity.name];
|
||||
```
|
||||
|
||||
**Для сложных сущностей с отношениями:**
|
||||
```typescript
|
||||
// Базовый тип из Zeus
|
||||
export type IEntity = Zeus.ModelTypes['ExtensionEntity'];
|
||||
|
||||
// Тип с отношениями
|
||||
export type IEntityWithRelations =
|
||||
Queries.Extension.GetEntityWithRelations.IOutput[typeof Queries.Extension.GetEntityWithRelations.name];
|
||||
```
|
||||
|
||||
#### Правила именования типов
|
||||
- `IEntityName` - основная сущность
|
||||
- `IEntityNames` - массив сущностей
|
||||
- `IEntityNamePagination` - пагинированный ответ
|
||||
- `IGetEntityNameInput` - входные параметры для получения
|
||||
- `ICreateEntityNameInput/Output` - вход/выход для создания
|
||||
- `IUpdateEntityNameInput/Output` - вход/выход для обновления
|
||||
- `IDeleteEntityNameInput/Output` - вход/выход для удаления
|
||||
|
||||
### Организация Store (model/store.ts)
|
||||
|
||||
#### Структура store
|
||||
```typescript
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, Ref } from 'vue';
|
||||
import { api } from '../api';
|
||||
import type { IEntity, IEntitiesPagination, IGetEntityInput } from './types';
|
||||
|
||||
const namespace = 'entityStore';
|
||||
|
||||
interface IEntityStore {
|
||||
// Реактивные состояния
|
||||
entities: Ref<IEntitiesPagination | null>;
|
||||
entity: Ref<IEntity | null>;
|
||||
|
||||
// Методы загрузки данных (только запросы!)
|
||||
loadEntities: (data: IGetEntitiesInput) => Promise<void>;
|
||||
loadEntity: (data: IGetEntityInput) => Promise<IEntity>;
|
||||
}
|
||||
|
||||
export const useEntityStore = defineStore(namespace, (): IEntityStore => {
|
||||
// Реактивные ref'ы
|
||||
const entities = ref<IEntitiesPagination | null>(null);
|
||||
const entity = ref<IEntity | null>(null);
|
||||
|
||||
// Методы загрузки (только чтение!)
|
||||
const loadEntities = async (data: IGetEntitiesInput): Promise<void> => {
|
||||
const loadedData = await api.loadEntities(data);
|
||||
entities.value = loadedData;
|
||||
};
|
||||
|
||||
const loadEntity = async (data: IGetEntityInput): Promise<IEntity> => {
|
||||
const loadedData = await api.loadEntity(data);
|
||||
entity.value = loadedData;
|
||||
return loadedData;
|
||||
};
|
||||
|
||||
return {
|
||||
entities,
|
||||
entity,
|
||||
loadEntities,
|
||||
loadEntity,
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
#### Правила для store
|
||||
- **Только запросы**: Store содержит только методы чтения данных
|
||||
- **Реактивное состояние**: Все данные хранятся в ref'ах
|
||||
- **Интерфейс типизации**: Обязателен для типизации store
|
||||
- **Namespace**: Уникальное имя для каждого store
|
||||
- **Методы обновления списка**: Для поддержания консистентности данных
|
||||
|
||||
#### Методы управления списком
|
||||
```typescript
|
||||
const addEntityToList = (entityData: IEntity) => {
|
||||
if (entities.value) {
|
||||
const existingIndex = entities.value.items.findIndex(
|
||||
(item) => item._id === entityData._id,
|
||||
);
|
||||
|
||||
if (existingIndex !== -1) {
|
||||
// Заменяем существующую
|
||||
entities.value.items[existingIndex] = entityData;
|
||||
} else {
|
||||
// Добавляем новую в начало
|
||||
entities.value.items = [entityData, ...entities.value.items];
|
||||
entities.value.totalCount += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const updateEntityInList = (entityData: IEntity) => {
|
||||
if (entities.value) {
|
||||
const existingIndex = entities.value.items.findIndex(
|
||||
(item) => item._id === entityData._id,
|
||||
);
|
||||
|
||||
if (existingIndex !== -1) {
|
||||
// Обновляем существующую на месте
|
||||
entities.value.items[existingIndex] = entityData;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const removeEntityFromList = (entityId: string) => {
|
||||
if (entities.value) {
|
||||
const index = entities.value.items.findIndex(
|
||||
(item) => item._id === entityId,
|
||||
);
|
||||
|
||||
if (index !== -1) {
|
||||
entities.value.items.splice(index, 1);
|
||||
entities.value.totalCount -= 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### Организация API (api/index.ts)
|
||||
|
||||
#### Структура API функций
|
||||
```typescript
|
||||
import { client } from 'src/shared/api/client';
|
||||
import { Queries } from '@coopenomics/sdk';
|
||||
import type { IEntity, IGetEntityInput } from '../model';
|
||||
|
||||
async function loadEntities(data: IGetEntitiesInput): Promise<IEntitiesPagination> {
|
||||
const { [Queries.Extension.GetEntities.name]: output } = await client.Query(
|
||||
Queries.Extension.GetEntities.query,
|
||||
{
|
||||
variables: data,
|
||||
},
|
||||
);
|
||||
return output;
|
||||
}
|
||||
|
||||
async function loadEntity(data: IGetEntityInput): Promise<IEntity> {
|
||||
const { [Queries.Extension.GetEntity.name]: output } = await client.Query(
|
||||
Queries.Extension.GetEntity.query,
|
||||
{
|
||||
variables: {
|
||||
data,
|
||||
},
|
||||
},
|
||||
);
|
||||
return output;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
loadEntities,
|
||||
loadEntity,
|
||||
};
|
||||
```
|
||||
|
||||
#### Правила для API
|
||||
- **Только функции-запросы**: Никаких мутаций в API сущностей
|
||||
- **Строгая типизация**: Все параметры и возвраты типизированы
|
||||
- **Использование SDK**: Все запросы через GraphQL Zeus SDK
|
||||
- **Единый экспорт**: Все функции экспортируются через объект `api`
|
||||
|
||||
### Экспорты и индексные файлы
|
||||
|
||||
#### Сущность (entities/EntityName/index.ts)
|
||||
```typescript
|
||||
export * as EntityNameModel from './model';
|
||||
export * from './api';
|
||||
```
|
||||
|
||||
#### Слой сущностей (entities/index.ts)
|
||||
```typescript
|
||||
export * as EntityName from './EntityName';
|
||||
export * as AnotherEntity from './AnotherEntity';
|
||||
```
|
||||
|
||||
#### Расширение (extensions/extension-name/index.ts)
|
||||
```typescript
|
||||
export * from './entities';
|
||||
export * from './features';
|
||||
export * from './widgets';
|
||||
```
|
||||
|
||||
### Важные принципы
|
||||
|
||||
#### Типизация
|
||||
- Все типы из SDK GraphQL Zeus
|
||||
- Строгая типизация всех входов/выходов
|
||||
- Интерфейсы для store объектов
|
||||
|
||||
#### Реактивность
|
||||
- Все данные в Pinia stores
|
||||
- Использование Vue ref/computed
|
||||
- Автоматические обновления UI
|
||||
|
||||
#### Запреты
|
||||
- ❌ Мутации в сущностях
|
||||
- ❌ Прямые запросы к API в компонентах
|
||||
- ❌ Неиспользуемые переменные/импорты
|
||||
@@ -1,130 +0,0 @@
|
||||
---
|
||||
globs: ["**/desktop/extensions/**/*"]
|
||||
alwaysApply: false
|
||||
---
|
||||
## Расширения (Extensions)
|
||||
|
||||
### Общее устройство
|
||||
|
||||
Расширения - это изолированные модули приложения, каждый из которых реализует отдельный функционал. Расширения максимально независимы и следуют принципам Feature Sliced Design.
|
||||
|
||||
### Структура расширения
|
||||
|
||||
```
|
||||
extension-name/
|
||||
entities/ # Сущности расширения
|
||||
features/ # Фичи расширения
|
||||
widgets/ # Виджеты расширения
|
||||
pages/ # Страницы расширения
|
||||
index.ts # export * from './entities' etc.
|
||||
install.ts # IWorkspaceConfig с маршрутами и меню
|
||||
```
|
||||
|
||||
### Файл install.ts
|
||||
|
||||
#### Роль и назначение
|
||||
Файл `install.ts` отвечает за интеграцию расширения в систему рабочих столов. Он:
|
||||
- Определяет маршруты расширения
|
||||
- Настраивает меню рабочего стола
|
||||
- Управляет видимостью в левом дровере
|
||||
- Задает роли доступа к разделам
|
||||
|
||||
#### Структура конфигурации
|
||||
```typescript
|
||||
import { markRaw } from 'vue';
|
||||
import type { IWorkspaceConfig } from 'src/shared/lib/types/workspace';
|
||||
|
||||
export default async function (): Promise<IWorkspaceConfig> {
|
||||
return {
|
||||
workspace: 'extension-name', // Уникальный идентификатор рабочего стола
|
||||
title: 'Название рабочего стола', // Отображаемое название
|
||||
defaultRoute: 'default-route', // Маршрут по умолчанию
|
||||
routes: [
|
||||
{
|
||||
meta: {
|
||||
title: 'Название раздела',
|
||||
icon: 'fa-solid fa-icon',
|
||||
roles: ['chairman', 'member', 'user'], // Роли с доступом: chairman, member, user. Пустой массив [] - доступ у всех
|
||||
},
|
||||
path: '/:coopname/extension-name',
|
||||
name: 'extension-name',
|
||||
component: markRaw(MainComponent),
|
||||
children: [
|
||||
// Вложенные маршруты
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### Уровни видимости в меню
|
||||
|
||||
##### Первый уровень: Рабочий стол
|
||||
- Расширение появляется в меню рабочих столов
|
||||
- Задается в корневом маршруте с `path: '/:coopname/extension-name'`
|
||||
|
||||
##### Второй уровень: Левый дровер
|
||||
- Кнопки разделов внутри рабочего стола
|
||||
- Управляется свойством `hidden: true/false` в meta роута
|
||||
- Если `hidden: true` - раздел не отображается в левом дровере
|
||||
|
||||
#### Пример с видимостью
|
||||
```typescript
|
||||
{
|
||||
path: 'projects/:project_hash/tasks/:issue_hash',
|
||||
name: 'project-issue',
|
||||
component: markRaw(IssuePage),
|
||||
meta: {
|
||||
title: 'Задача',
|
||||
icon: 'fa-solid fa-task',
|
||||
roles: [],
|
||||
agreements: agreementsBase,
|
||||
requiresAuth: true,
|
||||
hidden: true, // Не показывать в левом дровере
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Экспорты расширения
|
||||
|
||||
#### index.ts расширения
|
||||
```typescript
|
||||
export * from './entities';
|
||||
export * from './features';
|
||||
export * from './widgets';
|
||||
export * from './pages';
|
||||
```
|
||||
|
||||
#### Регистрация в системе
|
||||
Расширения регистрируются через систему расширений и становятся доступны в магазине расширений для установки.
|
||||
|
||||
### Правила изоляции
|
||||
|
||||
#### Запреты
|
||||
- ❌ Импорты между расширениями запрещены
|
||||
- ❌ Совместное использование сущностей между расширениями
|
||||
- ❌ Прямые зависимости между расширениями
|
||||
|
||||
#### Разрешения
|
||||
- ✅ Импорт из `src/shared` (общие компоненты)
|
||||
- ✅ Исключения: `FailAlert`, `SuccessAlert` (глобальные уведомления)
|
||||
|
||||
### Важные принципы
|
||||
|
||||
#### Разделение ответственности
|
||||
- **Entities**: Только данные и их загрузка
|
||||
- **Features**: Бизнес-логика и действия
|
||||
- **Widgets**: Комплексные UI компоненты
|
||||
- **Pages**: Корневые страницы расширения
|
||||
|
||||
#### Конфигурация меню
|
||||
- Роли доступа: `chairman`, `member`, `user`
|
||||
- Пустой массив `roles: []` означает доступ у всех ролей
|
||||
- Видимость управляется через `hidden` свойство
|
||||
- Иконки и названия должны быть информативными
|
||||
|
||||
#### Разработка расширений
|
||||
- Расширения разрабатываются независимо
|
||||
- Каждый расширение - отдельный функциональный модуль
|
||||
- Возможность отключения/включения расширений
|
||||
@@ -1,234 +0,0 @@
|
||||
---
|
||||
globs: ["**/desktop/features/**/*.ts", "**/desktop/features/**/*.vue"]
|
||||
alwaysApply: false
|
||||
---
|
||||
## Архитектура фич (Features Layer)
|
||||
|
||||
### Общее устройство
|
||||
|
||||
Фичи - это бизнес-логика приложения, реализованная в виде Vue composables. Каждая фича отвечает за конкретное действие или набор связанных действий. Фичи взаимодействуют с entities для получения и обновления данных.
|
||||
|
||||
**Запрещено**: Размещать методы чтения данных в фичах. Чтение данных - задача entities.
|
||||
|
||||
### Структура папок фичи
|
||||
|
||||
```
|
||||
features/
|
||||
FeatureName/
|
||||
index.ts # Экспорт composable и UI (если есть)
|
||||
api/
|
||||
index.ts # Мутации GraphQL через SDK
|
||||
model/
|
||||
index.ts # Экспорт composable функции
|
||||
ui/ # Опционально: UI компоненты фичи
|
||||
index.ts # Экспорт UI компонентов
|
||||
Component.vue # Компоненты фичи
|
||||
```
|
||||
|
||||
### Типизация фич
|
||||
|
||||
#### Импорты типов напрямую из SDK
|
||||
```typescript
|
||||
import { Mutations } from '@coopenomics/sdk';
|
||||
|
||||
// Типы входных данных мутации
|
||||
export type IFeatureInput = Mutations.Extension.FeatureAction.IInput['data'];
|
||||
|
||||
// Типы выходных данных мутации
|
||||
export type IFeatureOutput = Mutations.Extension.FeatureAction.IOutput[typeof Mutations.Extension.FeatureAction.name];
|
||||
```
|
||||
|
||||
**Важно**: Ничего не переопределяем вручную! Все типы берутся напрямую из GraphQL Zeus SDK.
|
||||
|
||||
### Организация API (api/index.ts)
|
||||
|
||||
#### Структура API функций для мутаций
|
||||
```typescript
|
||||
import { client } from 'src/shared/api/client';
|
||||
import { Mutations } from '@coopenomics/sdk';
|
||||
import type { IFeatureInput, IFeatureOutput } from '../model';
|
||||
|
||||
async function featureAction(
|
||||
data: IFeatureInput,
|
||||
): Promise<IFeatureOutput> {
|
||||
const { [Mutations.Extension.FeatureAction.name]: result } =
|
||||
await client.Mutation(Mutations.Extension.FeatureAction.mutation, {
|
||||
variables: {
|
||||
data,
|
||||
},
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
featureAction,
|
||||
};
|
||||
```
|
||||
|
||||
#### Правила для API фич
|
||||
- **Только мутации**: API фич содержит только GraphQL мутации
|
||||
- **Строгая типизация**: Все параметры и возвраты типизированы через SDK
|
||||
- **Использование SDK**: Все мутации через GraphQL Zeus SDK
|
||||
- **Единый экспорт**: Все функции экспортируются через объект `api`
|
||||
|
||||
### Организация Model (model/index.ts)
|
||||
|
||||
#### Структура composable функции
|
||||
```typescript
|
||||
import { useEntityStore } from 'app/extensions/extension-name/entities/Entity/model';
|
||||
import { api } from '../api';
|
||||
import type { IFeatureInput, IFeatureOutput } from '../api';
|
||||
|
||||
export function useFeatureAction() {
|
||||
const store = useEntityStore();
|
||||
|
||||
const featureAction = async (input: IFeatureInput): Promise<IFeatureOutput> => {
|
||||
const result = await api.featureAction(input);
|
||||
|
||||
if (result) {
|
||||
// Обновляем store сущности для поддержания консистентности
|
||||
store.updateEntityInList(result);
|
||||
// или store.removeEntityFromList(input.entity_id);
|
||||
// или store.addEntityToList(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
return { featureAction };
|
||||
}
|
||||
```
|
||||
|
||||
#### Правила для composables
|
||||
- **Использование store**: Фичи обновляют store entities после выполнения мутаций
|
||||
- **Поддержание консистентности**: Данные в store всегда актуальны после действий
|
||||
- **Возвращение результата**: Composable возвращает результат мутации
|
||||
- **Обработка ошибок**: Ошибки обрабатываются в UI компонентах
|
||||
|
||||
### UI слой фич (опционально)
|
||||
|
||||
#### Когда использовать UI в фичах
|
||||
- **Иногда, но не всегда**: UI слой используется только для специфических компонентов фичи
|
||||
- **Простые действия**: Для кнопок, форм, специфичных для данной фичи
|
||||
- **Не общие компоненты**: Не размещать shared компоненты в фичах
|
||||
|
||||
#### Экспорты UI компонентов
|
||||
|
||||
##### В ui/index.ts (первый уровень экспорта)
|
||||
```typescript
|
||||
export { default as FeatureButton } from './FeatureButton.vue';
|
||||
export { default as FeatureDialog } from './FeatureDialog.vue';
|
||||
```
|
||||
|
||||
##### В корне фичи index.ts (второй уровень экспорта)
|
||||
```typescript
|
||||
export { useFeatureAction } from './model';
|
||||
export * from './ui'; // Экспортируем все UI компоненты
|
||||
```
|
||||
|
||||
### Экспорты и индексные файлы
|
||||
|
||||
#### Фича (features/FeatureName/index.ts)
|
||||
```typescript
|
||||
export { useFeatureAction } from './model';
|
||||
export * from './ui'; // Если есть UI слой
|
||||
```
|
||||
|
||||
#### Слой фич (features/index.ts)
|
||||
```typescript
|
||||
export { useFeatureAction } from './FeatureName';
|
||||
export { useAnotherFeature } from './AnotherFeature';
|
||||
```
|
||||
|
||||
#### Расширение (extensions/extension-name/index.ts)
|
||||
```typescript
|
||||
export * from './entities';
|
||||
export * from './features'; // Экспорт всех фич расширения
|
||||
export * from './widgets';
|
||||
export * from './pages';
|
||||
```
|
||||
|
||||
### Принципы работы с фичами
|
||||
|
||||
#### Разделение ответственности
|
||||
- **Entities**: Хранение и чтение данных
|
||||
- **Features**: Бизнес-логика и действия (мутации)
|
||||
- **Widgets**: Сложные UI компоненты
|
||||
- **Pages**: Корневые страницы
|
||||
|
||||
#### Взаимодействие со stores
|
||||
- **Обязательное обновление**: После каждой мутации обновлять соответствующий store
|
||||
- **Консистентность данных**: UI всегда показывает актуальные данные из store
|
||||
- **Оптимистичные обновления**: Возможны для лучшего UX
|
||||
|
||||
#### Типизация
|
||||
- **SDK-first**: Все типы из GraphQL Zeus SDK
|
||||
- **Не переопределять**: Никаких ручных переопределений типов
|
||||
- **Строгая типизация**: Все входы/выходы типизированы
|
||||
|
||||
#### Организация кода
|
||||
- **Composable pattern**: Каждая фича - отдельный composable
|
||||
- **Единая ответственность**: Одна фича = одно действие или группа связанных действий
|
||||
- **Переиспользование**: Фичи могут использоваться в разных частях приложения
|
||||
|
||||
### Примеры
|
||||
|
||||
#### Простая фича (только composable)
|
||||
```
|
||||
DeleteStory/
|
||||
api/
|
||||
index.ts # Мутация deleteStory
|
||||
model/
|
||||
index.ts # useDeleteStory composable
|
||||
index.ts # Экспорт composable
|
||||
```
|
||||
|
||||
#### Фича с UI компонентами
|
||||
```
|
||||
CreateProject/
|
||||
api/
|
||||
index.ts # Мутация createProject
|
||||
model/
|
||||
index.ts # useCreateProject composable
|
||||
ui/
|
||||
index.ts # Экспорт CreateProjectButton
|
||||
CreateProjectButton.vue
|
||||
index.ts # Экспорт composable + UI
|
||||
```
|
||||
|
||||
#### Использование фичи в компоненте
|
||||
```vue
|
||||
<script setup>
|
||||
import { useDeleteStory } from 'features/Story/DeleteStory';
|
||||
|
||||
const { deleteStory } = useDeleteStory();
|
||||
|
||||
// Использование в обработчике
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
await deleteStory({ story_hash: '...' });
|
||||
// Обработка успеха
|
||||
} catch (error) {
|
||||
// Обработка ошибки
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
### Важные принципы
|
||||
|
||||
#### Четкое разделение слоев
|
||||
- **Entities** управляют данными
|
||||
- **Features** управляют действиями
|
||||
- **UI** управляет интерфейсом
|
||||
|
||||
#### Консистентность архитектуры
|
||||
- Все фичи следуют единому паттерну
|
||||
- Типизация через SDK
|
||||
- Обновление stores после мутаций
|
||||
|
||||
#### Производительность
|
||||
- Оптимистичные обновления где возможно
|
||||
- Минимальные перерисовки UI
|
||||
- Эффективное управление состоянием
|
||||
@@ -1,291 +0,0 @@
|
||||
---
|
||||
globs: ["**/desktop/pages/**/*.ts", "**/desktop/pages/**/*.vue"]
|
||||
alwaysApply: false
|
||||
---
|
||||
## Архитектура страниц (Pages Layer)
|
||||
|
||||
### Общее устройство
|
||||
|
||||
Страницы - это корневые компоненты приложения, которые используются в маршрутах. Страницы отвечают за оркестрацию виджетов, управление состоянием UI и навигацию. Они могут быть как простыми контейнерами, так и сложными композитными компонентами.
|
||||
|
||||
### Структура папок страницы
|
||||
|
||||
```
|
||||
pages/
|
||||
PageName/
|
||||
index.ts # Экспорт страницы
|
||||
ui/ # Vue компонент страницы
|
||||
PageName.vue # Основной компонент
|
||||
index.ts # Экспорт из ui
|
||||
```
|
||||
|
||||
### Типы страниц
|
||||
|
||||
#### 1. Контейнерные страницы
|
||||
Простые страницы, которые делегируют отображение дочерним маршрутам:
|
||||
|
||||
```vue
|
||||
<template lang="pug">
|
||||
div
|
||||
// Лоадер пока идет предзагрузка данных
|
||||
WindowLoader(v-if="isLoading", text="Загрузка данных...")
|
||||
|
||||
// Основной контент после загрузки
|
||||
router-view(v-else)
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { WindowLoader } from 'src/shared/ui/Loader';
|
||||
import { useContributorStore } from 'entities/Contributor/model';
|
||||
|
||||
const contributorStore = useContributorStore();
|
||||
const isLoading = ref(true);
|
||||
|
||||
onMounted(async () => {
|
||||
// Предзагрузка необходимых данных
|
||||
await contributorStore.loadSelf({ username: session.username });
|
||||
isLoading.value = false;
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
#### 2. Композитные страницы
|
||||
Страницы с сложной композицией виджетов и собственной логикой:
|
||||
|
||||
```vue
|
||||
<template lang="pug">
|
||||
div
|
||||
q-card(flat)
|
||||
// Композиция виджетов
|
||||
StoriesWidget(
|
||||
:stories='storyStore.stories?.items || []',
|
||||
:loading='loading'
|
||||
)
|
||||
|
||||
// Собственная логика пагинации
|
||||
q-card-actions(align='center')
|
||||
q-pagination(
|
||||
v-model='pagination.page',
|
||||
:max='Math.ceil((storyStore.stories?.totalCount || 0) / pagination.rowsPerPage)',
|
||||
@update:model-value='onPageChange'
|
||||
)
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { StoriesWidget } from 'widgets/StoriesWidget';
|
||||
import { useStoryStore } from 'entities/Story/model';
|
||||
|
||||
const storyStore = useStoryStore();
|
||||
const loading = ref(false);
|
||||
|
||||
// Собственная логика пагинации
|
||||
const pagination = ref({
|
||||
page: 1,
|
||||
rowsPerPage: 25,
|
||||
});
|
||||
|
||||
const loadData = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
await storyStore.loadStories({
|
||||
filter: { /* фильтры */ },
|
||||
pagination: pagination.value,
|
||||
});
|
||||
} catch (error) {
|
||||
FailAlert(`Ошибка загрузки: ${error.message}`);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
### Принципы организации страниц
|
||||
|
||||
#### Использование слоев
|
||||
```typescript
|
||||
// ✅ Правильно: страницы используют все нижележащие слои
|
||||
import { useEntityStore } from 'entities/Entity/model'; // Данные
|
||||
import { useEntityAction } from 'features/Entity/EntityAction'; // Действия
|
||||
import { EntityWidget } from 'widgets/EntityWidget'; // Композиция UI
|
||||
|
||||
// ❌ Неправильно: страницы не используют другие страницы
|
||||
// import { OtherPage } from 'pages/OtherPage';
|
||||
```
|
||||
|
||||
#### Управление состоянием
|
||||
Страницы управляют:
|
||||
- **Загрузкой данных**: инициализация, фильтры, пагинация
|
||||
- **UI состоянием**: загрузка, ошибки, навигация
|
||||
- **Взаимодействием**: обработка событий от виджетов
|
||||
|
||||
#### Композиция виджетов
|
||||
```vue
|
||||
<template lang="pug">
|
||||
// Простая композиция
|
||||
EntityWidget(:items='store.items')
|
||||
|
||||
// Сложная композиция со слотами
|
||||
LayoutWidget
|
||||
template(#header)
|
||||
PageHeader(title='Заголовок страницы')
|
||||
|
||||
template(#content)
|
||||
EntityWidget(:items='store.items')
|
||||
|
||||
template(#sidebar)
|
||||
FiltersWidget(@filter-changed='onFilterChange')
|
||||
</template>
|
||||
```
|
||||
|
||||
### Экспорты и индексные файлы
|
||||
|
||||
#### Страница (pages/PageName/index.ts)
|
||||
```typescript
|
||||
export { default as PageName } from './ui/PageName.vue';
|
||||
```
|
||||
|
||||
#### Слой страниц (pages/index.ts)
|
||||
```typescript
|
||||
export * from './PageName';
|
||||
export * from './AnotherPage';
|
||||
```
|
||||
|
||||
#### Использование в маршрутах
|
||||
Страницы используются в `install.ts` расширения:
|
||||
|
||||
```typescript
|
||||
routes: [{
|
||||
path: '/:coopname/extension/page',
|
||||
name: 'page',
|
||||
component: markRaw(PageName), // Импорт из pages
|
||||
meta: { /* ... */ }
|
||||
}]
|
||||
```
|
||||
|
||||
### Принципы работы со страницами
|
||||
|
||||
#### Разделение ответственности
|
||||
- **Entities**: Данные и их хранение
|
||||
- **Features**: Бизнес-логика и действия
|
||||
- **Widgets**: Композиция UI компонентов
|
||||
- **Pages**: Оркестрация всего вышеперечисленного
|
||||
|
||||
#### Загрузка данных
|
||||
Страницы отвечают за:
|
||||
- Инициализацию данных при монтировании
|
||||
- Управление пагинацией и фильтрами
|
||||
- Обработку ошибок загрузки
|
||||
- Кеширование и оптимизацию запросов
|
||||
|
||||
#### Навигация и взаимодействие
|
||||
```typescript
|
||||
const router = useRouter();
|
||||
|
||||
const onItemClick = (item) => {
|
||||
router.push({
|
||||
name: 'item-details',
|
||||
params: { id: item.id }
|
||||
});
|
||||
};
|
||||
|
||||
const onActionComplete = () => {
|
||||
// Обновление данных после действия
|
||||
loadData();
|
||||
};
|
||||
```
|
||||
|
||||
### Примеры использования
|
||||
|
||||
#### Простая страница списка
|
||||
```vue
|
||||
<template lang="pug">
|
||||
div
|
||||
q-card(flat)
|
||||
// Виджет таблицы
|
||||
ItemsListWidget(
|
||||
:items='store.items',
|
||||
:loading='loading',
|
||||
@item-click='onItemClick'
|
||||
)
|
||||
|
||||
// Пагинация
|
||||
q-pagination(
|
||||
v-model='currentPage',
|
||||
:max='totalPages',
|
||||
@update:model-value='loadPage'
|
||||
)
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { ItemsListWidget } from 'widgets/ItemsListWidget';
|
||||
import { useItemStore } from 'entities/Item/model';
|
||||
|
||||
const store = useItemStore();
|
||||
const currentPage = ref(1);
|
||||
|
||||
const totalPages = computed(() =>
|
||||
Math.ceil((store.items?.totalCount || 0) / 25)
|
||||
);
|
||||
|
||||
const loadPage = async (page) => {
|
||||
await store.loadItems({ page });
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
#### Страница с несколькими виджетами
|
||||
```vue
|
||||
<template lang="pug">
|
||||
.q-pa-md
|
||||
// Заголовок страницы
|
||||
PageHeader(title='Дашборд')
|
||||
|
||||
.row.q-gutter-md
|
||||
.col-12.col-md-6
|
||||
// Статистика
|
||||
StatsWidget(:stats='stats')
|
||||
|
||||
.col-12.col-md-6
|
||||
// Недавние действия
|
||||
RecentActionsWidget(:actions='recentActions')
|
||||
|
||||
// Основной контент
|
||||
MainContentWidget(
|
||||
:data='mainData',
|
||||
@refresh='loadData'
|
||||
)
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue';
|
||||
import PageHeader from 'src/shared/ui/PageHeader';
|
||||
import StatsWidget from 'widgets/StatsWidget';
|
||||
import RecentActionsWidget from 'widgets/RecentActionsWidget';
|
||||
import MainContentWidget from 'widgets/MainContentWidget';
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### Важные принципы
|
||||
|
||||
#### Производительность
|
||||
- Ленивая загрузка данных
|
||||
- Оптимизация перерисовок
|
||||
- Эффективное управление памятью
|
||||
|
||||
#### Доступность
|
||||
- Правильная структура заголовков
|
||||
- Клавиатурная навигация
|
||||
- Screen reader поддержка
|
||||
|
||||
#### SEO и метаданные
|
||||
- Управление title страницы
|
||||
- Meta теги для социальных сетей
|
||||
- Структурированные данные
|
||||
@@ -1,388 +0,0 @@
|
||||
---
|
||||
globs: ["**/desktop/widgets/**/*.ts", "**/desktop/widgets/**/*.vue"]
|
||||
alwaysApply: false
|
||||
---
|
||||
## Архитектура виджетов (Widgets Layer)
|
||||
|
||||
### Общее устройство
|
||||
|
||||
Виджеты - это комплексные UI компоненты, которые комбинируют данные из entities и действия из features для создания полноценных интерфейсных блоков. Виджеты отвечают за композицию и представление сложных частей интерфейса.
|
||||
|
||||
**Запрещено**: Виджеты не переиспользуют другие виджеты. Виджеты могут использовать только фичи и shared компоненты.
|
||||
|
||||
### Структура папок виджета
|
||||
|
||||
```
|
||||
widgets/
|
||||
WidgetName/
|
||||
index.ts # Экспорт виджета
|
||||
WidgetName.vue # Vue компонент виджета
|
||||
# или
|
||||
ui/
|
||||
WidgetName.vue # Vue компонент в подпапке
|
||||
index.ts # Экспорт из ui
|
||||
```
|
||||
|
||||
### Принципы организации виджетов
|
||||
|
||||
#### Использование только разрешенных слоев
|
||||
```typescript
|
||||
// ✅ Разрешено: entities для данных
|
||||
import { useEntityStore, type IEntity } from 'entities/Entity/model';
|
||||
|
||||
// ✅ Разрешено: features для действий
|
||||
import { useEntityAction } from 'features/Entity/EntityAction';
|
||||
import { EntityActionButton } from 'features/Entity/EntityAction';
|
||||
|
||||
// ✅ Разрешено: shared компоненты
|
||||
import { SharedComponent } from 'src/shared/ui/SharedComponent';
|
||||
|
||||
// ❌ Запрещено: импорт других виджетов
|
||||
// import { AnotherWidget } from 'widgets/AnotherWidget';
|
||||
```
|
||||
|
||||
#### Композиция через слоты (опционально)
|
||||
Виджеты могут содержать слоты для композиции с другими компонентами на уровне страниц:
|
||||
|
||||
```vue
|
||||
<template lang="pug">
|
||||
.widget-container
|
||||
.widget-header
|
||||
slot(name='header')
|
||||
| Заголовок по умолчанию
|
||||
|
||||
.widget-content
|
||||
slot(name='content')
|
||||
| Контент по умолчанию
|
||||
|
||||
.widget-actions
|
||||
slot(name='actions')
|
||||
</template>
|
||||
```
|
||||
|
||||
### Типы виджетов
|
||||
|
||||
#### 1. Презентационные виджеты
|
||||
Отображают данные в виде таблиц, списков, карточек:
|
||||
|
||||
```vue
|
||||
<template lang="pug">
|
||||
q-card(flat)
|
||||
q-table(
|
||||
:rows='entities',
|
||||
:columns='columns',
|
||||
:loading='loading'
|
||||
)
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useEntityStore } from 'entities/Entity/model';
|
||||
|
||||
const store = useEntityStore();
|
||||
// Логика загрузки и отображения данных
|
||||
</script>
|
||||
```
|
||||
|
||||
#### 2. Интерактивные виджеты
|
||||
Комбинируют отображение данных с действиями:
|
||||
|
||||
```vue
|
||||
<template lang="pug">
|
||||
.widget
|
||||
// Отображение данных
|
||||
.data-section
|
||||
EntityItem(v-for='item in items', :item='item')
|
||||
|
||||
// Действия
|
||||
.actions-section
|
||||
CreateEntityButton
|
||||
UpdateEntityButton
|
||||
</template>
|
||||
```
|
||||
|
||||
#### 3. Композитные виджеты со слотами
|
||||
Предоставляют гибкую структуру для страниц:
|
||||
|
||||
```vue
|
||||
<template lang="pug">
|
||||
.layout-widget
|
||||
header
|
||||
slot(name='header')
|
||||
main
|
||||
slot(name='main')
|
||||
aside
|
||||
slot(name='sidebar')
|
||||
footer
|
||||
slot(name='footer')
|
||||
</template>
|
||||
```
|
||||
|
||||
### Props и конфигурация
|
||||
|
||||
#### Гибкая конфигурация через props
|
||||
```typescript
|
||||
interface Props {
|
||||
// Данные
|
||||
items?: IEntity[];
|
||||
loading?: boolean;
|
||||
|
||||
// Конфигурация отображения
|
||||
showActions?: boolean;
|
||||
maxItems?: number;
|
||||
emptyMessage?: string;
|
||||
|
||||
// Callbacks для взаимодействия
|
||||
onItemClick?: (item: IEntity) => void;
|
||||
onActionComplete?: () => void;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showActions: true,
|
||||
maxItems: 50,
|
||||
emptyMessage: 'Нет данных',
|
||||
});
|
||||
```
|
||||
|
||||
#### Управление состоянием
|
||||
```typescript
|
||||
// Локальное состояние виджета
|
||||
const expandedItems = ref<Set<string>>(new Set());
|
||||
const selectedItem = ref<IEntity | null>(null);
|
||||
|
||||
// Props для внешнего управления
|
||||
interface Props {
|
||||
expandedItems?: string[];
|
||||
selectedItemId?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Использование entities в виджетах
|
||||
|
||||
#### Чтение данных из stores
|
||||
```typescript
|
||||
import { useEntityStore } from 'entities/Entity/model';
|
||||
|
||||
const store = useEntityStore();
|
||||
|
||||
// Доступ к реактивным данным (уже реактивны через Pinia)
|
||||
const entities = computed(() => store.entities);
|
||||
const loading = computed(() => store.loading);
|
||||
|
||||
// Методы загрузки
|
||||
const loadData = () => {
|
||||
store.loadEntities({ filter: props.filter });
|
||||
};
|
||||
```
|
||||
|
||||
#### Важно о реактивности stores
|
||||
**Store автоматически реактивен через Pinia** - не нужно использовать `storeToRefs()` или `.value`:
|
||||
```typescript
|
||||
const store = useEntityStore();
|
||||
|
||||
// ✅ Правильно - данные уже реактивны
|
||||
const entities = computed(() => store.entities);
|
||||
|
||||
// ❌ Неправильно - storeToRefs не нужен
|
||||
// const { entities } = storeToRefs(store);
|
||||
|
||||
// ❌ Неправильно - данные уже реактивны, .value не нужен
|
||||
// const entities = store.entities.value;
|
||||
```
|
||||
|
||||
### Использование features в виджетах
|
||||
|
||||
#### Импорт и использование фич
|
||||
```typescript
|
||||
import { useEntityAction } from 'features/Entity/EntityAction';
|
||||
import { EntityActionButton } from 'features/Entity/EntityAction';
|
||||
|
||||
const { entityAction } = useEntityAction();
|
||||
|
||||
// Использование в обработчиках
|
||||
const handleAction = async () => {
|
||||
try {
|
||||
await entityAction(params);
|
||||
// Обработка успеха
|
||||
} catch (error) {
|
||||
// Обработка ошибки
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### Интеграция UI компонентов фич
|
||||
```vue
|
||||
<template>
|
||||
<div class="widget">
|
||||
<!-- Данные -->
|
||||
<EntityList :items="entities" />
|
||||
|
||||
<!-- Действия из фич -->
|
||||
<div class="actions">
|
||||
<CreateEntityButton @created="onEntityCreated" />
|
||||
<UpdateEntityButton :entity="selectedEntity" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Обработка событий и коммуникация
|
||||
|
||||
#### Внутренняя коммуникация
|
||||
```typescript
|
||||
const emit = defineEmits<{
|
||||
itemSelected: [item: IEntity];
|
||||
actionCompleted: [action: string, result: any];
|
||||
}>();
|
||||
|
||||
// Генерация событий
|
||||
const onItemClick = (item: IEntity) => {
|
||||
emit('itemSelected', item);
|
||||
};
|
||||
|
||||
const onActionSuccess = (result: any) => {
|
||||
emit('actionCompleted', 'create', result);
|
||||
};
|
||||
```
|
||||
|
||||
#### Внешнее управление через props
|
||||
```typescript
|
||||
interface Props {
|
||||
selectedItemId?: string;
|
||||
onItemSelect?: (item: IEntity) => void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
// Синхронизация с внешним состоянием
|
||||
watch(() => props.selectedItemId, (newId) => {
|
||||
if (newId) {
|
||||
const item = entities.value.find(e => e.id === newId);
|
||||
if (item) {
|
||||
selectedItem.value = item;
|
||||
props.onItemSelect?.(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Экспорты и индексные файлы
|
||||
|
||||
#### Виджет (widgets/WidgetName/index.ts)
|
||||
```typescript
|
||||
export { default as WidgetName } from './WidgetName.vue';
|
||||
// или
|
||||
export { default as WidgetName } from './ui/WidgetName.vue';
|
||||
```
|
||||
|
||||
#### Слой виджетов (widgets/index.ts)
|
||||
```typescript
|
||||
export * from './WidgetName';
|
||||
export * from './AnotherWidget';
|
||||
```
|
||||
|
||||
#### Расширение (extensions/extension-name/index.ts)
|
||||
```typescript
|
||||
export * from './entities';
|
||||
export * from './features';
|
||||
export * from './widgets'; // Экспорт всех виджетов расширения
|
||||
export * from './pages';
|
||||
```
|
||||
|
||||
### Примеры использования
|
||||
|
||||
#### Простой виджет списка
|
||||
```vue
|
||||
<template lang="pug">
|
||||
q-card(flat)
|
||||
q-card-section
|
||||
.text-h6 Список элементов
|
||||
|
||||
q-card-section
|
||||
q-list
|
||||
q-item(
|
||||
v-for='item in items',
|
||||
:key='item.id',
|
||||
clickable,
|
||||
@click='$emit("itemClick", item)'
|
||||
)
|
||||
q-item-section {{ item.name }}
|
||||
|
||||
.text-center.q-pa-md(v-if='!items.length && !loading')
|
||||
| {{ emptyMessage }}
|
||||
|
||||
.text-center.q-pa-md(v-if='loading')
|
||||
q-spinner(color='primary')
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { IEntity } from 'entities/Entity/model';
|
||||
|
||||
interface Props {
|
||||
items: IEntity[];
|
||||
loading?: boolean;
|
||||
emptyMessage?: string;
|
||||
}
|
||||
|
||||
defineEmits<{
|
||||
itemClick: [item: IEntity];
|
||||
}>();
|
||||
</script>
|
||||
```
|
||||
|
||||
#### Виджет с действиями
|
||||
```vue
|
||||
<template lang="pug">
|
||||
.widget
|
||||
.widget-header
|
||||
h3 {{ title }}
|
||||
CreateEntityButton(@created='onEntityCreated')
|
||||
|
||||
.widget-content
|
||||
EntityTable(
|
||||
:entities='entities',
|
||||
:loading='loading',
|
||||
@entity-selected='onEntitySelected'
|
||||
)
|
||||
|
||||
.widget-footer
|
||||
q-pagination(
|
||||
v-model='currentPage',
|
||||
:max='totalPages',
|
||||
@update:model-value='onPageChange'
|
||||
)
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useEntityStore } from 'entities/Entity/model';
|
||||
import { CreateEntityButton } from 'features/Entity/CreateEntity';
|
||||
import EntityTable from './EntityTable.vue'; // Shared компонент
|
||||
|
||||
const store = useEntityStore();
|
||||
const { entities, loading } = storeToRefs(store);
|
||||
|
||||
// Логика пагинации, фильтрации и т.д.
|
||||
</script>
|
||||
```
|
||||
|
||||
### Важные принципы
|
||||
|
||||
#### Разделение ответственности
|
||||
- **Entities**: Данные и их хранение
|
||||
- **Features**: Бизнес-логика и действия
|
||||
- **Widgets**: Композиция UI компонентов
|
||||
- **Pages**: Оркестрация виджетов
|
||||
|
||||
#### Композиция через слоты
|
||||
- Виджеты могут предоставлять слоты для гибкой композиции
|
||||
- Страницы используют слоты для вставки виджетов
|
||||
- Избегать жесткой связи между виджетами
|
||||
|
||||
#### Переиспользование
|
||||
- Виджеты могут переиспользоваться в разных страницах
|
||||
- Конфигурация через props для адаптации
|
||||
- Shared компоненты для общих элементов UI
|
||||
|
||||
#### Производительность
|
||||
- Ленивая загрузка данных
|
||||
- Виртуализация для больших списков
|
||||
- Оптимистичные обновления через features
|
||||
@@ -21,7 +21,7 @@ interface IContributorStore {
|
||||
loadSelf: (data: IGetContributorInput) => Promise<IContributor | null | undefined>;
|
||||
updateSelf: (contributorData: IContributor) => void;
|
||||
hasClearance: (projectHash: string) => boolean;
|
||||
isGenerationAgreementCompleted: ComputedRef<boolean>;
|
||||
isGenerationContractCompleted: ComputedRef<boolean>;
|
||||
isCapitalAgreementCompleted: ComputedRef<boolean>;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ export const useContributorStore = defineStore(
|
||||
};
|
||||
|
||||
// Вычисляемые свойства для проверки завершения шагов регистрации
|
||||
const isGenerationAgreementCompleted = computed(() => {
|
||||
const isGenerationContractCompleted = computed(() => {
|
||||
return !!self.value?.contract;
|
||||
});
|
||||
|
||||
@@ -87,7 +87,7 @@ export const useContributorStore = defineStore(
|
||||
loadSelf,
|
||||
updateSelf,
|
||||
hasClearance,
|
||||
isGenerationAgreementCompleted,
|
||||
isGenerationContractCompleted,
|
||||
isCapitalAgreementCompleted,
|
||||
};
|
||||
},
|
||||
|
||||
+22
-22
@@ -7,15 +7,15 @@ export type IMakeClearanceInput =
|
||||
export type IMakeClearanceOutput =
|
||||
Mutations.Capital.MakeClearance.IOutput[typeof Mutations.Capital.MakeClearance.name];
|
||||
|
||||
export type IGenerateProjectGenerationAgreementInput =
|
||||
Mutations.Capital.GenerateProjectGenerationAgreement.IInput['data'];
|
||||
export type IGenerateProjectGenerationAgreementOutput =
|
||||
Mutations.Capital.GenerateProjectGenerationAgreement.IOutput[typeof Mutations.Capital.GenerateProjectGenerationAgreement.name];
|
||||
export type IGenerateProjectGenerationContractInput =
|
||||
Mutations.Capital.GenerateProjectGenerationContract.IInput['data'];
|
||||
export type IGenerateProjectGenerationContractOutput =
|
||||
Mutations.Capital.GenerateProjectGenerationContract.IOutput[typeof Mutations.Capital.GenerateProjectGenerationContract.name];
|
||||
|
||||
export type IGenerateComponentGenerationAgreementInput =
|
||||
Mutations.Capital.GenerateComponentGenerationAgreement.IInput['data'];
|
||||
export type IGenerateComponentGenerationAgreementOutput =
|
||||
Mutations.Capital.GenerateComponentGenerationAgreement.IOutput[typeof Mutations.Capital.GenerateComponentGenerationAgreement.name];
|
||||
export type IGenerateComponentGenerationContractInput =
|
||||
Mutations.Capital.GenerateComponentGenerationContract.IInput['data'];
|
||||
export type IGenerateComponentGenerationContractOutput =
|
||||
Mutations.Capital.GenerateComponentGenerationContract.IOutput[typeof Mutations.Capital.GenerateComponentGenerationContract.name];
|
||||
|
||||
export type { IGenerateDocumentInput, IGeneratedDocumentOutput };
|
||||
|
||||
@@ -32,12 +32,12 @@ async function makeClearance(
|
||||
return result;
|
||||
}
|
||||
|
||||
async function generateProjectGenerationAgreement(
|
||||
data: IGenerateProjectGenerationAgreementInput,
|
||||
options?: Mutations.Capital.GenerateProjectGenerationAgreement.IInput['options'],
|
||||
): Promise<IGenerateProjectGenerationAgreementOutput> {
|
||||
const { [Mutations.Capital.GenerateProjectGenerationAgreement.name]: result } =
|
||||
await client.Mutation(Mutations.Capital.GenerateProjectGenerationAgreement.mutation, {
|
||||
async function generateProjectGenerationContract(
|
||||
data: IGenerateProjectGenerationContractInput,
|
||||
options?: Mutations.Capital.GenerateProjectGenerationContract.IInput['options'],
|
||||
): Promise<IGenerateProjectGenerationContractOutput> {
|
||||
const { [Mutations.Capital.GenerateProjectGenerationContract.name]: result } =
|
||||
await client.Mutation(Mutations.Capital.GenerateProjectGenerationContract.mutation, {
|
||||
variables: {
|
||||
data,
|
||||
options,
|
||||
@@ -47,12 +47,12 @@ async function generateProjectGenerationAgreement(
|
||||
return result;
|
||||
}
|
||||
|
||||
async function generateComponentGenerationAgreement(
|
||||
data: IGenerateComponentGenerationAgreementInput,
|
||||
options?: Mutations.Capital.GenerateComponentGenerationAgreement.IInput['options'],
|
||||
): Promise<IGenerateComponentGenerationAgreementOutput> {
|
||||
const { [Mutations.Capital.GenerateComponentGenerationAgreement.name]: result } =
|
||||
await client.Mutation(Mutations.Capital.GenerateComponentGenerationAgreement.mutation, {
|
||||
async function generateComponentGenerationContract(
|
||||
data: IGenerateComponentGenerationContractInput,
|
||||
options?: Mutations.Capital.GenerateComponentGenerationContract.IInput['options'],
|
||||
): Promise<IGenerateComponentGenerationContractOutput> {
|
||||
const { [Mutations.Capital.GenerateComponentGenerationContract.name]: result } =
|
||||
await client.Mutation(Mutations.Capital.GenerateComponentGenerationContract.mutation, {
|
||||
variables: {
|
||||
data,
|
||||
options,
|
||||
@@ -64,6 +64,6 @@ async function generateComponentGenerationAgreement(
|
||||
|
||||
export const api = {
|
||||
makeClearance,
|
||||
generateProjectGenerationAgreement,
|
||||
generateComponentGenerationAgreement,
|
||||
generateProjectGenerationContract,
|
||||
generateComponentGenerationContract,
|
||||
};
|
||||
|
||||
+22
-22
@@ -3,10 +3,10 @@ import {
|
||||
api,
|
||||
type IMakeClearanceInput,
|
||||
type IMakeClearanceOutput,
|
||||
type IGenerateProjectGenerationAgreementInput,
|
||||
type IGenerateProjectGenerationAgreementOutput,
|
||||
type IGenerateComponentGenerationAgreementInput,
|
||||
type IGenerateComponentGenerationAgreementOutput
|
||||
type IGenerateProjectGenerationContractInput,
|
||||
type IGenerateProjectGenerationContractOutput,
|
||||
type IGenerateComponentGenerationContractInput,
|
||||
type IGenerateComponentGenerationContractOutput
|
||||
} from '../api';
|
||||
import { useSignDocument } from 'src/shared/lib/document/model/entity';
|
||||
import { useSessionStore } from 'src/entities/Session/model';
|
||||
@@ -15,8 +15,8 @@ import type { IGenerateDocumentInput, IGeneratedDocumentOutput } from 'src/share
|
||||
import type { IGetProjectOutput } from 'app/extensions/capital/entities/Project/model';
|
||||
|
||||
export type { IMakeClearanceInput, IMakeClearanceOutput };
|
||||
export type { IGenerateProjectGenerationAgreementInput, IGenerateProjectGenerationAgreementOutput };
|
||||
export type { IGenerateComponentGenerationAgreementInput, IGenerateComponentGenerationAgreementOutput };
|
||||
export type { IGenerateProjectGenerationContractInput, IGenerateProjectGenerationContractOutput };
|
||||
export type { IGenerateComponentGenerationContractInput, IGenerateComponentGenerationContractOutput };
|
||||
export type { IGenerateDocumentInput, IGeneratedDocumentOutput };
|
||||
|
||||
export function useMakeClearance() {
|
||||
@@ -37,26 +37,26 @@ export function useMakeClearance() {
|
||||
}
|
||||
};
|
||||
|
||||
const generateProjectGenerationAgreement = async (
|
||||
input: IGenerateProjectGenerationAgreementInput,
|
||||
options?: Parameters<typeof api.generateProjectGenerationAgreement>[1]
|
||||
): Promise<IGenerateProjectGenerationAgreementOutput> => {
|
||||
const generateProjectGenerationContract = async (
|
||||
input: IGenerateProjectGenerationContractInput,
|
||||
options?: Parameters<typeof api.generateProjectGenerationContract>[1]
|
||||
): Promise<IGenerateProjectGenerationContractOutput> => {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
const result = await api.generateProjectGenerationAgreement(input, options);
|
||||
const result = await api.generateProjectGenerationContract(input, options);
|
||||
return result;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const generateComponentGenerationAgreement = async (
|
||||
input: IGenerateComponentGenerationAgreementInput,
|
||||
options?: Parameters<typeof api.generateComponentGenerationAgreement>[1]
|
||||
): Promise<IGenerateComponentGenerationAgreementOutput> => {
|
||||
const generateComponentGenerationContract = async (
|
||||
input: IGenerateComponentGenerationContractInput,
|
||||
options?: Parameters<typeof api.generateComponentGenerationContract>[1]
|
||||
): Promise<IGenerateComponentGenerationContractOutput> => {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
const result = await api.generateComponentGenerationAgreement(input, options);
|
||||
const result = await api.generateComponentGenerationContract(input, options);
|
||||
return result;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
@@ -99,7 +99,7 @@ export function useMakeClearance() {
|
||||
throw new Error('Родительский проект не найден для компонента');
|
||||
}
|
||||
|
||||
const generateInput: IGenerateComponentGenerationAgreementInput = {
|
||||
const generateInput: IGenerateComponentGenerationContractInput = {
|
||||
coopname,
|
||||
username,
|
||||
component_hash: project.project_hash,
|
||||
@@ -107,17 +107,17 @@ export function useMakeClearance() {
|
||||
lang: 'ru',
|
||||
};
|
||||
|
||||
generatedDocument = await generateComponentGenerationAgreement(generateInput);
|
||||
generatedDocument = await generateComponentGenerationContract(generateInput);
|
||||
} else {
|
||||
// Генерируем документ приложения к договору для проекта (1002)
|
||||
const generateInput: IGenerateProjectGenerationAgreementInput = {
|
||||
const generateInput: IGenerateProjectGenerationContractInput = {
|
||||
coopname,
|
||||
username,
|
||||
project_hash: project.project_hash,
|
||||
lang: 'ru',
|
||||
};
|
||||
|
||||
generatedDocument = await generateProjectGenerationAgreement(generateInput);
|
||||
generatedDocument = await generateProjectGenerationContract(generateInput);
|
||||
}
|
||||
|
||||
// 2. Подписываем сгенерированный документ одинарной подписью
|
||||
@@ -139,8 +139,8 @@ export function useMakeClearance() {
|
||||
|
||||
return {
|
||||
makeClearance,
|
||||
generateProjectGenerationAgreement,
|
||||
generateComponentGenerationAgreement,
|
||||
generateProjectGenerationContract,
|
||||
generateComponentGenerationContract,
|
||||
signGeneratedDocument,
|
||||
respondToInvite,
|
||||
isLoading,
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
import type { IRegisterContributorOutput } from 'app/extensions/capital/entities/Contributor/model';
|
||||
import type { IRegisterContributorInput, IGenerateGenerationAgreementInput } from '../model';
|
||||
import type { IRegisterContributorInput, IGenerateGenerationContractInput } from '../model';
|
||||
import type {
|
||||
IGenerateDocumentOptionsInput,
|
||||
IGeneratedDocumentOutput,
|
||||
@@ -20,12 +20,12 @@ async function registerContributor(
|
||||
return result;
|
||||
}
|
||||
|
||||
async function generateGenerationAgreement(
|
||||
data: IGenerateGenerationAgreementInput,
|
||||
async function generateGenerationContract(
|
||||
data: IGenerateGenerationContractInput,
|
||||
options?: IGenerateDocumentOptionsInput,
|
||||
): Promise<IGeneratedDocumentOutput> {
|
||||
const { [Mutations.Capital.GenerateGenerationAgreement.name]: result } =
|
||||
await client.Mutation(Mutations.Capital.GenerateGenerationAgreement.mutation, {
|
||||
const { [Mutations.Capital.GenerateGenerationContract.name]: result } =
|
||||
await client.Mutation(Mutations.Capital.GenerateGenerationContract.mutation, {
|
||||
variables: {
|
||||
data,
|
||||
options,
|
||||
@@ -37,5 +37,5 @@ async function generateGenerationAgreement(
|
||||
|
||||
export const api = {
|
||||
registerContributor,
|
||||
generateGenerationAgreement,
|
||||
generateGenerationContract,
|
||||
};
|
||||
|
||||
+4
-4
@@ -14,8 +14,8 @@ import { generateUniqueHash } from 'src/shared/lib/utils/generateUniqueHash';
|
||||
export type IRegisterContributorInput =
|
||||
Mutations.Capital.RegisterContributor.IInput['data'];
|
||||
|
||||
export type IGenerateGenerationAgreementInput =
|
||||
Mutations.Capital.GenerateGenerationAgreement.IInput['data'];
|
||||
export type IGenerateGenerationContractInput =
|
||||
Mutations.Capital.GenerateGenerationContract.IInput['data'];
|
||||
|
||||
export function useRegisterContributor() {
|
||||
const store = useContributorStore();
|
||||
@@ -54,13 +54,13 @@ export function useRegisterContributor() {
|
||||
contributorHash.value = await generateUniqueHash();
|
||||
}
|
||||
|
||||
const data: IGenerateGenerationAgreementInput = {
|
||||
const data: IGenerateGenerationContractInput = {
|
||||
coopname: system.info.coopname,
|
||||
username: session.username,
|
||||
contributor_hash: contributorHash.value,
|
||||
};
|
||||
console.log('🔐 Генерируем данные для генерации договора:', data);
|
||||
generatedDocument.value = await api.generateGenerationAgreement(data);
|
||||
generatedDocument.value = await api.generateGenerationContract(data);
|
||||
return generatedDocument.value;
|
||||
} catch (error) {
|
||||
console.error('Ошибка при генерации договора:', error);
|
||||
|
||||
@@ -26,7 +26,7 @@ export const useCapitalOnboarding = () => {
|
||||
// Маппинг шагов на registry_id
|
||||
const stepToRegistryId: Record<string, number> = {
|
||||
'generator_program_template': 994,
|
||||
'generation_agreement_template': 997,
|
||||
'generation_contract_template': 997,
|
||||
'blagorost_program': 998,
|
||||
'blagorost_offer_template': 999,
|
||||
};
|
||||
@@ -89,15 +89,15 @@ export const useCapitalOnboarding = () => {
|
||||
hash: typeof state?.onboarding_generator_program_template_hash === 'string' && state.onboarding_generator_program_template_hash ? state.onboarding_generator_program_template_hash : null,
|
||||
},
|
||||
{
|
||||
id: 'generation_agreement_template',
|
||||
id: 'generation_contract_template',
|
||||
title: 'Шаблон договора участия в хозяйственной деятельности',
|
||||
description: 'Утверждение шаблона договора участия в хозяйственной деятельности для работы по программе',
|
||||
question: 'О утверждении шаблона договора участия в хозяйственной деятельности',
|
||||
decision: '', // Будет заполнено через генерацию документа
|
||||
decisionPrefix: 'Утвердить шаблон договора участия в хозяйственной деятельности:',
|
||||
status: state?.generation_agreement_template_done ? 'completed' :
|
||||
state?.onboarding_generation_agreement_template_hash ? 'in_progress' : 'pending',
|
||||
hash: typeof state?.onboarding_generation_agreement_template_hash === 'string' && state.onboarding_generation_agreement_template_hash ? state.onboarding_generation_agreement_template_hash : null,
|
||||
status: state?.generation_contract_template_done ? 'completed' :
|
||||
state?.onboarding_generation_contract_template_hash ? 'in_progress' : 'pending',
|
||||
hash: typeof state?.onboarding_generation_contract_template_hash === 'string' && state.onboarding_generation_contract_template_hash ? state.onboarding_generation_contract_template_hash : null,
|
||||
},
|
||||
{
|
||||
id: 'blagorost_program',
|
||||
@@ -109,7 +109,7 @@ export const useCapitalOnboarding = () => {
|
||||
status: state?.blagorost_provision_done ? 'completed' :
|
||||
state?.onboarding_blagorost_provision_hash ? 'in_progress' : 'pending',
|
||||
hash: typeof state?.onboarding_blagorost_provision_hash === 'string' && state.onboarding_blagorost_provision_hash ? state.onboarding_blagorost_provision_hash : null,
|
||||
depends_on: ['generation_agreement_template'], // Зависит от утверждения шаблона договора
|
||||
// depends_on: ['generation_contract_template'], // Зависит от утверждения шаблона договора
|
||||
},
|
||||
{
|
||||
id: 'blagorost_offer_template',
|
||||
|
||||
@@ -40,7 +40,7 @@ const { isOnboardingCompleted, loadState } = useCapitalOnboarding();
|
||||
|
||||
// Проверка полной регистрации (есть контракт И есть соглашение с программой)
|
||||
const isFullyRegistered = computed(() => {
|
||||
return contributorStore.isGenerationAgreementCompleted && contributorStore.isCapitalAgreementCompleted;
|
||||
return contributorStore.isGenerationContractCompleted && contributorStore.isCapitalAgreementCompleted;
|
||||
});
|
||||
|
||||
// Проверка завершения онбординга капитализации
|
||||
|
||||
+2
-2
@@ -440,7 +440,7 @@ const prevStep = () => {
|
||||
|
||||
// Обновление текущего шага на основе состояния регистрации
|
||||
const updateCurrentStep = () => {
|
||||
if (!contributorStore.isGenerationAgreementCompleted) {
|
||||
if (!contributorStore.isGenerationContractCompleted) {
|
||||
// Если регистрация участия не завершена, начинаем с выбора ролей
|
||||
currentStep.value = steps.roles;
|
||||
} else if (!contributorStore.isCapitalAgreementCompleted) {
|
||||
@@ -453,7 +453,7 @@ const updateCurrentStep = () => {
|
||||
};
|
||||
|
||||
// Следим только за изменениями статуса регистрации
|
||||
watch(() => contributorStore.isGenerationAgreementCompleted, updateCurrentStep);
|
||||
watch(() => contributorStore.isGenerationContractCompleted, updateCurrentStep);
|
||||
watch(() => contributorStore.isCapitalAgreementCompleted, updateCurrentStep);
|
||||
|
||||
/**
|
||||
|
||||
@@ -119,7 +119,6 @@ export const useRegistratorStore = defineStore(
|
||||
email: '',
|
||||
selectedBranch: '',
|
||||
selectedProgramKey: '',
|
||||
requiresProgramSelection: false,
|
||||
account: structuredClone(initialAccountState),
|
||||
userData: structuredClone(initialUserDataState),
|
||||
signature: '',
|
||||
@@ -162,10 +161,18 @@ export const useRegistratorStore = defineStore(
|
||||
() => system.info?.cooperator_account.is_branched,
|
||||
);
|
||||
|
||||
// Проверяем необходимость выбора программы на основе типа аккаунта
|
||||
const requiresProgramSelection = computed(() => {
|
||||
const accountType = state.userData.type;
|
||||
// Для voskhod показываем выбор программы для индивидуальных и предпринимательских аккаунтов
|
||||
return system.info?.coopname === 'voskhod' &&
|
||||
(accountType === 'individual' || accountType === 'entrepreneur');
|
||||
});
|
||||
|
||||
const filteredSteps = computed(() =>
|
||||
stepNames.filter((step) => {
|
||||
if (step === 'SelectBranch' && !isBranched.value) return false;
|
||||
if (step === 'SelectProgram' && !state.requiresProgramSelection) return false;
|
||||
if (step === 'SelectProgram' && !requiresProgramSelection.value) return false;
|
||||
return true;
|
||||
}),
|
||||
);
|
||||
@@ -215,7 +222,6 @@ export const useRegistratorStore = defineStore(
|
||||
state.step = 1;
|
||||
state.selectedBranch = '';
|
||||
state.selectedProgramKey = '';
|
||||
state.requiresProgramSelection = false;
|
||||
state.email = '';
|
||||
state.account = structuredClone(initialAccountState);
|
||||
state.agreements = structuredClone(initialAgreementsState);
|
||||
|
||||
@@ -140,7 +140,7 @@ export function useCreateUser() {
|
||||
privacy_agreement: privacyDoc?.signed_document || store.privacyAgreement,
|
||||
signature_agreement: signatureDoc?.signed_document || store.signatureAgreement,
|
||||
user_agreement: userDoc?.signed_document || store.userAgreement,
|
||||
program_key: registratorStore.state.selectedProgramKey || undefined,
|
||||
program_key: registratorStore.state.selectedProgramKey as Zeus.ProgramKey | undefined,
|
||||
};
|
||||
|
||||
// Добавляем blagorost_offer если есть
|
||||
|
||||
@@ -18,7 +18,7 @@ q-step(
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, watch, onMounted } from 'vue';
|
||||
import { computed, watch, ref } from 'vue';
|
||||
import { useCreateUser } from 'src/features/User/CreateUser';
|
||||
import { FailAlert } from 'src/shared/api';
|
||||
import { useSystemStore } from 'src/entities/System/model';
|
||||
@@ -33,21 +33,37 @@ const api = useCreateUser();
|
||||
|
||||
const step = computed(() => store.state.step);
|
||||
const coop = useCooperativeStore();
|
||||
const isCreatingPayment = ref(false);
|
||||
|
||||
// Computed property для payment с правильной типизацией
|
||||
const payment = computed(() => store.state.payment);
|
||||
|
||||
const currentStep = store.steps.PayInitial;
|
||||
|
||||
onMounted(async () => {
|
||||
await coop.loadPublicCooperativeData(info.coopname);
|
||||
// Загружаем данные кооператива один раз при инициализации
|
||||
coop.loadPublicCooperativeData(info.coopname);
|
||||
|
||||
if (step.value === currentStep) createInitialPayment();
|
||||
});
|
||||
const createInitialPayment = async () => {
|
||||
// Защита от повторных вызовов
|
||||
if (isCreatingPayment.value || store.state.is_paid) return;
|
||||
|
||||
try {
|
||||
isCreatingPayment.value = true;
|
||||
await api.createInitialPayment();
|
||||
} catch (e: any) {
|
||||
FailAlert(
|
||||
'Возникла ошибка на этапе оплаты. Попробуйте позже или обратитесь в поддержку.',
|
||||
);
|
||||
console.error(e);
|
||||
} finally {
|
||||
isCreatingPayment.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Используем только watch с immediate: true вместо onMounted + watch
|
||||
watch(step, (newValue) => {
|
||||
if (newValue === currentStep) createInitialPayment();
|
||||
});
|
||||
}, { immediate: true });
|
||||
|
||||
watch(
|
||||
() => store.state.is_paid,
|
||||
@@ -59,19 +75,6 @@ watch(
|
||||
},
|
||||
);
|
||||
|
||||
const createInitialPayment = async () => {
|
||||
try {
|
||||
if (!store.state.is_paid) {
|
||||
await api.createInitialPayment();
|
||||
}
|
||||
} catch (e: any) {
|
||||
FailAlert(
|
||||
'Возникла ошибка на этапе оплаты. Попробуйте позже или обратитесь в поддержку.',
|
||||
);
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const paymentFail = (): void => {
|
||||
FailAlert(
|
||||
'Возникла ошибка на этапе оплаты. Попробуйте позже или обратитесь в поддержку.',
|
||||
|
||||
@@ -44,7 +44,7 @@ div
|
||||
q-btn.q-mt-lg.q-mb-lg(color='primary', label='Продолжить', :disabled='!agreeWithAll' @click='registratorStore.next()')
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch, onMounted, nextTick } from 'vue'
|
||||
import { ref, computed, watch, nextTick } from 'vue'
|
||||
import { useCreateUser } from 'src/features/User/CreateUser'
|
||||
import { FailAlert } from 'src/shared/api';
|
||||
import { Loader } from 'src/shared/ui/Loader';
|
||||
@@ -70,16 +70,23 @@ const agreeWithAll = computed(() => {
|
||||
|
||||
const html = ref()
|
||||
const isLoading = ref(false)
|
||||
const isGenerating = ref(false)
|
||||
const loadingText = ref('Загружаем документы...')
|
||||
const statementDiv = ref<any>()
|
||||
|
||||
const loadDocuments = async (): Promise<void> => {
|
||||
// Защита от повторных вызовов
|
||||
if (isGenerating.value) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
isGenerating.value = true
|
||||
isLoading.value = true
|
||||
loadingText.value = 'Генерируем документы для подписи...'
|
||||
|
||||
// Генерируем все документы регистрации с бэкенда (они уже содержат HTML для отображения)
|
||||
await generateAllRegistrationDocuments(registratorStore.state.selectedProgramKey)
|
||||
await generateAllRegistrationDocuments(registratorStore.state.selectedProgramKey || undefined)
|
||||
|
||||
loadingText.value = 'Заполняем заявление...'
|
||||
|
||||
@@ -93,6 +100,8 @@ const loadDocuments = async (): Promise<void> => {
|
||||
} catch (e: any) {
|
||||
isLoading.value = false
|
||||
FailAlert(e)
|
||||
} finally {
|
||||
isGenerating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,17 +113,12 @@ const back = () => {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (registratorStore.state.step === registratorStore.steps.ReadStatement) {
|
||||
loadDocuments()
|
||||
}
|
||||
})
|
||||
|
||||
// Используем только watch с immediate: true вместо onMounted + watch
|
||||
watch(() => registratorStore.state.step, (value: number) => {
|
||||
if (value === registratorStore.steps.ReadStatement) {
|
||||
loadDocuments()
|
||||
}
|
||||
})
|
||||
}, { immediate: true })
|
||||
|
||||
const statuteLink = computed(() => {
|
||||
return systemStore.info.vars?.statute_link || ''
|
||||
|
||||
@@ -25,26 +25,30 @@ import { FailAlert } from 'src/shared/api';
|
||||
import { useSystemStore } from 'src/entities/System/model';
|
||||
const { info } = useSystemStore()
|
||||
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { computed, watch, ref } from 'vue'
|
||||
import { BranchSelector } from 'src/shared/ui/BranchSelector';
|
||||
|
||||
const store = useRegistratorStore()
|
||||
const branchStore = useBranchStore()
|
||||
const isLoading = ref(false)
|
||||
|
||||
const load = async () => {
|
||||
if (store.isStep('SelectBranch'))
|
||||
try{
|
||||
await branchStore.loadPublicBranches({ coopname: info.coopname })
|
||||
} catch(e: any){
|
||||
FailAlert(e)
|
||||
}
|
||||
if (!store.isStep('SelectBranch') || isLoading.value) return;
|
||||
|
||||
try {
|
||||
isLoading.value = true
|
||||
await branchStore.loadPublicBranches({ coopname: info.coopname })
|
||||
} catch(e: any) {
|
||||
FailAlert(e)
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => load())
|
||||
|
||||
// Используем только watch с immediate: true вместо onMounted + watch
|
||||
watch(() => store.state.step, async () => {
|
||||
load()
|
||||
})
|
||||
}, { immediate: true })
|
||||
|
||||
// Массив используется без изменений
|
||||
const branches = computed(() => branchStore.publicBranches)
|
||||
|
||||
@@ -51,7 +51,7 @@ div
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import { useRegistratorStore } from 'src/entities/Registrator';
|
||||
import { useSystemStore } from 'src/entities/System/model';
|
||||
import { Loader } from 'src/shared/ui/Loader';
|
||||
@@ -63,10 +63,17 @@ const registratorStore = useRegistratorStore();
|
||||
const systemStore = useSystemStore();
|
||||
|
||||
const isLoading = ref(false);
|
||||
const isLoadingPrograms = ref(false);
|
||||
const programs = ref<any[]>([]);
|
||||
|
||||
const loadPrograms = async () => {
|
||||
// Защита от повторных вызовов
|
||||
if (isLoadingPrograms.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
isLoadingPrograms.value = true;
|
||||
isLoading.value = true;
|
||||
|
||||
const accountType = registratorStore.state.userData.type;
|
||||
@@ -98,8 +105,11 @@ const loadPrograms = async () => {
|
||||
} catch (e: any) {
|
||||
console.error('Error loading programs:', e);
|
||||
FailAlert(e);
|
||||
// В случае ошибки все равно переходим дальше
|
||||
registratorStore.next();
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
isLoadingPrograms.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -107,15 +117,10 @@ const selectProgram = (key: string) => {
|
||||
registratorStore.state.selectedProgramKey = key;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (registratorStore.state.step === registratorStore.steps.SelectProgram) {
|
||||
loadPrograms();
|
||||
}
|
||||
});
|
||||
|
||||
// Используем только watch с immediate: true вместо onMounted + watch
|
||||
watch(() => registratorStore.state.step, (value: number) => {
|
||||
if (value === registratorStore.steps.SelectProgram) {
|
||||
loadPrograms();
|
||||
}
|
||||
});
|
||||
}, { immediate: true });
|
||||
</script>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user