[C28-33][@ant] feat: реквизиты получателей по payment_method со снимком в БД + СЗ 2010 по бумажному образцу — полные реквизиты в документ подставляет сервер, фронт видит сокращённые; вычищены осиротевшие authorizeExpenseReport/declineExpenseReport
This commit is contained in:
+17
@@ -41,6 +41,23 @@ class ExpenseProposalItemInputDTO implements ItemAction {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
requisites?: string;
|
||||
|
||||
@Field({
|
||||
description:
|
||||
'Идентификатор сохранённых реквизитов получателя-пайщика — сервер подставит полные реквизиты в документ.',
|
||||
nullable: true,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
payment_method_id?: string;
|
||||
|
||||
@Field({
|
||||
description: 'Имя аккаунта получателя-пайщика (владелец реквизитов).',
|
||||
nullable: true,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
recipient_username?: string;
|
||||
}
|
||||
|
||||
@InputType('ExpenseProposalHeaderInput')
|
||||
|
||||
+18
-1
@@ -8,6 +8,7 @@ import {
|
||||
type InterExpenseItem,
|
||||
type InterExpenseProposalRead,
|
||||
type InterExpenseProposalStatus,
|
||||
type InterExpenseRequisiteItemInput,
|
||||
} from '@coopenomics/inter';
|
||||
import { AccountDataPort, ACCOUNT_DATA_PORT } from '~/domain/account/ports/account-data.port';
|
||||
import { CapitalBlockchainPort, CAPITAL_BLOCKCHAIN_PORT } from '../../domain/interfaces/capital-blockchain.port';
|
||||
@@ -50,6 +51,18 @@ export class ProgramExpensesManagementService {
|
||||
) {}
|
||||
|
||||
async createProgramExpense(data: CreateProgramExpenseInputDTO): Promise<TransactResult> {
|
||||
// Реквизиты получателей: валидация ДО on-chain заявки, снимок в шасси —
|
||||
// ПОСЛЕ (фиксация «куда платить» на момент создания СЗ).
|
||||
const requisiteItems: InterExpenseRequisiteItemInput[] = data.items.map((it) => ({
|
||||
proposalHash: data.expense_hash,
|
||||
itemHash: it.item_hash,
|
||||
recipient: it.recipient,
|
||||
isOrganization: it.recipient_type === ExpenseRecipientType.ORG,
|
||||
paymentMethodId: it.payment_method_id,
|
||||
requisites: it.requisites,
|
||||
}));
|
||||
await this.expenseChassis.validateRequisites(data.coopname, requisiteItems);
|
||||
|
||||
const zeroAmount = `0.0000 ${config.blockchain.root_govern_symbol}`;
|
||||
const blockchainData: CapitalContract.Actions.CreateProgramExpense.ICreateProgramExpense = {
|
||||
coopname: data.coopname,
|
||||
@@ -73,7 +86,11 @@ export class ProgramExpensesManagementService {
|
||||
description: data.description,
|
||||
statement: this.domainToBlockchainUtils.convertSignedDocumentToBlockchainFormat(data.statement),
|
||||
};
|
||||
return this.capitalBlockchainPort.createProgramExpense(blockchainData);
|
||||
const result = await this.capitalBlockchainPort.createProgramExpense(blockchainData);
|
||||
|
||||
await this.expenseChassis.snapshotRequisites(data.coopname, requisiteItems);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async topupProgramExpense(data: TopupProgramExpenseInputDTO): Promise<TransactResult> {
|
||||
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsNotEmpty, IsString, ValidateNested } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ExpenseProposalDecisionSignedDocumentInputDTO } from '~/application/document/documents-dto/expense-proposal-decision-document.dto';
|
||||
|
||||
/**
|
||||
* Input авторизации СЗ-отчёта (председатель / совет утверждает закрытие сметы).
|
||||
*
|
||||
* Сабмит: `expense::authexp` через `ExpensesBlockchainPort.authExp`.
|
||||
* Подпись `decision` (registry 2011) сделана на стороне UI.
|
||||
*
|
||||
* После цепочки на-цепи: REPORT_SUBMITTED → CLOSED → `ExpensesCapitalTriggerService`
|
||||
* запускает capitalization Благороста (capital::createrid на сумму total_actual).
|
||||
*/
|
||||
@InputType('AuthorizeExpenseReportInput')
|
||||
export class AuthorizeExpenseReportInputDTO {
|
||||
@Field(() => String, { description: 'Имя кооператива.' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
coopname!: string;
|
||||
|
||||
@Field(() => String, { description: 'Хеш сметы расхода.' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
proposal_hash!: string;
|
||||
|
||||
@Field(() => ExpenseProposalDecisionSignedDocumentInputDTO, {
|
||||
description: 'Подписанное решение совета (document2, registry 2011).',
|
||||
})
|
||||
@ValidateNested()
|
||||
@Type(() => ExpenseProposalDecisionSignedDocumentInputDTO)
|
||||
decision!: ExpenseProposalDecisionSignedDocumentInputDTO;
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsNotEmpty, IsString, MaxLength } from 'class-validator';
|
||||
|
||||
/**
|
||||
* Input отклонения СЗ-отчёта (председатель / совет не утверждает закрытие).
|
||||
*
|
||||
* На-цепи: `expense::declineexp` (REPORT_SUBMITTED → DECLINED).
|
||||
* Capitalization Благороста НЕ запускается. Reason — обязательный, видимый в UI
|
||||
* и в audit log пайщика; до 1000 символов.
|
||||
*/
|
||||
@InputType('DeclineExpenseReportInput')
|
||||
export class DeclineExpenseReportInputDTO {
|
||||
@Field(() => String, { description: 'Имя кооператива.' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
coopname!: string;
|
||||
|
||||
@Field(() => String, { description: 'Хеш сметы расхода.' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
proposal_hash!: string;
|
||||
|
||||
@Field(() => String, { description: 'Причина отклонения СЗ-отчёта (до 1000 символов).' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@MaxLength(1000)
|
||||
reason!: string;
|
||||
}
|
||||
+18
-1
@@ -1,5 +1,5 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
||||
import { IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
import { ExpenseMechanics } from '../../domain/enums/expense-mechanics.enum';
|
||||
import { ExpenseRecipientType } from '../../domain/enums/expense-recipient-type.enum';
|
||||
|
||||
@@ -31,4 +31,21 @@ export class ExpenseItemInputDTO {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
planned_amount!: string;
|
||||
|
||||
@Field(() => String, {
|
||||
nullable: true,
|
||||
description:
|
||||
'Идентификатор сохранённых реквизитов получателя-пайщика — реквизиты снимаются в момент создания и прикладываются к платежу.',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
payment_method_id?: string;
|
||||
|
||||
@Field(() => String, {
|
||||
nullable: true,
|
||||
description: 'Реквизиты получателя-организации (вводятся вручную).',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
requisites?: string;
|
||||
}
|
||||
|
||||
+3
-28
@@ -16,16 +16,14 @@ import { ReportExpenseItemInputDTO } from '../dto/report-expense-item.input';
|
||||
import { ReturnExpenseItemInputDTO } from '../dto/return-expense-item.input';
|
||||
import { OverspendExpenseItemInputDTO } from '../dto/overspend-expense-item.input';
|
||||
import { SubmitExpenseReportInputDTO } from '../dto/submit-expense-report.input';
|
||||
import { AuthorizeExpenseReportInputDTO } from '../dto/authorize-expense-report.input';
|
||||
import { DeclineExpenseReportInputDTO } from '../dto/decline-expense-report.input';
|
||||
|
||||
/**
|
||||
* GraphQL Mutation-резолвер контракта `expense`.
|
||||
*
|
||||
* 8 actions полного lifecycle: createExpenseProposal / authorizeExpenseReport /
|
||||
* payExpenseItem / reportExpenseItem / returnExpenseItem / overspendExpenseItem /
|
||||
* submitExpenseReport / declineExpenseReport. Все маршрутизируются в
|
||||
* createExpenseProposal / payExpenseItem / reportExpenseItem / returnExpenseItem /
|
||||
* overspendExpenseItem / submitExpenseReport. Все маршрутизируются в
|
||||
* `ExpensesMutationsService`, который сабмитит через `ExpensesBlockchainPort`.
|
||||
* Авторизация и отклонение СЗ — решение совета (повестка), не мутации backend'а.
|
||||
*/
|
||||
@Resolver()
|
||||
export class ExpenseMutationsResolver {
|
||||
@@ -135,27 +133,4 @@ export class ExpenseMutationsResolver {
|
||||
return this.expensesMutations.submitExpenseReport(data);
|
||||
}
|
||||
|
||||
@Mutation(() => TransactionDTO, {
|
||||
name: 'authorizeExpenseReport',
|
||||
description: 'Утвердить СЗ-отчёт (закрытие сметы). Триггерит капитализацию РИД в Благоросте.',
|
||||
})
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
@AuthRoles(['chairman'])
|
||||
async authorizeExpenseReport(
|
||||
@Args('data', { type: () => AuthorizeExpenseReportInputDTO }) data: AuthorizeExpenseReportInputDTO
|
||||
): Promise<TransactionDTO> {
|
||||
return this.expensesMutations.authorizeExpenseReport(data);
|
||||
}
|
||||
|
||||
@Mutation(() => TransactionDTO, {
|
||||
name: 'declineExpenseReport',
|
||||
description: 'Отклонить СЗ-отчёт с указанием причины. Смета переходит в DECLINED.',
|
||||
})
|
||||
@UseGuards(GqlJwtAuthGuard, RolesGuard)
|
||||
@AuthRoles(['chairman'])
|
||||
async declineExpenseReport(
|
||||
@Args('data', { type: () => DeclineExpenseReportInputDTO }) data: DeclineExpenseReportInputDTO
|
||||
): Promise<TransactionDTO> {
|
||||
return this.expensesMutations.declineExpenseReport(data);
|
||||
}
|
||||
}
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
import { BadRequestException, Inject, Injectable, Logger } from '@nestjs/common'
|
||||
import { InjectRepository } from '@nestjs/typeorm'
|
||||
import { Repository } from 'typeorm'
|
||||
import type { InterExpenseRequisiteItemInput } from '@coopenomics/inter'
|
||||
import { PAYMENT_METHOD_REPOSITORY, PaymentMethodRepository } from '~/domain/common/repositories/payment-method.repository'
|
||||
import { ExpenseRequisiteSnapshotTypeormEntity } from '../../infrastructure/entities/expense-requisite-snapshot.typeorm-entity'
|
||||
import { formatPaymentMethodRequisites } from '../../domain/utils/format-requisites.util'
|
||||
|
||||
/**
|
||||
* Снимки реквизитов получателей по строкам СЗ. Канон gateway prepareWithdraw →
|
||||
* persistWithdraw: `validate` зовётся ДО on-chain заявки (метод существует),
|
||||
* `snapshot` — ПОСЛЕ (данные метода копируются на момент создания СЗ).
|
||||
* Реквизиты в чейн не пишутся — только в БД шасси и в документ 2010.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ExpenseRequisiteSnapshotsService {
|
||||
private readonly logger = new Logger(ExpenseRequisiteSnapshotsService.name)
|
||||
|
||||
constructor(
|
||||
@Inject(PAYMENT_METHOD_REPOSITORY)
|
||||
private readonly paymentMethods: PaymentMethodRepository,
|
||||
@InjectRepository(ExpenseRequisiteSnapshotTypeormEntity)
|
||||
private readonly repository: Repository<ExpenseRequisiteSnapshotTypeormEntity>
|
||||
) {}
|
||||
|
||||
async validate(coopname: string, items: InterExpenseRequisiteItemInput[]): Promise<void> {
|
||||
await this.resolve(coopname, items)
|
||||
}
|
||||
|
||||
async snapshot(coopname: string, items: InterExpenseRequisiteItemInput[]): Promise<void> {
|
||||
const snapshots = await this.resolve(coopname, items)
|
||||
if (snapshots.length === 0) return
|
||||
try {
|
||||
await this.repository.save(snapshots)
|
||||
} catch (error: any) {
|
||||
// On-chain заявка уже создана — без снимка бухгалтер не получит реквизиты
|
||||
// для оплаты. Требуется ручная сверка.
|
||||
this.logger.error(
|
||||
`КРИТИЧНО: СЗ ${items[0]?.proposalHash} создана on-chain, но снимок реквизитов не сохранён: ${error.message}`,
|
||||
error
|
||||
)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/** Полные реквизиты платёжного метода пайщика строкой — для документов. */
|
||||
async formatForOwner(username: string, methodId: string): Promise<string> {
|
||||
const method = await this.paymentMethods.get({ username, method_id: methodId })
|
||||
return formatPaymentMethodRequisites(method)
|
||||
}
|
||||
|
||||
private async resolve(
|
||||
coopname: string,
|
||||
items: InterExpenseRequisiteItemInput[]
|
||||
): Promise<ExpenseRequisiteSnapshotTypeormEntity[]> {
|
||||
return Promise.all(
|
||||
items.map(async (it) => {
|
||||
const snapshot = this.repository.create({
|
||||
coopname,
|
||||
proposal_hash: it.proposalHash.toLowerCase(),
|
||||
item_hash: it.itemHash.toLowerCase(),
|
||||
recipient: it.recipient,
|
||||
method_id: null,
|
||||
method_type: null,
|
||||
data: null,
|
||||
requisites: it.requisites ?? '',
|
||||
})
|
||||
|
||||
if (it.isOrganization) {
|
||||
return snapshot
|
||||
}
|
||||
|
||||
if (!it.paymentMethodId) {
|
||||
throw new BadRequestException(
|
||||
`Не указаны реквизиты получателя ${it.recipient} (платёжный метод) по строке расхода`
|
||||
)
|
||||
}
|
||||
|
||||
const method = await this.paymentMethods.get({
|
||||
username: it.recipient,
|
||||
method_id: it.paymentMethodId,
|
||||
})
|
||||
|
||||
snapshot.method_id = method.method_id
|
||||
snapshot.method_type = method.method_type
|
||||
snapshot.data = method.data as unknown as Record<string, unknown>
|
||||
snapshot.requisites = formatPaymentMethodRequisites(method)
|
||||
return snapshot
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
+48
-36
@@ -6,39 +6,46 @@ import type { ReportExpenseItemInputDTO } from '../dto/report-expense-item.input
|
||||
import type { ReturnExpenseItemInputDTO } from '../dto/return-expense-item.input'
|
||||
import type { OverspendExpenseItemInputDTO } from '../dto/overspend-expense-item.input'
|
||||
import type { SubmitExpenseReportInputDTO } from '../dto/submit-expense-report.input'
|
||||
import type { AuthorizeExpenseReportInputDTO } from '../dto/authorize-expense-report.input'
|
||||
import type { CreateExpenseProposalInputDTO } from '../dto/create-expense-proposal.input'
|
||||
import type { DeclineExpenseReportInputDTO } from '../dto/decline-expense-report.input'
|
||||
import { ExpenseMechanics } from '../../domain/enums/expense-mechanics.enum'
|
||||
import { ExpenseRecipientType } from '../../domain/enums/expense-recipient-type.enum'
|
||||
|
||||
/**
|
||||
* Контракт-тест: все 8 mutations пробрасывают payload в `ExpensesBlockchainPort`
|
||||
* с корректным action-mapping. createExpenseProposal/authorizeExpenseReport
|
||||
* раскладывают подписанный document2 через `.toDocument()`.
|
||||
* Контракт-тест: backend-mutations пробрасывают payload в `ExpensesBlockchainPort`
|
||||
* с корректным action-mapping. createExpenseProposal раскладывает подписанный
|
||||
* document2 через `.toDocument()` и снимает реквизиты получателей-пайщиков.
|
||||
* authexp/declexp — callbacks решения совета, у backend'а их нет.
|
||||
*/
|
||||
describe('ExpensesMutationsService', () => {
|
||||
let service: ExpensesMutationsService
|
||||
let chain: jest.Mocked<ExpensesBlockchainPort>
|
||||
let generator: jest.Mocked<Pick<GeneratorInfrastructureService, 'generateDocument'>>
|
||||
let requisiteSnapshots: { validate: jest.Mock; snapshot: jest.Mock; formatForOwner: jest.Mock }
|
||||
|
||||
const fakeResult = { response: { transaction_id: 'tx_abc' } } as never
|
||||
|
||||
beforeEach(() => {
|
||||
chain = {
|
||||
createExp: jest.fn().mockResolvedValue(fakeResult),
|
||||
authExp: jest.fn().mockResolvedValue(fakeResult),
|
||||
payExp: jest.fn().mockResolvedValue(fakeResult),
|
||||
reportExp: jest.fn().mockResolvedValue(fakeResult),
|
||||
returnExp: jest.fn().mockResolvedValue(fakeResult),
|
||||
overspendExp: jest.fn().mockResolvedValue(fakeResult),
|
||||
closeExp: jest.fn().mockResolvedValue(fakeResult),
|
||||
declineExp: jest.fn().mockResolvedValue(fakeResult),
|
||||
}
|
||||
} as unknown as jest.Mocked<ExpensesBlockchainPort>
|
||||
generator = {
|
||||
generateDocument: jest.fn().mockResolvedValue({} as never),
|
||||
}
|
||||
service = new ExpensesMutationsService(chain, generator as unknown as GeneratorInfrastructureService)
|
||||
requisiteSnapshots = {
|
||||
validate: jest.fn().mockResolvedValue(undefined),
|
||||
snapshot: jest.fn().mockResolvedValue(undefined),
|
||||
formatForOwner: jest.fn().mockResolvedValue('Банковский перевод: счёт 40817810000000000000, Банк ВТБ (ПАО)'),
|
||||
}
|
||||
service = new ExpensesMutationsService(
|
||||
chain,
|
||||
generator as unknown as GeneratorInfrastructureService,
|
||||
requisiteSnapshots as never
|
||||
)
|
||||
})
|
||||
|
||||
const makeSignedDoc = (overrides: Partial<{ hash: string; doc_hash: string; meta_hash: string }> = {}) => ({
|
||||
@@ -84,6 +91,7 @@ describe('ExpensesMutationsService', () => {
|
||||
recipient: 'petrov',
|
||||
description: 'Закупка кормов',
|
||||
planned_amount: '5000.0000 RUB',
|
||||
payment_method_id: 'pm-1',
|
||||
},
|
||||
],
|
||||
statement: makeSignedDoc() as any,
|
||||
@@ -103,22 +111,42 @@ describe('ExpensesMutationsService', () => {
|
||||
expect(call.items[0].recipient_type).toBe(1)
|
||||
expect(call.callback).toEqual({ contract: '', action: '', data: '' })
|
||||
expect(call.statement.doc_hash).toBe('0xdoc')
|
||||
|
||||
// Реквизиты: валидация до блокчейна, снимок — после.
|
||||
expect(requisiteSnapshots.validate).toHaveBeenCalledTimes(1)
|
||||
expect(requisiteSnapshots.snapshot).toHaveBeenCalledTimes(1)
|
||||
const snapItems = requisiteSnapshots.snapshot.mock.calls[0][1]
|
||||
expect(snapItems[0]).toMatchObject({
|
||||
proposalHash: '0xabc',
|
||||
itemHash: '0xitem1',
|
||||
recipient: 'petrov',
|
||||
isOrganization: false,
|
||||
paymentMethodId: 'pm-1',
|
||||
})
|
||||
})
|
||||
|
||||
it('authorizeExpenseReport → chain.authExp с decision document2', async () => {
|
||||
const input: AuthorizeExpenseReportInputDTO = {
|
||||
it('createExpenseProposal: ошибка валидации реквизитов не доходит до блокчейна', async () => {
|
||||
requisiteSnapshots.validate.mockRejectedValue(new Error('Не указаны реквизиты получателя'))
|
||||
const input: CreateExpenseProposalInputDTO = {
|
||||
coopname: 'voskhod',
|
||||
username: 'ivanov',
|
||||
proposal_hash: '0xabc',
|
||||
decision: makeSignedDoc({ doc_hash: '0xdecdoc' }) as any,
|
||||
} as AuthorizeExpenseReportInputDTO
|
||||
source_wallet: 'w.cap.blago',
|
||||
items: [
|
||||
{
|
||||
item_hash: '0xitem1',
|
||||
mechanics: ExpenseMechanics.ADVANCE,
|
||||
recipient_type: ExpenseRecipientType.SELF,
|
||||
recipient: 'ivanov',
|
||||
description: 'Канцелярия',
|
||||
planned_amount: '1000.0000 RUB',
|
||||
},
|
||||
],
|
||||
statement: makeSignedDoc() as any,
|
||||
} as CreateExpenseProposalInputDTO
|
||||
|
||||
await service.authorizeExpenseReport(input)
|
||||
|
||||
expect(chain.authExp).toHaveBeenCalledTimes(1)
|
||||
const call = chain.authExp.mock.calls[0][0]
|
||||
expect(call.coopname).toBe('voskhod')
|
||||
expect(call.proposal_hash).toBe('0xabc')
|
||||
expect(call.decision.doc_hash).toBe('0xdecdoc')
|
||||
await expect(service.createExpenseProposal(input)).rejects.toThrow('Не указаны реквизиты')
|
||||
expect(chain.createExp).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('payExpenseItem → chain.payExp({coopname, proposal_hash, item_hash, actual_amount})', async () => {
|
||||
@@ -203,20 +231,4 @@ describe('ExpensesMutationsService', () => {
|
||||
|
||||
expect(chain.closeExp).toHaveBeenCalledWith({ coopname: 'voskhod', proposal_hash: '0xabc' })
|
||||
})
|
||||
|
||||
it('declineExpenseReport → chain.declineExp({coopname, proposal_hash, reason})', async () => {
|
||||
const input = {
|
||||
coopname: 'voskhod',
|
||||
proposal_hash: '0xabc',
|
||||
reason: 'не утверждаю',
|
||||
} as DeclineExpenseReportInputDTO
|
||||
|
||||
await service.declineExpenseReport(input)
|
||||
|
||||
expect(chain.declineExp).toHaveBeenCalledWith({
|
||||
coopname: 'voskhod',
|
||||
proposal_hash: '0xabc',
|
||||
reason: 'не утверждаю',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+53
-30
@@ -1,38 +1,41 @@
|
||||
import { Inject, Injectable } from '@nestjs/common'
|
||||
import type { TransactResult } from '@wharfkit/session'
|
||||
import type { InterExpenseRequisiteItemInput } from '@coopenomics/inter'
|
||||
import { Cooperative } from 'cooptypes'
|
||||
import { GeneratorInfrastructureService } from '~/infrastructure/generator/generator.service'
|
||||
import type { DocumentDomainEntity } from '~/domain/document/entity/document-domain.entity'
|
||||
import { ExpenseProposalStatementGenerateDocumentInputDTO } from '~/application/document/documents-dto/expense-proposal-statement-document.dto'
|
||||
import { ExpenseProposalDecisionGenerateDocumentInputDTO } from '~/application/document/documents-dto/expense-proposal-decision-document.dto'
|
||||
import { CreateExpenseProposalInputDTO } from '../dto/create-expense-proposal.input'
|
||||
import type { ExpenseItemInputDTO } from '../dto/expense-item.input'
|
||||
import { PayExpenseItemInputDTO } from '../dto/pay-expense-item.input'
|
||||
import { ReportExpenseItemInputDTO } from '../dto/report-expense-item.input'
|
||||
import { ReturnExpenseItemInputDTO } from '../dto/return-expense-item.input'
|
||||
import { OverspendExpenseItemInputDTO } from '../dto/overspend-expense-item.input'
|
||||
import { SubmitExpenseReportInputDTO } from '../dto/submit-expense-report.input'
|
||||
import { AuthorizeExpenseReportInputDTO } from '../dto/authorize-expense-report.input'
|
||||
import { DeclineExpenseReportInputDTO } from '../dto/decline-expense-report.input'
|
||||
import {
|
||||
EXPENSES_BLOCKCHAIN_PORT,
|
||||
ExpensesBlockchainPort,
|
||||
} from '../../domain/interfaces/expenses-blockchain.port'
|
||||
import { ExpenseMechanics } from '../../domain/enums/expense-mechanics.enum'
|
||||
import { ExpenseRecipientType } from '../../domain/enums/expense-recipient-type.enum'
|
||||
import { ExpenseRequisiteSnapshotsService } from './expense-requisite-snapshots.service'
|
||||
|
||||
/**
|
||||
* Write-сервис расходов (8 actions полного lifecycle).
|
||||
* Write-сервис расходов.
|
||||
*
|
||||
* Подписывает ключом кооператива (`active`), `account = expense`. Для пайщик-actions
|
||||
* (`reportexp` / `returnexp`) — тот же канон, что в capital (`createCommit` пайщика
|
||||
* сервисно подписан кооперативом).
|
||||
* сервисно подписан кооперативом). Авторизация / отклонение СЗ идут через решение
|
||||
* совета (callbacks authexp/declexp от контракта soviet) — здесь их нет.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ExpensesMutationsService {
|
||||
constructor(
|
||||
@Inject(EXPENSES_BLOCKCHAIN_PORT)
|
||||
private readonly chain: ExpensesBlockchainPort,
|
||||
private readonly generator: GeneratorInfrastructureService
|
||||
private readonly generator: GeneratorInfrastructureService,
|
||||
private readonly requisiteSnapshots: ExpenseRequisiteSnapshotsService
|
||||
) {}
|
||||
|
||||
async generateExpenseProposalStatementDocument(
|
||||
@@ -40,6 +43,30 @@ export class ExpensesMutationsService {
|
||||
options: Cooperative.Document.IGenerationOptions
|
||||
): Promise<DocumentDomainEntity> {
|
||||
data.registry_id = Cooperative.Registry.ExpenseProposalStatement.registry_id
|
||||
|
||||
// Полные реквизиты получателей подставляет сервер: фронт оперирует только
|
||||
// идентификатором платёжного метода (и видит лишь сокращённое представление).
|
||||
// Служебные поля (payment_method_id / recipient_username) в meta документа
|
||||
// не попадают — items пересобираются на документные поля.
|
||||
data.items = await Promise.all(
|
||||
data.items.map(async (item) => {
|
||||
let requisites = item.requisites
|
||||
if (item.payment_method_id) {
|
||||
const owner = item.recipient_username || data.username
|
||||
requisites = await this.requisiteSnapshots.formatForOwner(owner, item.payment_method_id)
|
||||
}
|
||||
return {
|
||||
number: item.number,
|
||||
description: item.description,
|
||||
amount: item.amount,
|
||||
recipient_type: item.recipient_type,
|
||||
mechanics: item.mechanics,
|
||||
recipient_name: item.recipient_name,
|
||||
requisites,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
return this.generator.generateDocument({ data: data as unknown as Cooperative.Registry.ExpenseProposalStatement.Action, options: options || {} })
|
||||
}
|
||||
|
||||
@@ -52,6 +79,11 @@ export class ExpensesMutationsService {
|
||||
}
|
||||
|
||||
async createExpenseProposal(input: CreateExpenseProposalInputDTO): Promise<TransactResult> {
|
||||
// Валидация реквизитов ДО блокчейна, снимок — ПОСЛЕ (канон gateway
|
||||
// prepareWithdraw → persistWithdraw). Реквизиты в чейн не пишутся.
|
||||
const requisiteItems = toRequisiteItems(input.proposal_hash, input.items)
|
||||
await this.requisiteSnapshots.validate(input.coopname, requisiteItems)
|
||||
|
||||
const items = input.items.map((it) => ({
|
||||
item_hash: it.item_hash,
|
||||
mechanics: it.mechanics === ExpenseMechanics.DIRECT ? 1 : 0,
|
||||
@@ -70,7 +102,7 @@ export class ExpensesMutationsService {
|
||||
|
||||
const statement = input.statement.toDocument()
|
||||
|
||||
return this.chain.createExp({
|
||||
const result = await this.chain.createExp({
|
||||
coopname: input.coopname,
|
||||
username: input.username,
|
||||
proposal_hash: input.proposal_hash,
|
||||
@@ -90,6 +122,10 @@ export class ExpensesMutationsService {
|
||||
signatures: statement.signatures,
|
||||
} as any,
|
||||
})
|
||||
|
||||
await this.requisiteSnapshots.snapshot(input.coopname, requisiteItems)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async payExpenseItem(input: PayExpenseItemInputDTO): Promise<TransactResult> {
|
||||
@@ -133,28 +169,15 @@ export class ExpensesMutationsService {
|
||||
proposal_hash: input.proposal_hash,
|
||||
})
|
||||
}
|
||||
|
||||
async authorizeExpenseReport(input: AuthorizeExpenseReportInputDTO): Promise<TransactResult> {
|
||||
const decision = input.decision.toDocument()
|
||||
return this.chain.authExp({
|
||||
coopname: input.coopname,
|
||||
proposal_hash: input.proposal_hash,
|
||||
decision: {
|
||||
version: decision.version,
|
||||
hash: decision.hash,
|
||||
doc_hash: decision.doc_hash,
|
||||
meta_hash: decision.meta_hash,
|
||||
meta: decision.meta,
|
||||
signatures: decision.signatures,
|
||||
} as any,
|
||||
})
|
||||
}
|
||||
|
||||
async declineExpenseReport(input: DeclineExpenseReportInputDTO): Promise<TransactResult> {
|
||||
return this.chain.declineExp({
|
||||
coopname: input.coopname,
|
||||
proposal_hash: input.proposal_hash,
|
||||
reason: input.reason,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function toRequisiteItems(proposalHash: string, items: ExpenseItemInputDTO[]): InterExpenseRequisiteItemInput[] {
|
||||
return items.map((it) => ({
|
||||
proposalHash,
|
||||
itemHash: it.item_hash,
|
||||
recipient: it.recipient,
|
||||
isOrganization: it.recipient_type === ExpenseRecipientType.ORG,
|
||||
paymentMethodId: it.payment_method_id,
|
||||
requisites: it.requisites,
|
||||
}))
|
||||
}
|
||||
|
||||
+6
-6
@@ -5,25 +5,25 @@ import type { TransactResult } from '@wharfkit/session'
|
||||
* Блокчейн-порт контракта `expense`. Hexagonal: domain видит интерфейс,
|
||||
* implementation в `infrastructure/blockchain/adapters`.
|
||||
*
|
||||
* 8 actions полного lifecycle:
|
||||
* - `createexp` — создание + подача СЗ (signact1 statement_doc, type=2010)
|
||||
* - `authexp` — авторизация советом (signact2 decision_doc, type=2011)
|
||||
* 6 actions, доступных backend'у. Авторизация (`authexp`) и отклонение
|
||||
* (`declexp`) СЗ исполняются контрактом soviet как callbacks решения совета —
|
||||
* backend их не вызывает.
|
||||
*
|
||||
* - `createexp` — создание + подача СЗ (signact1 statement_doc, type=2010);
|
||||
* ставит вопрос в повестку совета
|
||||
* - `payexp` — оплата item (ADVANCE/DIRECT)
|
||||
* - `reportexp` — закрытие item чеком (ADVANCE)
|
||||
* - `returnexp` — возврат неиспользованного аванса
|
||||
* - `overspendexp` — доплата при перерасходе (ADVANCE)
|
||||
* - `closeexp` — финализация СЗ-отчёта (REPORT_SUBMITTED → CLOSED)
|
||||
* - `declexp` — отклонение СЗ
|
||||
*/
|
||||
export interface ExpensesBlockchainPort {
|
||||
createExp(data: ExpenseContract.Actions.CreateExp.ICreateExp): Promise<TransactResult>
|
||||
authExp(data: ExpenseContract.Actions.AuthExp.IAuthExp): Promise<TransactResult>
|
||||
payExp(data: ExpenseContract.Actions.PayExp.IPayExp): Promise<TransactResult>
|
||||
reportExp(data: ExpenseContract.Actions.ReportExp.IReportExp): Promise<TransactResult>
|
||||
returnExp(data: ExpenseContract.Actions.ReturnExp.IReturnExp): Promise<TransactResult>
|
||||
overspendExp(data: ExpenseContract.Actions.OverspendExp.IOverspendExp): Promise<TransactResult>
|
||||
closeExp(data: ExpenseContract.Actions.CloseExp.ICloseExp): Promise<TransactResult>
|
||||
declineExp(data: ExpenseContract.Actions.DeclineExp.IDeclineExp): Promise<TransactResult>
|
||||
}
|
||||
|
||||
export const EXPENSES_BLOCKCHAIN_PORT = Symbol('ExpensesBlockchainPort')
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { PaymentMethodDomainEntity } from '~/domain/payment-method/entities/method-domain.entity';
|
||||
import type {
|
||||
SBPDataDomainInterface,
|
||||
BankTransferDataDomainInterface,
|
||||
} from '~/domain/payment-method/interfaces/payment-methods-domain.interface';
|
||||
|
||||
/**
|
||||
* Полная строка реквизитов платёжного метода — для документов (СЗ в совет,
|
||||
* поручение бухгалтеру). Сокращённое представление для UI делает фронт.
|
||||
*/
|
||||
export function formatPaymentMethodRequisites(method: PaymentMethodDomainEntity): string {
|
||||
if (method.method_type === 'sbp') {
|
||||
const data = method.data as SBPDataDomainInterface;
|
||||
return `СБП, телефон ${data.phone}`;
|
||||
}
|
||||
|
||||
const data = method.data as BankTransferDataDomainInterface;
|
||||
const parts = [`Банковский перевод: счёт ${data.account_number}`];
|
||||
if (data.bank_name) parts.push(data.bank_name);
|
||||
if (data.details?.bik) parts.push(`БИК ${data.details.bik}`);
|
||||
if (data.details?.corr) parts.push(`к/с ${data.details.corr}`);
|
||||
if (data.card_number) parts.push(`карта ${data.card_number}`);
|
||||
return parts.join(', ');
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import { EXPENSES_BLOCKCHAIN_PORT } from './domain/interfaces/expenses-blockchai
|
||||
import { ExpenseProposalSyncService } from './application/syncers/expense-proposal-sync.service';
|
||||
import { ExpensesManagementService } from './application/services/expenses-management.service';
|
||||
import { ExpensesMutationsService } from './application/services/expenses-mutations.service';
|
||||
import { ExpenseRequisiteSnapshotsService } from './application/services/expense-requisite-snapshots.service';
|
||||
import { ExpensesCapitalTriggerService } from './application/services/expenses-capital-trigger.service';
|
||||
import { ExpenseFilesService } from './application/services/expense-files.service';
|
||||
import { ExpenseProposalResolver } from './application/resolvers/expense-proposal.resolver';
|
||||
@@ -66,6 +67,7 @@ import { ExpensesInterExpenseChassisAdapter } from './infrastructure/inter/expen
|
||||
},
|
||||
ExpenseProposalSyncService,
|
||||
ExpensesManagementService,
|
||||
ExpenseRequisiteSnapshotsService,
|
||||
ExpensesMutationsService,
|
||||
ExpensesCapitalTriggerService,
|
||||
ExpenseFilesService,
|
||||
|
||||
+4
-23
@@ -11,9 +11,10 @@ import { ExpensesBlockchainPort } from '../../../domain/interfaces/expenses-bloc
|
||||
* Адаптер блокчейн-порта `expense`. Канон взят с `CapitalBlockchainAdapter`:
|
||||
* подпись ключом кооператива (`active` permission), `account = contractName.production`.
|
||||
*
|
||||
* 8 actions полного lifecycle: createexp / authexp / payexp / reportexp / returnexp /
|
||||
* overspendexp / closeexp / declexp. Документы document2 (statement / decision) идут
|
||||
* как часть `data` action'а — wharfkit сериализует поля по C++ struct document2.
|
||||
* 6 backend-actions: createexp / payexp / reportexp / returnexp / overspendexp /
|
||||
* closeexp (authexp/declexp вызывает контракт soviet по решению совета).
|
||||
* Документы document2 (statement) идут как часть `data` action'а — wharfkit
|
||||
* сериализует поля по C++ struct document2.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ExpensesBlockchainAdapter implements ExpensesBlockchainPort {
|
||||
@@ -40,16 +41,6 @@ export class ExpensesBlockchainAdapter implements ExpensesBlockchainPort {
|
||||
})
|
||||
}
|
||||
|
||||
async authExp(data: ExpenseContract.Actions.AuthExp.IAuthExp): Promise<TransactResult> {
|
||||
await this.initWithCoopKey(data.coopname)
|
||||
return this.blockchainService.transact({
|
||||
account: ExpenseContract.contractName.production,
|
||||
name: ExpenseContract.Actions.AuthExp.actionName,
|
||||
authorization: [{ actor: data.coopname, permission: 'active' }],
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
async payExp(data: ExpenseContract.Actions.PayExp.IPayExp): Promise<TransactResult> {
|
||||
await this.initWithCoopKey(data.coopname)
|
||||
return this.blockchainService.transact({
|
||||
@@ -99,14 +90,4 @@ export class ExpensesBlockchainAdapter implements ExpensesBlockchainPort {
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
async declineExp(data: ExpenseContract.Actions.DeclineExp.IDeclineExp): Promise<TransactResult> {
|
||||
await this.initWithCoopKey(data.coopname)
|
||||
return this.blockchainService.transact({
|
||||
account: ExpenseContract.contractName.production,
|
||||
name: ExpenseContract.Actions.DeclineExp.actionName,
|
||||
authorization: [{ actor: data.coopname, permission: 'active' }],
|
||||
data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -2,16 +2,19 @@ import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ExpenseProposalTypeormEntity } from '../entities/expense-proposal.typeorm-entity';
|
||||
import { ExpenseFileTypeormEntity } from '../entities/expense-file.typeorm-entity';
|
||||
import { ExpenseRequisiteSnapshotTypeormEntity } from '../entities/expense-requisite-snapshot.typeorm-entity';
|
||||
import { EntityVersionTypeormEntity } from '~/shared/sync/entities/entity-version.typeorm-entity';
|
||||
|
||||
/**
|
||||
* TypeORM-модуль расширения `expense` — зеркало SP-расходов + реестр файлов.
|
||||
* TypeORM-модуль расширения `expense` — зеркало SP-расходов + реестр файлов
|
||||
* + снимки реквизитов получателей.
|
||||
*/
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
ExpenseProposalTypeormEntity,
|
||||
ExpenseFileTypeormEntity,
|
||||
ExpenseRequisiteSnapshotTypeormEntity,
|
||||
EntityVersionTypeormEntity,
|
||||
]),
|
||||
],
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { Entity, Column, Index, PrimaryGeneratedColumn, CreateDateColumn } from 'typeorm';
|
||||
|
||||
export const EntityName = 'expense_requisite_snapshots';
|
||||
|
||||
/**
|
||||
* Снимок реквизитов получателя по строке расхода — фиксируется в момент
|
||||
* создания СЗ (как payment_details в gateway-платежах): последующее изменение
|
||||
* или удаление платёжного метода пайщиком не меняет то, куда платить по уже
|
||||
* поданной смете. Источник реквизитов для поручения бухгалтеру (payexp).
|
||||
*/
|
||||
@Entity(EntityName)
|
||||
@Index(`uq_${EntityName}_item`, ['coopname', 'proposal_hash', 'item_hash'], { unique: true })
|
||||
@Index(`idx_${EntityName}_proposal`, ['coopname', 'proposal_hash'])
|
||||
export class ExpenseRequisiteSnapshotTypeormEntity {
|
||||
static getTableName(): string {
|
||||
return EntityName;
|
||||
}
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column({ type: 'varchar' })
|
||||
coopname!: string;
|
||||
|
||||
@Column({ type: 'varchar' })
|
||||
proposal_hash!: string;
|
||||
|
||||
@Column({ type: 'varchar' })
|
||||
item_hash!: string;
|
||||
|
||||
@Column({ type: 'varchar', comment: 'Получатель платежа (username пайщика или имя организации)' })
|
||||
recipient!: string;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true, comment: 'Идентификатор платёжного метода получателя (для пайщиков)' })
|
||||
method_id!: string | null;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true, comment: 'Тип метода (sbp / bank_transfer)' })
|
||||
method_type!: string | null;
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true, comment: 'Снимок данных платёжного метода на момент создания СЗ' })
|
||||
data!: Record<string, unknown> | null;
|
||||
|
||||
@Column({ type: 'text', comment: 'Реквизиты строкой — как в документе СЗ' })
|
||||
requisites!: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at!: Date;
|
||||
}
|
||||
+11
@@ -8,9 +8,11 @@ import type {
|
||||
InterExpensePaginatedResult,
|
||||
InterExpenseProposalRead,
|
||||
InterExpenseProposalStatus,
|
||||
InterExpenseRequisiteItemInput,
|
||||
} from '@coopenomics/inter';
|
||||
import { ExpenseProposalTypeormEntity } from '../entities/expense-proposal.typeorm-entity';
|
||||
import { ExpenseProposalStatus } from '../../domain/enums/expense-proposal-status.enum';
|
||||
import { ExpenseRequisiteSnapshotsService } from '../../application/services/expense-requisite-snapshots.service';
|
||||
|
||||
/**
|
||||
* Реализация `InterExpenseChassisPort` для consumer-расширений (capital, marketplace, EMP).
|
||||
@@ -24,8 +26,17 @@ export class ExpensesInterExpenseChassisAdapter implements InterExpenseChassisPo
|
||||
constructor(
|
||||
@InjectRepository(ExpenseProposalTypeormEntity)
|
||||
private readonly repository: Repository<ExpenseProposalTypeormEntity>,
|
||||
private readonly requisiteSnapshots: ExpenseRequisiteSnapshotsService,
|
||||
) {}
|
||||
|
||||
async validateRequisites(coopname: string, items: InterExpenseRequisiteItemInput[]): Promise<void> {
|
||||
await this.requisiteSnapshots.validate(coopname, items);
|
||||
}
|
||||
|
||||
async snapshotRequisites(coopname: string, items: InterExpenseRequisiteItemInput[]): Promise<void> {
|
||||
await this.requisiteSnapshots.snapshot(coopname, items);
|
||||
}
|
||||
|
||||
async readProposalByHash(coopname: string, proposalHash: string): Promise<InterExpenseProposalRead | null> {
|
||||
const entity = await this.repository.findOne({
|
||||
where: { coopname, proposal_hash: proposalHash.toLowerCase() },
|
||||
|
||||
@@ -125,9 +125,6 @@ export const AllTypesProps: Record<string,any> = {
|
||||
AuthorizeDecisionInput:{
|
||||
document:"SignedDigitalDocumentInput"
|
||||
},
|
||||
AuthorizeExpenseReportInput:{
|
||||
decision:"ExpenseProposalDecisionSignedDocumentInput"
|
||||
},
|
||||
BankAccountDetailsInput:{
|
||||
|
||||
},
|
||||
@@ -396,9 +393,6 @@ export const AllTypesProps: Record<string,any> = {
|
||||
},
|
||||
DeclineDecisionInput:{
|
||||
|
||||
},
|
||||
DeclineExpenseReportInput:{
|
||||
|
||||
},
|
||||
DeclineRequestInput:{
|
||||
|
||||
@@ -472,15 +466,6 @@ export const AllTypesProps: Record<string,any> = {
|
||||
},
|
||||
ExpenseProposalDecisionItemInput:{
|
||||
|
||||
},
|
||||
ExpenseProposalDecisionSignedDocumentInput:{
|
||||
meta:"ExpenseProposalDecisionSignedMetaDocumentInput",
|
||||
signatures:"SignatureInfoInput"
|
||||
},
|
||||
ExpenseProposalDecisionSignedMetaDocumentInput:{
|
||||
decision:"ExpenseProposalDecisionBodyInput",
|
||||
items:"ExpenseProposalDecisionItemInput",
|
||||
proposal:"ExpenseProposalDecisionHeaderInput"
|
||||
},
|
||||
ExpenseProposalHeaderInput:{
|
||||
|
||||
@@ -727,9 +712,6 @@ export const AllTypesProps: Record<string,any> = {
|
||||
authorizeDecision:{
|
||||
data:"AuthorizeDecisionInput"
|
||||
},
|
||||
authorizeExpenseReport:{
|
||||
data:"AuthorizeExpenseReportInput"
|
||||
},
|
||||
cancelRequest:{
|
||||
data:"CancelRequestInput"
|
||||
},
|
||||
@@ -1073,9 +1055,6 @@ export const AllTypesProps: Record<string,any> = {
|
||||
declineDecision:{
|
||||
data:"DeclineDecisionInput"
|
||||
},
|
||||
declineExpenseReport:{
|
||||
data:"DeclineExpenseReportInput"
|
||||
},
|
||||
declineRequest:{
|
||||
data:"DeclineRequestInput"
|
||||
},
|
||||
@@ -3802,7 +3781,6 @@ export const ReturnTypes: Record<string,any> = {
|
||||
addPaymentMethod:"PaymentMethod",
|
||||
addTrustedAccount:"Branch",
|
||||
authorizeDecision:"Transaction",
|
||||
authorizeExpenseReport:"Transaction",
|
||||
cancelRequest:"Transaction",
|
||||
capitalAddAuthor:"CapitalProject",
|
||||
capitalApproveCommit:"CapitalCommit",
|
||||
@@ -3911,7 +3889,6 @@ export const ReturnTypes: Record<string,any> = {
|
||||
deactivateWebPushSubscriptionById:"Boolean",
|
||||
declineAgreement:"Transaction",
|
||||
declineDecision:"Transaction",
|
||||
declineExpenseReport:"Transaction",
|
||||
declineRequest:"Transaction",
|
||||
deleteAccount:"Boolean",
|
||||
deleteBranch:"Boolean",
|
||||
|
||||
@@ -1940,14 +1940,6 @@ export type ValueTypes = {
|
||||
decision_id: number | Variable<any, string>,
|
||||
/** Подписанный председателем документ утверждения решения */
|
||||
document: ValueTypes["SignedDigitalDocumentInput"] | Variable<any, string>
|
||||
};
|
||||
["AuthorizeExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string | Variable<any, string>,
|
||||
/** Подписанное решение совета (document2, registry 2011). */
|
||||
decision: ValueTypes["ExpenseProposalDecisionSignedDocumentInput"] | Variable<any, string>,
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string | Variable<any, string>
|
||||
};
|
||||
["AvailableReport"]: AliasType<{
|
||||
deadline?:boolean | `@${string}`,
|
||||
@@ -4924,14 +4916,6 @@ export type ValueTypes = {
|
||||
coopname: string | Variable<any, string>,
|
||||
/** Идентификатор решения */
|
||||
decision_id: number | Variable<any, string>
|
||||
};
|
||||
["DeclineExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string | Variable<any, string>,
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string | Variable<any, string>,
|
||||
/** Причина отклонения СЗ-отчёта (до 1000 символов). */
|
||||
reason: string | Variable<any, string>
|
||||
};
|
||||
["DeclineRequestInput"]: {
|
||||
/** Имя аккаунта кооператива */
|
||||
@@ -5296,12 +5280,16 @@ export type ValueTypes = {
|
||||
item_hash: string | Variable<any, string>,
|
||||
/** Способ оплаты (ADVANCE / DIRECT). */
|
||||
mechanics: ValueTypes["ExpenseMechanics"] | Variable<any, string>,
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — реквизиты снимаются в момент создания и прикладываются к платежу. */
|
||||
payment_method_id?: string | undefined | null | Variable<any, string>,
|
||||
/** Планируемая сумма (asset, eg "1000.0000 RUB"). */
|
||||
planned_amount: string | Variable<any, string>,
|
||||
/** Получатель (username / eosio::name организации). */
|
||||
recipient: string | Variable<any, string>,
|
||||
/** Тип получателя. */
|
||||
recipient_type: ValueTypes["ExpenseRecipientType"] | Variable<any, string>
|
||||
recipient_type: ValueTypes["ExpenseRecipientType"] | Variable<any, string>,
|
||||
/** Реквизиты получателя-организации (вводятся вручную). */
|
||||
requisites?: string | undefined | null | Variable<any, string>
|
||||
};
|
||||
/** Статус строки расхода. */
|
||||
["ExpenseItemStatus"]:ExpenseItemStatus;
|
||||
@@ -5404,52 +5392,6 @@ export type ValueTypes = {
|
||||
recipient_name?: string | undefined | null | Variable<any, string>,
|
||||
recipient_type: string | Variable<any, string>,
|
||||
requisites?: string | undefined | null | Variable<any, string>
|
||||
};
|
||||
["ExpenseProposalDecisionSignedDocumentInput"]: {
|
||||
/** Хэш содержимого документа */
|
||||
doc_hash: string | Variable<any, string>,
|
||||
/** Общий хэш (doc_hash + meta_hash) */
|
||||
hash: string | Variable<any, string>,
|
||||
/** Метаинформация решения по СЗ */
|
||||
meta: ValueTypes["ExpenseProposalDecisionSignedMetaDocumentInput"] | Variable<any, string>,
|
||||
/** Хэш мета-данных */
|
||||
meta_hash: string | Variable<any, string>,
|
||||
/** Вектор подписей */
|
||||
signatures: Array<ValueTypes["SignatureInfoInput"]> | Variable<any, string>,
|
||||
/** Версия стандарта документа */
|
||||
version: string | Variable<any, string>
|
||||
};
|
||||
["ExpenseProposalDecisionSignedMetaDocumentInput"]: {
|
||||
/** Номер блока, на котором был создан документ */
|
||||
block_num: number | Variable<any, string>,
|
||||
/** Название кооператива, связанное с документом */
|
||||
coopname: string | Variable<any, string>,
|
||||
/** Дата и время создания документа */
|
||||
created_at: string | Variable<any, string>,
|
||||
/** Тело решения */
|
||||
decision: ValueTypes["ExpenseProposalDecisionBodyInput"] | Variable<any, string>,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator: string | Variable<any, string>,
|
||||
/** Позиции расхода */
|
||||
items: Array<ValueTypes["ExpenseProposalDecisionItemInput"]> | Variable<any, string>,
|
||||
/** Язык документа */
|
||||
lang: string | Variable<any, string>,
|
||||
/** Ссылки, связанные с документом */
|
||||
links: Array<string> | Variable<any, string>,
|
||||
/** Шапка СЗ */
|
||||
proposal: ValueTypes["ExpenseProposalDecisionHeaderInput"] | Variable<any, string>,
|
||||
/** Хеш сметы расхода */
|
||||
proposal_hash: string | Variable<any, string>,
|
||||
/** ID документа в реестре */
|
||||
registry_id: number | Variable<any, string>,
|
||||
/** Часовой пояс, в котором был создан документ */
|
||||
timezone: string | Variable<any, string>,
|
||||
/** Название документа */
|
||||
title: string | Variable<any, string>,
|
||||
/** Имя пользователя, создавшего документ */
|
||||
username: string | Variable<any, string>,
|
||||
/** Версия генератора, использованного для создания документа */
|
||||
version: string | Variable<any, string>
|
||||
};
|
||||
["ExpenseProposalHeaderInput"]: {
|
||||
/** Описание цели расходов */
|
||||
@@ -5470,10 +5412,14 @@ export type ValueTypes = {
|
||||
mechanics: string | Variable<any, string>,
|
||||
/** Порядковый номер строки */
|
||||
number: string | Variable<any, string>,
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — сервер подставит полные реквизиты в документ. */
|
||||
payment_method_id?: string | undefined | null | Variable<any, string>,
|
||||
/** Имя получателя */
|
||||
recipient_name?: string | undefined | null | Variable<any, string>,
|
||||
/** Тип получателя (SELF / MEMBER / ORG) */
|
||||
recipient_type: string | Variable<any, string>,
|
||||
/** Имя аккаунта получателя-пайщика (владелец реквизитов). */
|
||||
recipient_username?: string | undefined | null | Variable<any, string>,
|
||||
/** Реквизиты получателя */
|
||||
requisites?: string | undefined | null | Variable<any, string>
|
||||
};
|
||||
@@ -6890,7 +6836,6 @@ addParticipant?: [{ data: ValueTypes["AddParticipantInput"] | Variable<any, stri
|
||||
addPaymentMethod?: [{ data: ValueTypes["AddPaymentMethodInput"] | Variable<any, string>},ValueTypes["PaymentMethod"]],
|
||||
addTrustedAccount?: [{ data: ValueTypes["AddTrustedAccountInput"] | Variable<any, string>},ValueTypes["Branch"]],
|
||||
authorizeDecision?: [{ data: ValueTypes["AuthorizeDecisionInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
authorizeExpenseReport?: [{ data: ValueTypes["AuthorizeExpenseReportInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
cancelRequest?: [{ data: ValueTypes["CancelRequestInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
capitalAddAuthor?: [{ data: ValueTypes["AddAuthorInput"] | Variable<any, string>},ValueTypes["CapitalProject"]],
|
||||
capitalApproveCommit?: [{ data: ValueTypes["CommitApproveInput"] | Variable<any, string>},ValueTypes["CapitalCommit"]],
|
||||
@@ -7002,7 +6947,6 @@ createWithdraw?: [{ data: ValueTypes["CreateWithdrawInput"] | Variable<any, stri
|
||||
deactivateWebPushSubscriptionById?: [{ data: ValueTypes["DeactivateSubscriptionInput"] | Variable<any, string>},boolean | `@${string}`],
|
||||
declineAgreement?: [{ data: ValueTypes["DeclineAgreementInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
declineDecision?: [{ data: ValueTypes["DeclineDecisionInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
declineExpenseReport?: [{ data: ValueTypes["DeclineExpenseReportInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
declineRequest?: [{ data: ValueTypes["DeclineRequestInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
deleteAccount?: [{ data: ValueTypes["DeleteAccountInput"] | Variable<any, string>},boolean | `@${string}`],
|
||||
deleteBranch?: [{ data: ValueTypes["DeleteBranchInput"] | Variable<any, string>},boolean | `@${string}`],
|
||||
@@ -11111,14 +11055,6 @@ export type ResolverInputTypes = {
|
||||
decision_id: number,
|
||||
/** Подписанный председателем документ утверждения решения */
|
||||
document: ResolverInputTypes["SignedDigitalDocumentInput"]
|
||||
};
|
||||
["AuthorizeExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string,
|
||||
/** Подписанное решение совета (document2, registry 2011). */
|
||||
decision: ResolverInputTypes["ExpenseProposalDecisionSignedDocumentInput"],
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string
|
||||
};
|
||||
["AvailableReport"]: AliasType<{
|
||||
deadline?:boolean | `@${string}`,
|
||||
@@ -14018,14 +13954,6 @@ export type ResolverInputTypes = {
|
||||
coopname: string,
|
||||
/** Идентификатор решения */
|
||||
decision_id: number
|
||||
};
|
||||
["DeclineExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string,
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string,
|
||||
/** Причина отклонения СЗ-отчёта (до 1000 символов). */
|
||||
reason: string
|
||||
};
|
||||
["DeclineRequestInput"]: {
|
||||
/** Имя аккаунта кооператива */
|
||||
@@ -14378,12 +14306,16 @@ export type ResolverInputTypes = {
|
||||
item_hash: string,
|
||||
/** Способ оплаты (ADVANCE / DIRECT). */
|
||||
mechanics: ResolverInputTypes["ExpenseMechanics"],
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — реквизиты снимаются в момент создания и прикладываются к платежу. */
|
||||
payment_method_id?: string | undefined | null,
|
||||
/** Планируемая сумма (asset, eg "1000.0000 RUB"). */
|
||||
planned_amount: string,
|
||||
/** Получатель (username / eosio::name организации). */
|
||||
recipient: string,
|
||||
/** Тип получателя. */
|
||||
recipient_type: ResolverInputTypes["ExpenseRecipientType"]
|
||||
recipient_type: ResolverInputTypes["ExpenseRecipientType"],
|
||||
/** Реквизиты получателя-организации (вводятся вручную). */
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
/** Статус строки расхода. */
|
||||
["ExpenseItemStatus"]:ExpenseItemStatus;
|
||||
@@ -14485,52 +14417,6 @@ export type ResolverInputTypes = {
|
||||
recipient_name?: string | undefined | null,
|
||||
recipient_type: string,
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
["ExpenseProposalDecisionSignedDocumentInput"]: {
|
||||
/** Хэш содержимого документа */
|
||||
doc_hash: string,
|
||||
/** Общий хэш (doc_hash + meta_hash) */
|
||||
hash: string,
|
||||
/** Метаинформация решения по СЗ */
|
||||
meta: ResolverInputTypes["ExpenseProposalDecisionSignedMetaDocumentInput"],
|
||||
/** Хэш мета-данных */
|
||||
meta_hash: string,
|
||||
/** Вектор подписей */
|
||||
signatures: Array<ResolverInputTypes["SignatureInfoInput"]>,
|
||||
/** Версия стандарта документа */
|
||||
version: string
|
||||
};
|
||||
["ExpenseProposalDecisionSignedMetaDocumentInput"]: {
|
||||
/** Номер блока, на котором был создан документ */
|
||||
block_num: number,
|
||||
/** Название кооператива, связанное с документом */
|
||||
coopname: string,
|
||||
/** Дата и время создания документа */
|
||||
created_at: string,
|
||||
/** Тело решения */
|
||||
decision: ResolverInputTypes["ExpenseProposalDecisionBodyInput"],
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator: string,
|
||||
/** Позиции расхода */
|
||||
items: Array<ResolverInputTypes["ExpenseProposalDecisionItemInput"]>,
|
||||
/** Язык документа */
|
||||
lang: string,
|
||||
/** Ссылки, связанные с документом */
|
||||
links: Array<string>,
|
||||
/** Шапка СЗ */
|
||||
proposal: ResolverInputTypes["ExpenseProposalDecisionHeaderInput"],
|
||||
/** Хеш сметы расхода */
|
||||
proposal_hash: string,
|
||||
/** ID документа в реестре */
|
||||
registry_id: number,
|
||||
/** Часовой пояс, в котором был создан документ */
|
||||
timezone: string,
|
||||
/** Название документа */
|
||||
title: string,
|
||||
/** Имя пользователя, создавшего документ */
|
||||
username: string,
|
||||
/** Версия генератора, использованного для создания документа */
|
||||
version: string
|
||||
};
|
||||
["ExpenseProposalHeaderInput"]: {
|
||||
/** Описание цели расходов */
|
||||
@@ -14551,10 +14437,14 @@ export type ResolverInputTypes = {
|
||||
mechanics: string,
|
||||
/** Порядковый номер строки */
|
||||
number: string,
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — сервер подставит полные реквизиты в документ. */
|
||||
payment_method_id?: string | undefined | null,
|
||||
/** Имя получателя */
|
||||
recipient_name?: string | undefined | null,
|
||||
/** Тип получателя (SELF / MEMBER / ORG) */
|
||||
recipient_type: string,
|
||||
/** Имя аккаунта получателя-пайщика (владелец реквизитов). */
|
||||
recipient_username?: string | undefined | null,
|
||||
/** Реквизиты получателя */
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
@@ -15932,7 +15822,6 @@ addParticipant?: [{ data: ResolverInputTypes["AddParticipantInput"]},ResolverInp
|
||||
addPaymentMethod?: [{ data: ResolverInputTypes["AddPaymentMethodInput"]},ResolverInputTypes["PaymentMethod"]],
|
||||
addTrustedAccount?: [{ data: ResolverInputTypes["AddTrustedAccountInput"]},ResolverInputTypes["Branch"]],
|
||||
authorizeDecision?: [{ data: ResolverInputTypes["AuthorizeDecisionInput"]},ResolverInputTypes["Transaction"]],
|
||||
authorizeExpenseReport?: [{ data: ResolverInputTypes["AuthorizeExpenseReportInput"]},ResolverInputTypes["Transaction"]],
|
||||
cancelRequest?: [{ data: ResolverInputTypes["CancelRequestInput"]},ResolverInputTypes["Transaction"]],
|
||||
capitalAddAuthor?: [{ data: ResolverInputTypes["AddAuthorInput"]},ResolverInputTypes["CapitalProject"]],
|
||||
capitalApproveCommit?: [{ data: ResolverInputTypes["CommitApproveInput"]},ResolverInputTypes["CapitalCommit"]],
|
||||
@@ -16044,7 +15933,6 @@ createWithdraw?: [{ data: ResolverInputTypes["CreateWithdrawInput"]},ResolverInp
|
||||
deactivateWebPushSubscriptionById?: [{ data: ResolverInputTypes["DeactivateSubscriptionInput"]},boolean | `@${string}`],
|
||||
declineAgreement?: [{ data: ResolverInputTypes["DeclineAgreementInput"]},ResolverInputTypes["Transaction"]],
|
||||
declineDecision?: [{ data: ResolverInputTypes["DeclineDecisionInput"]},ResolverInputTypes["Transaction"]],
|
||||
declineExpenseReport?: [{ data: ResolverInputTypes["DeclineExpenseReportInput"]},ResolverInputTypes["Transaction"]],
|
||||
declineRequest?: [{ data: ResolverInputTypes["DeclineRequestInput"]},ResolverInputTypes["Transaction"]],
|
||||
deleteAccount?: [{ data: ResolverInputTypes["DeleteAccountInput"]},boolean | `@${string}`],
|
||||
deleteBranch?: [{ data: ResolverInputTypes["DeleteBranchInput"]},boolean | `@${string}`],
|
||||
@@ -20033,14 +19921,6 @@ export type ModelTypes = {
|
||||
decision_id: number,
|
||||
/** Подписанный председателем документ утверждения решения */
|
||||
document: ModelTypes["SignedDigitalDocumentInput"]
|
||||
};
|
||||
["AuthorizeExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string,
|
||||
/** Подписанное решение совета (document2, registry 2011). */
|
||||
decision: ModelTypes["ExpenseProposalDecisionSignedDocumentInput"],
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string
|
||||
};
|
||||
["AvailableReport"]: {
|
||||
deadline: string,
|
||||
@@ -22855,14 +22735,6 @@ export type ModelTypes = {
|
||||
coopname: string,
|
||||
/** Идентификатор решения */
|
||||
decision_id: number
|
||||
};
|
||||
["DeclineExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string,
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string,
|
||||
/** Причина отклонения СЗ-отчёта (до 1000 символов). */
|
||||
reason: string
|
||||
};
|
||||
["DeclineRequestInput"]: {
|
||||
/** Имя аккаунта кооператива */
|
||||
@@ -23201,12 +23073,16 @@ export type ModelTypes = {
|
||||
item_hash: string,
|
||||
/** Способ оплаты (ADVANCE / DIRECT). */
|
||||
mechanics: ModelTypes["ExpenseMechanics"],
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — реквизиты снимаются в момент создания и прикладываются к платежу. */
|
||||
payment_method_id?: string | undefined | null,
|
||||
/** Планируемая сумма (asset, eg "1000.0000 RUB"). */
|
||||
planned_amount: string,
|
||||
/** Получатель (username / eosio::name организации). */
|
||||
recipient: string,
|
||||
/** Тип получателя. */
|
||||
recipient_type: ModelTypes["ExpenseRecipientType"]
|
||||
recipient_type: ModelTypes["ExpenseRecipientType"],
|
||||
/** Реквизиты получателя-организации (вводятся вручную). */
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
["ExpenseItemStatus"]:ExpenseItemStatus;
|
||||
["ExpenseMechanics"]:ExpenseMechanics;
|
||||
@@ -23305,52 +23181,6 @@ export type ModelTypes = {
|
||||
recipient_name?: string | undefined | null,
|
||||
recipient_type: string,
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
["ExpenseProposalDecisionSignedDocumentInput"]: {
|
||||
/** Хэш содержимого документа */
|
||||
doc_hash: string,
|
||||
/** Общий хэш (doc_hash + meta_hash) */
|
||||
hash: string,
|
||||
/** Метаинформация решения по СЗ */
|
||||
meta: ModelTypes["ExpenseProposalDecisionSignedMetaDocumentInput"],
|
||||
/** Хэш мета-данных */
|
||||
meta_hash: string,
|
||||
/** Вектор подписей */
|
||||
signatures: Array<ModelTypes["SignatureInfoInput"]>,
|
||||
/** Версия стандарта документа */
|
||||
version: string
|
||||
};
|
||||
["ExpenseProposalDecisionSignedMetaDocumentInput"]: {
|
||||
/** Номер блока, на котором был создан документ */
|
||||
block_num: number,
|
||||
/** Название кооператива, связанное с документом */
|
||||
coopname: string,
|
||||
/** Дата и время создания документа */
|
||||
created_at: string,
|
||||
/** Тело решения */
|
||||
decision: ModelTypes["ExpenseProposalDecisionBodyInput"],
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator: string,
|
||||
/** Позиции расхода */
|
||||
items: Array<ModelTypes["ExpenseProposalDecisionItemInput"]>,
|
||||
/** Язык документа */
|
||||
lang: string,
|
||||
/** Ссылки, связанные с документом */
|
||||
links: Array<string>,
|
||||
/** Шапка СЗ */
|
||||
proposal: ModelTypes["ExpenseProposalDecisionHeaderInput"],
|
||||
/** Хеш сметы расхода */
|
||||
proposal_hash: string,
|
||||
/** ID документа в реестре */
|
||||
registry_id: number,
|
||||
/** Часовой пояс, в котором был создан документ */
|
||||
timezone: string,
|
||||
/** Название документа */
|
||||
title: string,
|
||||
/** Имя пользователя, создавшего документ */
|
||||
username: string,
|
||||
/** Версия генератора, использованного для создания документа */
|
||||
version: string
|
||||
};
|
||||
["ExpenseProposalHeaderInput"]: {
|
||||
/** Описание цели расходов */
|
||||
@@ -23371,10 +23201,14 @@ export type ModelTypes = {
|
||||
mechanics: string,
|
||||
/** Порядковый номер строки */
|
||||
number: string,
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — сервер подставит полные реквизиты в документ. */
|
||||
payment_method_id?: string | undefined | null,
|
||||
/** Имя получателя */
|
||||
recipient_name?: string | undefined | null,
|
||||
/** Тип получателя (SELF / MEMBER / ORG) */
|
||||
recipient_type: string,
|
||||
/** Имя аккаунта получателя-пайщика (владелец реквизитов). */
|
||||
recipient_username?: string | undefined | null,
|
||||
/** Реквизиты получателя */
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
@@ -24713,10 +24547,6 @@ export type ModelTypes = {
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
authorizeDecision: ModelTypes["Transaction"],
|
||||
/** Утвердить СЗ-отчёт (закрытие сметы). Триггерит капитализацию РИД в Благоросте.
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
authorizeExpenseReport: ModelTypes["Transaction"],
|
||||
/** Отменить заявку */
|
||||
cancelRequest: ModelTypes["Transaction"],
|
||||
/** Добавление автора проекта в CAPITAL контракте
|
||||
@@ -25133,10 +24963,6 @@ export type ModelTypes = {
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
declineDecision: ModelTypes["Transaction"],
|
||||
/** Отклонить СЗ-отчёт с указанием причины. Смета переходит в DECLINED.
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
declineExpenseReport: ModelTypes["Transaction"],
|
||||
/** Отклонить заявку */
|
||||
declineRequest: ModelTypes["Transaction"],
|
||||
/** Удалить аккаунт пайщика из системы учёта провайдера. Доступно только для незавершённых регистрационных статусов (черновик, неоплачен/отклонён). Активный, заблокированный и любой зарегистрированный в блокчейне аккаунт удалить нельзя. Используется для очистки реестра и освобождения e-mail под перерегистрацию.
|
||||
@@ -29461,14 +29287,6 @@ export type GraphQLTypes = {
|
||||
decision_id: number,
|
||||
/** Подписанный председателем документ утверждения решения */
|
||||
document: GraphQLTypes["SignedDigitalDocumentInput"]
|
||||
};
|
||||
["AuthorizeExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string,
|
||||
/** Подписанное решение совета (document2, registry 2011). */
|
||||
decision: GraphQLTypes["ExpenseProposalDecisionSignedDocumentInput"],
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string
|
||||
};
|
||||
["AvailableReport"]: {
|
||||
__typename: "AvailableReport",
|
||||
@@ -32445,14 +32263,6 @@ export type GraphQLTypes = {
|
||||
coopname: string,
|
||||
/** Идентификатор решения */
|
||||
decision_id: number
|
||||
};
|
||||
["DeclineExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string,
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string,
|
||||
/** Причина отклонения СЗ-отчёта (до 1000 символов). */
|
||||
reason: string
|
||||
};
|
||||
["DeclineRequestInput"]: {
|
||||
/** Имя аккаунта кооператива */
|
||||
@@ -32817,12 +32627,16 @@ export type GraphQLTypes = {
|
||||
item_hash: string,
|
||||
/** Способ оплаты (ADVANCE / DIRECT). */
|
||||
mechanics: GraphQLTypes["ExpenseMechanics"],
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — реквизиты снимаются в момент создания и прикладываются к платежу. */
|
||||
payment_method_id?: string | undefined | null,
|
||||
/** Планируемая сумма (asset, eg "1000.0000 RUB"). */
|
||||
planned_amount: string,
|
||||
/** Получатель (username / eosio::name организации). */
|
||||
recipient: string,
|
||||
/** Тип получателя. */
|
||||
recipient_type: GraphQLTypes["ExpenseRecipientType"]
|
||||
recipient_type: GraphQLTypes["ExpenseRecipientType"],
|
||||
/** Реквизиты получателя-организации (вводятся вручную). */
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
/** Статус строки расхода. */
|
||||
["ExpenseItemStatus"]: ExpenseItemStatus;
|
||||
@@ -32925,52 +32739,6 @@ export type GraphQLTypes = {
|
||||
recipient_name?: string | undefined | null,
|
||||
recipient_type: string,
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
["ExpenseProposalDecisionSignedDocumentInput"]: {
|
||||
/** Хэш содержимого документа */
|
||||
doc_hash: string,
|
||||
/** Общий хэш (doc_hash + meta_hash) */
|
||||
hash: string,
|
||||
/** Метаинформация решения по СЗ */
|
||||
meta: GraphQLTypes["ExpenseProposalDecisionSignedMetaDocumentInput"],
|
||||
/** Хэш мета-данных */
|
||||
meta_hash: string,
|
||||
/** Вектор подписей */
|
||||
signatures: Array<GraphQLTypes["SignatureInfoInput"]>,
|
||||
/** Версия стандарта документа */
|
||||
version: string
|
||||
};
|
||||
["ExpenseProposalDecisionSignedMetaDocumentInput"]: {
|
||||
/** Номер блока, на котором был создан документ */
|
||||
block_num: number,
|
||||
/** Название кооператива, связанное с документом */
|
||||
coopname: string,
|
||||
/** Дата и время создания документа */
|
||||
created_at: string,
|
||||
/** Тело решения */
|
||||
decision: GraphQLTypes["ExpenseProposalDecisionBodyInput"],
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator: string,
|
||||
/** Позиции расхода */
|
||||
items: Array<GraphQLTypes["ExpenseProposalDecisionItemInput"]>,
|
||||
/** Язык документа */
|
||||
lang: string,
|
||||
/** Ссылки, связанные с документом */
|
||||
links: Array<string>,
|
||||
/** Шапка СЗ */
|
||||
proposal: GraphQLTypes["ExpenseProposalDecisionHeaderInput"],
|
||||
/** Хеш сметы расхода */
|
||||
proposal_hash: string,
|
||||
/** ID документа в реестре */
|
||||
registry_id: number,
|
||||
/** Часовой пояс, в котором был создан документ */
|
||||
timezone: string,
|
||||
/** Название документа */
|
||||
title: string,
|
||||
/** Имя пользователя, создавшего документ */
|
||||
username: string,
|
||||
/** Версия генератора, использованного для создания документа */
|
||||
version: string
|
||||
};
|
||||
["ExpenseProposalHeaderInput"]: {
|
||||
/** Описание цели расходов */
|
||||
@@ -32991,10 +32759,14 @@ export type GraphQLTypes = {
|
||||
mechanics: string,
|
||||
/** Порядковый номер строки */
|
||||
number: string,
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — сервер подставит полные реквизиты в документ. */
|
||||
payment_method_id?: string | undefined | null,
|
||||
/** Имя получателя */
|
||||
recipient_name?: string | undefined | null,
|
||||
/** Тип получателя (SELF / MEMBER / ORG) */
|
||||
recipient_type: string,
|
||||
/** Имя аккаунта получателя-пайщика (владелец реквизитов). */
|
||||
recipient_username?: string | undefined | null,
|
||||
/** Реквизиты получателя */
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
@@ -34423,10 +34195,6 @@ export type GraphQLTypes = {
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
authorizeDecision: GraphQLTypes["Transaction"],
|
||||
/** Утвердить СЗ-отчёт (закрытие сметы). Триггерит капитализацию РИД в Благоросте.
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
authorizeExpenseReport: GraphQLTypes["Transaction"],
|
||||
/** Отменить заявку */
|
||||
cancelRequest: GraphQLTypes["Transaction"],
|
||||
/** Добавление автора проекта в CAPITAL контракте
|
||||
@@ -34843,10 +34611,6 @@ export type GraphQLTypes = {
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
declineDecision: GraphQLTypes["Transaction"],
|
||||
/** Отклонить СЗ-отчёт с указанием причины. Смета переходит в DECLINED.
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
declineExpenseReport: GraphQLTypes["Transaction"],
|
||||
/** Отклонить заявку */
|
||||
declineRequest: GraphQLTypes["Transaction"],
|
||||
/** Удалить аккаунт пайщика из системы учёта провайдера. Доступно только для незавершённых регистрационных статусов (черновик, неоплачен/отклонён). Активный, заблокированный и любой зарегистрированный в блокчейне аккаунт удалить нельзя. Используется для очистки реестра и освобождения e-mail под перерегистрацию.
|
||||
@@ -39065,7 +38829,6 @@ type ZEUS_VARIABLES = {
|
||||
["AssetContributionStatementSignedDocumentInput"]: ValueTypes["AssetContributionStatementSignedDocumentInput"];
|
||||
["AssetContributionStatementSignedMetaDocumentInput"]: ValueTypes["AssetContributionStatementSignedMetaDocumentInput"];
|
||||
["AuthorizeDecisionInput"]: ValueTypes["AuthorizeDecisionInput"];
|
||||
["AuthorizeExpenseReportInput"]: ValueTypes["AuthorizeExpenseReportInput"];
|
||||
["BankAccountDetailsInput"]: ValueTypes["BankAccountDetailsInput"];
|
||||
["BankAccountInput"]: ValueTypes["BankAccountInput"];
|
||||
["BuhotchSignerType"]: ValueTypes["BuhotchSignerType"];
|
||||
@@ -39155,7 +38918,6 @@ type ZEUS_VARIABLES = {
|
||||
["DeclineAgreementInput"]: ValueTypes["DeclineAgreementInput"];
|
||||
["DeclineApproveInput"]: ValueTypes["DeclineApproveInput"];
|
||||
["DeclineDecisionInput"]: ValueTypes["DeclineDecisionInput"];
|
||||
["DeclineExpenseReportInput"]: ValueTypes["DeclineExpenseReportInput"];
|
||||
["DeclineRequestInput"]: ValueTypes["DeclineRequestInput"];
|
||||
["DeleteAccountInput"]: ValueTypes["DeleteAccountInput"];
|
||||
["DeleteBranchInput"]: ValueTypes["DeleteBranchInput"];
|
||||
@@ -39182,8 +38944,6 @@ type ZEUS_VARIABLES = {
|
||||
["ExpenseProposalDecisionGenerateDocumentInput"]: ValueTypes["ExpenseProposalDecisionGenerateDocumentInput"];
|
||||
["ExpenseProposalDecisionHeaderInput"]: ValueTypes["ExpenseProposalDecisionHeaderInput"];
|
||||
["ExpenseProposalDecisionItemInput"]: ValueTypes["ExpenseProposalDecisionItemInput"];
|
||||
["ExpenseProposalDecisionSignedDocumentInput"]: ValueTypes["ExpenseProposalDecisionSignedDocumentInput"];
|
||||
["ExpenseProposalDecisionSignedMetaDocumentInput"]: ValueTypes["ExpenseProposalDecisionSignedMetaDocumentInput"];
|
||||
["ExpenseProposalHeaderInput"]: ValueTypes["ExpenseProposalHeaderInput"];
|
||||
["ExpenseProposalItemInput"]: ValueTypes["ExpenseProposalItemInput"];
|
||||
["ExpenseProposalStatementGenerateDocumentInput"]: ValueTypes["ExpenseProposalStatementGenerateDocumentInput"];
|
||||
|
||||
+16
-16
@@ -44,26 +44,25 @@ export interface Model {
|
||||
export const title = 'Служебная записка-смета о расходах'
|
||||
export const description = 'Заявка-смета председателю кооператива на финансирование расходов по программе. Содержит описание цели и массив позиций расхода.'
|
||||
|
||||
export const context = `<style>h1{margin:0;text-align:center;}h3{margin:0;padding-top:15px;text-align:center;}.digital-document{padding:20px;white-space:pre-wrap;}.subheader{padding-bottom:20px;}table{width:100%;border-collapse:collapse;}th,td{border:1px solid #ccc;padding:8px;text-align:left;word-wrap:break-word;overflow-wrap:break-word;}th{background-color:#f4f4f4;}.summary{margin-top:20px;text-align:right;font-weight:bold;}.signature{padding-top:30px;}</style><div class="digital-document"><div style="padding-bottom:30px;"><h1 style="text-align:center">{% trans 'PROPOSAL_TITLE' %}</h1><p style="text-align:center">{{vars.full_abbr_genitive}} «{{vars.name}}»</p><p style="text-align:right;padding-top:20px">{{ coop.city }}, {{ meta.created_at }}</p></div><h3>{% trans 'PURPOSE' %}</h3><p>{{ proposal.description }}</p><h3 style="padding-top:20px;">{% trans 'ITEMS' %}</h3><table><thead><tr><th>№</th><th>{% trans 'ITEM_DESCRIPTION' %}</th><th>{% trans 'ITEM_AMOUNT' %}</th><th>{% trans 'ITEM_RECIPIENT' %}</th><th>{% trans 'ITEM_MECHANICS' %}</th><th>{% trans 'ITEM_REQUISITES' %}</th></tr></thead><tbody>{% for item in items %}<tr><td>{{ item.number }}</td><td>{{ item.description }}</td><td>{{ item.amount }}</td><td>{% if item.recipient_type == 'SELF' %}{% trans 'RECIPIENT_SELF' %}{% elif item.recipient_type == 'MEMBER' %}{% trans 'RECIPIENT_MEMBER' %}: {{ item.recipient_name }}{% else %}{% trans 'RECIPIENT_ORG' %}: {{ item.recipient_name }}{% endif %}</td><td>{% if item.mechanics == 'ADVANCE' %}{% trans 'MECH_ADVANCE' %}{% else %}{% trans 'MECH_DIRECT' %}{% endif %}</td><td>{{ item.requisites }}</td></tr>{% endfor %}</tbody></table><p class="summary">{% trans 'TOTAL' %}: {{ proposal.total_amount }} ({{ proposal.items_count }} {% trans 'ITEMS_COUNT_SUFFIX' %})</p><p style="padding-top:15px;">{% trans 'SOURCE_WALLET' %}: {{ proposal.source_wallet }}</p><div class="signature"><p>{{ user.full_name_or_short_name }}</p><p>{% trans 'SIGNED_DIGITALLY' %}</p></div></div>`
|
||||
// Вёрстка по бумажному образцу служебной записки: шапка-адресат справа,
|
||||
// дата слева, центрированный заголовок с идентификатором (= proposal_hash,
|
||||
// по нему записку можно найти), позиции — нумерованным списком с полными
|
||||
// реквизитами получателей. Кошелёк-источник в документе не указывается.
|
||||
export const context = `<style>h1{margin:0;text-align:center;}.digital-document{padding:20px;}.doc-header{text-align:right;padding-bottom:25px;}.doc-header p{margin:2px 0;}.doc-id{text-align:center;font-size:12px;word-break:break-all;padding-top:6px;padding-bottom:25px;}ol.items{padding-left:25px;margin:10px 0;}ol.items li{padding-bottom:10px;}.signature{padding-top:40px;}</style><div class="digital-document"><div class="doc-header"><p>{% trans 'TO_COUNCIL' %} {{vars.full_abbr_genitive}} «{{vars.name}}»</p><p>{% trans 'FROM_MEMBER' %} {{ user.full_name_or_short_name }}</p></div><p>{% trans 'DATE' %}: {{ meta.created_at }}</p><h1 style="padding-top:25px;">{% trans 'PROPOSAL_TITLE' %}</h1><p class="doc-id">№ {{ proposal_hash }}</p><p>{% trans 'BODY_INTRO' %} {{ proposal.total_amount }}, {% trans 'BODY_NAMELY' %}:</p><ol class="items">{% for item in items %}<li>{{ item.description }} — {{ item.amount }} ({% if item.mechanics == 'ADVANCE' %}{% trans 'MECH_ADVANCE' %}{% else %}{% trans 'MECH_DIRECT' %}{% endif %}). {% trans 'ITEM_RECIPIENT' %}: {% if item.recipient_type == 'SELF' %}{{ user.full_name_or_short_name }}{% else %}{{ item.recipient_name }}{% endif %}{% if item.requisites %}. {% trans 'ITEM_REQUISITES' %}: {{ item.requisites }}{% endif %}</li>{% endfor %}</ol><p>{% trans 'PURPOSE' %}: {{ proposal.description }}</p><div class="signature"><p>{{ user.full_name_or_short_name }}</p><p>{% trans 'SIGNED_DIGITALLY' %}</p></div></div>`
|
||||
|
||||
export const translations = {
|
||||
ru: {
|
||||
PROPOSAL_TITLE: 'СЛУЖЕБНАЯ ЗАПИСКА-СМЕТА О РАСХОДАХ',
|
||||
PURPOSE: 'ЦЕЛЬ РАСХОДОВ',
|
||||
ITEMS: 'ПОЗИЦИИ РАСХОДА',
|
||||
ITEM_DESCRIPTION: 'Описание',
|
||||
ITEM_AMOUNT: 'Сумма',
|
||||
TO_COUNCIL: 'В Совет',
|
||||
FROM_MEMBER: 'от пайщика',
|
||||
DATE: 'Дата',
|
||||
PROPOSAL_TITLE: 'СЛУЖЕБНАЯ ЗАПИСКА',
|
||||
BODY_INTRO: 'Прошу согласовать списание затрат в общей сумме',
|
||||
BODY_NAMELY: 'а именно',
|
||||
MECH_ADVANCE: 'аванс под отчёт',
|
||||
MECH_DIRECT: 'прямая оплата',
|
||||
ITEM_RECIPIENT: 'Получатель',
|
||||
ITEM_MECHANICS: 'Механика',
|
||||
ITEM_REQUISITES: 'Реквизиты',
|
||||
RECIPIENT_SELF: 'Я (creator)',
|
||||
RECIPIENT_MEMBER: 'Пайщик',
|
||||
RECIPIENT_ORG: 'Организация',
|
||||
MECH_ADVANCE: 'Аванс под отчёт',
|
||||
MECH_DIRECT: 'Прямая оплата',
|
||||
TOTAL: 'Итого',
|
||||
ITEMS_COUNT_SUFFIX: 'поз.',
|
||||
SOURCE_WALLET: 'Источник средств',
|
||||
PURPOSE: 'Цель расходов',
|
||||
SIGNED_DIGITALLY: 'подписано электронной подписью',
|
||||
},
|
||||
}
|
||||
@@ -73,6 +72,7 @@ export const exampleData = {
|
||||
meta: { created_at: '02.06.2026 14:00' },
|
||||
vars: { full_abbr_genitive: 'ПК', name: 'Восход' },
|
||||
user: { full_name_or_short_name: 'Иванов И.И.' },
|
||||
proposal_hash: '55c470039a8c53ce1b4b6e842fe8063ab3d5b85ba2ba8ab0ae6e30be3ad328b7',
|
||||
proposal: {
|
||||
description: 'Закупка хостинга и бухгалтерских услуг на июнь 2026',
|
||||
total_amount: '15000.00 RUB',
|
||||
@@ -96,7 +96,7 @@ export const exampleData = {
|
||||
recipient_type: 'SELF',
|
||||
mechanics: 'ADVANCE',
|
||||
recipient_name: '',
|
||||
requisites: '',
|
||||
requisites: 'Банковский перевод: счёт 40817810000000000000, Банк ВТБ (ПАО), БИК 044525187',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
+11
@@ -21,6 +21,8 @@ export interface ICreateProgramExpenseDraftItem {
|
||||
requisites?: string;
|
||||
recipient_account?: string;
|
||||
item_hash?: string;
|
||||
/** Идентификатор платёжного метода получателя-пайщика (SELF/MEMBER). */
|
||||
payment_method_id?: string | null;
|
||||
}
|
||||
|
||||
export interface ICreateProgramExpenseDraft {
|
||||
@@ -63,6 +65,8 @@ export function useCreateProgramExpense() {
|
||||
const totalAmount = draft.items.reduce((sum, it) => sum + parseFloat(it.amount || '0'), 0);
|
||||
const total_amount = formatToAsset(totalAmount, symbol, precision);
|
||||
|
||||
// Полные реквизиты в документ подставляет сервер по payment_method_id;
|
||||
// фронт знает только сокращённое представление.
|
||||
const itemsForDoc = draft.items.map((it, idx) => ({
|
||||
number: String(idx + 1),
|
||||
description: it.description,
|
||||
@@ -71,6 +75,11 @@ export function useCreateProgramExpense() {
|
||||
mechanics: it.mechanics,
|
||||
recipient_name: it.recipient_name ?? '',
|
||||
requisites: it.requisites ?? '',
|
||||
payment_method_id: it.payment_method_id ?? undefined,
|
||||
recipient_username:
|
||||
it.recipient_type === Zeus.ExpenseRecipientType.MEMBER
|
||||
? it.recipient_account?.trim()
|
||||
: undefined,
|
||||
}));
|
||||
|
||||
const generateInput: GenerateStatementInput = {
|
||||
@@ -102,6 +111,8 @@ export function useCreateProgramExpense() {
|
||||
recipient: resolveRecipient(it, session.username),
|
||||
description: it.description,
|
||||
planned_amount: formatToAsset(it.amount, symbol, precision),
|
||||
payment_method_id: it.payment_method_id ?? undefined,
|
||||
requisites: it.requisites || undefined,
|
||||
}));
|
||||
|
||||
const result = await createProgramExpense({
|
||||
|
||||
+27
-4
@@ -43,7 +43,8 @@ BaseDialog(
|
||||
BaseSelect(
|
||||
v-model='item.recipient_type',
|
||||
:options='recipientTypeOptions',
|
||||
label='Тип получателя'
|
||||
label='Тип получателя',
|
||||
@update:model-value='item.payment_method_id = null'
|
||||
)
|
||||
.col-12.col-md-6
|
||||
BaseSelect(
|
||||
@@ -57,7 +58,8 @@ BaseDialog(
|
||||
v-model='item.recipient_account',
|
||||
label='Аккаунт пайщика-получателя',
|
||||
placeholder='username',
|
||||
required
|
||||
required,
|
||||
@update:model-value='item.payment_method_id = null'
|
||||
)
|
||||
.col-12.col-md-6(v-if='item.recipient_type === Zeus.ExpenseRecipientType.ORG')
|
||||
BaseInput(
|
||||
@@ -79,11 +81,26 @@ BaseDialog(
|
||||
label='Описание',
|
||||
placeholder='Назначение расхода'
|
||||
)
|
||||
.col-12(v-if='item.recipient_type === Zeus.ExpenseRecipientType.SELF')
|
||||
PaymentMethodSelect(
|
||||
v-model='item.payment_method_id',
|
||||
:username='session.username',
|
||||
hint='Реквизиты фиксируются на момент подачи — на них придёт выплата',
|
||||
required
|
||||
)
|
||||
.col-12(v-if='item.recipient_type === Zeus.ExpenseRecipientType.MEMBER')
|
||||
PaymentMethodSelect(
|
||||
v-model='item.payment_method_id',
|
||||
:username='item.recipient_account?.trim() || ""',
|
||||
:hint='item.recipient_account?.trim() ? "Реквизиты фиксируются на момент подачи" : "Сначала укажите аккаунт пайщика-получателя"',
|
||||
required
|
||||
)
|
||||
.col-12(v-if='item.recipient_type === Zeus.ExpenseRecipientType.ORG')
|
||||
BaseInput(
|
||||
v-model='item.requisites',
|
||||
label='Реквизиты получателя',
|
||||
placeholder='ИНН, р/с, БИК'
|
||||
placeholder='ИНН, р/с, БИК',
|
||||
required
|
||||
)
|
||||
|
||||
template(#footer)
|
||||
@@ -102,11 +119,13 @@ import { computed, reactive, ref } from 'vue';
|
||||
import { Zeus } from '@coopenomics/sdk';
|
||||
import { FailAlert, SuccessAlert } from 'src/shared/api';
|
||||
import { useSystemStore } from 'src/entities/System/model';
|
||||
import { useSessionStore } from 'src/entities/Session';
|
||||
import { BaseDialog } from 'src/shared/ui/base/BaseDialog';
|
||||
import { BaseButton } from 'src/shared/ui/base/BaseButton';
|
||||
import { BaseInput } from 'src/shared/ui/base/BaseInput';
|
||||
import { BaseSelect } from 'src/shared/ui/base/BaseSelect';
|
||||
import { EmptyState } from 'src/shared/ui/base/EmptyState';
|
||||
import { PaymentMethodSelect } from 'src/shared/ui/domain/PaymentMethodSelect';
|
||||
import {
|
||||
useCreateProgramExpense,
|
||||
type ICreateProgramExpenseDraftItem,
|
||||
@@ -119,6 +138,7 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const system = useSystemStore();
|
||||
const session = useSessionStore();
|
||||
const { submitProgramExpense } = useCreateProgramExpense();
|
||||
|
||||
const amountPlaceholder = computed(() => {
|
||||
@@ -153,7 +173,9 @@ const canSubmit = computed(
|
||||
i.amount.trim() &&
|
||||
i.description.trim() &&
|
||||
(i.recipient_type !== Zeus.ExpenseRecipientType.MEMBER || i.recipient_account?.trim()) &&
|
||||
(i.recipient_type !== Zeus.ExpenseRecipientType.ORG || i.recipient_name?.trim()),
|
||||
(i.recipient_type === Zeus.ExpenseRecipientType.ORG
|
||||
? Boolean(i.recipient_name?.trim() && i.requisites?.trim())
|
||||
: Boolean(i.payment_method_id)),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -167,6 +189,7 @@ function addItem(): void {
|
||||
recipient_name: '',
|
||||
requisites: '',
|
||||
recipient_account: '',
|
||||
payment_method_id: null,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -93,8 +93,7 @@ Queries.Expenses.ExpenseFiles.query
|
||||
// Кассир + админ
|
||||
Mutations.Expenses.PayExpenseItem.mutation
|
||||
Mutations.Expenses.SubmitExpenseReport.mutation
|
||||
Mutations.Expenses.AuthorizeExpenseReport.mutation
|
||||
Mutations.Expenses.DeclineExpenseReport.mutation
|
||||
// Авторизация/отклонение СЗ — решение совета (повестка), мутаций нет
|
||||
Mutations.Expenses.ReturnExpenseItem.mutation
|
||||
Mutations.Expenses.UploadExpenseFile.mutation
|
||||
|
||||
|
||||
@@ -24,7 +24,3 @@ export { generateExpenseProposalDecisionDocument } from './mutations/generateExp
|
||||
export type { IGenerateExpenseProposalDecisionDocumentInput } from './mutations/generateExpenseProposalDecisionDocument';
|
||||
export { createExpenseProposal } from './mutations/createExpenseProposal';
|
||||
export type { ICreateExpenseProposalInput } from './mutations/createExpenseProposal';
|
||||
export { authorizeExpenseReport } from './mutations/authorizeExpenseReport';
|
||||
export type { IAuthorizeExpenseReportInput } from './mutations/authorizeExpenseReport';
|
||||
export { declineExpenseReport } from './mutations/declineExpenseReport';
|
||||
export type { IDeclineExpenseReportInput } from './mutations/declineExpenseReport';
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { client } from 'src/shared/api/client';
|
||||
import { Mutations } from '@coopenomics/sdk';
|
||||
|
||||
export type IAuthorizeExpenseReportInput = Mutations.Expense.AuthorizeExpenseReport.IInput['data'];
|
||||
|
||||
export async function authorizeExpenseReport(data: IAuthorizeExpenseReportInput) {
|
||||
return client.Mutation(Mutations.Expense.AuthorizeExpenseReport.mutation, {
|
||||
variables: { data },
|
||||
});
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { client } from 'src/shared/api/client';
|
||||
import { Mutations } from '@coopenomics/sdk';
|
||||
|
||||
export type IDeclineExpenseReportInput = Mutations.Expense.DeclineExpenseReport.IInput['data'];
|
||||
|
||||
export async function declineExpenseReport(data: IDeclineExpenseReportInput) {
|
||||
return client.Mutation(Mutations.Expense.DeclineExpenseReport.mutation, {
|
||||
variables: { data },
|
||||
});
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
ExpensesRegistryPage,
|
||||
ExpenseDetailPage,
|
||||
ExpensesAdminApprovePage,
|
||||
ExpensesAdminAuthorizePage,
|
||||
CashierPage,
|
||||
MyAdvancesPage,
|
||||
} from './pages';
|
||||
@@ -56,19 +55,6 @@ export default async function (): Promise<IWorkspaceConfig[]> {
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
path: 'admin/authorize',
|
||||
name: 'expenses-admin-authorize',
|
||||
component: markRaw(ExpensesAdminAuthorizePage),
|
||||
meta: {
|
||||
title: 'На авторизацию совета',
|
||||
icon: 'how_to_vote',
|
||||
roles: ['chairman'],
|
||||
agreements: agreementsBase,
|
||||
requiresAuth: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
path: 'cashier',
|
||||
name: 'expenses-cashier',
|
||||
|
||||
@@ -5,10 +5,7 @@ import type { Cooperative } from 'cooptypes';
|
||||
import { generateUniqueHash } from 'src/shared/lib/utils/generateUniqueHash';
|
||||
import {
|
||||
generateExpenseProposalStatementDocument,
|
||||
generateExpenseProposalDecisionDocument,
|
||||
createExpenseProposal,
|
||||
authorizeExpenseReport,
|
||||
declineExpenseReport,
|
||||
} from '../api';
|
||||
|
||||
export interface ICreateProposalDraftItem {
|
||||
@@ -29,17 +26,6 @@ export interface ICreateProposalDraft {
|
||||
items: ICreateProposalDraftItem[];
|
||||
}
|
||||
|
||||
export interface IAuthorizeProposalDraft {
|
||||
proposal_hash: string;
|
||||
description: string;
|
||||
source_wallet: string;
|
||||
items: ICreateProposalDraftItem[];
|
||||
decision_kind: 'approve' | 'decline';
|
||||
decision_reason?: string;
|
||||
protocol_number?: string;
|
||||
protocol_date?: string;
|
||||
}
|
||||
|
||||
export function useExpenseProposalActions() {
|
||||
const { info } = useSystemStore();
|
||||
const session = useSessionStore();
|
||||
@@ -103,66 +89,7 @@ export function useExpenseProposalActions() {
|
||||
} as any);
|
||||
}
|
||||
|
||||
async function authorizeProposal(draft: IAuthorizeProposalDraft) {
|
||||
// Отклонение — без протокола решения: контрактный declexp принимает
|
||||
// только причину. Документ 2011 генерируется лишь при утверждении.
|
||||
if (draft.decision_kind === 'decline') {
|
||||
return declineExpenseReport({
|
||||
coopname: info.coopname,
|
||||
proposal_hash: draft.proposal_hash,
|
||||
reason: draft.decision_reason || 'Отклонено председателем',
|
||||
} as any);
|
||||
}
|
||||
|
||||
const itemsForDoc = draft.items.map((it, idx) => ({
|
||||
number: String(idx + 1),
|
||||
description: it.description,
|
||||
amount: it.amount,
|
||||
recipient_type: it.recipient_type,
|
||||
mechanics: it.mechanics,
|
||||
recipient_name: it.recipient_name ?? '',
|
||||
requisites: it.requisites ?? '',
|
||||
}));
|
||||
|
||||
const totalAmount = draft.items.reduce((sum, it) => sum + parseFloat(it.amount || '0'), 0);
|
||||
const total_amount = `${totalAmount.toFixed(4)} RUB`;
|
||||
|
||||
const generated = await generateExpenseProposalDecisionDocument({
|
||||
coopname: info.coopname,
|
||||
username: session.username,
|
||||
proposal_hash: draft.proposal_hash,
|
||||
proposal: {
|
||||
description: draft.description,
|
||||
total_amount,
|
||||
items_count: draft.items.length,
|
||||
source_wallet: draft.source_wallet,
|
||||
},
|
||||
items: itemsForDoc,
|
||||
decision: {
|
||||
kind: draft.decision_kind,
|
||||
reason: draft.decision_reason,
|
||||
protocol_number: draft.protocol_number,
|
||||
protocol_date: draft.protocol_date,
|
||||
},
|
||||
} as any);
|
||||
|
||||
const docKey = 'generateExpenseProposalDecisionDocument' as const;
|
||||
const generatedDoc = (generated as any)[docKey];
|
||||
|
||||
const digital = new DigitalDocument(generatedDoc);
|
||||
const signed = await digital.sign<Cooperative.Registry.ExpenseProposalDecision.Meta>(
|
||||
session.username,
|
||||
1,
|
||||
);
|
||||
|
||||
return authorizeExpenseReport({
|
||||
coopname: info.coopname,
|
||||
proposal_hash: draft.proposal_hash,
|
||||
decision: signed as any,
|
||||
} as any);
|
||||
}
|
||||
|
||||
return { submitProposal, authorizeProposal };
|
||||
return { submitProposal };
|
||||
}
|
||||
|
||||
function generateItemHash(proposal_hash: string, idx: number): string {
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
<template lang="pug">
|
||||
BaseDialog(
|
||||
:model-value='modelValue',
|
||||
title='Авторизация служебной записки',
|
||||
size='md',
|
||||
@update:model-value='$emit("update:modelValue", $event)'
|
||||
)
|
||||
.auth-form(v-if='proposal')
|
||||
.banner.banner--info.q-mb-md
|
||||
q-icon.banner__icon(name='gavel', size='20px')
|
||||
.banner__body
|
||||
| Решение председателя/совета по СЗ {{ truncateHash(proposal.proposal_hash) }}.
|
||||
| Документ-протокол подписывается вашим ключом и публикуется в блокчейне.
|
||||
|
||||
.field-group
|
||||
.t-section.q-mb-sm Сводка
|
||||
DataRow(label='Пайщик', :value='proposal.username || "—"')
|
||||
DataRow(label='Назначение', :value='proposalPurpose || "—"')
|
||||
DataRow(label='Сумма (план)', :value='proposal.total_planned || "—"')
|
||||
DataRow(label='Сумма (факт)', :value='proposal.total_actual || "—"')
|
||||
|
||||
.field-group
|
||||
.t-section.q-mb-sm Решение
|
||||
q-option-group(
|
||||
v-model='form.kind',
|
||||
:options='kindOptions',
|
||||
type='radio',
|
||||
color='primary'
|
||||
)
|
||||
|
||||
BaseInput(
|
||||
v-if='form.kind === "decline"',
|
||||
v-model='form.reason',
|
||||
label='Причина отказа',
|
||||
placeholder='Например: «Превышение лимита программы»',
|
||||
required
|
||||
)
|
||||
|
||||
.row.q-col-gutter-sm
|
||||
.col-12.col-md-6
|
||||
BaseInput(
|
||||
v-model='form.protocol_number',
|
||||
label='Номер протокола',
|
||||
placeholder='Например: 15'
|
||||
)
|
||||
.col-12.col-md-6
|
||||
BaseInput(
|
||||
v-model='form.protocol_date',
|
||||
label='Дата протокола',
|
||||
placeholder='03.06.2026'
|
||||
)
|
||||
|
||||
template(#footer)
|
||||
.footer-bar
|
||||
BaseButton(variant='ghost', @click='close') Отмена
|
||||
BaseButton(
|
||||
variant='primary',
|
||||
:loading='submitting',
|
||||
:disabled='!canSubmit',
|
||||
@click='submit'
|
||||
) {{ form.kind === 'approve' ? 'Утвердить' : 'Отклонить' }}
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
import { FailAlert, SuccessAlert } from 'src/shared/api';
|
||||
import { BaseDialog } from 'src/shared/ui/base/BaseDialog';
|
||||
import { BaseButton } from 'src/shared/ui/base/BaseButton';
|
||||
import { BaseInput } from 'src/shared/ui/base/BaseInput';
|
||||
import { DataRow } from 'src/shared/ui/domain';
|
||||
import {
|
||||
getExpenseProposal,
|
||||
type IExpenseProposalResult,
|
||||
} from '../api';
|
||||
import { useExpenseProposalActions, type ICreateProposalDraftItem } from '../model';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean;
|
||||
proposalHash: string;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: boolean): void;
|
||||
(e: 'authorized'): void;
|
||||
}>();
|
||||
|
||||
const { authorizeProposal } = useExpenseProposalActions();
|
||||
|
||||
const proposal = ref<NonNullable<IExpenseProposalResult> | null>(null);
|
||||
|
||||
const form = reactive({
|
||||
kind: 'approve' as 'approve' | 'decline',
|
||||
reason: '',
|
||||
protocol_number: '',
|
||||
protocol_date: '',
|
||||
});
|
||||
|
||||
const submitting = ref(false);
|
||||
|
||||
const kindOptions = [
|
||||
{ label: 'Утвердить', value: 'approve' },
|
||||
{ label: 'Отклонить', value: 'decline' },
|
||||
];
|
||||
|
||||
const canSubmit = computed(() => {
|
||||
if (!proposal.value) return false;
|
||||
if (form.kind === 'decline' && !form.reason.trim()) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
// Назначение СЗ = перечень позиций (в зеркале шасси нет отдельного описания —
|
||||
// оно живёт в документе-заявлении 2010).
|
||||
const proposalPurpose = computed(() =>
|
||||
(proposal.value?.items ?? []).map((it: any) => it.description).filter(Boolean).join('; '),
|
||||
);
|
||||
|
||||
watch(
|
||||
() => [props.modelValue, props.proposalHash],
|
||||
async ([open]) => {
|
||||
if (!open || !props.proposalHash) return;
|
||||
try {
|
||||
const result = await getExpenseProposal({ proposal_hash: props.proposalHash });
|
||||
proposal.value = result as NonNullable<IExpenseProposalResult> | null;
|
||||
} catch (e) {
|
||||
FailAlert(e);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function truncateHash(hash?: string | null): string {
|
||||
if (!hash) return '';
|
||||
if (hash.length <= 16) return hash;
|
||||
return `${hash.slice(0, 8)}…${hash.slice(-6)}`;
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
emit('update:modelValue', false);
|
||||
}
|
||||
|
||||
async function submit(): Promise<void> {
|
||||
if (!proposal.value) return;
|
||||
try {
|
||||
submitting.value = true;
|
||||
const items: ICreateProposalDraftItem[] = (proposal.value.items ?? []).map((it: any, idx: number) => ({
|
||||
number: String(idx + 1),
|
||||
description: it.description ?? '',
|
||||
amount: it.planned_amount ?? '',
|
||||
recipient_type: (it.recipient_type ?? 'SELF') as 'SELF' | 'MEMBER' | 'ORG',
|
||||
mechanics: (it.mechanics ?? 'ADVANCE') as 'ADVANCE' | 'DIRECT',
|
||||
recipient_name: it.recipient ?? '',
|
||||
requisites: '',
|
||||
item_hash: it.item_hash,
|
||||
recipient_account: it.recipient ?? '',
|
||||
}));
|
||||
|
||||
await authorizeProposal({
|
||||
proposal_hash: proposal.value.proposal_hash,
|
||||
description: proposalPurpose.value || 'Расход',
|
||||
source_wallet: proposal.value.source_wallet ?? '',
|
||||
items,
|
||||
decision_kind: form.kind,
|
||||
decision_reason: form.reason || undefined,
|
||||
protocol_number: form.protocol_number || undefined,
|
||||
protocol_date: form.protocol_date || undefined,
|
||||
});
|
||||
|
||||
SuccessAlert(
|
||||
form.kind === 'approve'
|
||||
? 'СЗ утверждена — решение подписано и отправлено в блокчейн'
|
||||
: 'СЗ отклонена — решение подписано и отправлено в блокчейн',
|
||||
);
|
||||
emit('authorized');
|
||||
close();
|
||||
} catch (e) {
|
||||
FailAlert(e);
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.auth-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--p-4);
|
||||
}
|
||||
|
||||
.field-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--p-3);
|
||||
}
|
||||
|
||||
.footer-bar {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--p-2);
|
||||
padding: var(--p-3) var(--p-4);
|
||||
border-top: 1px solid var(--p-line);
|
||||
background: var(--p-canvas);
|
||||
}
|
||||
</style>
|
||||
@@ -1,241 +0,0 @@
|
||||
<template lang="pug">
|
||||
.q-pa-md
|
||||
PageHead(
|
||||
eyebrow='Шасси расходов · Председатель',
|
||||
title='На авторизацию отчётов',
|
||||
subtitle='Очередь служебных записок со статусом «Отчёт подан» — ждут авторизации председателя'
|
||||
)
|
||||
|
||||
.admin-queue
|
||||
TableSkeleton(
|
||||
v-if='loading && !filtered.length',
|
||||
:columns='skeletonColumns',
|
||||
:rows='6',
|
||||
min-width='880px'
|
||||
)
|
||||
|
||||
.table-wrap(v-else-if='filtered.length')
|
||||
.table-scroll
|
||||
table.table
|
||||
thead
|
||||
tr
|
||||
th Пайщик
|
||||
th.col-date Дата создания
|
||||
th.col-num Сумма (план)
|
||||
th.col-num Сумма (факт)
|
||||
th Хеш
|
||||
th.col-actions
|
||||
tbody
|
||||
tr.data-row(
|
||||
v-for='row in filtered',
|
||||
:key='row.proposal_hash',
|
||||
@click='openDetail(row.proposal_hash)'
|
||||
)
|
||||
td.cell-name {{ row.username || '—' }}
|
||||
td {{ formatCreatedAt(row.created_at) }}
|
||||
td.col-num {{ row.total_planned || '—' }}
|
||||
td.col-num {{ row.total_actual || '—' }}
|
||||
td.col-hash.t-mono-sm {{ truncateHash(row.proposal_hash) }}
|
||||
td.col-actions
|
||||
.row-actions
|
||||
BaseButton(
|
||||
variant='primary',
|
||||
size='sm',
|
||||
icon='gavel',
|
||||
@click.stop='openAuthorize(row.proposal_hash)'
|
||||
) Авторизовать
|
||||
BaseButton(
|
||||
variant='ghost',
|
||||
size='sm',
|
||||
icon='chevron_right',
|
||||
@click.stop='openDetail(row.proposal_hash)'
|
||||
) Открыть
|
||||
|
||||
.table-foot
|
||||
span {{ rangeLabel }}
|
||||
BaseButton(
|
||||
v-if='hasMore',
|
||||
variant='ghost',
|
||||
size='sm',
|
||||
:loading='loading',
|
||||
@click='loadMore'
|
||||
) Загрузить ещё
|
||||
|
||||
EmptyState(
|
||||
v-else,
|
||||
title='Очередь пуста',
|
||||
body='Отчёты по расходам не ожидают авторизации.'
|
||||
)
|
||||
template(#icon)
|
||||
q-icon(name='task_alt', size='48px')
|
||||
|
||||
ExpenseProposalAuthorizeDialog(
|
||||
v-model='authorizeOpen',
|
||||
:proposal-hash='authorizingHash',
|
||||
@authorized='onAuthorized'
|
||||
)
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { Zeus } from '@coopenomics/sdk';
|
||||
import { PageHead } from 'src/shared/ui/layout';
|
||||
import { BaseButton } from 'src/shared/ui/base/BaseButton';
|
||||
import { EmptyState } from 'src/shared/ui/base/EmptyState';
|
||||
import { TableSkeleton } from 'src/shared/ui/base/TableSkeleton';
|
||||
import type { TableSkeletonColumn } from 'src/shared/ui/base/TableSkeleton';
|
||||
import { FailAlert } from 'src/shared/api';
|
||||
import {
|
||||
getExpenseProposalsByCooperative,
|
||||
type IExpenseProposalsByCooperativeResult,
|
||||
} from '../api';
|
||||
import ExpenseProposalAuthorizeDialog from './ExpenseProposalAuthorizeDialog.vue';
|
||||
|
||||
type IProposalRow = NonNullable<IExpenseProposalsByCooperativeResult['items']>[number];
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const loading = ref(false);
|
||||
const items = ref<IProposalRow[]>([]);
|
||||
const currentPage = ref(1);
|
||||
const totalPages = ref(1);
|
||||
const totalCount = ref(0);
|
||||
|
||||
const PAGE_LIMIT = 25;
|
||||
|
||||
const skeletonColumns = computed<TableSkeletonColumn[]>(() => [
|
||||
{ label: 'Пайщик', cell: 'text' },
|
||||
{ label: 'Дата создания', cell: 'text', cellWidth: '120px' },
|
||||
{ label: 'Сумма (план)', class: 'col-num', cell: 'text', cellWidth: '110px' },
|
||||
{ label: 'Сумма (факт)', class: 'col-num', cell: 'text', cellWidth: '110px' },
|
||||
{ label: 'Хеш', cell: 'text', cellWidth: '160px' },
|
||||
{ label: '', cell: 'text', cellWidth: '120px' },
|
||||
]);
|
||||
|
||||
const filtered = computed(() =>
|
||||
items.value.filter((p) => p.status === Zeus.ExpenseProposalStatus.REPORT_SUBMITTED),
|
||||
);
|
||||
|
||||
const hasMore = computed(() => currentPage.value < totalPages.value);
|
||||
|
||||
const rangeLabel = computed(() => {
|
||||
const shown = filtered.value.length;
|
||||
return `${shown} в очереди · загружено ${items.value.length} из ${totalCount.value}`;
|
||||
});
|
||||
|
||||
function formatCreatedAt(createdAt?: string | null): string {
|
||||
if (!createdAt) return '—';
|
||||
const date = new Date(createdAt);
|
||||
if (Number.isNaN(date.getTime())) return createdAt;
|
||||
return date.toLocaleString('ru-RU', { dateStyle: 'short', timeStyle: 'short' });
|
||||
}
|
||||
|
||||
function truncateHash(hash: string): string {
|
||||
if (!hash) return '';
|
||||
if (hash.length <= 16) return hash;
|
||||
return `${hash.slice(0, 8)}…${hash.slice(-6)}`;
|
||||
}
|
||||
|
||||
async function loadPage(page = 1): Promise<void> {
|
||||
const coopname = route.params.coopname as string;
|
||||
if (!coopname) return;
|
||||
try {
|
||||
loading.value = true;
|
||||
const result = await getExpenseProposalsByCooperative({
|
||||
coopname,
|
||||
options: {
|
||||
page,
|
||||
limit: PAGE_LIMIT,
|
||||
sortBy: 'created_at',
|
||||
sortOrder: 'ASC',
|
||||
},
|
||||
});
|
||||
const incoming = (result.items ?? []) as IProposalRow[];
|
||||
items.value = page === 1 ? incoming : [...items.value, ...incoming];
|
||||
currentPage.value = result.currentPage ?? page;
|
||||
totalPages.value = result.totalPages ?? 1;
|
||||
totalCount.value = result.totalCount ?? items.value.length;
|
||||
} catch (e) {
|
||||
FailAlert(e);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function loadMore(): void {
|
||||
if (loading.value || !hasMore.value) return;
|
||||
void loadPage(currentPage.value + 1);
|
||||
}
|
||||
|
||||
function openDetail(hash: string): void {
|
||||
void router.push({ name: 'expenses-detail', params: { hash } });
|
||||
}
|
||||
|
||||
const authorizeOpen = ref(false);
|
||||
const authorizingHash = ref('');
|
||||
|
||||
function openAuthorize(hash: string): void {
|
||||
authorizingHash.value = hash;
|
||||
authorizeOpen.value = true;
|
||||
}
|
||||
|
||||
function onAuthorized(): void {
|
||||
void loadPage(1);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void loadPage(1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.admin-queue {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-scroll {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.table {
|
||||
table-layout: fixed;
|
||||
min-width: 880px;
|
||||
}
|
||||
|
||||
.col-date {
|
||||
width: 132px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.col-num {
|
||||
width: 120px;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.col-hash {
|
||||
width: 160px;
|
||||
color: var(--p-ink-2);
|
||||
}
|
||||
|
||||
.col-actions {
|
||||
width: 220px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.row-actions {
|
||||
display: flex;
|
||||
gap: var(--p-2);
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.cell-name {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.data-row {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,6 @@
|
||||
export { default as ExpensesRegistryPage } from './ExpensesRegistryPage.vue';
|
||||
export { default as ExpenseDetailPage } from './ExpenseDetailPage.vue';
|
||||
export { default as ExpensesAdminApprovePage } from './ExpensesAdminApprovePage.vue';
|
||||
export { default as ExpensesAdminAuthorizePage } from './ExpensesAdminAuthorizePage.vue';
|
||||
export { default as CashierPage } from './CashierPage.vue';
|
||||
export { default as MyAdvancesPage } from './MyAdvancesPage.vue';
|
||||
export { default as ExpenseProposalCreateDialog } from './ExpenseProposalCreateDialog.vue';
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<template lang="pug">
|
||||
BaseSelect(
|
||||
:model-value='modelValue',
|
||||
:options='options',
|
||||
:label='label',
|
||||
:placeholder='placeholder',
|
||||
:hint='hint',
|
||||
:error='error || loadError',
|
||||
:disabled='disabled || loading',
|
||||
:required='required',
|
||||
@update:model-value='$emit("update:modelValue", $event === null ? null : String($event))'
|
||||
)
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { client } from 'src/shared/api/client';
|
||||
import { Queries } from '@coopenomics/sdk';
|
||||
import { BaseSelect } from 'src/shared/ui/base/BaseSelect';
|
||||
import type { BaseSelectOption } from 'src/shared/ui/base/BaseSelect';
|
||||
import {
|
||||
formatPaymentMethodShort,
|
||||
type IPaymentMethodLike,
|
||||
} from './formatPaymentMethod';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue?: string | null;
|
||||
/** Чьи реквизиты выбираем — владелец платёжных методов. */
|
||||
username: string;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
hint?: string;
|
||||
error?: string;
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
}>(),
|
||||
{
|
||||
modelValue: null,
|
||||
label: 'Реквизиты получателя',
|
||||
placeholder: 'Выберите способ получения средств',
|
||||
},
|
||||
);
|
||||
|
||||
defineEmits<{ (e: 'update:modelValue', value: string | null): void }>();
|
||||
|
||||
const methods = ref<IPaymentMethodLike[]>([]);
|
||||
const loading = ref(false);
|
||||
const loadError = ref('');
|
||||
|
||||
const options = computed<BaseSelectOption[]>(() =>
|
||||
methods.value.map((m) => ({
|
||||
value: String(m.method_id),
|
||||
label: formatPaymentMethodShort(m),
|
||||
})),
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.username,
|
||||
async (username) => {
|
||||
methods.value = [];
|
||||
loadError.value = '';
|
||||
if (!username) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await client.Query(Queries.PaymentMethods.GetPaymentMethods.query, {
|
||||
variables: { data: { username, limit: 100, page: 1 } },
|
||||
});
|
||||
methods.value = (result.getPaymentMethods.items as unknown as IPaymentMethodLike[]) ?? [];
|
||||
if (!methods.value.length) {
|
||||
loadError.value = 'У получателя нет сохранённых реквизитов';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Ошибка загрузки платёжных методов:', e);
|
||||
loadError.value = 'Не удалось загрузить реквизиты получателя';
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Сокращённое отображение реквизитов платёжного метода для UI: телефон СБП —
|
||||
* со звёздочками, банковский счёт — банк + последние 4 цифры. Полные реквизиты
|
||||
* в интерфейсе не показываются — они подставляются сервером в документы.
|
||||
*/
|
||||
export interface IPaymentMethodLike {
|
||||
method_id: string | number;
|
||||
method_type: string;
|
||||
data: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export function formatPaymentMethodShort(method: IPaymentMethodLike): string {
|
||||
if (method.method_type === 'sbp') {
|
||||
const phone = String(method.data?.phone ?? '');
|
||||
const masked = phone.length > 4 ? `${phone.slice(0, 2)}***${phone.slice(-2)}` : phone;
|
||||
return `СБП (${masked})`;
|
||||
}
|
||||
const account = String(method.data?.account_number ?? '');
|
||||
const bank = String(method.data?.bank_name ?? '');
|
||||
const last4 = account.slice(-4);
|
||||
return bank ? `${bank} (***${last4})` : `Банковский счёт ***${last4}`;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as PaymentMethodSelect } from './PaymentMethodSelect.vue';
|
||||
export { formatPaymentMethodShort, type IPaymentMethodLike } from './formatPaymentMethod';
|
||||
@@ -14,6 +14,7 @@ export * from './FilterBar';
|
||||
export * from './IdentityPanel';
|
||||
export * from './NotificationCenter';
|
||||
export * from './OtpInput';
|
||||
export * from './PaymentMethodSelect';
|
||||
export * from './PersonCard';
|
||||
export * from './RailUserCard';
|
||||
export * from './SignatureCard';
|
||||
|
||||
@@ -81,6 +81,21 @@ export interface InterExpensePaginatedResult<T> {
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
|
||||
/** Строка СЗ для снимка реквизитов получателя (фиксация «куда платить» на момент создания). */
|
||||
export interface InterExpenseRequisiteItemInput {
|
||||
proposalHash: string;
|
||||
itemHash: string;
|
||||
/** Получатель: username пайщика (владелец платёжного метода) либо пусто для организации. */
|
||||
recipient: string;
|
||||
/** true — получатель-организация: реквизиты приходят строкой `requisites`. */
|
||||
isOrganization: boolean;
|
||||
/** Идентификатор платёжного метода получателя-пайщика. */
|
||||
paymentMethodId?: string;
|
||||
/** Реквизиты строкой (организации — ручной ввод из формы). */
|
||||
requisites?: string;
|
||||
}
|
||||
|
||||
export interface InterExpenseChassisPort {
|
||||
/**
|
||||
* Чтение proposal'а по хэшу. Возвращает null, если шасси не видит
|
||||
@@ -105,4 +120,17 @@ export interface InterExpenseChassisPort {
|
||||
ownerAction?: string,
|
||||
pagination?: InterExpensePagination,
|
||||
): Promise<InterExpensePaginatedResult<InterExpenseProposalRead>>;
|
||||
|
||||
/**
|
||||
* Валидация реквизитов строк СЗ ДО постановки on-chain заявки: у каждой
|
||||
* строки с получателем-пайщиком должен существовать указанный платёжный метод.
|
||||
*/
|
||||
validateRequisites(coopname: string, items: InterExpenseRequisiteItemInput[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Снимок реквизитов ПОСЛЕ успешной on-chain заявки: данные платёжного метода
|
||||
* копируются в хранилище шасси на момент создания СЗ — последующее изменение
|
||||
* метода пайщиком не меняет то, куда платить по уже поданной смете.
|
||||
*/
|
||||
snapshotRequisites(coopname: string, items: InterExpenseRequisiteItemInput[]): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -69,4 +69,5 @@ export type {
|
||||
InterExpensePaginatedResult,
|
||||
InterExpenseProposalRead,
|
||||
InterExpenseProposalStatus,
|
||||
InterExpenseRequisiteItemInput,
|
||||
} from './expense-chassis.port';
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { rawTransactionSelector } from '../../selectors'
|
||||
import { $, type GraphQLTypes, type InputType, type ModelTypes, Selector } from '../../zeus/index'
|
||||
|
||||
export const name = 'authorizeExpenseReport'
|
||||
|
||||
export const mutation = Selector('Mutation')({
|
||||
[name]: [{ data: $('data', 'AuthorizeExpenseReportInput!') }, rawTransactionSelector],
|
||||
})
|
||||
|
||||
export interface IInput {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
[key: string]: unknown
|
||||
|
||||
data: ModelTypes['AuthorizeExpenseReportInput']
|
||||
}
|
||||
|
||||
export type IOutput = InputType<GraphQLTypes['Mutation'], typeof mutation>
|
||||
@@ -1,19 +0,0 @@
|
||||
import { rawTransactionSelector } from '../../selectors'
|
||||
import { $, type GraphQLTypes, type InputType, type ModelTypes, Selector } from '../../zeus/index'
|
||||
|
||||
export const name = 'declineExpenseReport'
|
||||
|
||||
export const mutation = Selector('Mutation')({
|
||||
[name]: [{ data: $('data', 'DeclineExpenseReportInput!') }, rawTransactionSelector],
|
||||
})
|
||||
|
||||
export interface IInput {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
[key: string]: unknown
|
||||
|
||||
data: ModelTypes['DeclineExpenseReportInput']
|
||||
}
|
||||
|
||||
export type IOutput = InputType<GraphQLTypes['Mutation'], typeof mutation>
|
||||
@@ -6,5 +6,3 @@ export * as ReportExpenseItem from './reportExpenseItem'
|
||||
export * as ReturnExpenseItem from './returnExpenseItem'
|
||||
export * as OverspendExpenseItem from './overspendExpenseItem'
|
||||
export * as SubmitExpenseReport from './submitExpenseReport'
|
||||
export * as AuthorizeExpenseReport from './authorizeExpenseReport'
|
||||
export * as DeclineExpenseReport from './declineExpenseReport'
|
||||
|
||||
@@ -125,9 +125,6 @@ export const AllTypesProps: Record<string,any> = {
|
||||
AuthorizeDecisionInput:{
|
||||
document:"SignedDigitalDocumentInput"
|
||||
},
|
||||
AuthorizeExpenseReportInput:{
|
||||
decision:"ExpenseProposalDecisionSignedDocumentInput"
|
||||
},
|
||||
BankAccountDetailsInput:{
|
||||
|
||||
},
|
||||
@@ -396,9 +393,6 @@ export const AllTypesProps: Record<string,any> = {
|
||||
},
|
||||
DeclineDecisionInput:{
|
||||
|
||||
},
|
||||
DeclineExpenseReportInput:{
|
||||
|
||||
},
|
||||
DeclineRequestInput:{
|
||||
|
||||
@@ -472,15 +466,6 @@ export const AllTypesProps: Record<string,any> = {
|
||||
},
|
||||
ExpenseProposalDecisionItemInput:{
|
||||
|
||||
},
|
||||
ExpenseProposalDecisionSignedDocumentInput:{
|
||||
meta:"ExpenseProposalDecisionSignedMetaDocumentInput",
|
||||
signatures:"SignatureInfoInput"
|
||||
},
|
||||
ExpenseProposalDecisionSignedMetaDocumentInput:{
|
||||
decision:"ExpenseProposalDecisionBodyInput",
|
||||
items:"ExpenseProposalDecisionItemInput",
|
||||
proposal:"ExpenseProposalDecisionHeaderInput"
|
||||
},
|
||||
ExpenseProposalHeaderInput:{
|
||||
|
||||
@@ -727,9 +712,6 @@ export const AllTypesProps: Record<string,any> = {
|
||||
authorizeDecision:{
|
||||
data:"AuthorizeDecisionInput"
|
||||
},
|
||||
authorizeExpenseReport:{
|
||||
data:"AuthorizeExpenseReportInput"
|
||||
},
|
||||
cancelRequest:{
|
||||
data:"CancelRequestInput"
|
||||
},
|
||||
@@ -1073,9 +1055,6 @@ export const AllTypesProps: Record<string,any> = {
|
||||
declineDecision:{
|
||||
data:"DeclineDecisionInput"
|
||||
},
|
||||
declineExpenseReport:{
|
||||
data:"DeclineExpenseReportInput"
|
||||
},
|
||||
declineRequest:{
|
||||
data:"DeclineRequestInput"
|
||||
},
|
||||
@@ -3802,7 +3781,6 @@ export const ReturnTypes: Record<string,any> = {
|
||||
addPaymentMethod:"PaymentMethod",
|
||||
addTrustedAccount:"Branch",
|
||||
authorizeDecision:"Transaction",
|
||||
authorizeExpenseReport:"Transaction",
|
||||
cancelRequest:"Transaction",
|
||||
capitalAddAuthor:"CapitalProject",
|
||||
capitalApproveCommit:"CapitalCommit",
|
||||
@@ -3911,7 +3889,6 @@ export const ReturnTypes: Record<string,any> = {
|
||||
deactivateWebPushSubscriptionById:"Boolean",
|
||||
declineAgreement:"Transaction",
|
||||
declineDecision:"Transaction",
|
||||
declineExpenseReport:"Transaction",
|
||||
declineRequest:"Transaction",
|
||||
deleteAccount:"Boolean",
|
||||
deleteBranch:"Boolean",
|
||||
|
||||
@@ -1940,14 +1940,6 @@ export type ValueTypes = {
|
||||
decision_id: number | Variable<any, string>,
|
||||
/** Подписанный председателем документ утверждения решения */
|
||||
document: ValueTypes["SignedDigitalDocumentInput"] | Variable<any, string>
|
||||
};
|
||||
["AuthorizeExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string | Variable<any, string>,
|
||||
/** Подписанное решение совета (document2, registry 2011). */
|
||||
decision: ValueTypes["ExpenseProposalDecisionSignedDocumentInput"] | Variable<any, string>,
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string | Variable<any, string>
|
||||
};
|
||||
["AvailableReport"]: AliasType<{
|
||||
deadline?:boolean | `@${string}`,
|
||||
@@ -4924,14 +4916,6 @@ export type ValueTypes = {
|
||||
coopname: string | Variable<any, string>,
|
||||
/** Идентификатор решения */
|
||||
decision_id: number | Variable<any, string>
|
||||
};
|
||||
["DeclineExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string | Variable<any, string>,
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string | Variable<any, string>,
|
||||
/** Причина отклонения СЗ-отчёта (до 1000 символов). */
|
||||
reason: string | Variable<any, string>
|
||||
};
|
||||
["DeclineRequestInput"]: {
|
||||
/** Имя аккаунта кооператива */
|
||||
@@ -5296,12 +5280,16 @@ export type ValueTypes = {
|
||||
item_hash: string | Variable<any, string>,
|
||||
/** Способ оплаты (ADVANCE / DIRECT). */
|
||||
mechanics: ValueTypes["ExpenseMechanics"] | Variable<any, string>,
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — реквизиты снимаются в момент создания и прикладываются к платежу. */
|
||||
payment_method_id?: string | undefined | null | Variable<any, string>,
|
||||
/** Планируемая сумма (asset, eg "1000.0000 RUB"). */
|
||||
planned_amount: string | Variable<any, string>,
|
||||
/** Получатель (username / eosio::name организации). */
|
||||
recipient: string | Variable<any, string>,
|
||||
/** Тип получателя. */
|
||||
recipient_type: ValueTypes["ExpenseRecipientType"] | Variable<any, string>
|
||||
recipient_type: ValueTypes["ExpenseRecipientType"] | Variable<any, string>,
|
||||
/** Реквизиты получателя-организации (вводятся вручную). */
|
||||
requisites?: string | undefined | null | Variable<any, string>
|
||||
};
|
||||
/** Статус строки расхода. */
|
||||
["ExpenseItemStatus"]:ExpenseItemStatus;
|
||||
@@ -5404,52 +5392,6 @@ export type ValueTypes = {
|
||||
recipient_name?: string | undefined | null | Variable<any, string>,
|
||||
recipient_type: string | Variable<any, string>,
|
||||
requisites?: string | undefined | null | Variable<any, string>
|
||||
};
|
||||
["ExpenseProposalDecisionSignedDocumentInput"]: {
|
||||
/** Хэш содержимого документа */
|
||||
doc_hash: string | Variable<any, string>,
|
||||
/** Общий хэш (doc_hash + meta_hash) */
|
||||
hash: string | Variable<any, string>,
|
||||
/** Метаинформация решения по СЗ */
|
||||
meta: ValueTypes["ExpenseProposalDecisionSignedMetaDocumentInput"] | Variable<any, string>,
|
||||
/** Хэш мета-данных */
|
||||
meta_hash: string | Variable<any, string>,
|
||||
/** Вектор подписей */
|
||||
signatures: Array<ValueTypes["SignatureInfoInput"]> | Variable<any, string>,
|
||||
/** Версия стандарта документа */
|
||||
version: string | Variable<any, string>
|
||||
};
|
||||
["ExpenseProposalDecisionSignedMetaDocumentInput"]: {
|
||||
/** Номер блока, на котором был создан документ */
|
||||
block_num: number | Variable<any, string>,
|
||||
/** Название кооператива, связанное с документом */
|
||||
coopname: string | Variable<any, string>,
|
||||
/** Дата и время создания документа */
|
||||
created_at: string | Variable<any, string>,
|
||||
/** Тело решения */
|
||||
decision: ValueTypes["ExpenseProposalDecisionBodyInput"] | Variable<any, string>,
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator: string | Variable<any, string>,
|
||||
/** Позиции расхода */
|
||||
items: Array<ValueTypes["ExpenseProposalDecisionItemInput"]> | Variable<any, string>,
|
||||
/** Язык документа */
|
||||
lang: string | Variable<any, string>,
|
||||
/** Ссылки, связанные с документом */
|
||||
links: Array<string> | Variable<any, string>,
|
||||
/** Шапка СЗ */
|
||||
proposal: ValueTypes["ExpenseProposalDecisionHeaderInput"] | Variable<any, string>,
|
||||
/** Хеш сметы расхода */
|
||||
proposal_hash: string | Variable<any, string>,
|
||||
/** ID документа в реестре */
|
||||
registry_id: number | Variable<any, string>,
|
||||
/** Часовой пояс, в котором был создан документ */
|
||||
timezone: string | Variable<any, string>,
|
||||
/** Название документа */
|
||||
title: string | Variable<any, string>,
|
||||
/** Имя пользователя, создавшего документ */
|
||||
username: string | Variable<any, string>,
|
||||
/** Версия генератора, использованного для создания документа */
|
||||
version: string | Variable<any, string>
|
||||
};
|
||||
["ExpenseProposalHeaderInput"]: {
|
||||
/** Описание цели расходов */
|
||||
@@ -5470,10 +5412,14 @@ export type ValueTypes = {
|
||||
mechanics: string | Variable<any, string>,
|
||||
/** Порядковый номер строки */
|
||||
number: string | Variable<any, string>,
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — сервер подставит полные реквизиты в документ. */
|
||||
payment_method_id?: string | undefined | null | Variable<any, string>,
|
||||
/** Имя получателя */
|
||||
recipient_name?: string | undefined | null | Variable<any, string>,
|
||||
/** Тип получателя (SELF / MEMBER / ORG) */
|
||||
recipient_type: string | Variable<any, string>,
|
||||
/** Имя аккаунта получателя-пайщика (владелец реквизитов). */
|
||||
recipient_username?: string | undefined | null | Variable<any, string>,
|
||||
/** Реквизиты получателя */
|
||||
requisites?: string | undefined | null | Variable<any, string>
|
||||
};
|
||||
@@ -6890,7 +6836,6 @@ addParticipant?: [{ data: ValueTypes["AddParticipantInput"] | Variable<any, stri
|
||||
addPaymentMethod?: [{ data: ValueTypes["AddPaymentMethodInput"] | Variable<any, string>},ValueTypes["PaymentMethod"]],
|
||||
addTrustedAccount?: [{ data: ValueTypes["AddTrustedAccountInput"] | Variable<any, string>},ValueTypes["Branch"]],
|
||||
authorizeDecision?: [{ data: ValueTypes["AuthorizeDecisionInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
authorizeExpenseReport?: [{ data: ValueTypes["AuthorizeExpenseReportInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
cancelRequest?: [{ data: ValueTypes["CancelRequestInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
capitalAddAuthor?: [{ data: ValueTypes["AddAuthorInput"] | Variable<any, string>},ValueTypes["CapitalProject"]],
|
||||
capitalApproveCommit?: [{ data: ValueTypes["CommitApproveInput"] | Variable<any, string>},ValueTypes["CapitalCommit"]],
|
||||
@@ -7002,7 +6947,6 @@ createWithdraw?: [{ data: ValueTypes["CreateWithdrawInput"] | Variable<any, stri
|
||||
deactivateWebPushSubscriptionById?: [{ data: ValueTypes["DeactivateSubscriptionInput"] | Variable<any, string>},boolean | `@${string}`],
|
||||
declineAgreement?: [{ data: ValueTypes["DeclineAgreementInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
declineDecision?: [{ data: ValueTypes["DeclineDecisionInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
declineExpenseReport?: [{ data: ValueTypes["DeclineExpenseReportInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
declineRequest?: [{ data: ValueTypes["DeclineRequestInput"] | Variable<any, string>},ValueTypes["Transaction"]],
|
||||
deleteAccount?: [{ data: ValueTypes["DeleteAccountInput"] | Variable<any, string>},boolean | `@${string}`],
|
||||
deleteBranch?: [{ data: ValueTypes["DeleteBranchInput"] | Variable<any, string>},boolean | `@${string}`],
|
||||
@@ -11111,14 +11055,6 @@ export type ResolverInputTypes = {
|
||||
decision_id: number,
|
||||
/** Подписанный председателем документ утверждения решения */
|
||||
document: ResolverInputTypes["SignedDigitalDocumentInput"]
|
||||
};
|
||||
["AuthorizeExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string,
|
||||
/** Подписанное решение совета (document2, registry 2011). */
|
||||
decision: ResolverInputTypes["ExpenseProposalDecisionSignedDocumentInput"],
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string
|
||||
};
|
||||
["AvailableReport"]: AliasType<{
|
||||
deadline?:boolean | `@${string}`,
|
||||
@@ -14018,14 +13954,6 @@ export type ResolverInputTypes = {
|
||||
coopname: string,
|
||||
/** Идентификатор решения */
|
||||
decision_id: number
|
||||
};
|
||||
["DeclineExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string,
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string,
|
||||
/** Причина отклонения СЗ-отчёта (до 1000 символов). */
|
||||
reason: string
|
||||
};
|
||||
["DeclineRequestInput"]: {
|
||||
/** Имя аккаунта кооператива */
|
||||
@@ -14378,12 +14306,16 @@ export type ResolverInputTypes = {
|
||||
item_hash: string,
|
||||
/** Способ оплаты (ADVANCE / DIRECT). */
|
||||
mechanics: ResolverInputTypes["ExpenseMechanics"],
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — реквизиты снимаются в момент создания и прикладываются к платежу. */
|
||||
payment_method_id?: string | undefined | null,
|
||||
/** Планируемая сумма (asset, eg "1000.0000 RUB"). */
|
||||
planned_amount: string,
|
||||
/** Получатель (username / eosio::name организации). */
|
||||
recipient: string,
|
||||
/** Тип получателя. */
|
||||
recipient_type: ResolverInputTypes["ExpenseRecipientType"]
|
||||
recipient_type: ResolverInputTypes["ExpenseRecipientType"],
|
||||
/** Реквизиты получателя-организации (вводятся вручную). */
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
/** Статус строки расхода. */
|
||||
["ExpenseItemStatus"]:ExpenseItemStatus;
|
||||
@@ -14485,52 +14417,6 @@ export type ResolverInputTypes = {
|
||||
recipient_name?: string | undefined | null,
|
||||
recipient_type: string,
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
["ExpenseProposalDecisionSignedDocumentInput"]: {
|
||||
/** Хэш содержимого документа */
|
||||
doc_hash: string,
|
||||
/** Общий хэш (doc_hash + meta_hash) */
|
||||
hash: string,
|
||||
/** Метаинформация решения по СЗ */
|
||||
meta: ResolverInputTypes["ExpenseProposalDecisionSignedMetaDocumentInput"],
|
||||
/** Хэш мета-данных */
|
||||
meta_hash: string,
|
||||
/** Вектор подписей */
|
||||
signatures: Array<ResolverInputTypes["SignatureInfoInput"]>,
|
||||
/** Версия стандарта документа */
|
||||
version: string
|
||||
};
|
||||
["ExpenseProposalDecisionSignedMetaDocumentInput"]: {
|
||||
/** Номер блока, на котором был создан документ */
|
||||
block_num: number,
|
||||
/** Название кооператива, связанное с документом */
|
||||
coopname: string,
|
||||
/** Дата и время создания документа */
|
||||
created_at: string,
|
||||
/** Тело решения */
|
||||
decision: ResolverInputTypes["ExpenseProposalDecisionBodyInput"],
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator: string,
|
||||
/** Позиции расхода */
|
||||
items: Array<ResolverInputTypes["ExpenseProposalDecisionItemInput"]>,
|
||||
/** Язык документа */
|
||||
lang: string,
|
||||
/** Ссылки, связанные с документом */
|
||||
links: Array<string>,
|
||||
/** Шапка СЗ */
|
||||
proposal: ResolverInputTypes["ExpenseProposalDecisionHeaderInput"],
|
||||
/** Хеш сметы расхода */
|
||||
proposal_hash: string,
|
||||
/** ID документа в реестре */
|
||||
registry_id: number,
|
||||
/** Часовой пояс, в котором был создан документ */
|
||||
timezone: string,
|
||||
/** Название документа */
|
||||
title: string,
|
||||
/** Имя пользователя, создавшего документ */
|
||||
username: string,
|
||||
/** Версия генератора, использованного для создания документа */
|
||||
version: string
|
||||
};
|
||||
["ExpenseProposalHeaderInput"]: {
|
||||
/** Описание цели расходов */
|
||||
@@ -14551,10 +14437,14 @@ export type ResolverInputTypes = {
|
||||
mechanics: string,
|
||||
/** Порядковый номер строки */
|
||||
number: string,
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — сервер подставит полные реквизиты в документ. */
|
||||
payment_method_id?: string | undefined | null,
|
||||
/** Имя получателя */
|
||||
recipient_name?: string | undefined | null,
|
||||
/** Тип получателя (SELF / MEMBER / ORG) */
|
||||
recipient_type: string,
|
||||
/** Имя аккаунта получателя-пайщика (владелец реквизитов). */
|
||||
recipient_username?: string | undefined | null,
|
||||
/** Реквизиты получателя */
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
@@ -15932,7 +15822,6 @@ addParticipant?: [{ data: ResolverInputTypes["AddParticipantInput"]},ResolverInp
|
||||
addPaymentMethod?: [{ data: ResolverInputTypes["AddPaymentMethodInput"]},ResolverInputTypes["PaymentMethod"]],
|
||||
addTrustedAccount?: [{ data: ResolverInputTypes["AddTrustedAccountInput"]},ResolverInputTypes["Branch"]],
|
||||
authorizeDecision?: [{ data: ResolverInputTypes["AuthorizeDecisionInput"]},ResolverInputTypes["Transaction"]],
|
||||
authorizeExpenseReport?: [{ data: ResolverInputTypes["AuthorizeExpenseReportInput"]},ResolverInputTypes["Transaction"]],
|
||||
cancelRequest?: [{ data: ResolverInputTypes["CancelRequestInput"]},ResolverInputTypes["Transaction"]],
|
||||
capitalAddAuthor?: [{ data: ResolverInputTypes["AddAuthorInput"]},ResolverInputTypes["CapitalProject"]],
|
||||
capitalApproveCommit?: [{ data: ResolverInputTypes["CommitApproveInput"]},ResolverInputTypes["CapitalCommit"]],
|
||||
@@ -16044,7 +15933,6 @@ createWithdraw?: [{ data: ResolverInputTypes["CreateWithdrawInput"]},ResolverInp
|
||||
deactivateWebPushSubscriptionById?: [{ data: ResolverInputTypes["DeactivateSubscriptionInput"]},boolean | `@${string}`],
|
||||
declineAgreement?: [{ data: ResolverInputTypes["DeclineAgreementInput"]},ResolverInputTypes["Transaction"]],
|
||||
declineDecision?: [{ data: ResolverInputTypes["DeclineDecisionInput"]},ResolverInputTypes["Transaction"]],
|
||||
declineExpenseReport?: [{ data: ResolverInputTypes["DeclineExpenseReportInput"]},ResolverInputTypes["Transaction"]],
|
||||
declineRequest?: [{ data: ResolverInputTypes["DeclineRequestInput"]},ResolverInputTypes["Transaction"]],
|
||||
deleteAccount?: [{ data: ResolverInputTypes["DeleteAccountInput"]},boolean | `@${string}`],
|
||||
deleteBranch?: [{ data: ResolverInputTypes["DeleteBranchInput"]},boolean | `@${string}`],
|
||||
@@ -20033,14 +19921,6 @@ export type ModelTypes = {
|
||||
decision_id: number,
|
||||
/** Подписанный председателем документ утверждения решения */
|
||||
document: ModelTypes["SignedDigitalDocumentInput"]
|
||||
};
|
||||
["AuthorizeExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string,
|
||||
/** Подписанное решение совета (document2, registry 2011). */
|
||||
decision: ModelTypes["ExpenseProposalDecisionSignedDocumentInput"],
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string
|
||||
};
|
||||
["AvailableReport"]: {
|
||||
deadline: string,
|
||||
@@ -22855,14 +22735,6 @@ export type ModelTypes = {
|
||||
coopname: string,
|
||||
/** Идентификатор решения */
|
||||
decision_id: number
|
||||
};
|
||||
["DeclineExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string,
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string,
|
||||
/** Причина отклонения СЗ-отчёта (до 1000 символов). */
|
||||
reason: string
|
||||
};
|
||||
["DeclineRequestInput"]: {
|
||||
/** Имя аккаунта кооператива */
|
||||
@@ -23201,12 +23073,16 @@ export type ModelTypes = {
|
||||
item_hash: string,
|
||||
/** Способ оплаты (ADVANCE / DIRECT). */
|
||||
mechanics: ModelTypes["ExpenseMechanics"],
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — реквизиты снимаются в момент создания и прикладываются к платежу. */
|
||||
payment_method_id?: string | undefined | null,
|
||||
/** Планируемая сумма (asset, eg "1000.0000 RUB"). */
|
||||
planned_amount: string,
|
||||
/** Получатель (username / eosio::name организации). */
|
||||
recipient: string,
|
||||
/** Тип получателя. */
|
||||
recipient_type: ModelTypes["ExpenseRecipientType"]
|
||||
recipient_type: ModelTypes["ExpenseRecipientType"],
|
||||
/** Реквизиты получателя-организации (вводятся вручную). */
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
["ExpenseItemStatus"]:ExpenseItemStatus;
|
||||
["ExpenseMechanics"]:ExpenseMechanics;
|
||||
@@ -23305,52 +23181,6 @@ export type ModelTypes = {
|
||||
recipient_name?: string | undefined | null,
|
||||
recipient_type: string,
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
["ExpenseProposalDecisionSignedDocumentInput"]: {
|
||||
/** Хэш содержимого документа */
|
||||
doc_hash: string,
|
||||
/** Общий хэш (doc_hash + meta_hash) */
|
||||
hash: string,
|
||||
/** Метаинформация решения по СЗ */
|
||||
meta: ModelTypes["ExpenseProposalDecisionSignedMetaDocumentInput"],
|
||||
/** Хэш мета-данных */
|
||||
meta_hash: string,
|
||||
/** Вектор подписей */
|
||||
signatures: Array<ModelTypes["SignatureInfoInput"]>,
|
||||
/** Версия стандарта документа */
|
||||
version: string
|
||||
};
|
||||
["ExpenseProposalDecisionSignedMetaDocumentInput"]: {
|
||||
/** Номер блока, на котором был создан документ */
|
||||
block_num: number,
|
||||
/** Название кооператива, связанное с документом */
|
||||
coopname: string,
|
||||
/** Дата и время создания документа */
|
||||
created_at: string,
|
||||
/** Тело решения */
|
||||
decision: ModelTypes["ExpenseProposalDecisionBodyInput"],
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator: string,
|
||||
/** Позиции расхода */
|
||||
items: Array<ModelTypes["ExpenseProposalDecisionItemInput"]>,
|
||||
/** Язык документа */
|
||||
lang: string,
|
||||
/** Ссылки, связанные с документом */
|
||||
links: Array<string>,
|
||||
/** Шапка СЗ */
|
||||
proposal: ModelTypes["ExpenseProposalDecisionHeaderInput"],
|
||||
/** Хеш сметы расхода */
|
||||
proposal_hash: string,
|
||||
/** ID документа в реестре */
|
||||
registry_id: number,
|
||||
/** Часовой пояс, в котором был создан документ */
|
||||
timezone: string,
|
||||
/** Название документа */
|
||||
title: string,
|
||||
/** Имя пользователя, создавшего документ */
|
||||
username: string,
|
||||
/** Версия генератора, использованного для создания документа */
|
||||
version: string
|
||||
};
|
||||
["ExpenseProposalHeaderInput"]: {
|
||||
/** Описание цели расходов */
|
||||
@@ -23371,10 +23201,14 @@ export type ModelTypes = {
|
||||
mechanics: string,
|
||||
/** Порядковый номер строки */
|
||||
number: string,
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — сервер подставит полные реквизиты в документ. */
|
||||
payment_method_id?: string | undefined | null,
|
||||
/** Имя получателя */
|
||||
recipient_name?: string | undefined | null,
|
||||
/** Тип получателя (SELF / MEMBER / ORG) */
|
||||
recipient_type: string,
|
||||
/** Имя аккаунта получателя-пайщика (владелец реквизитов). */
|
||||
recipient_username?: string | undefined | null,
|
||||
/** Реквизиты получателя */
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
@@ -24713,10 +24547,6 @@ export type ModelTypes = {
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
authorizeDecision: ModelTypes["Transaction"],
|
||||
/** Утвердить СЗ-отчёт (закрытие сметы). Триггерит капитализацию РИД в Благоросте.
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
authorizeExpenseReport: ModelTypes["Transaction"],
|
||||
/** Отменить заявку */
|
||||
cancelRequest: ModelTypes["Transaction"],
|
||||
/** Добавление автора проекта в CAPITAL контракте
|
||||
@@ -25133,10 +24963,6 @@ export type ModelTypes = {
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
declineDecision: ModelTypes["Transaction"],
|
||||
/** Отклонить СЗ-отчёт с указанием причины. Смета переходит в DECLINED.
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
declineExpenseReport: ModelTypes["Transaction"],
|
||||
/** Отклонить заявку */
|
||||
declineRequest: ModelTypes["Transaction"],
|
||||
/** Удалить аккаунт пайщика из системы учёта провайдера. Доступно только для незавершённых регистрационных статусов (черновик, неоплачен/отклонён). Активный, заблокированный и любой зарегистрированный в блокчейне аккаунт удалить нельзя. Используется для очистки реестра и освобождения e-mail под перерегистрацию.
|
||||
@@ -29461,14 +29287,6 @@ export type GraphQLTypes = {
|
||||
decision_id: number,
|
||||
/** Подписанный председателем документ утверждения решения */
|
||||
document: GraphQLTypes["SignedDigitalDocumentInput"]
|
||||
};
|
||||
["AuthorizeExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string,
|
||||
/** Подписанное решение совета (document2, registry 2011). */
|
||||
decision: GraphQLTypes["ExpenseProposalDecisionSignedDocumentInput"],
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string
|
||||
};
|
||||
["AvailableReport"]: {
|
||||
__typename: "AvailableReport",
|
||||
@@ -32445,14 +32263,6 @@ export type GraphQLTypes = {
|
||||
coopname: string,
|
||||
/** Идентификатор решения */
|
||||
decision_id: number
|
||||
};
|
||||
["DeclineExpenseReportInput"]: {
|
||||
/** Имя кооператива. */
|
||||
coopname: string,
|
||||
/** Хеш сметы расхода. */
|
||||
proposal_hash: string,
|
||||
/** Причина отклонения СЗ-отчёта (до 1000 символов). */
|
||||
reason: string
|
||||
};
|
||||
["DeclineRequestInput"]: {
|
||||
/** Имя аккаунта кооператива */
|
||||
@@ -32817,12 +32627,16 @@ export type GraphQLTypes = {
|
||||
item_hash: string,
|
||||
/** Способ оплаты (ADVANCE / DIRECT). */
|
||||
mechanics: GraphQLTypes["ExpenseMechanics"],
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — реквизиты снимаются в момент создания и прикладываются к платежу. */
|
||||
payment_method_id?: string | undefined | null,
|
||||
/** Планируемая сумма (asset, eg "1000.0000 RUB"). */
|
||||
planned_amount: string,
|
||||
/** Получатель (username / eosio::name организации). */
|
||||
recipient: string,
|
||||
/** Тип получателя. */
|
||||
recipient_type: GraphQLTypes["ExpenseRecipientType"]
|
||||
recipient_type: GraphQLTypes["ExpenseRecipientType"],
|
||||
/** Реквизиты получателя-организации (вводятся вручную). */
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
/** Статус строки расхода. */
|
||||
["ExpenseItemStatus"]: ExpenseItemStatus;
|
||||
@@ -32925,52 +32739,6 @@ export type GraphQLTypes = {
|
||||
recipient_name?: string | undefined | null,
|
||||
recipient_type: string,
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
["ExpenseProposalDecisionSignedDocumentInput"]: {
|
||||
/** Хэш содержимого документа */
|
||||
doc_hash: string,
|
||||
/** Общий хэш (doc_hash + meta_hash) */
|
||||
hash: string,
|
||||
/** Метаинформация решения по СЗ */
|
||||
meta: GraphQLTypes["ExpenseProposalDecisionSignedMetaDocumentInput"],
|
||||
/** Хэш мета-данных */
|
||||
meta_hash: string,
|
||||
/** Вектор подписей */
|
||||
signatures: Array<GraphQLTypes["SignatureInfoInput"]>,
|
||||
/** Версия стандарта документа */
|
||||
version: string
|
||||
};
|
||||
["ExpenseProposalDecisionSignedMetaDocumentInput"]: {
|
||||
/** Номер блока, на котором был создан документ */
|
||||
block_num: number,
|
||||
/** Название кооператива, связанное с документом */
|
||||
coopname: string,
|
||||
/** Дата и время создания документа */
|
||||
created_at: string,
|
||||
/** Тело решения */
|
||||
decision: GraphQLTypes["ExpenseProposalDecisionBodyInput"],
|
||||
/** Имя генератора, использованного для создания документа */
|
||||
generator: string,
|
||||
/** Позиции расхода */
|
||||
items: Array<GraphQLTypes["ExpenseProposalDecisionItemInput"]>,
|
||||
/** Язык документа */
|
||||
lang: string,
|
||||
/** Ссылки, связанные с документом */
|
||||
links: Array<string>,
|
||||
/** Шапка СЗ */
|
||||
proposal: GraphQLTypes["ExpenseProposalDecisionHeaderInput"],
|
||||
/** Хеш сметы расхода */
|
||||
proposal_hash: string,
|
||||
/** ID документа в реестре */
|
||||
registry_id: number,
|
||||
/** Часовой пояс, в котором был создан документ */
|
||||
timezone: string,
|
||||
/** Название документа */
|
||||
title: string,
|
||||
/** Имя пользователя, создавшего документ */
|
||||
username: string,
|
||||
/** Версия генератора, использованного для создания документа */
|
||||
version: string
|
||||
};
|
||||
["ExpenseProposalHeaderInput"]: {
|
||||
/** Описание цели расходов */
|
||||
@@ -32991,10 +32759,14 @@ export type GraphQLTypes = {
|
||||
mechanics: string,
|
||||
/** Порядковый номер строки */
|
||||
number: string,
|
||||
/** Идентификатор сохранённых реквизитов получателя-пайщика — сервер подставит полные реквизиты в документ. */
|
||||
payment_method_id?: string | undefined | null,
|
||||
/** Имя получателя */
|
||||
recipient_name?: string | undefined | null,
|
||||
/** Тип получателя (SELF / MEMBER / ORG) */
|
||||
recipient_type: string,
|
||||
/** Имя аккаунта получателя-пайщика (владелец реквизитов). */
|
||||
recipient_username?: string | undefined | null,
|
||||
/** Реквизиты получателя */
|
||||
requisites?: string | undefined | null
|
||||
};
|
||||
@@ -34423,10 +34195,6 @@ export type GraphQLTypes = {
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
authorizeDecision: GraphQLTypes["Transaction"],
|
||||
/** Утвердить СЗ-отчёт (закрытие сметы). Триггерит капитализацию РИД в Благоросте.
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
authorizeExpenseReport: GraphQLTypes["Transaction"],
|
||||
/** Отменить заявку */
|
||||
cancelRequest: GraphQLTypes["Transaction"],
|
||||
/** Добавление автора проекта в CAPITAL контракте
|
||||
@@ -34843,10 +34611,6 @@ export type GraphQLTypes = {
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
declineDecision: GraphQLTypes["Transaction"],
|
||||
/** Отклонить СЗ-отчёт с указанием причины. Смета переходит в DECLINED.
|
||||
|
||||
Требуемые роли: chairman. */
|
||||
declineExpenseReport: GraphQLTypes["Transaction"],
|
||||
/** Отклонить заявку */
|
||||
declineRequest: GraphQLTypes["Transaction"],
|
||||
/** Удалить аккаунт пайщика из системы учёта провайдера. Доступно только для незавершённых регистрационных статусов (черновик, неоплачен/отклонён). Активный, заблокированный и любой зарегистрированный в блокчейне аккаунт удалить нельзя. Используется для очистки реестра и освобождения e-mail под перерегистрацию.
|
||||
@@ -39065,7 +38829,6 @@ type ZEUS_VARIABLES = {
|
||||
["AssetContributionStatementSignedDocumentInput"]: ValueTypes["AssetContributionStatementSignedDocumentInput"];
|
||||
["AssetContributionStatementSignedMetaDocumentInput"]: ValueTypes["AssetContributionStatementSignedMetaDocumentInput"];
|
||||
["AuthorizeDecisionInput"]: ValueTypes["AuthorizeDecisionInput"];
|
||||
["AuthorizeExpenseReportInput"]: ValueTypes["AuthorizeExpenseReportInput"];
|
||||
["BankAccountDetailsInput"]: ValueTypes["BankAccountDetailsInput"];
|
||||
["BankAccountInput"]: ValueTypes["BankAccountInput"];
|
||||
["BuhotchSignerType"]: ValueTypes["BuhotchSignerType"];
|
||||
@@ -39155,7 +38918,6 @@ type ZEUS_VARIABLES = {
|
||||
["DeclineAgreementInput"]: ValueTypes["DeclineAgreementInput"];
|
||||
["DeclineApproveInput"]: ValueTypes["DeclineApproveInput"];
|
||||
["DeclineDecisionInput"]: ValueTypes["DeclineDecisionInput"];
|
||||
["DeclineExpenseReportInput"]: ValueTypes["DeclineExpenseReportInput"];
|
||||
["DeclineRequestInput"]: ValueTypes["DeclineRequestInput"];
|
||||
["DeleteAccountInput"]: ValueTypes["DeleteAccountInput"];
|
||||
["DeleteBranchInput"]: ValueTypes["DeleteBranchInput"];
|
||||
@@ -39182,8 +38944,6 @@ type ZEUS_VARIABLES = {
|
||||
["ExpenseProposalDecisionGenerateDocumentInput"]: ValueTypes["ExpenseProposalDecisionGenerateDocumentInput"];
|
||||
["ExpenseProposalDecisionHeaderInput"]: ValueTypes["ExpenseProposalDecisionHeaderInput"];
|
||||
["ExpenseProposalDecisionItemInput"]: ValueTypes["ExpenseProposalDecisionItemInput"];
|
||||
["ExpenseProposalDecisionSignedDocumentInput"]: ValueTypes["ExpenseProposalDecisionSignedDocumentInput"];
|
||||
["ExpenseProposalDecisionSignedMetaDocumentInput"]: ValueTypes["ExpenseProposalDecisionSignedMetaDocumentInput"];
|
||||
["ExpenseProposalHeaderInput"]: ValueTypes["ExpenseProposalHeaderInput"];
|
||||
["ExpenseProposalItemInput"]: ValueTypes["ExpenseProposalItemInput"];
|
||||
["ExpenseProposalStatementGenerateDocumentInput"]: ValueTypes["ExpenseProposalStatementGenerateDocumentInput"];
|
||||
|
||||
Reference in New Issue
Block a user