Unified search algorithm across all lists; per-section record visibility (contacts, deals, activities, leads, finance, tickets, notes, projects)

This commit is contained in:
2026-10-06 15:04:46 +03:30
parent 43717392b2
commit e08f1452ef
38 changed files with 699 additions and 420 deletions
+20 -12
View File
@@ -12,6 +12,7 @@ import type {
} from '@mento/shared';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import { buildSearchWhere, ci } from '../common/search';
import { type Actor, ownerWhere } from '../common/record-scope';
type QuoteActor = Pick<Actor, 'userId' | 'role'>;
@@ -50,21 +51,13 @@ export class QuotesService {
const dateFilter = buildDateRangeFilter(opts.from, opts.to);
const where: Prisma.QuoteWhereInput = {
organizationId,
...ownerWhere(actor),
...ownerWhere(actor, 'finance'),
...(opts.status ? { status: opts.status as never } : {}),
...(opts.dealId ? { dealId: opts.dealId } : {}),
...(opts.personId ? { personId: opts.personId } : {}),
...(opts.companyId ? { companyId: opts.companyId } : {}),
...(dateFilter ? dateFilter : {}),
...(opts.q?.trim()
? {
OR: [
{ title: { contains: opts.q.trim(), mode: 'insensitive' } },
{ number: { contains: opts.q.trim(), mode: 'insensitive' } },
{ notes: { contains: opts.q.trim(), mode: 'insensitive' } },
],
}
: {}),
...quoteSearchWhere(opts.q),
};
const [rows, total] = await this.prisma.$transaction([
@@ -92,7 +85,7 @@ export class QuotesService {
actor: QuoteActor,
): Promise<QuoteDto> {
const row = await this.prisma.quote.findFirst({
where: { id, organizationId, ...ownerWhere(actor) },
where: { id, organizationId, ...ownerWhere(actor, 'finance') },
include,
});
if (!row) throw new NotFoundException('پیش‌فاکتور پیدا نشد');
@@ -295,7 +288,7 @@ export class QuotesService {
actor: QuoteActor,
) {
const found = await this.prisma.quote.findFirst({
where: { id, organizationId, ...ownerWhere(actor) },
where: { id, organizationId, ...ownerWhere(actor, 'finance') },
select: { id: true },
});
if (!found) throw new NotFoundException('پیش‌فاکتور پیدا نشد');
@@ -413,6 +406,21 @@ function roundMoney(value: number): number {
return Math.round((value + Number.EPSILON) * 100) / 100;
}
function quoteSearchWhere(q?: string): Prisma.QuoteWhereInput {
const search = buildSearchWhere<Prisma.QuoteWhereInput>(q, (t) => [
{ title: ci(t) },
{ number: ci(t) },
{ notes: ci(t) },
{
person: {
OR: [{ firstName: ci(t) }, { lastName: ci(t) }, { mobile: { contains: t } }],
},
},
{ company: { name: ci(t) } },
]);
return search ? { AND: [search] } : {};
}
function buildDateRangeFilter(
from?: string,
to?: string,