fix(documents): tighten aggregation — filter ended memberships + symmetry
Four follow-ups from Task 8 code review: 1. Aggregation now filters companyMemberships to active rows only (isNull(endDate)) on both client→companies and company→clients joins. Previously a rep who left a company 2y ago would still see that company's files in their aggregated view. Brings this service in line with the 8 other call sites in the codebase that already filter on endDate. 2. Move collectRelatedEntities import to the top of documents.service.ts — was wedged mid-file. 3. listInflightWorkflowsAggregatedByEntity now calls assertEntityInPort for symmetry with the files version. Cross- port reads short-circuit early instead of executing N empty port-scoped queries. 4. Add a cross-port leakage regression test for the workflow projection. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,7 @@ import {
|
||||
type FolderNode,
|
||||
type EntityType,
|
||||
} from '@/lib/services/document-folders.service';
|
||||
import { assertEntityInPort, collectRelatedEntities } from '@/lib/services/files';
|
||||
import type {
|
||||
CreateDocumentInput,
|
||||
UpdateDocumentInput,
|
||||
@@ -1822,8 +1823,6 @@ export async function createFromUpload(
|
||||
|
||||
// ─── Aggregated Workflow Projection ───────────────────────────────────────────
|
||||
|
||||
import { collectRelatedEntities } from '@/lib/services/files';
|
||||
|
||||
export interface AggregatedWorkflowGroup {
|
||||
label: string;
|
||||
source: 'direct' | 'client' | 'company' | 'yacht';
|
||||
@@ -1844,6 +1843,9 @@ export async function listInflightWorkflowsAggregatedByEntity(
|
||||
entityType: 'client' | 'company' | 'yacht',
|
||||
entityId: string,
|
||||
): Promise<{ groups: AggregatedWorkflowGroup[] }> {
|
||||
const entityExists = await assertEntityInPort(portId, entityType, entityId);
|
||||
if (!entityExists) return { groups: [] };
|
||||
|
||||
const related = await collectRelatedEntities(portId, entityType, entityId);
|
||||
const groups: AggregatedWorkflowGroup[] = [];
|
||||
|
||||
@@ -1910,11 +1912,7 @@ async function fetchWorkflowGroupRows(
|
||||
.select()
|
||||
.from(documents)
|
||||
.where(
|
||||
and(
|
||||
eq(documents.portId, portId),
|
||||
inArray(documents.status, inflightStatuses),
|
||||
predicate,
|
||||
),
|
||||
and(eq(documents.portId, portId), inArray(documents.status, inflightStatuses), predicate),
|
||||
)
|
||||
.orderBy(desc(documents.updatedAt))
|
||||
.limit(WORKFLOW_GROUP_LIMIT);
|
||||
@@ -1923,11 +1921,7 @@ async function fetchWorkflowGroupRows(
|
||||
.select({ count: sql<number>`count(*)::int` })
|
||||
.from(documents)
|
||||
.where(
|
||||
and(
|
||||
eq(documents.portId, portId),
|
||||
inArray(documents.status, inflightStatuses),
|
||||
predicate,
|
||||
),
|
||||
and(eq(documents.portId, portId), inArray(documents.status, inflightStatuses), predicate),
|
||||
);
|
||||
|
||||
return { rows, total: Number(countRow?.count ?? 0) };
|
||||
|
||||
Reference in New Issue
Block a user