Native PHP plugin (no Composer) that syncs: - Product stock and pricing from BC to WooCommerce (scheduled cron) - Orders from WooCommerce to BC (on payment received) - Auto-creates customers in BC from WooCommerce billing data Product matching: WooCommerce SKU → BC Item Number, fallback to GTIN (EAN). OAuth2 client credentials auth with encrypted secret storage. Admin settings page with connection test, manual sync, and log viewer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
271 lines
8.3 KiB
PHP
271 lines
8.3 KiB
PHP
<?php
|
|
/**
|
|
* Customer Sync to Business Central
|
|
*
|
|
* @package WooBusinessCentral
|
|
*/
|
|
|
|
// Prevent direct access
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Class WBC_Customer_Sync
|
|
*
|
|
* Handles syncing customers from WooCommerce to Business Central.
|
|
*/
|
|
class WBC_Customer_Sync {
|
|
|
|
/**
|
|
* Get or create a customer in Business Central
|
|
*
|
|
* @param WC_Order $order WooCommerce order.
|
|
* @return string|WP_Error BC customer number or error.
|
|
*/
|
|
public function get_or_create_customer( $order ) {
|
|
$email = $order->get_billing_email();
|
|
|
|
if ( empty( $email ) ) {
|
|
return new WP_Error( 'wbc_no_email', __( 'Order has no billing email.', 'woo-business-central' ) );
|
|
}
|
|
|
|
WBC_Logger::debug( 'CustomerSync', 'Getting or creating customer', array( 'email' => $email ) );
|
|
|
|
// Check if we have a cached BC customer ID for this user
|
|
$user_id = $order->get_user_id();
|
|
if ( $user_id ) {
|
|
$cached_customer_number = get_user_meta( $user_id, '_wbc_bc_customer_number', true );
|
|
if ( ! empty( $cached_customer_number ) ) {
|
|
// Verify the customer still exists in BC
|
|
$exists = $this->verify_customer_exists( $cached_customer_number );
|
|
if ( $exists ) {
|
|
WBC_Logger::debug( 'CustomerSync', 'Using cached BC customer', array(
|
|
'customer_number' => $cached_customer_number,
|
|
) );
|
|
return $cached_customer_number;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try to find existing customer in BC by email
|
|
$existing = $this->find_customer_by_email( $email );
|
|
|
|
if ( is_wp_error( $existing ) ) {
|
|
return $existing;
|
|
}
|
|
|
|
if ( $existing ) {
|
|
$customer_number = $existing['number'];
|
|
|
|
// Cache the customer number for this user
|
|
if ( $user_id ) {
|
|
update_user_meta( $user_id, '_wbc_bc_customer_id', $existing['id'] );
|
|
update_user_meta( $user_id, '_wbc_bc_customer_number', $customer_number );
|
|
}
|
|
|
|
WBC_Logger::info( 'CustomerSync', 'Found existing BC customer', array(
|
|
'email' => $email,
|
|
'customer_number' => $customer_number,
|
|
) );
|
|
|
|
return $customer_number;
|
|
}
|
|
|
|
// Create new customer in BC
|
|
$new_customer = $this->create_customer( $order );
|
|
|
|
if ( is_wp_error( $new_customer ) ) {
|
|
return $new_customer;
|
|
}
|
|
|
|
$customer_number = $new_customer['number'];
|
|
|
|
// Cache the customer number for this user
|
|
if ( $user_id ) {
|
|
update_user_meta( $user_id, '_wbc_bc_customer_id', $new_customer['id'] );
|
|
update_user_meta( $user_id, '_wbc_bc_customer_number', $customer_number );
|
|
}
|
|
|
|
WBC_Logger::info( 'CustomerSync', 'Created new BC customer', array(
|
|
'email' => $email,
|
|
'customer_number' => $customer_number,
|
|
) );
|
|
|
|
return $customer_number;
|
|
}
|
|
|
|
/**
|
|
* Find customer in BC by email
|
|
*
|
|
* @param string $email Customer email.
|
|
* @return array|false|WP_Error Customer data, false if not found, or error.
|
|
*/
|
|
private function find_customer_by_email( $email ) {
|
|
$filter = "email eq '" . WBC_API_Client::escape_odata_string( $email ) . "'";
|
|
$result = WBC_API_Client::get_customers( $filter );
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
return $result;
|
|
}
|
|
|
|
$customers = isset( $result['value'] ) ? $result['value'] : array();
|
|
|
|
if ( empty( $customers ) ) {
|
|
return false;
|
|
}
|
|
|
|
return $customers[0];
|
|
}
|
|
|
|
/**
|
|
* Verify a customer exists in BC
|
|
*
|
|
* @param string $customer_number BC customer number.
|
|
* @return bool Whether the customer exists.
|
|
*/
|
|
private function verify_customer_exists( $customer_number ) {
|
|
$filter = "number eq '" . WBC_API_Client::escape_odata_string( $customer_number ) . "'";
|
|
$result = WBC_API_Client::get_customers( $filter );
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
return false;
|
|
}
|
|
|
|
$customers = isset( $result['value'] ) ? $result['value'] : array();
|
|
|
|
return ! empty( $customers );
|
|
}
|
|
|
|
/**
|
|
* Create a new customer in BC
|
|
*
|
|
* @param WC_Order $order WooCommerce order.
|
|
* @return array|WP_Error Created customer data or error.
|
|
*/
|
|
private function create_customer( $order ) {
|
|
// Build customer data from order
|
|
$customer_data = $this->build_customer_data( $order );
|
|
|
|
WBC_Logger::debug( 'CustomerSync', 'Creating new customer in BC', array(
|
|
'data' => $customer_data,
|
|
) );
|
|
|
|
$result = WBC_API_Client::create_customer( $customer_data );
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
WBC_Logger::error( 'CustomerSync', 'Failed to create customer in BC', array(
|
|
'error' => $result->get_error_message(),
|
|
'data' => $customer_data,
|
|
) );
|
|
return $result;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Build customer data from WC order
|
|
*
|
|
* @param WC_Order $order WooCommerce order.
|
|
* @return array Customer data for BC API.
|
|
*/
|
|
private function build_customer_data( $order ) {
|
|
// Get billing info
|
|
$first_name = $order->get_billing_first_name();
|
|
$last_name = $order->get_billing_last_name();
|
|
$company = $order->get_billing_company();
|
|
|
|
// Determine display name and type
|
|
if ( ! empty( $company ) ) {
|
|
$display_name = $company;
|
|
$type = 'Company';
|
|
} else {
|
|
$display_name = trim( $first_name . ' ' . $last_name );
|
|
$type = 'Person';
|
|
}
|
|
|
|
// Build address
|
|
$address_line1 = $order->get_billing_address_1();
|
|
$address_line2 = $order->get_billing_address_2();
|
|
|
|
if ( ! empty( $address_line2 ) ) {
|
|
$address_line1 .= ', ' . $address_line2;
|
|
}
|
|
|
|
// Map country code
|
|
$country = $order->get_billing_country();
|
|
|
|
// Build customer payload
|
|
$customer_data = array(
|
|
'displayName' => substr( $display_name, 0, 100 ), // BC has max length
|
|
'type' => $type,
|
|
'email' => $order->get_billing_email(),
|
|
);
|
|
|
|
// Add optional fields if present
|
|
$phone = $order->get_billing_phone();
|
|
if ( ! empty( $phone ) ) {
|
|
$customer_data['phoneNumber'] = substr( $phone, 0, 30 );
|
|
}
|
|
|
|
if ( ! empty( $address_line1 ) ) {
|
|
$customer_data['addressLine1'] = substr( $address_line1, 0, 100 );
|
|
}
|
|
|
|
$city = $order->get_billing_city();
|
|
if ( ! empty( $city ) ) {
|
|
$customer_data['city'] = substr( $city, 0, 30 );
|
|
}
|
|
|
|
$state = $order->get_billing_state();
|
|
if ( ! empty( $state ) ) {
|
|
$customer_data['state'] = substr( $state, 0, 30 );
|
|
}
|
|
|
|
$postcode = $order->get_billing_postcode();
|
|
if ( ! empty( $postcode ) ) {
|
|
$customer_data['postalCode'] = substr( $postcode, 0, 20 );
|
|
}
|
|
|
|
if ( ! empty( $country ) ) {
|
|
$customer_data['country'] = $country;
|
|
}
|
|
|
|
// Add currency if available
|
|
$currency = $order->get_currency();
|
|
if ( ! empty( $currency ) ) {
|
|
$customer_data['currencyCode'] = $currency;
|
|
}
|
|
|
|
return $customer_data;
|
|
}
|
|
|
|
/**
|
|
* Update customer in BC with WC order data
|
|
*
|
|
* @param string $customer_id BC customer ID.
|
|
* @param WC_Order $order WooCommerce order.
|
|
* @return array|WP_Error Updated customer data or error.
|
|
*/
|
|
public function update_customer( $customer_id, $order ) {
|
|
$customer_data = $this->build_customer_data( $order );
|
|
|
|
WBC_Logger::debug( 'CustomerSync', 'Updating customer in BC', array(
|
|
'customer_id' => $customer_id,
|
|
'data' => $customer_data,
|
|
) );
|
|
|
|
$result = WBC_API_Client::patch( '/customers(' . $customer_id . ')', $customer_data );
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
WBC_Logger::error( 'CustomerSync', 'Failed to update customer in BC', array(
|
|
'error' => $result->get_error_message(),
|
|
'customer_id' => $customer_id,
|
|
) );
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
}
|