fix: keep imported follow-ups out of current work
They stayed on the board when a non-prior deal was attached, even though the person was prior. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -38,7 +38,7 @@ import {
|
||||
|
||||
type DtoContext = { stages?: StageDefaults; types?: SystemTypes };
|
||||
|
||||
type ActivityActor = Pick<Actor, 'userId' | 'role' | 'priorLens'>;
|
||||
type ActivityActor = Pick<Actor, 'userId' | 'role' | 'priorLens' | 'priorCutoff'>;
|
||||
|
||||
/** stageId = شناسهٔ مرحله یا 'all'؛ وقتی ست باشد فیلتر زمانی وضعیت را تحمیل نمیکند (جز عقبافتاده) */
|
||||
type StageScope = { stageId: string };
|
||||
@@ -119,7 +119,7 @@ export class ActivitiesService {
|
||||
): Promise<ActivitySummaryDto> {
|
||||
const { start, end } = dayBounds();
|
||||
const scope = activityAccessWhere(actor);
|
||||
const prior = activityPriorWhere(actor.priorLens);
|
||||
const prior = activityPriorWhere(actor.priorLens, new Date(), actor.priorCutoff);
|
||||
const [todayCount, overdueCount] = await Promise.all([
|
||||
this.prisma.activity.count({
|
||||
where: {
|
||||
@@ -700,7 +700,9 @@ export class ActivitiesService {
|
||||
]);
|
||||
if (search) andParts.push(search);
|
||||
const onRecord = Boolean(entity?.personId || entity?.companyId || entity?.dealId);
|
||||
if (!onRecord) andParts.push(activityPriorWhere(actor.priorLens));
|
||||
if (!onRecord) {
|
||||
andParts.push(activityPriorWhere(actor.priorLens, new Date(), actor.priorCutoff));
|
||||
}
|
||||
const base: Prisma.ActivityWhereInput = {
|
||||
organizationId,
|
||||
AND: andParts,
|
||||
|
||||
@@ -406,7 +406,7 @@ Rules:
|
||||
organizationId: orgId,
|
||||
...assigneeWhere(actor),
|
||||
...created,
|
||||
...activityPriorWhere(actor.priorLens),
|
||||
...activityPriorWhere(actor.priorLens, new Date(), actor.priorCutoff),
|
||||
},
|
||||
}),
|
||||
ticket: () =>
|
||||
|
||||
@@ -64,7 +64,7 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
dataVisibilityScopes: true,
|
||||
priorArchiveEnabled: true,
|
||||
priorArchiveLens: true,
|
||||
organization: { select: { enabledModules: true } },
|
||||
organization: { select: { enabledModules: true, priorArchiveEnabledAt: true } },
|
||||
},
|
||||
});
|
||||
if (!membership) {
|
||||
@@ -107,6 +107,7 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
visibleUserIds,
|
||||
scopes,
|
||||
priorLens: priorGranted ? priorLens : 'off',
|
||||
priorCutoff: priorGranted ? membership.organization.priorArchiveEnabledAt : null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -287,17 +287,24 @@ export class BillingService {
|
||||
)
|
||||
)
|
||||
OR (
|
||||
a."dealId" IS NULL
|
||||
AND a."createdAt" < ${cutoff}
|
||||
a."createdAt" < ${cutoff}
|
||||
AND (
|
||||
EXISTS (
|
||||
SELECT 1 FROM "Person" AS p
|
||||
WHERE p."id" = a."personId" AND p."importSource" = 'didar'
|
||||
WHERE p."id" = a."personId"
|
||||
AND (
|
||||
p."importSource" = 'didar'
|
||||
OR p."previousImportSource" = 'didar'
|
||||
)
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM "Company" AS c
|
||||
WHERE c."id" = a."companyId" AND c."importSource" = 'didar'
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM "Deal" AS d2
|
||||
WHERE d2."id" = a."dealId" AND d2."importSource" = 'didar'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -25,6 +25,39 @@ function liveActivity(now: Date): Prisma.ActivityWhereInput {
|
||||
};
|
||||
}
|
||||
|
||||
/** فعالیت باز با موعد آینده، حتی اگر قبلی باشد، در کار جاری میماند. */
|
||||
function stillOpenAhead(now: Date): Prisma.ActivityWhereInput {
|
||||
return { status: 'open', dueAt: { gt: now } };
|
||||
}
|
||||
|
||||
/**
|
||||
* فعالیت قبلی است اگر منبعش didar باشد،
|
||||
* یا قبل از روشن شدن ماژول ساخته شده و به شخص/شرکت/معاملهٔ قبلی وصل باشد.
|
||||
* معاملهٔ غیرقبلی مانع نمیشود؛ شخص قبلی (حتی اگر بعداً منبعش عوض شده) کافی است.
|
||||
*/
|
||||
function historicalActivity(cutoff: Date): Prisma.ActivityWhereInput {
|
||||
return {
|
||||
OR: [
|
||||
{ importSource: PRIOR_IMPORT_SOURCE },
|
||||
{
|
||||
createdAt: { lt: cutoff },
|
||||
OR: [
|
||||
{
|
||||
person: {
|
||||
OR: [
|
||||
{ importSource: PRIOR_IMPORT_SOURCE },
|
||||
{ previousImportSource: PRIOR_IMPORT_SOURCE },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ company: { importSource: PRIOR_IMPORT_SOURCE } },
|
||||
{ deal: { importSource: PRIOR_IMPORT_SOURCE } },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function personPriorWhere(lens: PriorLens | undefined): Prisma.PersonWhereInput {
|
||||
if (lens === 'archive') return { importSource: PRIOR_IMPORT_SOURCE };
|
||||
if (lens === 'current') return { importSource: { not: PRIOR_IMPORT_SOURCE } };
|
||||
@@ -50,14 +83,19 @@ export function dealPriorWhere(lens: PriorLens | undefined, now = new Date()): P
|
||||
export function activityPriorWhere(
|
||||
lens: PriorLens | undefined,
|
||||
now = new Date(),
|
||||
cutoff?: Date | null,
|
||||
): Prisma.ActivityWhereInput {
|
||||
if (lens === 'current') {
|
||||
return { OR: [{ importSource: { not: PRIOR_IMPORT_SOURCE } }, liveActivity(now)] };
|
||||
}
|
||||
if (lens === 'archive') {
|
||||
if (lens !== 'current' && lens !== 'archive') return {};
|
||||
if (!cutoff) {
|
||||
if (lens === 'current') {
|
||||
return { OR: [{ importSource: { not: PRIOR_IMPORT_SOURCE } }, liveActivity(now)] };
|
||||
}
|
||||
return { importSource: PRIOR_IMPORT_SOURCE, NOT: liveActivity(now) };
|
||||
}
|
||||
return {};
|
||||
const prior = historicalActivity(cutoff);
|
||||
const live = stillOpenAhead(now);
|
||||
if (lens === 'current') return { OR: [{ NOT: prior }, live] };
|
||||
return { AND: [prior, { NOT: live }] };
|
||||
}
|
||||
|
||||
/** فهرست شخص/شرکت: در کار جاری، جستجو و فیلتر صریح منبع، قبلیها را هم پیدا میکند. */
|
||||
|
||||
@@ -22,6 +22,8 @@ export type Actor = {
|
||||
scopes?: Partial<Record<DataScope, ScopeVisibility>>;
|
||||
/** off = فیلتر خاموش؛ current = کار جاری؛ archive = بایگانی اطلاعات قبل */
|
||||
priorLens?: 'off' | 'current' | 'archive';
|
||||
/** لحظهٔ روشن شدن ماژول برای این محیط؛ فعالیتهای ساختهشده قبل از آن میتوانند قبلی باشند */
|
||||
priorCutoff?: Date | null;
|
||||
};
|
||||
|
||||
export type ScopedActor = Pick<Actor, 'userId' | 'role' | 'dataVisibility' | 'visibleUserIds' | 'scopes'>;
|
||||
|
||||
@@ -64,7 +64,7 @@ export class ReportsService {
|
||||
const now = new Date();
|
||||
const peoplePrior = personPriorWhere(actor.priorLens);
|
||||
const dealsPrior = dealPriorWhere(actor.priorLens, now);
|
||||
const activitiesPrior = activityPriorWhere(actor.priorLens, now);
|
||||
const activitiesPrior = activityPriorWhere(actor.priorLens, now, actor.priorCutoff);
|
||||
const { jy, jm } = dateToJalali(now);
|
||||
const sixAgo = addJalaliMonths(jy, jm, -5);
|
||||
const sixMonthStart = startOfJalaliMonth(sixAgo.jy, sixAgo.jm);
|
||||
@@ -1553,7 +1553,7 @@ export class ReportsService {
|
||||
weekAgo.setDate(weekAgo.getDate() - 7);
|
||||
const peoplePrior = personPriorWhere(actor.priorLens);
|
||||
const dealsPrior = dealPriorWhere(actor.priorLens, now);
|
||||
const activitiesPrior = activityPriorWhere(actor.priorLens, now);
|
||||
const activitiesPrior = activityPriorWhere(actor.priorLens, now, actor.priorCutoff);
|
||||
|
||||
const [
|
||||
wonDeals,
|
||||
|
||||
Reference in New Issue
Block a user