- Replace WooCommerce sync with Event Tickets (Tickets Commerce) support - Add sync_attendees() for Contacts + Campaign Members - Add sync_rsvps() for Leads + Campaign Members - Fix user roles filter (hvac_trainer/hvac_master_trainer) - Fix event query to include past events - Update admin UI with new sync buttons - Correct meta keys for Tickets Commerce (_tec_tickets_commerce_*) - Correct meta keys for RSVPs (_tribe_rsvp_*) Dry-run tested on staging: - Events: 20 records - Trainers: 53 records - Attendees: 79 records - RSVPs: 4 records - Orders: 52 records 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
835 lines
No EOL
30 KiB
PHP
835 lines
No EOL
30 KiB
PHP
<?php
|
|
/**
|
|
* Zoho CRM Sync Handler
|
|
*
|
|
* @package HVACCommunityEvents
|
|
*/
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Zoho Sync Class
|
|
*/
|
|
class HVAC_Zoho_Sync {
|
|
|
|
/**
|
|
* Zoho Auth instance
|
|
*
|
|
* @var HVAC_Zoho_CRM_Auth
|
|
*/
|
|
private $auth;
|
|
|
|
/**
|
|
* Staging mode flag
|
|
*
|
|
* @var bool
|
|
*/
|
|
private $is_staging;
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public function __construct() {
|
|
require_once HVAC_PLUGIN_DIR . 'includes/zoho/class-zoho-crm-auth.php';
|
|
$this->auth = new HVAC_Zoho_CRM_Auth();
|
|
|
|
// Determine if we're in staging mode
|
|
$site_url = get_site_url();
|
|
$this->is_staging = strpos($site_url, 'upskillhvac.com') === false;
|
|
}
|
|
|
|
/**
|
|
* Check if sync is allowed
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function is_sync_allowed() {
|
|
// Only allow sync on production (upskillhvac.com)
|
|
$site_url = get_site_url();
|
|
return strpos($site_url, 'upskillhvac.com') !== false;
|
|
}
|
|
|
|
/**
|
|
* Sync events to Zoho Campaigns
|
|
*
|
|
* @return array Sync results
|
|
*/
|
|
public function sync_events() {
|
|
$results = array(
|
|
'total' => 0,
|
|
'synced' => 0,
|
|
'failed' => 0,
|
|
'errors' => array(),
|
|
'staging_mode' => $this->is_staging
|
|
);
|
|
|
|
// Get all published events (past and future - 'custom' bypasses date filtering)
|
|
$events = tribe_get_events(array(
|
|
'posts_per_page' => -1,
|
|
'eventDisplay' => 'custom',
|
|
'start_date' => '2020-01-01', // Include all historical events
|
|
));
|
|
|
|
$results['total'] = count($events);
|
|
|
|
// If staging mode, simulate the sync
|
|
if ($this->is_staging) {
|
|
$results['message'] = 'STAGING MODE: Sync simulation only. No data sent to Zoho.';
|
|
$results['synced'] = $results['total'];
|
|
$results['test_data'] = array();
|
|
|
|
foreach ($events as $event) {
|
|
$campaign_data = $this->prepare_campaign_data($event);
|
|
$results['test_data'][] = array(
|
|
'event_id' => $event->ID,
|
|
'event_title' => get_the_title($event),
|
|
'zoho_data' => $campaign_data
|
|
);
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
// Production sync
|
|
if (!$this->is_sync_allowed()) {
|
|
$results['errors'][] = 'Sync not allowed on this domain. Only upskillhvac.com can sync to production.';
|
|
return $results;
|
|
}
|
|
|
|
foreach ($events as $event) {
|
|
try {
|
|
$campaign_data = $this->prepare_campaign_data($event);
|
|
|
|
// Check if campaign already exists in Zoho
|
|
$search_response = $this->auth->make_api_request('GET', '/crm/v2/Campaigns/search', array(
|
|
'criteria' => "(Campaign_Name:equals:{$campaign_data['Campaign_Name']})"
|
|
));
|
|
|
|
if (!empty($search_response['data'])) {
|
|
// Update existing campaign
|
|
$campaign_id = $search_response['data'][0]['id'];
|
|
$update_response = $this->auth->make_api_request('PUT', "/crm/v2/Campaigns/{$campaign_id}", array(
|
|
'data' => array($campaign_data)
|
|
));
|
|
} else {
|
|
// Create new campaign
|
|
$create_response = $this->auth->make_api_request('POST', '/crm/v2/Campaigns', array(
|
|
'data' => array($campaign_data)
|
|
));
|
|
}
|
|
|
|
$results['synced']++;
|
|
|
|
// Update event meta with Zoho ID
|
|
if (isset($campaign_id)) {
|
|
update_post_meta($event->ID, '_zoho_campaign_id', $campaign_id);
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$results['failed']++;
|
|
$results['errors'][] = sprintf('Event %s: %s', $event->ID, $e->getMessage());
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Sync users to Zoho Contacts
|
|
*
|
|
* @return array Sync results
|
|
*/
|
|
public function sync_users() {
|
|
$results = array(
|
|
'total' => 0,
|
|
'synced' => 0,
|
|
'failed' => 0,
|
|
'errors' => array(),
|
|
'staging_mode' => $this->is_staging
|
|
);
|
|
|
|
// Get trainers (hvac_trainer and hvac_master_trainer roles)
|
|
$users = get_users(array(
|
|
'role__in' => array('hvac_trainer', 'hvac_master_trainer'),
|
|
'meta_query' => array(
|
|
'relation' => 'OR',
|
|
array(
|
|
'key' => '_sync_to_zoho',
|
|
'value' => '1',
|
|
'compare' => '='
|
|
),
|
|
array(
|
|
'key' => '_sync_to_zoho',
|
|
'compare' => 'NOT EXISTS'
|
|
)
|
|
)
|
|
));
|
|
|
|
$results['total'] = count($users);
|
|
|
|
// If staging mode, simulate the sync
|
|
if ($this->is_staging) {
|
|
$results['message'] = 'STAGING MODE: Sync simulation only. No data sent to Zoho.';
|
|
$results['synced'] = $results['total'];
|
|
$results['test_data'] = array();
|
|
|
|
foreach ($users as $user) {
|
|
$contact_data = $this->prepare_contact_data($user);
|
|
$results['test_data'][] = array(
|
|
'user_id' => $user->ID,
|
|
'user_email' => $user->user_email,
|
|
'user_role' => implode(', ', $user->roles),
|
|
'zoho_data' => $contact_data
|
|
);
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
// Production sync
|
|
if (!$this->is_sync_allowed()) {
|
|
$results['errors'][] = 'Sync not allowed on this domain. Only upskillhvac.com can sync to production.';
|
|
return $results;
|
|
}
|
|
|
|
foreach ($users as $user) {
|
|
try {
|
|
$contact_data = $this->prepare_contact_data($user);
|
|
|
|
// Check if contact already exists in Zoho
|
|
$search_response = $this->auth->make_api_request('GET', '/crm/v2/Contacts/search', array(
|
|
'criteria' => "(Email:equals:{$contact_data['Email']})"
|
|
));
|
|
|
|
if (!empty($search_response['data'])) {
|
|
// Update existing contact
|
|
$contact_id = $search_response['data'][0]['id'];
|
|
$update_response = $this->auth->make_api_request('PUT', "/crm/v2/Contacts/{$contact_id}", array(
|
|
'data' => array($contact_data)
|
|
));
|
|
} else {
|
|
// Create new contact
|
|
$create_response = $this->auth->make_api_request('POST', '/crm/v2/Contacts', array(
|
|
'data' => array($contact_data)
|
|
));
|
|
|
|
if (!empty($create_response['data'][0]['details']['id'])) {
|
|
$contact_id = $create_response['data'][0]['details']['id'];
|
|
}
|
|
}
|
|
|
|
$results['synced']++;
|
|
|
|
// Update user meta with Zoho ID
|
|
if (isset($contact_id)) {
|
|
update_user_meta($user->ID, '_zoho_contact_id', $contact_id);
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$results['failed']++;
|
|
$results['errors'][] = sprintf('User %s: %s', $user->ID, $e->getMessage());
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Sync Event Tickets orders to Zoho Invoices
|
|
*
|
|
* @return array Sync results
|
|
*/
|
|
public function sync_purchases() {
|
|
$results = array(
|
|
'total' => 0,
|
|
'synced' => 0,
|
|
'failed' => 0,
|
|
'errors' => array(),
|
|
'staging_mode' => $this->is_staging
|
|
);
|
|
|
|
// Get Tickets Commerce orders (tec_tc_order post type)
|
|
$orders = get_posts(array(
|
|
'post_type' => 'tec_tc_order',
|
|
'posts_per_page' => -1,
|
|
'post_status' => 'tec-tc-completed',
|
|
));
|
|
|
|
$results['total'] = count($orders);
|
|
|
|
if ($results['total'] === 0) {
|
|
$results['message'] = 'No completed ticket orders found.';
|
|
return $results;
|
|
}
|
|
|
|
// If staging mode, simulate the sync
|
|
if ($this->is_staging) {
|
|
$results['message'] = 'STAGING MODE: Sync simulation only. No data sent to Zoho.';
|
|
$results['synced'] = $results['total'];
|
|
$results['test_data'] = array();
|
|
|
|
foreach ($orders as $order) {
|
|
$invoice_data = $this->prepare_tc_invoice_data($order);
|
|
$results['test_data'][] = array(
|
|
'order_id' => $order->ID,
|
|
'purchaser_email' => get_post_meta($order->ID, '_tec_tc_order_purchaser_email', true),
|
|
'gateway' => get_post_meta($order->ID, '_tec_tc_order_gateway', true),
|
|
'date' => $order->post_date,
|
|
'zoho_data' => $invoice_data
|
|
);
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
// Production sync
|
|
if (!$this->is_sync_allowed()) {
|
|
$results['errors'][] = 'Sync not allowed on this domain. Only upskillhvac.com can sync to production.';
|
|
return $results;
|
|
}
|
|
|
|
foreach ($orders as $order) {
|
|
try {
|
|
$invoice_data = $this->prepare_tc_invoice_data($order);
|
|
|
|
// Check if invoice already exists in Zoho (by WordPress Order ID)
|
|
$search_response = $this->auth->make_api_request(
|
|
'/Invoices/search?criteria=(WordPress_Order_ID:equals:' . $order->ID . ')',
|
|
'GET'
|
|
);
|
|
|
|
if (!empty($search_response['data'])) {
|
|
// Update existing invoice
|
|
$invoice_id = $search_response['data'][0]['id'];
|
|
$this->auth->make_api_request("/Invoices/{$invoice_id}", 'PUT', array(
|
|
'data' => array($invoice_data)
|
|
));
|
|
} else {
|
|
// Create new invoice
|
|
$create_response = $this->auth->make_api_request('/Invoices', 'POST', array(
|
|
'data' => array($invoice_data)
|
|
));
|
|
|
|
if (!empty($create_response['data'][0]['details']['id'])) {
|
|
$invoice_id = $create_response['data'][0]['details']['id'];
|
|
}
|
|
}
|
|
|
|
$results['synced']++;
|
|
|
|
// Update order meta with Zoho ID
|
|
if (isset($invoice_id)) {
|
|
update_post_meta($order->ID, '_zoho_invoice_id', $invoice_id);
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$results['failed']++;
|
|
$results['errors'][] = sprintf('Order %s: %s', $order->ID, $e->getMessage());
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Sync ticket attendees to Zoho Contacts + Campaign Members
|
|
*
|
|
* @return array Sync results
|
|
*/
|
|
public function sync_attendees() {
|
|
$results = array(
|
|
'total' => 0,
|
|
'synced' => 0,
|
|
'failed' => 0,
|
|
'errors' => array(),
|
|
'staging_mode' => $this->is_staging,
|
|
'contacts_created' => 0,
|
|
'campaign_members_created' => 0
|
|
);
|
|
|
|
// Get all ticket attendees (Tickets Commerce)
|
|
$attendees = get_posts(array(
|
|
'post_type' => 'tec_tc_attendee',
|
|
'posts_per_page' => -1,
|
|
'post_status' => 'any',
|
|
));
|
|
|
|
// Also get PayPal attendees if any
|
|
$tpp_attendees = get_posts(array(
|
|
'post_type' => 'tribe_tpp_attendees',
|
|
'posts_per_page' => -1,
|
|
'post_status' => 'any',
|
|
));
|
|
|
|
$all_attendees = array_merge($attendees, $tpp_attendees);
|
|
$results['total'] = count($all_attendees);
|
|
|
|
if ($results['total'] === 0) {
|
|
$results['message'] = 'No ticket attendees found.';
|
|
return $results;
|
|
}
|
|
|
|
// If staging mode, simulate the sync
|
|
if ($this->is_staging) {
|
|
$results['message'] = 'STAGING MODE: Sync simulation only. No data sent to Zoho.';
|
|
$results['synced'] = $results['total'];
|
|
$results['test_data'] = array();
|
|
|
|
foreach ($all_attendees as $attendee) {
|
|
$attendee_data = $this->prepare_attendee_data($attendee);
|
|
$results['test_data'][] = $attendee_data;
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
// Production sync
|
|
if (!$this->is_sync_allowed()) {
|
|
$results['errors'][] = 'Sync not allowed on this domain. Only upskillhvac.com can sync to production.';
|
|
return $results;
|
|
}
|
|
|
|
foreach ($all_attendees as $attendee) {
|
|
try {
|
|
$attendee_data = $this->prepare_attendee_data($attendee);
|
|
|
|
if (empty($attendee_data['email'])) {
|
|
$results['failed']++;
|
|
$results['errors'][] = sprintf('Attendee %s: No email address', $attendee->ID);
|
|
continue;
|
|
}
|
|
|
|
// Step 1: Create/Update Contact
|
|
$contact_id = $this->ensure_contact_exists($attendee_data);
|
|
if ($contact_id) {
|
|
$results['contacts_created']++;
|
|
}
|
|
|
|
// Step 2: Create Campaign Member (link Contact to Campaign)
|
|
if ($contact_id && !empty($attendee_data['event_id'])) {
|
|
$campaign_id = get_post_meta($attendee_data['event_id'], '_zoho_campaign_id', true);
|
|
if ($campaign_id) {
|
|
$this->create_campaign_member($contact_id, $campaign_id, 'Attended');
|
|
$results['campaign_members_created']++;
|
|
}
|
|
}
|
|
|
|
$results['synced']++;
|
|
|
|
// Update attendee meta with Zoho Contact ID
|
|
update_post_meta($attendee->ID, '_zoho_contact_id', $contact_id);
|
|
|
|
} catch (Exception $e) {
|
|
$results['failed']++;
|
|
$results['errors'][] = sprintf('Attendee %s: %s', $attendee->ID, $e->getMessage());
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Sync RSVPs to Zoho Leads + Campaign Members
|
|
*
|
|
* @return array Sync results
|
|
*/
|
|
public function sync_rsvps() {
|
|
$results = array(
|
|
'total' => 0,
|
|
'synced' => 0,
|
|
'failed' => 0,
|
|
'errors' => array(),
|
|
'staging_mode' => $this->is_staging,
|
|
'leads_created' => 0,
|
|
'campaign_members_created' => 0
|
|
);
|
|
|
|
// Get RSVP attendees
|
|
$rsvps = get_posts(array(
|
|
'post_type' => 'tribe_rsvp_attendees',
|
|
'posts_per_page' => -1,
|
|
'post_status' => 'any',
|
|
));
|
|
|
|
$results['total'] = count($rsvps);
|
|
|
|
if ($results['total'] === 0) {
|
|
$results['message'] = 'No RSVP attendees found.';
|
|
return $results;
|
|
}
|
|
|
|
// If staging mode, simulate the sync
|
|
if ($this->is_staging) {
|
|
$results['message'] = 'STAGING MODE: Sync simulation only. No data sent to Zoho.';
|
|
$results['synced'] = $results['total'];
|
|
$results['test_data'] = array();
|
|
|
|
foreach ($rsvps as $rsvp) {
|
|
$rsvp_data = $this->prepare_rsvp_data($rsvp);
|
|
$results['test_data'][] = $rsvp_data;
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
// Production sync
|
|
if (!$this->is_sync_allowed()) {
|
|
$results['errors'][] = 'Sync not allowed on this domain. Only upskillhvac.com can sync to production.';
|
|
return $results;
|
|
}
|
|
|
|
foreach ($rsvps as $rsvp) {
|
|
try {
|
|
$rsvp_data = $this->prepare_rsvp_data($rsvp);
|
|
|
|
if (empty($rsvp_data['email'])) {
|
|
$results['failed']++;
|
|
$results['errors'][] = sprintf('RSVP %s: No email address', $rsvp->ID);
|
|
continue;
|
|
}
|
|
|
|
// Step 1: Create/Update Lead
|
|
$lead_id = $this->ensure_lead_exists($rsvp_data);
|
|
if ($lead_id) {
|
|
$results['leads_created']++;
|
|
}
|
|
|
|
// Step 2: Create Campaign Member (link Lead to Campaign)
|
|
if ($lead_id && !empty($rsvp_data['event_id'])) {
|
|
$campaign_id = get_post_meta($rsvp_data['event_id'], '_zoho_campaign_id', true);
|
|
if ($campaign_id) {
|
|
$this->create_campaign_member($lead_id, $campaign_id, 'Responded', 'Leads');
|
|
$results['campaign_members_created']++;
|
|
}
|
|
}
|
|
|
|
$results['synced']++;
|
|
|
|
// Update RSVP meta with Zoho Lead ID
|
|
update_post_meta($rsvp->ID, '_zoho_lead_id', $lead_id);
|
|
|
|
} catch (Exception $e) {
|
|
$results['failed']++;
|
|
$results['errors'][] = sprintf('RSVP %s: %s', $rsvp->ID, $e->getMessage());
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Ensure a Contact exists in Zoho (create or get existing)
|
|
*
|
|
* @param array $data Attendee data
|
|
* @return string|null Zoho Contact ID
|
|
*/
|
|
private function ensure_contact_exists($data) {
|
|
// Search for existing contact by email
|
|
$search_response = $this->auth->make_api_request(
|
|
'/Contacts/search?criteria=(Email:equals:' . urlencode($data['email']) . ')',
|
|
'GET'
|
|
);
|
|
|
|
if (!empty($search_response['data'])) {
|
|
return $search_response['data'][0]['id'];
|
|
}
|
|
|
|
// Create new contact
|
|
$contact_data = array(
|
|
'First_Name' => $data['first_name'] ?: 'Unknown',
|
|
'Last_Name' => $data['last_name'] ?: 'Attendee',
|
|
'Email' => $data['email'],
|
|
'Lead_Source' => 'Event Tickets',
|
|
'Contact_Type' => 'Attendee',
|
|
'WordPress_Attendee_ID' => $data['attendee_id'],
|
|
);
|
|
|
|
$create_response = $this->auth->make_api_request('/Contacts', 'POST', array(
|
|
'data' => array($contact_data)
|
|
));
|
|
|
|
if (!empty($create_response['data'][0]['details']['id'])) {
|
|
return $create_response['data'][0]['details']['id'];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Ensure a Lead exists in Zoho (create or get existing)
|
|
*
|
|
* @param array $data RSVP data
|
|
* @return string|null Zoho Lead ID
|
|
*/
|
|
private function ensure_lead_exists($data) {
|
|
// Search for existing lead by email
|
|
$search_response = $this->auth->make_api_request(
|
|
'/Leads/search?criteria=(Email:equals:' . urlencode($data['email']) . ')',
|
|
'GET'
|
|
);
|
|
|
|
if (!empty($search_response['data'])) {
|
|
return $search_response['data'][0]['id'];
|
|
}
|
|
|
|
// Create new lead
|
|
$lead_data = array(
|
|
'First_Name' => $data['first_name'] ?: 'Unknown',
|
|
'Last_Name' => $data['last_name'] ?: 'RSVP',
|
|
'Email' => $data['email'],
|
|
'Lead_Source' => 'Event RSVP',
|
|
'Lead_Status' => 'Contacted',
|
|
'WordPress_RSVP_ID' => $data['rsvp_id'],
|
|
);
|
|
|
|
$create_response = $this->auth->make_api_request('/Leads', 'POST', array(
|
|
'data' => array($lead_data)
|
|
));
|
|
|
|
if (!empty($create_response['data'][0]['details']['id'])) {
|
|
return $create_response['data'][0]['details']['id'];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Create a Campaign Member (link Contact/Lead to Campaign)
|
|
*
|
|
* @param string $record_id Contact or Lead ID
|
|
* @param string $campaign_id Campaign ID
|
|
* @param string $status Member status (Invited, Responded, Attended, etc.)
|
|
* @param string $type 'Contacts' or 'Leads'
|
|
*/
|
|
private function create_campaign_member($record_id, $campaign_id, $status = 'Attended', $type = 'Contacts') {
|
|
$endpoint = "/Campaigns/{$campaign_id}/{$type}/{$record_id}";
|
|
|
|
$this->auth->make_api_request($endpoint, 'PUT', array(
|
|
'data' => array(
|
|
array('Member_Status' => $status)
|
|
)
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Prepare campaign data for Zoho
|
|
*
|
|
* @param WP_Post $event Event post object
|
|
* @return array Campaign data
|
|
*/
|
|
private function prepare_campaign_data($event) {
|
|
$trainer_id = get_post_meta($event->ID, '_trainer_id', true);
|
|
$trainer = get_user_by('id', $trainer_id);
|
|
$venue = tribe_get_venue($event->ID);
|
|
|
|
return array(
|
|
'Campaign_Name' => get_the_title($event->ID),
|
|
'Start_Date' => tribe_get_start_date($event->ID, false, 'Y-m-d'),
|
|
'End_Date' => tribe_get_end_date($event->ID, false, 'Y-m-d'),
|
|
'Status' => (tribe_get_end_date($event->ID, false, 'U') < time()) ? 'Completed' : 'Active',
|
|
'Description' => get_the_content(null, false, $event),
|
|
'Type' => 'Training Event',
|
|
'Expected_Revenue' => floatval(get_post_meta($event->ID, '_price', true)),
|
|
'Total_Capacity' => intval(get_post_meta($event->ID, '_stock', true)),
|
|
'Venue' => $venue ? get_the_title($venue) : '',
|
|
'Trainer_Name' => $trainer ? $trainer->display_name : '',
|
|
'Trainer_Email' => $trainer ? $trainer->user_email : '',
|
|
'WordPress_Event_ID' => $event->ID
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Prepare contact data for Zoho
|
|
*
|
|
* @param WP_User $user User object
|
|
* @return array Contact data
|
|
*/
|
|
private function prepare_contact_data($user) {
|
|
// Map WordPress roles to Zoho Contact_Type
|
|
if (in_array('hvac_master_trainer', $user->roles)) {
|
|
$role = 'Master Trainer';
|
|
} elseif (in_array('hvac_trainer', $user->roles)) {
|
|
$role = 'Trainer';
|
|
} else {
|
|
$role = 'Trainee';
|
|
}
|
|
|
|
return array(
|
|
'First_Name' => get_user_meta($user->ID, 'first_name', true),
|
|
'Last_Name' => get_user_meta($user->ID, 'last_name', true),
|
|
'Email' => $user->user_email,
|
|
'Phone' => get_user_meta($user->ID, 'phone_number', true),
|
|
'Title' => get_user_meta($user->ID, 'hvac_professional_title', true),
|
|
'Company' => get_user_meta($user->ID, 'hvac_company_name', true),
|
|
'Lead_Source' => 'HVAC Community Events',
|
|
'Contact_Type' => $role,
|
|
'WordPress_User_ID' => $user->ID,
|
|
'License_Number' => get_user_meta($user->ID, 'hvac_license_number', true),
|
|
'Years_Experience' => get_user_meta($user->ID, 'hvac_years_experience', true),
|
|
'Certification' => get_user_meta($user->ID, 'hvac_certifications', true)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Prepare invoice data for Tickets Commerce order
|
|
*
|
|
* @param WP_Post $order TC Order post object
|
|
* @return array Invoice data
|
|
*/
|
|
private function prepare_tc_invoice_data($order) {
|
|
$purchaser_email = get_post_meta($order->ID, '_tec_tc_order_purchaser_email', true);
|
|
$purchaser_name = get_post_meta($order->ID, '_tec_tc_order_purchaser_name', true);
|
|
$gateway = get_post_meta($order->ID, '_tec_tc_order_gateway', true);
|
|
$gateway_order_id = get_post_meta($order->ID, '_tec_tc_order_gateway_order_id', true);
|
|
$order_items = get_post_meta($order->ID, '_tec_tc_order_items', true);
|
|
|
|
// Parse order items to get event info and line items
|
|
$items = array();
|
|
$event_titles = array();
|
|
$total = 0;
|
|
|
|
if (is_array($order_items)) {
|
|
foreach ($order_items as $item) {
|
|
$event_id = isset($item['event_id']) ? $item['event_id'] : 0;
|
|
$ticket_id = isset($item['ticket_id']) ? $item['ticket_id'] : 0;
|
|
$quantity = isset($item['quantity']) ? intval($item['quantity']) : 1;
|
|
$price = isset($item['price']) ? floatval($item['price']) : 0;
|
|
|
|
if ($event_id) {
|
|
$event_titles[] = get_the_title($event_id);
|
|
}
|
|
|
|
$ticket_name = $ticket_id ? get_the_title($ticket_id) : 'Event Ticket';
|
|
|
|
$items[] = array(
|
|
'product' => array('name' => $ticket_name),
|
|
'quantity' => $quantity,
|
|
'list_price' => $price,
|
|
'total' => $price * $quantity
|
|
);
|
|
|
|
$total += $price * $quantity;
|
|
}
|
|
}
|
|
|
|
$event_summary = !empty($event_titles) ? implode(', ', array_unique($event_titles)) : 'Event Tickets';
|
|
|
|
return array(
|
|
'Subject' => "Ticket Purchase - {$event_summary}",
|
|
'Invoice_Date' => date('Y-m-d', strtotime($order->post_date)),
|
|
'Status' => 'Paid',
|
|
'Account_Name' => $purchaser_name,
|
|
'Description' => "Payment via {$gateway}. Transaction: {$gateway_order_id}",
|
|
'Grand_Total' => $total,
|
|
'WordPress_Order_ID' => $order->ID,
|
|
'Product_Details' => $items
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Prepare attendee data from Event Tickets attendee post
|
|
*
|
|
* @param WP_Post $attendee Attendee post object
|
|
* @return array Attendee data
|
|
*/
|
|
private function prepare_attendee_data($attendee) {
|
|
$post_type = $attendee->post_type;
|
|
|
|
// Handle different attendee post types
|
|
if ($post_type === 'tec_tc_attendee') {
|
|
// Tickets Commerce attendee (meta keys: _tec_tickets_commerce_*)
|
|
$event_id = get_post_meta($attendee->ID, '_tec_tickets_commerce_event', true);
|
|
$ticket_id = get_post_meta($attendee->ID, '_tec_tickets_commerce_ticket', true);
|
|
$order_id = get_post_meta($attendee->ID, '_tec_tickets_commerce_order', true);
|
|
$full_name = get_post_meta($attendee->ID, '_tec_tickets_commerce_full_name', true);
|
|
$email = get_post_meta($attendee->ID, '_tec_tickets_commerce_email', true);
|
|
$checkin = get_post_meta($attendee->ID, '_tec_tickets_commerce_checked_in', true);
|
|
} else {
|
|
// PayPal or other attendee types (tribe_tpp_attendees)
|
|
$event_id = get_post_meta($attendee->ID, '_tribe_tpp_event', true);
|
|
$ticket_id = get_post_meta($attendee->ID, '_tribe_tpp_product', true);
|
|
$order_id = get_post_meta($attendee->ID, '_tribe_tpp_order', true);
|
|
$full_name = get_post_meta($attendee->ID, '_tribe_tickets_full_name', true);
|
|
$email = get_post_meta($attendee->ID, '_tribe_tickets_email', true);
|
|
$checkin = get_post_meta($attendee->ID, '_tribe_tpp_checkin', true);
|
|
}
|
|
|
|
// Parse full name into first/last
|
|
$name_parts = explode(' ', trim($full_name), 2);
|
|
$first_name = isset($name_parts[0]) ? $name_parts[0] : '';
|
|
$last_name = isset($name_parts[1]) ? $name_parts[1] : '';
|
|
|
|
$event_title = $event_id ? get_the_title($event_id) : '';
|
|
$ticket_name = $ticket_id ? get_the_title($ticket_id) : '';
|
|
|
|
return array(
|
|
'attendee_id' => $attendee->ID,
|
|
'post_type' => $post_type,
|
|
'event_id' => $event_id,
|
|
'event_title' => $event_title,
|
|
'ticket_id' => $ticket_id,
|
|
'ticket_name' => $ticket_name,
|
|
'order_id' => $order_id,
|
|
'full_name' => $full_name,
|
|
'first_name' => $first_name,
|
|
'last_name' => $last_name,
|
|
'email' => $email,
|
|
'checked_in' => !empty($checkin),
|
|
'zoho_contact' => array(
|
|
'First_Name' => $first_name ?: 'Unknown',
|
|
'Last_Name' => $last_name ?: 'Attendee',
|
|
'Email' => $email,
|
|
'Lead_Source' => 'Event Tickets',
|
|
'Contact_Type' => 'Attendee',
|
|
),
|
|
'zoho_campaign_member' => array(
|
|
'Member_Status' => !empty($checkin) ? 'Attended' : 'Registered',
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Prepare RSVP data from Event Tickets RSVP attendee post
|
|
*
|
|
* @param WP_Post $rsvp RSVP attendee post object
|
|
* @return array RSVP data
|
|
*/
|
|
private function prepare_rsvp_data($rsvp) {
|
|
$event_id = get_post_meta($rsvp->ID, '_tribe_rsvp_event', true);
|
|
$ticket_id = get_post_meta($rsvp->ID, '_tribe_rsvp_product', true);
|
|
$full_name = get_post_meta($rsvp->ID, '_tribe_rsvp_full_name', true);
|
|
$email = get_post_meta($rsvp->ID, '_tribe_rsvp_email', true);
|
|
$rsvp_status = get_post_meta($rsvp->ID, '_tribe_rsvp_status', true); // yes, no
|
|
|
|
// Parse full name into first/last
|
|
$name_parts = explode(' ', trim($full_name), 2);
|
|
$first_name = isset($name_parts[0]) ? $name_parts[0] : '';
|
|
$last_name = isset($name_parts[1]) ? $name_parts[1] : '';
|
|
|
|
$event_title = $event_id ? get_the_title($event_id) : '';
|
|
|
|
return array(
|
|
'rsvp_id' => $rsvp->ID,
|
|
'event_id' => $event_id,
|
|
'event_title' => $event_title,
|
|
'ticket_id' => $ticket_id,
|
|
'full_name' => $full_name,
|
|
'first_name' => $first_name,
|
|
'last_name' => $last_name,
|
|
'email' => $email,
|
|
'rsvp_status' => $rsvp_status,
|
|
'zoho_lead' => array(
|
|
'First_Name' => $first_name ?: 'Unknown',
|
|
'Last_Name' => $last_name ?: 'RSVP',
|
|
'Email' => $email,
|
|
'Lead_Source' => 'Event RSVP',
|
|
'Lead_Status' => $rsvp_status === 'yes' ? 'Contacted' : 'Not Interested',
|
|
),
|
|
'zoho_campaign_member' => array(
|
|
'Member_Status' => $rsvp_status === 'yes' ? 'Responded' : 'Not Responded',
|
|
)
|
|
);
|
|
}
|
|
} |