Align companies with the Didar export: new fields, Didar-format Excel import/export, and people linked to companies by Didar code

This commit is contained in:
2026-10-06 12:25:20 +03:30
parent f88786ef61
commit f71f09c85d
8 changed files with 630 additions and 85 deletions
@@ -24,6 +24,13 @@ import {
resolveOwnerId,
} from '../common/record-scope';
import { normalizeTags } from '../common/tags';
import { normalizePersonImportSource } from './person-source';
function parseDateOrNull(raw?: string | null): Date | null {
if (!raw?.trim()) return null;
const d = new Date(raw);
return Number.isNaN(d.getTime()) ? null : d;
}
const companyInclude = {
_count: { select: { people: true } },
@@ -33,12 +40,25 @@ const companyInclude = {
type CompanyRow = {
id: string;
name: string;
code: string | null;
importSource: string;
sourceRef: string | null;
nationalId: string | null;
economicCode: string | null;
registrationNumber: string | null;
phone: string | null;
otherPhones: string | null;
fax: string | null;
email: string | null;
otherEmails: string | null;
website: string | null;
province: string | null;
city: string | null;
postalCode: string | null;
address: string | null;
groupName: string | null;
notes: string | null;
registeredAt: Date | null;
tags: string[];
customFields: unknown;
createdAt: Date;
@@ -67,6 +87,9 @@ export class CompaniesService {
{ email: { contains: q.trim(), mode: 'insensitive' } },
{ phone: { contains: q.trim() } },
{ nationalId: { contains: q.trim() } },
{ code: { contains: q.trim() } },
{ sourceRef: { contains: q.trim() } },
{ city: { contains: q.trim(), mode: 'insensitive' } },
],
}
: {}),
@@ -104,12 +127,25 @@ export class CompaniesService {
const data: Prisma.CompanyUncheckedCreateInput = {
organizationId: actor.organizationId,
name: dto.name.trim(),
code: emptyToNull(dto.code),
importSource: normalizePersonImportSource(dto.importSource),
sourceRef: emptyToNull(dto.sourceRef),
nationalId: emptyToNull(dto.nationalId),
economicCode: emptyToNull(dto.economicCode),
registrationNumber: emptyToNull(dto.registrationNumber),
phone: normalizePhone(dto.phone),
otherPhones: emptyToNull(dto.otherPhones),
fax: emptyToNull(dto.fax),
email: normalizeEmail(dto.email),
otherEmails: emptyToNull(dto.otherEmails),
website: emptyToNull(dto.website),
province: emptyToNull(dto.province),
city: emptyToNull(dto.city),
postalCode: emptyToNull(dto.postalCode),
address: emptyToNull(dto.address),
groupName: emptyToNull(dto.groupName),
notes: emptyToNull(dto.notes),
registeredAt: parseDateOrNull(dto.registeredAt),
tags: normalizeTags(dto.tags),
ownerId: resolveOwnerId(actor, dto.ownerId),
customFields: toJsonCustomFields(dto.customFields),
@@ -131,12 +167,29 @@ export class CompaniesService {
await this.ensureExists(actor.organizationId, id, actor);
const data: Prisma.CompanyUncheckedUpdateInput = {};
if (dto.name !== undefined) data.name = dto.name.trim();
if ('code' in dto) data.code = emptyToNull(dto.code);
if (dto.importSource !== undefined) {
data.importSource = normalizePersonImportSource(dto.importSource);
}
if ('sourceRef' in dto) data.sourceRef = emptyToNull(dto.sourceRef);
if ('nationalId' in dto) data.nationalId = emptyToNull(dto.nationalId);
if ('economicCode' in dto) data.economicCode = emptyToNull(dto.economicCode);
if ('registrationNumber' in dto) {
data.registrationNumber = emptyToNull(dto.registrationNumber);
}
if ('phone' in dto) data.phone = normalizePhone(dto.phone);
if ('otherPhones' in dto) data.otherPhones = emptyToNull(dto.otherPhones);
if ('fax' in dto) data.fax = emptyToNull(dto.fax);
if ('email' in dto) data.email = normalizeEmail(dto.email);
if ('otherEmails' in dto) data.otherEmails = emptyToNull(dto.otherEmails);
if ('website' in dto) data.website = emptyToNull(dto.website);
if ('province' in dto) data.province = emptyToNull(dto.province);
if ('city' in dto) data.city = emptyToNull(dto.city);
if ('postalCode' in dto) data.postalCode = emptyToNull(dto.postalCode);
if ('address' in dto) data.address = emptyToNull(dto.address);
if ('groupName' in dto) data.groupName = emptyToNull(dto.groupName);
if ('notes' in dto) data.notes = emptyToNull(dto.notes);
if ('registeredAt' in dto) data.registeredAt = parseDateOrNull(dto.registeredAt);
if ('tags' in dto) data.tags = normalizeTags(dto.tags);
if ('customFields' in dto) {
data.customFields = toJsonCustomFields(dto.customFields);
@@ -226,12 +279,25 @@ export class CompaniesService {
return {
id: row.id,
name: row.name,
code: row.code,
importSource: row.importSource || 'manual',
sourceRef: row.sourceRef,
nationalId: row.nationalId,
economicCode: row.economicCode,
registrationNumber: row.registrationNumber,
phone: row.phone,
otherPhones: row.otherPhones,
fax: row.fax,
email: row.email,
otherEmails: row.otherEmails,
website: row.website,
province: row.province,
city: row.city,
postalCode: row.postalCode,
address: row.address,
groupName: row.groupName,
notes: row.notes,
registeredAt: row.registeredAt ? row.registeredAt.toISOString() : null,
tags: row.tags ?? [],
customFields: asCustomFields(row.customFields),
peopleCount: row._count.people,