fix(zoho): Fix user sync search criteria and improve data validation

- Fix sync_users search: pass criteria in URL query string instead of
  as data parameter (GET requests ignore body data), which caused every
  user search to fail and fall through to create
- Improve validate_api_response to check Zoho-specific error codes
  before generic HTTP errors, and include field-level detail in messages
- Add Last_Name fallback to display_name/username when meta is empty
- Sanitize Phone to digits-only, require 10+ digits, omit if invalid
- Bump HVAC_PLUGIN_VERSION to 2.2.11 to bust browser cache

Result: 65/65 trainers now sync successfully (was 0/65).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
ben 2026-02-06 12:39:20 -04:00
parent 03b9bce52d
commit 4c22b9db8e

View file

@ -81,7 +81,30 @@ class HVAC_Zoho_Sync {
); );
} }
// Check for HTTP-level errors // Check for Zoho API error codes FIRST (more specific than generic HTTP errors)
if (isset($response['data'][0]['code']) && !in_array($response['data'][0]['code'], array('SUCCESS', 'DUPLICATE_DATA'))) {
$error_msg = isset($response['data'][0]['message']) ? $response['data'][0]['message'] : $response['data'][0]['code'];
// Include field-level details if available
if (isset($response['data'][0]['details'])) {
$details = $response['data'][0]['details'];
if (isset($details['api_name'])) {
$error_msg .= ' (field: ' . $details['api_name'] . ')';
}
if (isset($details['expected_data_type'])) {
$error_msg .= ' (expected: ' . $details['expected_data_type'] . ')';
}
if (isset($details['info'])) {
$error_msg .= ' - ' . $details['info'];
}
}
return array(
'success' => false,
'id' => null,
'error' => $error_msg
);
}
// Check for HTTP-level errors (generic fallback)
if (isset($response['error'])) { if (isset($response['error'])) {
return array( return array(
'success' => false, 'success' => false,
@ -90,16 +113,6 @@ class HVAC_Zoho_Sync {
); );
} }
// Check for Zoho API error codes
if (isset($response['data'][0]['code']) && !in_array($response['data'][0]['code'], array('SUCCESS', 'DUPLICATE_DATA'))) {
$error_msg = isset($response['data'][0]['message']) ? $response['data'][0]['message'] : $response['data'][0]['code'];
return array(
'success' => false,
'id' => null,
'error' => $error_msg
);
}
// Check for successful response with ID // Check for successful response with ID
if (isset($response['data'][0]['details']['id'])) { if (isset($response['data'][0]['details']['id'])) {
return array( return array(
@ -454,9 +467,10 @@ class HVAC_Zoho_Sync {
$sync_succeeded = false; $sync_succeeded = false;
// Check if contact already exists in Zoho // Check if contact already exists in Zoho
$search_response = $this->auth->make_api_request('/Contacts/search', 'GET', array( $search_response = $this->auth->make_api_request(
'criteria' => "(Email:equals:{$contact_data['Email']})" '/Contacts/search?criteria=(Email:equals:' . urlencode($contact_data['Email']) . ')',
)); 'GET'
);
if (!empty($search_response['data'])) { if (!empty($search_response['data'])) {
// Update existing contact // Update existing contact
@ -1334,11 +1348,25 @@ class HVAC_Zoho_Sync {
$role = 'Trainee'; $role = 'Trainee';
} }
return array( // Last_Name is required by Zoho - fallback to display_name or username
$last_name = html_entity_decode(get_user_meta($user->ID, 'last_name', true), ENT_QUOTES | ENT_HTML5, 'UTF-8');
if (empty(trim($last_name))) {
$last_name = $user->display_name ?: $user->user_login;
}
// Sanitize phone: strip to digits and leading +, require 10+ digits for validity
$raw_phone = get_user_meta($user->ID, 'phone_number', true);
$phone_digits = preg_replace('/\D/', '', $raw_phone);
$phone = '';
if (strlen($phone_digits) >= 10) {
// Preserve leading + if present, otherwise just use digits
$phone = (strpos(trim($raw_phone), '+') === 0 ? '+' : '') . $phone_digits;
}
$data = array(
'First_Name' => html_entity_decode(get_user_meta($user->ID, 'first_name', true), ENT_QUOTES | ENT_HTML5, 'UTF-8'), 'First_Name' => html_entity_decode(get_user_meta($user->ID, 'first_name', true), ENT_QUOTES | ENT_HTML5, 'UTF-8'),
'Last_Name' => html_entity_decode(get_user_meta($user->ID, 'last_name', true), ENT_QUOTES | ENT_HTML5, 'UTF-8'), 'Last_Name' => $last_name,
'Email' => $user->user_email, 'Email' => $user->user_email,
'Phone' => get_user_meta($user->ID, 'phone_number', true),
'Title' => html_entity_decode(get_user_meta($user->ID, 'hvac_professional_title', true), ENT_QUOTES | ENT_HTML5, 'UTF-8'), 'Title' => html_entity_decode(get_user_meta($user->ID, 'hvac_professional_title', true), ENT_QUOTES | ENT_HTML5, 'UTF-8'),
'Company' => html_entity_decode(get_user_meta($user->ID, 'hvac_company_name', true), ENT_QUOTES | ENT_HTML5, 'UTF-8'), 'Company' => html_entity_decode(get_user_meta($user->ID, 'hvac_company_name', true), ENT_QUOTES | ENT_HTML5, 'UTF-8'),
'Lead_Source' => 'HVAC Community Events', 'Lead_Source' => 'HVAC Community Events',
@ -1348,6 +1376,13 @@ class HVAC_Zoho_Sync {
'Years_Experience' => get_user_meta($user->ID, 'hvac_years_experience', true), 'Years_Experience' => get_user_meta($user->ID, 'hvac_years_experience', true),
'Certification' => get_user_meta($user->ID, 'hvac_certifications', true) 'Certification' => get_user_meta($user->ID, 'hvac_certifications', true)
); );
// Only include Phone if we have a valid value (10+ digits)
if (!empty($phone)) {
$data['Phone'] = $phone;
}
return $data;
} }
/** /**