fix(capital): заводить доли только в active-проектах, не в pending
По решению пользователя заход долей Благороста — только в активные проекты. В pending больше не заводим: при переходе pending→active придёт новая дельта capital::projects со status=active, на ней листенер и зарегистрирует. - Листенер ProgramShareRegistrationOnProjectDeltaListener: гейт pending|active → только active. - ProgramShareRegistrationService.findActiveProjects (путь крона и wallet-листенера): фильтр pending|active → только active. - Тест: pending теперь НЕ реагирует; active — позитивный кейс. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+9
-6
@@ -7,9 +7,11 @@ import { ProjectStatus } from '../../domain/enums/project-status.enum';
|
||||
import { ProgramShareRegistrationService } from '../services/program-share-registration.service';
|
||||
|
||||
/**
|
||||
* Слушатель дельт `capital::projects`. Как только в блокчейне появляется проект
|
||||
* (в т.ч. компонент) в статусе pending|active — СРАЗУ регистрирует доли всех
|
||||
* активных пайщиков Благороста в нём, не дожидаясь периодического scheduler'а.
|
||||
* Слушатель дельт `capital::projects`. Как только проект (в т.ч. компонент)
|
||||
* оказывается в статусе active — СРАЗУ регистрирует доли всех активных пайщиков
|
||||
* Благороста в нём, не дожидаясь периодического scheduler'а. В pending не
|
||||
* заводим: заход только в активные проекты (решение пользователя 2026-06-16);
|
||||
* при переходе pending→active придёт новая дельта со status=active.
|
||||
*
|
||||
* Why: окно между созданием проекта и его переводом в `result` может быть
|
||||
* минутами. Контракт `regshare` принимает только pending|active, а отката
|
||||
@@ -39,10 +41,11 @@ export class ProgramShareRegistrationOnProjectDeltaListener {
|
||||
const value = delta.value as CapitalContract.Tables.Projects.IProject | undefined;
|
||||
if (!value?.project_hash) return;
|
||||
|
||||
// regshare разрешён только для pending|active — на прочих статусах окно уже
|
||||
// закрыто (on-chain-проверка всё равно отклонит), реагировать незачем.
|
||||
// Заводим доли только в active-проектах: pending ещё не готов к заходу
|
||||
// (решение пользователя 2026-06-16). При переходе pending→active придёт
|
||||
// новая дельта со status=active — на ней и зайдём.
|
||||
const status = String(value.status);
|
||||
if (status !== ProjectStatus.PENDING && status !== ProjectStatus.ACTIVE) return;
|
||||
if (status !== ProjectStatus.ACTIVE) return;
|
||||
|
||||
const project_hash = String(value.project_hash);
|
||||
try {
|
||||
|
||||
+8
-6
@@ -47,12 +47,12 @@ export class ProgramShareRegistrationService {
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Обход участников в статусах active/import, проектов pending/active; при изменении user_shares относительно capital_contributor_shares — regshare.
|
||||
* Обход участников в статусах active/import по active-проектам; при изменении user_shares относительно capital_contributor_shares — regshare.
|
||||
*/
|
||||
async syncProgramSharesForCoop(coopname: string): Promise<void> {
|
||||
const projects = await this.findActiveProjects(coopname);
|
||||
if (projects.length === 0) {
|
||||
this.logger.debug(`Синхронизация regshare: нет проектов в статусах pending/active для ${coopname}`);
|
||||
this.logger.debug(`Синхронизация regshare: нет active-проектов для ${coopname}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export class ProgramShareRegistrationService {
|
||||
/**
|
||||
* Точечная синхронизация regshare для одного пайщика — вызывается из listener'а
|
||||
* на дельты `ledger2::userwallets[w.cap.blago]`. Не пишет лог, если у пайщика
|
||||
* нет ни одного pending/active проекта.
|
||||
* нет ни одного active-проекта.
|
||||
*/
|
||||
async syncProgramSharesForUser(coopname: string, username: string): Promise<void> {
|
||||
const projects = await this.findActiveProjects(coopname);
|
||||
@@ -114,11 +114,13 @@ export class ProgramShareRegistrationService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Только active-проекты: заход долей в pending отключён (решение пользователя
|
||||
* 2026-06-16) — заводим/сверяем доли лишь в активных проектах.
|
||||
*/
|
||||
private async findActiveProjects(coopname: string): Promise<ProjectDomainEntity[]> {
|
||||
return (await this.projectRepository.findAll()).filter(
|
||||
(p) =>
|
||||
p.coopname === coopname &&
|
||||
(p.status === ProjectStatus.PENDING || p.status === ProjectStatus.ACTIVE)
|
||||
(p) => p.coopname === coopname && p.status === ProjectStatus.ACTIVE
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -2,8 +2,8 @@
|
||||
* Unit-тесты ProgramShareRegistrationOnProjectDeltaListener.
|
||||
*
|
||||
* Фокус — гейтинг: доли регистрируются СРАЗУ при появлении проекта в статусе
|
||||
* pending|active, и только в своём кооперативе. На present=false, чужой scope и
|
||||
* статусы вне pending|active реакции нет (regshare там всё равно отклонится).
|
||||
* active, и только в своём кооперативе. На pending (заводим только в active),
|
||||
* present=false, чужой scope и прочие статусы реакции нет.
|
||||
*/
|
||||
|
||||
import config from '~/config/config';
|
||||
@@ -25,22 +25,22 @@ function makeDelta(overrides: Partial<IDelta> = {}, value: Record<string, any> =
|
||||
return {
|
||||
present: true,
|
||||
scope: config.coopname,
|
||||
value: { project_hash: PROJECT_HASH, status: ProjectStatus.PENDING, ...value },
|
||||
value: { project_hash: PROJECT_HASH, status: ProjectStatus.ACTIVE, ...value },
|
||||
...overrides,
|
||||
} as IDelta;
|
||||
}
|
||||
|
||||
describe('ProgramShareRegistrationOnProjectDeltaListener', () => {
|
||||
it('pending в своём кооперативе → регистрирует доли по project_hash', async () => {
|
||||
it('active в своём кооперативе → регистрирует доли по project_hash', async () => {
|
||||
const service = makeServiceStub();
|
||||
await makeListener(service).handleProjectDelta(makeDelta());
|
||||
expect(service.syncProgramSharesForProject).toHaveBeenCalledWith(config.coopname, PROJECT_HASH);
|
||||
});
|
||||
|
||||
it('active → тоже регистрирует', async () => {
|
||||
it('pending → не реагирует (заводим только в active, при переходе в active придёт новая дельта)', async () => {
|
||||
const service = makeServiceStub();
|
||||
await makeListener(service).handleProjectDelta(makeDelta({}, { status: ProjectStatus.ACTIVE }));
|
||||
expect(service.syncProgramSharesForProject).toHaveBeenCalledTimes(1);
|
||||
await makeListener(service).handleProjectDelta(makeDelta({}, { status: ProjectStatus.PENDING }));
|
||||
expect(service.syncProgramSharesForProject).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('result → не реагирует (окно закрыто, откат статуса контракт не даёт)', async () => {
|
||||
|
||||
Reference in New Issue
Block a user