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 * Map Company entity
*/ */
function mapCompany(data: any): Record<string, any> { 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 { return {
id: data.id, id: data.id,
company_name: data.companyName || data.name, company_name: data.companyName || data.name,
company_number: data.companyNumber, company_number: data.companyNumber,
phone: data.phone, phone: data.phone,
fax: data.fax, fax: data.fax,
website: data.webSiteURL, web_address: data.webAddress,
address1: data.address1, address1: data.address1,
address2: data.address2, address2: data.address2,
city: data.city, city: data.city,
state: data.state, state: data.state,
postal_code: data.postalCode, postal_code: data.postalCode,
country: data.country, country_id: data.countryID,
is_active: data.isActive !== undefined ? data.isActive : true, is_active: data.isActive !== undefined ? data.isActive : true,
company_type: data.companyType, company_type: data.companyType,
company_category_id: data.companyCategoryID,
owner_resource_id: data.ownerResourceID, owner_resource_id: data.ownerResourceID,
territory_id: data.territoryID, territory_id: data.territoryID,
market_segment_id: data.marketSegmentID, market_segment_id: data.marketSegmentID,
competitor_id: data.competitorID, competitor_id: data.competitorID,
parent_company_id: data.parentCompanyID,
billing_address1: data.billingAddress1, billing_address1: data.billingAddress1,
billing_address2: data.billingAddress2, billing_address2: data.billingAddress2,
billing_city: data.billingCity, billing_city: data.billToCity,
billing_state: data.billingState, billing_state: data.billToState,
billing_postal_code: data.billingPostalCode, billing_postal_code: data.billToZipCode,
billing_country: data.billingCountry, 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_id: data.taxID,
tax_exempt: data.taxExempt || false, tax_exempt: data.isTaxExempt || false,
tax_region_id: data.taxRegionID, tax_region_id: data.taxRegionID,
currency_id: data.currencyID, currency_id: data.currencyID,
invoice_method: data.invoice_method, invoice_method: data.invoiceMethod,
invoice_template_id: data.invoice_template_id, invoice_template_id: data.invoiceTemplateID,
quote_template_id: data.quote_template_id, invoice_email_message_id: data.invoiceEmailMessageID,
key_account_icon: data.key_account_icon, quote_template_id: data.quoteTemplateID,
last_activity_date: data.last_activity_date, purchase_order_template_id: data.purchaseOrderTemplateID,
last_tracked_modification_date_time: data.last_tracked_modification_date_time || data.last_modified_date, key_account_icon: data.keyAccountIcon,
api_vendor_id: data.api_vendor_id, is_client_portal_active: data.isClientPortalActive || false,
synced_at: data.synced_at, is_task_fire_active: data.isTaskFireActive || false,
is_deleted: data.is_deleted || 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;