feat: contacts sync — add all missing fields + UDFs
Migration 057:
- ADD user_defined_fields JSONB (GIN indexed)
- ADD middle_initial, note, external_id, country_id, company_location_id
- ADD create_date, impersonator_creator_resource_id
- ADD is_opted_out_from_bulk_email, bulk_email_opt_out_time
- ADD solicitation_opt_out_time, survey_opt_out
- ADD receives_email_notifications, billing_contact
entity-mapper.ts mapContact():
- Fix broken snake_case field refs → correct camelCase API names:
alternate_phone → alternatePhone, mobile_phone → mobilePhone
name_prefix/suffix → namePrefix/nameSuffix
facebook/twitter/linkedin_url → facebookUrl/twitterUrl/linkedInUrl
primary_contact → primaryContact, solicitation_opt_out → solicitationOptOut
last_activity/modified_date → lastActivityDate/lastModifiedDate
api_vendor_id → apiVendorID, is_active → isActive, is_deleted → isDeleted
- Add UDF conversion: userDefinedFields[] → JSONB {name: value}
- 17 UDFs stored: UserID, Birthday, O365License, VIP User, Department,
Password, Email Password, Archive Email, User System Profile, etc.
Results: 4234 contacts synced, 3318 with UDFs, 4140 with create_date
This commit is contained in:
parent
dd4cf68def
commit
9a448d111c
2 changed files with 64 additions and 25 deletions
|
|
@ -564,41 +564,60 @@ function mapConfigurationItem(data: any): Record<string, any> {
|
|||
* Map Contact entity
|
||||
*/
|
||||
function mapContact(data: any): Record<string, any> {
|
||||
// Convert userDefinedFields array → JSONB object { name: value }
|
||||
const udfs: Record<string, string | null> = {};
|
||||
if (Array.isArray(data.userDefinedFields)) {
|
||||
for (const udf of data.userDefinedFields) {
|
||||
udfs[udf.name] = udf.value ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: data.id,
|
||||
company_id: data.companyID,
|
||||
first_name: data.firstName,
|
||||
last_name: data.lastName,
|
||||
middle_initial: data.middleInitial,
|
||||
title: data.title,
|
||||
email_address: data.emailAddress || data.email_address || data.email,
|
||||
email_address2: data.emailAddress2 || data.email_address2,
|
||||
email_address3: data.emailAddress3 || data.email_address3,
|
||||
email_address: data.emailAddress,
|
||||
email_address2: data.emailAddress2,
|
||||
email_address3: data.emailAddress3,
|
||||
phone: data.phone,
|
||||
extension: data.extension,
|
||||
alternate_phone: data.alternate_phone,
|
||||
mobile_phone: data.mobile_phone,
|
||||
fax: data.faxNumber || data.fax,
|
||||
address_line: data.addressLine || data.address_line,
|
||||
address_line1: data.addressLine1 || data.address_line1 || data.address_line_1,
|
||||
alternate_phone: data.alternatePhone,
|
||||
mobile_phone: data.mobilePhone,
|
||||
fax: data.faxNumber,
|
||||
address_line: data.addressLine,
|
||||
address_line1: data.addressLine1,
|
||||
city: data.city,
|
||||
state: data.state,
|
||||
zip_code: data.zipCode || data.zip_code || data.postal_code,
|
||||
country: data.country,
|
||||
is_active: data.is_active !== undefined ? data.is_active : true,
|
||||
name_prefix: data.name_prefix,
|
||||
name_suffix: data.name_suffix,
|
||||
facebook_url: data.facebook_url,
|
||||
twitter_url: data.twitter_url,
|
||||
linked_in_url: data.linked_in_url,
|
||||
primary_contact: data.primary_contact || false,
|
||||
account_physical_location_id: data.account_physical_location_id,
|
||||
solicitation_opt_out: data.solicitation_opt_out || false,
|
||||
room_number: data.room_number,
|
||||
last_activity_date: data.last_activity_date,
|
||||
last_modified_date: data.last_modified_date,
|
||||
api_vendor_id: data.api_vendor_id,
|
||||
synced_at: data.synced_at,
|
||||
is_deleted: data.is_deleted || false,
|
||||
zip_code: data.zipCode,
|
||||
country_id: data.countryID,
|
||||
company_location_id: data.companyLocationID,
|
||||
is_active: data.isActive !== undefined ? data.isActive : true,
|
||||
name_prefix: data.namePrefix,
|
||||
name_suffix: data.nameSuffix,
|
||||
facebook_url: data.facebookUrl,
|
||||
twitter_url: data.twitterUrl,
|
||||
linked_in_url: data.linkedInUrl,
|
||||
primary_contact: data.primaryContact || false,
|
||||
billing_contact: data.billingContact || false,
|
||||
solicitation_opt_out: data.solicitationOptOut || false,
|
||||
solicitation_opt_out_time: data.solicitationOptOutTime,
|
||||
survey_opt_out: data.surveyOptOut || false,
|
||||
is_opted_out_from_bulk_email: data.isOptedOutFromBulkEmail || false,
|
||||
bulk_email_opt_out_time: data.bulkEmailOptOutTime,
|
||||
receives_email_notifications: data.receivesEmailNotifications !== undefined ? data.receivesEmailNotifications : true,
|
||||
room_number: data.roomNumber,
|
||||
note: data.note,
|
||||
external_id: data.externalID,
|
||||
api_vendor_id: data.apiVendorID,
|
||||
impersonator_creator_resource_id: data.impersonatorCreatorResourceID,
|
||||
create_date: data.createDate,
|
||||
last_activity_date: data.lastActivityDate,
|
||||
last_modified_date: data.lastModifiedDate,
|
||||
user_defined_fields: Object.keys(udfs).length > 0 ? JSON.stringify(udfs) : null,
|
||||
is_deleted: data.isDeleted || false,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
20
migrations/057_contacts_missing_fields.sql
Normal file
20
migrations/057_contacts_missing_fields.sql
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
-- Migration 057: Add missing contacts fields + UDF storage
|
||||
|
||||
ALTER TABLE contacts
|
||||
ADD COLUMN IF NOT EXISTS user_defined_fields JSONB,
|
||||
ADD COLUMN IF NOT EXISTS middle_initial VARCHAR(10),
|
||||
ADD COLUMN IF NOT EXISTS note TEXT,
|
||||
ADD COLUMN IF NOT EXISTS external_id VARCHAR(50),
|
||||
ADD COLUMN IF NOT EXISTS country_id INTEGER,
|
||||
ADD COLUMN IF NOT EXISTS company_location_id BIGINT,
|
||||
ADD COLUMN IF NOT EXISTS create_date TIMESTAMP WITHOUT TIME ZONE,
|
||||
ADD COLUMN IF NOT EXISTS is_opted_out_from_bulk_email BOOLEAN DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS bulk_email_opt_out_time TIMESTAMP WITHOUT TIME ZONE,
|
||||
ADD COLUMN IF NOT EXISTS solicitation_opt_out_time TIMESTAMP WITHOUT TIME ZONE,
|
||||
ADD COLUMN IF NOT EXISTS survey_opt_out BOOLEAN DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS receives_email_notifications BOOLEAN DEFAULT true,
|
||||
ADD COLUMN IF NOT EXISTS billing_contact BOOLEAN DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS impersonator_creator_resource_id BIGINT;
|
||||
|
||||
-- GIN index for UDF queries (e.g. find by UserID, O365License)
|
||||
CREATE INDEX IF NOT EXISTS idx_contacts_udfs ON contacts USING gin(user_defined_fields);
|
||||
Loading…
Add table
Add a link
Reference in a new issue