File size: 3,497 Bytes
c5fde49 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | import { prisma } from './prisma';
import { logger } from '../logger';
export interface ContactImportRow {
[key: string]: any;
}
export interface BulkImportResult {
created: number;
updated: number;
errors: number;
}
export class ContactService {
/**
* Identifies the phone number column using fuzzy matching
*/
static findPhoneKey(row: ContactImportRow): string | undefined {
return Object.keys(row).find(k =>
k.toLowerCase().includes('phone') ||
k.toLowerCase().includes('téléphone') ||
k.toLowerCase().includes('tel') ||
k.toLowerCase().includes('num') ||
k.toLowerCase().includes('whatsapp')
);
}
/**
* Identifies the name column using fuzzy matching
*/
static findNameKey(row: ContactImportRow): string | undefined {
return Object.keys(row).find(k =>
k.toLowerCase().includes('name') ||
k.toLowerCase().includes('nom') ||
k.toLowerCase().includes('contact') ||
k.toLowerCase().includes('client')
);
}
/**
* Normalizes a phone number (removes spaces, adds country code if needed)
*/
static normalizePhone(rawPhone: any): string | null {
if (!rawPhone) return null;
let phoneNumber = String(rawPhone)
.replace(/\s+/g, '')
.replace(/^\+/, '')
.replace(/\D/g, '');
if (phoneNumber.length < 7) return null;
// Senegal logic (9 digits -> add 221)
if (phoneNumber.length === 9) {
phoneNumber = `221${phoneNumber}`;
}
return phoneNumber;
}
/**
* Performs a bulk upsert of contacts and links them to a broadcast list
*/
static async bulkImport(organizationId: string, contacts: ContactImportRow[], listId: string): Promise<BulkImportResult> {
const results = { created: 0, updated: 0, errors: 0 };
for (const row of contacts) {
try {
const phoneKey = this.findPhoneKey(row);
const nameKey = this.findNameKey(row);
const phoneNumber = this.normalizePhone(phoneKey ? row[phoneKey] : null);
const name = nameKey ? String(row[nameKey]).trim() : null;
if (!phoneNumber) {
results.errors++;
continue;
}
const attributes: any = { ...row };
if (phoneKey) delete attributes[phoneKey];
if (nameKey) delete attributes[nameKey];
await prisma.contact.upsert({
where: {
phoneNumber_organizationId: { phoneNumber, organizationId }
},
update: {
name,
attributes,
broadcastLists: { connect: { id: listId } }
},
create: {
phoneNumber,
name,
attributes,
organizationId,
broadcastLists: { connect: { id: listId } }
}
});
results.created++;
} catch (err) {
logger.error({ err, row }, '[ContactService] Failed to import row');
results.errors++;
}
}
return results;
}
}
|