πŸ“‹ Changelog

All updates and improvements to SimProSync

v1.7.3

πŸ“¦ Auto-Create Quote/Job from GHL Project Custom Objects

Latest
25 Feb 2026
  • βœ“ NEW: Auto-create quote or job in SimPro when a Project custom object is created in GHL
  • βœ“ Reads the Lead/Quote/Job dropdown field on the GHL Project to determine entity type
  • βœ“ Configurable via new "βš™οΈ Project Type Field" setting in GHL β†’ SimPro field mappings
  • βœ“ NEW: Per-integration toggle: "Auto-create quote/job from GHL Project" in Settings
  • βœ“ NEW: Type change detection β€” changing Lead/Quote/Job dropdown from Quote to Job (or vice versa) creates the new entity in SimPro
  • βœ“ Type change detection uses project_sync_links table to compare previous vs current entity type
  • βœ“ Each type change creates a separate SimPro entity (mirrors SimPro behaviour where quotes and jobs are distinct records)
  • βœ“ NEW: Dedup protection via project_sync_links lookup β€” prevents duplicate creation when GHL workflow fires multiple times
  • βœ“ NEW: Creation lock (same pattern as site dedup) prevents race conditions from near-simultaneous webhooks
  • βœ“ Stores GHL↔SimPro links in new project_sync_links table for bidirectional tracking
  • βœ“ Writes project_id, quote_id or job_id back to GHL Project custom object after creation
  • βœ“ Loop prevention via ghl_custom_object_tracker table (5-minute TTL)
  • βœ“ NEW: "βš™οΈ SimProSync Settings" group added to SimPro fields dropdown for special configuration fields
  • βœ“ GHL workflow setup: "SimPROsync Projects Webhook" with Project Created/Changed triggers
v1.7.2

πŸ—οΈ Auto-Create Site, Quote-to-Job Conversion & Loop Prevention Fix

24 Feb 2026
  • βœ“ NEW: Auto-create Site in SimPro when a GHL contact has no existing site
  • βœ“ Per-integration toggle: "Auto-create site for new customers" in Settings
  • βœ“ Site creation uses customer name and address with dedup lock protection
  • βœ“ FIX: Loop prevention for site.created webhooks β€” site creation from unified webhook no longer triggers echo loop
  • βœ“ Added simpro_entity_loop_tracker table to track entities created by SimProSync
  • βœ“ NEW: Quote-to-Job conversion when opportunity moves to a job pipeline stage
  • βœ“ Pipeline stage mappings with entity_type = "job" now trigger automatic quoteβ†’job conversion in SimPro
  • βœ“ Conversion uses SimPro ConvertToJob endpoint with proper error handling
v1.8.1

πŸ›‘οΈ Race Condition Fixes, Duplicate Prevention & Context Detection Fix

23 Feb 2026
  • βœ“ FIX: GHL unified webhook was logging duplicate entries for single events
  • βœ“ Root cause: PHP script took too long processing (enrichment API calls) causing server-side request retries
  • βœ“ Added early 200 response β€” GHL now gets instant acknowledgement BEFORE heavy processing begins
  • βœ“ Added ignore_user_abort(true) and fastcgi_finish_request() for clean async processing
  • βœ“ NEW: Request ID debounce system β€” each webhook generates a unique 32-char ID
  • βœ“ Atomic INSERT...ON DUPLICATE KEY UPDATE claims the slot, then SELECT verifies ownership
  • βœ“ Even if two requests hit at the exact same millisecond, only one wins β€” the other blocks itself
  • βœ“ Replaces unreliable rowCount() approach which gave inconsistent results with conditional IF() updates
  • βœ“ unified_webhook_tracker table now has request_id column (auto-migrated)
  • βœ“ FIX: SimPro individual customer events were bypassing the throttle entirely
  • βœ“ individual.customer.updated and individual.customer.created now included alongside company variants
  • βœ“ FIX: SimPro webhook_processor.php had same SELECT-then-INSERT race condition
  • βœ“ Replaced with atomic upsert using process_count tracking for bulletproof deduplication
  • βœ“ FIX: Three-layer customer ID resolution in context_detection.php
  • βœ“ Layer 1: Raw webhook payload field display names (e.g. "Simpro Customer ID")
  • βœ“ Layer 2: ghl_discovered_fields table lookup (resolves GHL internal field IDs to display names)
  • βœ“ Layer 3: sync_links table fallback
  • βœ“ Previously only checked GHL internal field IDs which never matched β€” causing duplicate customer creation loops
  • βœ“ Increased GHL unified webhook debounce window from 15s to 30s
  • βœ“ SQL cleanup script provided to remove 107 duplicate entries from event_logs table
v1.8.0-beta

⚑ Unified Webhook & Full Job Support (v1.8.0-beta)

22 Feb 2026
  • βœ“ NEW: Unified Webhook Architecture β€” one webhook URL handles everything!
  • βœ“ No more separate workflows for customer.upsert, job.create, quote.create etc.
  • βœ“ Single GHL workflow with 4 triggers (Contact Created/Changed, Opportunity Created/Changed) replaces all individual action webhooks
  • βœ“ NEW: Pipeline Stage Mappings are now the Decision Engine
  • βœ“ entity_type column in pipeline_stage_mappings drives whether to create a Quote or Job
  • βœ“ Webhook automatically detects: create quote, create job, convert quoteβ†’job, or sync stage β€” all from pipeline stage context
  • βœ“ NEW: Full Job Creation support in unified webhook
  • βœ“ Jobs created directly when pipeline stage entity_type = "job" and no existing links found
  • βœ“ Applies SimPro stage/status from pipeline mapping automatically
  • βœ“ NEW: Customer Update support β€” existing SimPro customers now PATCHED when GHL contact fields change
  • βœ“ Previously only created new customers β€” now does full upsert (create if new, update if existing)
  • βœ“ Includes fallback: tries individuals endpoint first, then companies if that fails (or vice versa)
  • βœ“ NEW: Quote-to-Job Conversion
  • βœ“ When opportunity moves to a "job" stage but has an existing quote link, automatically converts: marks quote as Approved β†’ creates new job from quote details
  • βœ“ Preserves quote site, name, and type during conversion
  • βœ“ NEW: Dual Entity Stage Sync
  • βœ“ Stage changes now sync for both quotes AND jobs based on linked_entity_type
  • βœ“ Updates correct link table (opportunity_quote_links or opportunity_job_links) with new stage
  • βœ“ NEW: Loop Prevention for unified webhook
  • βœ“ Sync source check: blocks echo-back if SimPro synced same contact within 15 seconds
  • βœ“ Rapid-fire debounce: blocks duplicate webhooks for same contact within 3 seconds via unified_webhook_tracker table
  • βœ“ Race condition protection via ON DUPLICATE KEY on all link table INSERTs
  • βœ“ NEW: Simplified GHL Mappings Page (replaces old per-action page)
  • βœ“ Single page with unified webhook URL, setup status sidebar, and customer field mappings
  • βœ“ Live status showing customer mappings, pipeline stages (quote/job breakdown), associations, and linked entity counts
  • βœ“ Server-side customer mapping count with AJAX fallback for reliable status display
  • βœ“ Links to Pipeline Stage Mappings, Association Mappings, and Status Value Mappings pages
  • βœ“ All existing customer field mappings carry over automatically β€” no remapping needed
  • βœ“ Keeps all features: drag-and-drop groups, searchable dropdowns, discovered fields, clone, manual entry
  • βœ“ Page split into modular includes (ghl_mapping_fields.php, ghl_mapping_js.php) β€” 1,168 lines vs 2,166 old monolith
  • βœ“ IMPROVED: GHL Webhook Logs page now fully supports unified events
  • βœ“ Unified events show proper badges: Success (green), Loop Blocked (blue), Duplicate Blocked (blue), Error (red)
  • βœ“ Friendly event labels: "unified.customer_update+job_create" β†’ "Unified Customer Update+Job Create"
  • βœ“ New expandable "Unified Decision Context" section showing the decision engine results
  • βœ“ Fixed false "Ignored" badge caused by "Loop prevention checks passed" text matching legacy loop detection
  • βœ“ IMPROVED: Full event_logs logging on success AND error β€” unified webhook events now always appear in SimProSync logs
  • βœ“ IMPROVED: opportunity_quote_links table now stores ghl_pipeline_id, ghl_pipeline_stage_id, simpro_quote_stage columns
  • βœ“ IMPROVED: Reverse sync (SimProβ†’GHL) now reads real pipeline columns from quote links instead of NULL aliases
  • βœ“ IMPROVED: Production webhook INSERT statements updated to populate new quote link columns
  • βœ“ Fully backwards compatible β€” old per-action workflows still work alongside unified webhook
  • βœ“ Modular codebase: unified webhook split into context_detection.php, action_executor.php, entity_actions.php
  • βœ“ TESTED: Live production test with Definitive Electrical β€” customer update + job creation + loop prevention all confirmed working
v1.6.3

πŸ”„ SimPro β†’ GHL Opportunity Reverse Sync (The Final Piece!)

10 Feb 2026
  • βœ“ CRITICAL FIX: SimPro Quote updates now sync back to GHL Opportunities!
  • βœ“ getLinkedOpportunity() was only checking opportunity_job_links table - never checked opportunity_quote_links for quotes
  • βœ“ Function now checks the correct table based on entity type (quotes vs jobs)
  • βœ“ Added fallback to sync_links table for quotes created before this fix
  • βœ“ FIX: Quote creation from GHL Opportunities now saves link to opportunity_quote_links table
  • βœ“ Previously only saved to sync_links - reverse sync couldn't find the link
  • βœ“ Now saves to BOTH tables for complete coverage
  • βœ“ THE CIRCLE IS NOW COMPLETE: GHL Opportunity ↔ SimPro Quote ↔ GHL Project (Custom Object)
  • βœ“ Pipeline stage changes in SimPro now update the linked GHL Opportunity stage
v1.6.2

πŸ”„ Bidirectional Pipeline Stage Sync Fix & Integration ID Stability

4 Feb 2026
  • βœ“ CRITICAL FIX: Integration IDs no longer regenerate when saving settings!
  • βœ“ save_settings.php was using DELETE+INSERT pattern which created new auto-increment IDs
  • βœ“ This broke webhook URLs, field mappings, and pipeline mappings after every settings save
  • βœ“ Now uses UPDATE for existing records, INSERT only for new ones
  • βœ“ FIX: GHL β†’ SimPro pipeline stage sync now working for Jobs
  • βœ“ job_create.php now looks up pipeline_stage_mappings table to set Stage/Status
  • βœ“ When opportunity moves to "Job Scheduled πŸ“†", job is created with correct SimPro status
  • βœ“ FIX: GHL β†’ SimPro pipeline stage sync now working for Quotes
  • βœ“ quote_create.php now includes pipeline_stage_mappings lookup (was missing entirely)
  • βœ“ FIX: GHL β†’ SimPro pipeline stage sync now working for Leads
  • βœ“ lead_create.php now includes pipeline_stage_mappings lookup (was missing entirely)
  • βœ“ Pipeline stage mapping applied to both CREATE and UPDATE operations
  • βœ“ Respects sync_direction setting (both, ghl_to_simpro, simpro_to_ghl)
  • βœ“ Confirmed: Bidirectional sync working - SimPro ↔ GHL pipeline stages stay in sync
v1.6.1

πŸ—οΈ Major Code Refactoring, SimPro API Fixes & Google Drive Disconnect Fix

3 Feb 2026
  • βœ“ REFACTOR: Modularised webhook_ghl.php from 9,712 lines to 3,826 lines!
  • βœ“ Split 36 helper functions into 7 dedicated modules in core/ghl/
  • βœ“ Split 11 action handlers into individual files in core/ghl/actions/
  • βœ“ Main webhook file now acts as a slim router - much easier to maintain
  • βœ“ Helper modules: discovery.php, simpro_api.php, ghl_api.php, enrichment.php, pipeline.php, payload_builder.php, helpers.php
  • βœ“ Action modules: customer_upsert.php, site_create.php, job_create.php, quote_create.php, lead_create.php, notes.php, job_create_object.php, quote_create_object.php, lead_create_object.php, site_create_object.php, opportunity_stage_changed.php
  • βœ“ FIX: Status field in pipeline sync now sends plain integer instead of ['ID' => x] object
  • βœ“ FIX: Customer field in lead.create now sends plain integer instead of ['ID' => x] object
  • βœ“ FIX: Type field in orphan lead creation now sends string 'Project' instead of ['ID' => 1] object
  • βœ“ These fixes resolve silent failures when updating SimPro job statuses and creating leads
  • βœ“ FIX: Google Drive disconnect now works when admin is managing another user's settings
  • βœ“ Disconnect link now passes target_user_id parameter
  • βœ“ google_disconnect.php now respects admin context and disconnects correct user
  • βœ“ Redirect after disconnect preserves manage_user_id for admins
v1.6.0

πŸ”„ Generic Dropdown Resolver, API Optimisation & Project Type Routing

2 Feb 2026
  • βœ“ NEW: Generic Dropdown Field Resolver - converts text values to SimPro IDs (and vice versa) for ANY dropdown field
  • βœ“ Dropdown resolution is configuration-driven via simpro_dropdown_config table - no hardcoding needed!
  • βœ“ Supports: PaymentTerms, TaxStatus, JobType, CostCentre, PaymentMethods, and any future dropdown field
  • βœ“ Works bi-directionally: GHLβ†’SimPro (textβ†’ID) and SimProβ†’GHL (IDβ†’text)
  • βœ“ In-memory caching prevents duplicate API calls when resolving multiple records in same request
  • βœ“ NEW: API Call Optimisation via change detection hashing
  • βœ“ SimPro webhooks now skip full enrichment (20-30 API calls) when entity data hasn't changed
  • βœ“ Uses MD5 hash of key fields (Stage, Status, DateModified, names, etc.) to detect changes
  • βœ“ Most repeat webhooks now complete in under 1 second instead of 5-6 seconds
  • βœ“ Hash cache auto-cleans entries older than 24 hours
  • βœ“ FIX: Enhanced project_type_field detection for Quote/Job/Lead routing
  • βœ“ Now checks payload, customData block, AND enriched custom object data (was only checking payload)
  • βœ“ Better debug logging when project_type_field value can't be found - shows available payload keys
  • βœ“ Clearer setup instructions in logs when __setting_project_type_field is not configured
v1.5.62

πŸ”§ Customer Profile & Group Sync Fix (GHL β†’ SimPro)

19 Jan 2026
  • βœ“ FIX: GHL custom field unique keys now correctly map to webhook payload display names
  • βœ“ GHL sends "Customer Type" in payload but mappings use "contact.customer_type" unique key
  • βœ“ Added automatic conversion: contact.customer_type β†’ "Customer Type" for payload lookup
  • βœ“ Also handles "OR" fields: contact.domestic_or_commercial β†’ "Domestic OR Commercial"
  • βœ“ Customer Profile and Customer Group now sync correctly from GHL to SimPro
v1.5.61

πŸ“± Phone Label Capitalisation Fix (SimPro β†’ GHL)

19 Jan 2026
  • βœ“ FIX: Additional phone labels now capitalised correctly for GHL API
  • βœ“ GHL requires "Mobile", "Home", "Work", "Landline" - was sending lowercase "mobile", "home"
  • βœ“ This was causing HTTP 422 errors and blocking ALL customer updates from SimPro to GHL
  • βœ“ Customer Profile and Customer Group syncs were also failing due to this error
  • βœ“ Added ucfirst(strtolower()) to ensure correct capitalisation
v1.5.60

πŸ“± Mobile Phone Mapping Fix (GHL β†’ SimPro)

19 Jan 2026
  • βœ“ FIX: Mobile Phone field now syncs correctly for Individual customers
  • βœ“ Root cause: contact.CellPhone was being used instead of customer.CellPhone
  • βœ“ contact.CellPhone is for Company customer Contacts only - Individual customers need customer.CellPhone
  • βœ“ Updated simpro_fields.php to clarify field labels and prevent future confusion
  • βœ“ Contact/Employee fields now show "(Company Contacts Only)" suffix in dropdown
  • βœ“ Customer Phone field now shows "(Primary)" suffix for clarity
  • βœ“ DATA FIX: Corrected existing bad mappings in ghl_field_mappings table for affected integrations
v1.5.59

πŸ‘€ Customer Profile Sync (GHL β†’ SimPro)

19 Jan 2026
  • βœ“ NEW: Customer Profile names are now automatically converted to SimPro IDs
  • βœ“ When GHL sends "Gold", system looks up the ID (e.g., 11) and sends that to SimPro
  • βœ“ Added findSimproCustomerProfileId() function - fetches and caches all profiles from SimPro
  • βœ“ Profiles are fetched from /setup/customerProfiles/ endpoint and cached per request
  • βœ“ Handles both customer.Profile.CustomerProfile.Name and entity.Profile.CustomerProfile.Name
  • βœ“ Logs show which profiles are available if a match is not found
v1.5.58

πŸ“± Phone Sync Fix (SimPro β†’ GHL)

19 Jan 2026
  • βœ“ FIX: additionalPhones now uses "phoneLabel" instead of "label" (was causing 422 errors)
  • βœ“ GHL API requires "phoneLabel" for additional phone numbers - our code was using wrong key
  • βœ“ FIX: formatUkPhoneNumber now properly preserves + prefix on international numbers
  • βœ“ Numbers like +441773775411 were being sent as 441773775411 (missing +)
  • βœ“ Improved UK phone formatting: handles 07, 0, and 44 prefixes correctly
v1.5.57

πŸ”’ Quote & Lead Duplicate Prevention

13 Jan 2026
  • βœ“ CRITICAL: Added METHOD 0 duplicate check for quote.create (matches job.create logic)
  • βœ“ CRITICAL: Added METHOD 0 duplicate check for lead.create (was completely missing!)
  • βœ“ Quotes/Leads now check enriched Contact Projects for existing quote_id/lead_id before creating
  • βœ“ Added opportunity_quote_links table for tracking Opportunity↔Quote associations
  • βœ“ Added opportunity_lead_links table for tracking Opportunity↔Lead associations
  • βœ“ Prevents duplicate quotes/leads when webhooks fire multiple times
v1.5.9

Read-Only Fields & Site Lookup Fix

26 Dec 2025
  • βœ“ Fixed HTTP 422 "Total.ExTax - This API Column does not allow POST requests" error
  • βœ“ Now removes read-only Total field from all Job, Quote, and Lead payloads
  • βœ“ Fixed Status field validation - must be integer ID, not object with Name
  • βœ“ Improved Site ID lookup for job.create_object - now searches contact custom fields properly
  • βœ“ Added fallback to fetch customer's primary site from SimPro when Site ID not found
  • βœ“ Applied fixes to: job.create, job.create_object, quote.create, quote.create_object, lead.create_object
  • βœ“ Fixed Retry button - was missing action parameter in URL, now properly reports success/failure
v1.5.8

Company vs Individual Customer Payload Fix

26 Dec 2025
  • βœ“ Fixed HTTP 422 errors when creating Company customers
  • βœ“ Company customers no longer send GivenName/FamilyName at top level
  • βœ“ Individual customers no longer send nested "contact" object
  • βœ“ Fixed cascade customer creation in site.create and job.create
  • βœ“ Fixed fallback CREATE scenario payload cleanup
v1.5.7

GHL β†’ SimPro Custom Object Create Fix

25 Dec 2025
  • βœ“ Fixed site.create_object was not looking up Customer ID from associated contact
  • βœ“ Fixed job.create_object was not looking up Customer ID from associated contact
  • βœ“ Fixed quote.create_object was not looking up Customer ID from associated contact
  • βœ“ Fixed lead.create_object was not looking up Customer ID from associated contact
  • βœ“ All *.create_object handlers now properly call getSimproCustomerIdFromAssociatedContact()
  • βœ“ Sites, Jobs, Quotes, and Leads can now be created from GHL custom objects when linked to a contact
v1.5.6

Object-to-Object Associations Fix

25 Dec 2025
  • βœ“ Fixed Sites ↔ Projects associations not being called
  • βœ“ Association functions are now properly invoked after successful API calls
  • βœ“ Improved logging for association creation
v1.5.0

Enrichment Engine & Smart Mapping Analysis

20 Dec 2025
  • βœ“ Added GHL Enrichment Engine - fetches full contact, business, opportunities, and custom objects
  • βœ“ Smart Mapping Analysis now shows all available fields from enriched data
  • βœ“ Improved field lookup with unified GHL data
  • βœ“ Added support for custom object properties in mappings
v1.4.0

Quote Creation, ID Writebacks & Safety Checks

13 Dec 2025
  • βœ“ Fixed quote creation - Customer now sent as integer, auto-fetches Site/Type/Name
  • βœ“ Fixed enrichment to use correct contact_id instead of opportunity ID
  • βœ“ Added Company vs Individual customer auto-detection
  • βœ“ Improved Contact events handling (separate from Customer events)
  • βœ“ Added safety check to prevent accidental mapping deletion
  • βœ“ Fixed Site ID writeback API URL bug
  • βœ“ Added Job ID writeback for custom objects
  • βœ“ Added Quote ID writeback for custom objects
  • βœ“ Added smart fallbacks for site_id, job_id, quote_id field names
  • βœ“ Removed debug logging code
v1.3.0

Site Update Fixes & Log Improvements

10 Dec 2025
  • βœ“ Fixed site update failures - removed trailing slashes from PATCH URLs
  • βœ“ Fixed site.create_object payload structure
  • βœ“ Improved log display with loop prevention detection
  • βœ“ Added "No Action" status display
  • βœ“ Added detailed SimPro API Calls section in logs
v1.2.0

Site ID Writeback & Dynamic Field Mapping

8 Dec 2025
  • βœ“ Made Site ID writeback fully dynamic using ghl_field_mappings table
  • βœ“ Supports any custom object name and field name
  • βœ“ Improved association handling between custom objects and contacts
v1.1.0

Pipeline Stage Mapping

1 Dec 2025
  • βœ“ Added bi-directional pipeline stage sync
  • βœ“ SimPro job stages now sync to GHL opportunity stages
  • βœ“ GHL opportunity stage changes sync back to SimPro
  • βœ“ Added pipeline stage mapping configuration page
v1.0.0

Initial Release

15 Nov 2025
  • βœ“ Bi-directional sync between SimPro and GoHighLevel
  • βœ“ Customer, Site, Job, Quote, Lead, Invoice sync
  • βœ“ Custom object support
  • βœ“ Field mapping configuration
  • βœ“ Event toggle controls

SimProSync by My Local Trades