fix(events): refresh saved payout calendars once per hourly pass (WealthLedger/backend#53) (#157)
Backend verify / commits (push) Skipped
Backend verify / security (push) Successful in 1m6s
Backend verify / verify (push) Failing after 6m50s

This commit was merged in pull request #157.
This commit is contained in:
2026-11-02 23:06:14 +03:00
parent 169bee1da5
commit aeb4647fda
4 changed files with 131 additions and 2 deletions
@@ -110,6 +110,7 @@ export class CatalogEnrichmentService {
tickers?: Record<string, string>;
to: string;
},
refreshBefore?: Date,
) {
const { revision } = await this.access.local(userId);
const inputs = sourceInputs(instrumentType, uids, kinds, range);
@@ -123,6 +124,7 @@ export class CatalogEnrichmentService {
false,
kinds,
range,
refreshBefore,
);
}
const work = inputs.length
@@ -175,6 +177,7 @@ export class CatalogEnrichmentService {
tickers?: Record<string, string>;
to: string;
},
refreshBefore?: Date,
) {
const inputs = sourceInputs(instrumentType, uids, kinds, range);
const reservation = this.reservations.then(async () => {
@@ -189,7 +192,9 @@ export class CatalogEnrichmentService {
);
const policy = sourcePolicy(input.kind);
const needsRefresh = force || !row?.fetchedAt ||
(policy === 'quote' && tbankSourceNeedsRefresh('quote', row.fetchedAt, new Date()));
(policy === 'quote' && tbankSourceNeedsRefresh('quote', row.fetchedAt, new Date())) ||
// A walk reads a saved calendar once: later accounts see the new fetchedAt.
(policy === 'calendar' && refreshBefore !== undefined && row.fetchedAt < refreshBefore);
if (!needsRefresh) {
continue;
}
@@ -665,6 +670,7 @@ function sourcePolicy(kind: CatalogSource['kind']) {
) {
return 'quote';
}
if (['bond-events', 'dividends'].includes(kind)) return 'calendar';
return 'stable';
}
function producerKey(input: CatalogSource) {
@@ -268,6 +268,7 @@ export class TbankHourlyScheduler implements OnModuleDestroy, OnModuleInit {
account.accountId,
dispatch,
dispatchProblems,
walkStartedAt,
);
}
}
@@ -340,9 +341,12 @@ export class TbankHourlyScheduler implements OnModuleDestroy, OnModuleInit {
accountId: string,
dispatch: DispatchStep,
dispatchProblems: HourlySection[],
walkStartedAt: Date,
) {
try {
const ids = await this.withBound(this.events.refresh(userId, accountId));
const ids = await this.withBound(
this.events.refresh(userId, accountId, {}, walkStartedAt),
);
if (ids !== BOUND_EXPIRED) {
for (const id of ids) {
await dispatch({ accountId, id, section: 'events' });
@@ -185,6 +185,7 @@ export class UpcomingEventsService {
userId: string,
accountId: string,
query: UpcomingEventsQueryDto = {},
walkStartedAt?: Date,
): Promise<string[]> {
const window = dateWindow(query);
await this.access.local(userId, accountId);
@@ -195,6 +196,7 @@ export class UpcomingEventsService {
positions,
window,
false,
walkStartedAt,
);
return launched;
}
@@ -204,6 +206,7 @@ export class UpcomingEventsService {
positions: Position[],
window: { from: string; to: string },
observe: boolean,
refreshBefore?: Date,
) {
const metadata = await this.metadata.savedMany(positions);
const sources: Awaited<ReturnType<CatalogEnrichmentService['readMany']>> =
@@ -229,6 +232,7 @@ export class UpcomingEventsService {
identities.map((entry) => [entry.uid, entry.ticker]),
),
},
refreshBefore,
);
sources.push(...batch);
for (const source of batch) {