feat: companies sync — add UDFs and fix all field mappings

Migration 056:
- ADD user_defined_fields JSONB (GIN indexed)
- ADD parent_company_id, web_address, create_date, company_category_id
- ADD is_client_portal_active, is_task_fire_active, is_comanaged
- ADD country_id, bill_to_country_id, bill_to_address_to_use
- ADD invoice_email_message_id, purchase_order_template_id
- ADD additional_address_information, sic_code, stock_market, etc.

entity-mapper.ts mapCompany():
- Fix broken snake_case field refs (was data.invoice_method, data.last_activity_date etc.)
- All corrected to actual Autotask camelCase API names (invoiceMethod, lastActivityDate, etc.)
- was data.webSiteURL → data.webAddress
- was data.taxExempt → data.isTaxExempt
- was data.lastTrackedModifiedDateTime (various) → data.lastTrackedModifiedDateTime
- Add UDF conversion: userDefinedFields[] array → JSONB {name: value} object
- 38 UDFs now stored: PassportalID, MimecastID, MSP Service Model, Short Name,
  Workstation Lifecycle Plan, Seats (End Users), CSProfileUID, UniFiID, etc.
This commit is contained in:
lorentz 2026-03-17 23:56:40 -04:00
parent 8315a46da0
commit c8be128c5c
2 changed files with 75 additions and 16 deletions

View file

@ -96,44 +96,72 @@ export function mapAutotaskToDatabase(
* Map Company entity
*/
function mapCompany(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_name: data.companyName || data.name,
company_number: data.companyNumber,
phone: data.phone,
fax: data.fax,
website: data.webSiteURL,
web_address: data.webAddress,
address1: data.address1,
address2: data.address2,
city: data.city,
state: data.state,
postal_code: data.postalCode,
country: data.country,
country_id: data.countryID,
is_active: data.isActive !== undefined ? data.isActive : true,
company_type: data.companyType,
company_category_id: data.companyCategoryID,
owner_resource_id: data.ownerResourceID,
territory_id: data.territoryID,
market_segment_id: data.marketSegmentID,
competitor_id: data.competitorID,
parent_company_id: data.parentCompanyID,
billing_address1: data.billingAddress1,
billing_address2: data.billingAddress2,
billing_city: data.billingCity,
billing_state: data.billingState,
billing_postal_code: data.billingPostalCode,
billing_country: data.billingCountry,
billing_city: data.billToCity,
billing_state: data.billToState,
billing_postal_code: data.billToZipCode,
bill_to_country_id: data.billToCountryID,
bill_to_address_to_use: data.billToAddressToUse,
bill_to_attention: data.billToAttention,
additional_address_information: data.additionalAddressInformation,
bill_to_additional_address_info: data.billToAdditionalAddressInformation,
tax_id: data.taxID,
tax_exempt: data.taxExempt || false,
tax_exempt: data.isTaxExempt || false,
tax_region_id: data.taxRegionID,
currency_id: data.currencyID,
invoice_method: data.invoice_method,
invoice_template_id: data.invoice_template_id,
quote_template_id: data.quote_template_id,
key_account_icon: data.key_account_icon,
last_activity_date: data.last_activity_date,
last_tracked_modification_date_time: data.last_tracked_modification_date_time || data.last_modified_date,
api_vendor_id: data.api_vendor_id,
synced_at: data.synced_at,
is_deleted: data.is_deleted || false,
invoice_method: data.invoiceMethod,
invoice_template_id: data.invoiceTemplateID,
invoice_email_message_id: data.invoiceEmailMessageID,
quote_template_id: data.quoteTemplateID,
purchase_order_template_id: data.purchaseOrderTemplateID,
key_account_icon: data.keyAccountIcon,
is_client_portal_active: data.isClientPortalActive || false,
is_task_fire_active: data.isTaskFireActive || false,
is_comanaged: data.isEnabledForComanaged || false,
sic_code: data.sicCode,
stock_market: data.stockMarket,
stock_symbol: data.stockSymbol,
alternate_phone1: data.alternatePhone1,
alternate_phone2: data.alternatePhone2,
survey_company_rating: data.surveyCompanyRating,
asset_value: data.assetValue,
last_activity_date: data.lastActivityDate,
last_tracked_modification_date_time: data.lastTrackedModifiedDateTime,
api_vendor_id: data.apiVendorID,
create_date: data.createDate,
created_by_resource_id: data.createdByResourceID,
user_defined_fields: Object.keys(udfs).length > 0 ? JSON.stringify(udfs) : null,
is_deleted: data.isDeleted || false,
};
}

View file

@ -0,0 +1,31 @@
-- Migration 056: Add UDF storage + missing fields to companies table
ALTER TABLE companies
ADD COLUMN IF NOT EXISTS user_defined_fields JSONB,
ADD COLUMN IF NOT EXISTS parent_company_id BIGINT,
ADD COLUMN IF NOT EXISTS web_address VARCHAR(255),
ADD COLUMN IF NOT EXISTS create_date TIMESTAMP WITHOUT TIME ZONE,
ADD COLUMN IF NOT EXISTS company_category_id INTEGER,
ADD COLUMN IF NOT EXISTS is_client_portal_active BOOLEAN DEFAULT false,
ADD COLUMN IF NOT EXISTS is_task_fire_active BOOLEAN DEFAULT false,
ADD COLUMN IF NOT EXISTS is_comanaged BOOLEAN DEFAULT false,
ADD COLUMN IF NOT EXISTS country_id INTEGER,
ADD COLUMN IF NOT EXISTS bill_to_country_id INTEGER,
ADD COLUMN IF NOT EXISTS bill_to_address_to_use INTEGER,
ADD COLUMN IF NOT EXISTS invoice_email_message_id BIGINT,
ADD COLUMN IF NOT EXISTS purchase_order_template_id BIGINT,
ADD COLUMN IF NOT EXISTS additional_address_information TEXT,
ADD COLUMN IF NOT EXISTS bill_to_additional_address_info TEXT,
ADD COLUMN IF NOT EXISTS bill_to_attention VARCHAR(255),
ADD COLUMN IF NOT EXISTS sic_code VARCHAR(50),
ADD COLUMN IF NOT EXISTS stock_market VARCHAR(100),
ADD COLUMN IF NOT EXISTS stock_symbol VARCHAR(50),
ADD COLUMN IF NOT EXISTS alternate_phone1 VARCHAR(50),
ADD COLUMN IF NOT EXISTS alternate_phone2 VARCHAR(50),
ADD COLUMN IF NOT EXISTS survey_company_rating INTEGER,
ADD COLUMN IF NOT EXISTS asset_value NUMERIC(18,4),
ADD COLUMN IF NOT EXISTS created_by_resource_id BIGINT;
-- Index for UDF queries (e.g. find by PassportalID, MimecastID)
CREATE INDEX IF NOT EXISTS idx_companies_udfs ON companies USING gin(user_defined_fields);
CREATE INDEX IF NOT EXISTS idx_companies_parent ON companies(parent_company_id) WHERE parent_company_id IS NOT NULL;