fix(registration): resolve doc_data_hash for extension offers via registry spec
Шаблоны оферт ЦПП (#996/#1000) с c7824545cf требуют PrivateData, но общий
путь регистрации (RegistrationDocumentsService) не передавал doc_data_hash —
регистрация пайщика по GENERATION/CAPITALIZATION падала на генерации превью.
Ядро остаётся слепым к расширениям: AgreementRegistrationSpec получает
опциональный resolve_doc_data_hash, который расширение-владелец прикрепляет
к своим офертам при регистрации в реестре. Capital резолвит hash свежим
чтением своего конфига на каждую генерацию (совет может пересохранить
параметры без перезапуска). Гранулярность — per-оферта: при разделении
параметров по программам ядро менять не потребуется.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,14 @@ export interface IAgreementConfigItem {
|
||||
* Порядок отображения на фронтенде (сортировка)
|
||||
*/
|
||||
order: number;
|
||||
|
||||
/**
|
||||
* Резолвер hash'а приватных данных документа (doc_data_hash), если шаблон
|
||||
* оферты на фабрике требует PrivateData. Заполняется только для
|
||||
* extension-оферт из AgreementRegistrationSpec; платформенные соглашения
|
||||
* его не имеют. В GraphQL DTO не попадает (поля мапятся декораторами).
|
||||
*/
|
||||
resolve_doc_data_hash?: () => Promise<string | undefined>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -55,4 +55,18 @@ export interface AgreementRegistrationSpec {
|
||||
* реестр удаляет все записи с этим extension_name.
|
||||
*/
|
||||
extension_name: string;
|
||||
|
||||
/**
|
||||
* Опциональный резолвер hash'а приватных данных документа (doc_data_hash).
|
||||
* Если шаблон оферты на фабрике требует PrivateData (например, параметры
|
||||
* ЦПП, утверждённые советом), расширение предоставляет резолвер — ядро
|
||||
* вызывает его перед каждой генерацией документа этой оферты и передаёт
|
||||
* результат в фабрику как doc_data_hash.
|
||||
*
|
||||
* Резолвер вызывается на каждую генерацию (не кэшируется): источник
|
||||
* значения — конфигурация расширения, которая может обновляться без
|
||||
* перезапуска (совет пересохранил параметры программ). Ядро не знает,
|
||||
* откуда берётся hash — это контракт расширения с его фабричным шаблоном.
|
||||
*/
|
||||
resolve_doc_data_hash?: () => Promise<string | undefined>;
|
||||
}
|
||||
|
||||
+1
@@ -50,6 +50,7 @@ export class AgreementConfigurationService {
|
||||
link_to_statement: true,
|
||||
applicable_account_types: spec.applicable_account_types,
|
||||
order: spec.order,
|
||||
resolve_doc_data_hash: spec.resolve_doc_data_hash,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -132,11 +132,17 @@ export class RegistrationDocumentsService {
|
||||
): Promise<IGeneratedRegistrationDocument> {
|
||||
this.logger.debug(`Генерация документа: ${config.id} (registry_id=${config.registry_id})`);
|
||||
|
||||
// Если шаблон оферты требует PrivateData, расширение-владелец предоставило
|
||||
// резолвер hash'а в спеке регистрации — ядро не знает ни источника значения,
|
||||
// ни того, какие registry_id этого требуют.
|
||||
const doc_data_hash = config.resolve_doc_data_hash ? await config.resolve_doc_data_hash() : undefined;
|
||||
|
||||
const document = await this.documentInteractor.generateDocument({
|
||||
data: {
|
||||
coopname,
|
||||
username,
|
||||
registry_id: config.registry_id,
|
||||
...(doc_data_hash ? { doc_data_hash } : {}),
|
||||
},
|
||||
options: {
|
||||
skip_save: false, // Сохраняем документ в базу для последующей сверки
|
||||
|
||||
+10
-1
@@ -26,10 +26,17 @@ import type { IConfig } from '../../capital-extension.module';
|
||||
* • при завершённом L1 — две оферты (generator/blagorost) и две
|
||||
* программы (generation/capitalization);
|
||||
* • идемпотентность гарантируется AgreementRegistryService.
|
||||
*
|
||||
* `resolveDocDataHash` — резолвер hash'а PrivateData параметров ЦПП,
|
||||
* прикрепляется к обеим офертам: их фабричные шаблоны (#996/#1000) требуют
|
||||
* doc_data. Сейчас все документы ЦПП читают единый набор параметров
|
||||
* (capital_program_doc_data_hash); если параметры разделятся по программам,
|
||||
* каждая спека получит собственный резолвер — ядро менять не потребуется.
|
||||
*/
|
||||
export function registerCapitalInAgreementRegistry(
|
||||
port: AgreementRegistrationPort,
|
||||
extensionConfig: IConfig
|
||||
extensionConfig: IConfig,
|
||||
resolveDocDataHash?: () => Promise<string | undefined>
|
||||
): boolean {
|
||||
const onboardingDone =
|
||||
extensionConfig.onboarding_generator_program_template_done &&
|
||||
@@ -52,6 +59,7 @@ export function registerCapitalInAgreementRegistry(
|
||||
applicable_account_types: [],
|
||||
order: 6,
|
||||
extension_name: CAPITAL_EXTENSION_NAME,
|
||||
resolve_doc_data_hash: resolveDocDataHash,
|
||||
});
|
||||
|
||||
port.registerAgreement({
|
||||
@@ -70,6 +78,7 @@ export function registerCapitalInAgreementRegistry(
|
||||
applicable_account_types: [],
|
||||
order: 5,
|
||||
extension_name: CAPITAL_EXTENSION_NAME,
|
||||
resolve_doc_data_hash: resolveDocDataHash,
|
||||
});
|
||||
|
||||
port.registerProgram({
|
||||
|
||||
@@ -654,7 +654,25 @@ export class CapitalPlugin extends BaseExtModule {
|
||||
// Если L1 ещё не завершён — реестр остаётся пустым, SignUp не предлагает
|
||||
// программы capital (раздел 4.1 req 44 проекта 13).
|
||||
try {
|
||||
const registered = registerCapitalInAgreementRegistry(this.agreementRegistrationPort, extensionConfig as IConfig);
|
||||
// Резолвер читает конфиг заново на каждую генерацию: совет может
|
||||
// пересохранить параметры ЦПП (новый hash) без перезапуска расширения,
|
||||
// а спека в реестре регистрируется один раз на initialize.
|
||||
const resolveCapitalProgramDocDataHash = async (): Promise<string | undefined> => {
|
||||
const ext = await this.extensionRepository.findByName(this.name);
|
||||
const hash = ext?.config?.capital_program_doc_data_hash?.trim();
|
||||
if (!hash) {
|
||||
throw new Error(
|
||||
'Параметры документов ЦПП не заполнены: отсутствует capital_program_doc_data_hash в конфигурации capital'
|
||||
);
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
const registered = registerCapitalInAgreementRegistry(
|
||||
this.agreementRegistrationPort,
|
||||
extensionConfig as IConfig,
|
||||
resolveCapitalProgramDocDataHash
|
||||
);
|
||||
if (registered) {
|
||||
this.logger.log('[CAPITAL.REGISTRY] зарегистрировано 2 оферты + 2 программы capital');
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user