1
0
mirror of synced 2025-03-25 09:33:51 +03:00
woocommerce-module/src/include/class-wc-retailcrm-customers.php

725 lines
23 KiB
PHP

<?php
if (!class_exists('WC_Retailcrm_Customers')) :
/**
* PHP version 7.0
*
* Class WC_Retailcrm_Customers - Allows transfer data customers with CMS.
*
* @category Integration
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
class WC_Retailcrm_Customers
{
/** @var bool | WC_Retailcrm_Proxy | \WC_Retailcrm_Client_V5 */
protected $retailcrm;
/** @var array */
protected $retailcrm_settings = [];
/** @var WC_Retailcrm_Customer_Address */
protected $customer_address;
/** @var array */
private $customer = [];
/** @var array */
private $customerCorporate = [];
/** @var array */
private $customerCorporateCompany = [];
/** @var array */
private $customerCorporateAddress = [];
/**@var array */
private $customFields = [];
/**@var null */
public $isSubscribed = null;
/**
* WC_Retailcrm_Customers constructor.
*
* @param bool | WC_Retailcrm_Proxy $retailcrm
* @param array $retailcrm_settings
* @param WC_Retailcrm_Customer_Address $customer_address
*/
public function __construct($retailcrm, $retailcrm_settings, $customer_address)
{
$this->retailcrm = $retailcrm;
$this->retailcrm_settings = $retailcrm_settings;
$this->customer_address = $customer_address;
if (!empty($retailcrm_settings['customer-meta-data-retailcrm'])) {
$this->customFields = json_decode($retailcrm_settings['customer-meta-data-retailcrm'], true);
}
}
/**
* Return corporate customer
*
* @return array
*/
public function getCorporateCustomer()
{
return $this->customerCorporate;
}
/**
* Is corporate customers enabled in provided API
*
* @return bool
*/
public function isCorporateEnabled()
{
if (!$this->retailcrm instanceof WC_Retailcrm_Proxy) {
return false;
}
return $this->retailcrm->getCorporateEnabled();
}
/**
* Customer can registration on site, we need:
* 1. Check by email if the customer already exists in CRM - then update the customer details.
* 2. If the customer is not in CRM, then create a new customer.
*
* @param int $customerId
*
* @return void|null
* @throws Exception
*/
public function registerCustomer($customerId)
{
if (!$this->retailcrm instanceof WC_Retailcrm_Proxy) {
return null;
}
$wcCustomer = new WC_Customer($customerId);
$email = $wcCustomer->get_billing_email();
if (empty($email)) {
$email = $wcCustomer->get_email();
}
if (empty($email)) {
WC_Retailcrm_Logger::add('Error: Customer email is empty, externalId: ' . $wcCustomer->get_id());
return null;
} else {
$wcCustomer->set_billing_email($email);
$wcCustomer->save();
}
$response = $this->retailcrm->customersList(['email' => $email]);
if ($response->isSuccessful() && !empty($response['customers'])) {
$customers = $response['customers'];
$customer = reset($customers);
if (isset($customer['id'])) {
$this->updateCustomerById($customerId, $customer['id']);
$builder = new WC_Retailcrm_WC_Customer_Builder();
$builder
->setWcCustomer($wcCustomer)
->setPhones(!empty($customer['phones']) ? $customer['phones'] : [])
->setAddress(!empty($customer['address']) ? $customer['address'] : false)
->build()
->getResult()
->save();
WC_Retailcrm_Logger::add('Customer was edited, externalId: ' . $wcCustomer->get_id());
}
} else {
$this->createCustomer($customerId);
$message = $this->isSubscribed
? 'The client has agreed to receive promotional newsletter, email: '
: 'The client refused to receive promotional newsletters, email: ';
WC_Retailcrm_Logger::addCaller('subscribe', $message . $email);
WC_Retailcrm_Logger::add('Customer was created, externalId: ' . $wcCustomer->get_id());
}
}
/**
* Create customer in CRM
*
* @param int | WC_Customer $customer
*
* @param \WC_Order|null $order
*
* @return mixed
* @throws \Exception
*/
public function createCustomer($customer, $order = null)
{
if (!$this->retailcrm instanceof WC_Retailcrm_Proxy) {
return null;
}
if (is_int($customer)) {
$customer = $this->wcCustomerGet($customer);
}
if (!$customer instanceof WC_Customer) {
return null;
}
if ($this->isCustomer($customer)) {
$this->processCustomer($customer, $order);
$response = $this->retailcrm->customersCreate($this->customer);
if ((!empty($response) && $response->isSuccessful()) && isset($response['id'])) {
return $response['id'];
}
}
return null;
}
/**
* Update customer in CRM
*
* @param $customerId
*
* @return void|\WC_Customer
* @throws \Exception
*/
public function updateCustomer($customerId)
{
if (!$this->retailcrm instanceof WC_Retailcrm_Proxy) {
return;
}
$customer = $this->wcCustomerGet($customerId);
if ($this->isCustomer($customer)) {
$this->processCustomer($customer);
$this->retailcrm->customersEdit($this->customer);
}
return $customer;
}
/**
* Update customer in CRM by ID
*
* @param int $customerId
* @param int|string $crmCustomerId
*
* @return void|\WC_Customer
* @throws \Exception
*/
public function updateCustomerById($customerId, $crmCustomerId)
{
if (!$this->retailcrm instanceof WC_Retailcrm_Proxy) {
return;
}
$customer = $this->wcCustomerGet($customerId);
if ($this->isCustomer($customer)) {
$this->processCustomer($customer);
$this->customer['id'] = $crmCustomerId;
$this->retailcrm->customersEdit($this->customer, 'id');
}
return $customer;
}
/**
* Create corporate customer in CRM
*
* @param int $crmCustomerId
* @param int | WC_Customer $customer
* @param \WC_Order $order
*
* @return mixed
* @throws \Exception
*/
public function createCorporateCustomerForOrder($crmCustomerId, $customer, $order)
{
if (!$this->retailcrm instanceof WC_Retailcrm_Proxy) {
return null;
}
if (is_int($customer)) {
$customer = $this->wcCustomerGet($customer);
}
if (!$customer instanceof WC_Customer) {
return null;
}
if ($this->isCustomer($customer)) {
$this->processCorporateCustomer($crmCustomerId, $customer, $order);
$response = $this->retailcrm->customersCorporateCreate($this->customerCorporate);
return $this->fillCorporateCustomer($response);
}
return null;
}
/**
* Create new address in corporate customer (if needed).
*
* @param int $corporateId
* @param \WC_Customer $customer
* @param \WC_Order|null $order
*
* @return bool
*/
public function fillCorporateAddress($corporateId, $customer, $order = null)
{
$found = false;
$builder = new WC_Retailcrm_Customer_Corporate_Address();
$newAddress = $builder
->setIsMain(false)
->build($customer, $order)
->getData();
$addresses = $this->retailcrm->customersCorporateAddresses(
$corporateId,
[],
null,
100,
'id'
);
if (!empty($addresses['addresses']) && $addresses->isSuccessful()) {
foreach ($addresses['addresses'] as $address) {
foreach ($newAddress as $field => $value) {
if (isset($address[$field]) && $address[$field] != $value) {
continue 2;
}
}
$found = true;
break;
}
} else {
$found = true;
}
if (!$found) {
$this->retailcrm->customersCorporateAddressesCreate(
$corporateId,
$newAddress,
'id',
$this->retailcrm->getSingleSiteForKey()
);
}
return $found;
}
/**
* Fills corporate customer with required data after customer was created or updated.
* Create or update response after sending customer must be passed.
*
* @param \WC_Retailcrm_Response $response
*
* @return string|int|null
*/
protected function fillCorporateCustomer($response)
{
if ((empty($response) || !$response->isSuccessful()) && !$response->offsetExists('id')) {
return null;
}
$customerId = $response['id'];
$response = $this->retailcrm->customersCorporateAddressesCreate(
$customerId,
$this->customerCorporateAddress,
'id',
$this->retailcrm->getSingleSiteForKey()
);
if ($response->isSuccessful() && $response->offsetExists('id')) {
$this->customerCorporateCompany['address'] = [
'id' => $response['id'],
];
$this->retailcrm->customersCorporateCompaniesCreate(
$customerId,
$this->customerCorporateCompany,
'id',
$this->retailcrm->getSingleSiteForKey()
);
}
return $customerId;
}
/**
* Process customer for upload
*
* @param WC_Customer $customer
*
* @return void
*/
public function processCustomerForUpload($customer)
{
$this->processCustomer($customer);
}
/**
* Process customer
*
* @param WC_Customer $customer
* @param WC_Order|null $order
*
* @return void
* @throws \Exception
*/
protected function processCustomer($customer, $order = null)
{
$createdAt = $customer->get_date_created();
$firstName = $customer->get_first_name();
$lastName = $customer->get_last_name();
$billingPhone = $customer->get_billing_phone();
$email = strtolower($customer->get_billing_email());
if (empty($firstName) && empty($lastName) && $order instanceof WC_Order) {
$firstName = $order->get_billing_first_name();
$lastName = $order->get_billing_last_name();
if (empty($email)) {
$email = $order->get_billing_email();
}
if (empty($billingPhone)) {
$billingPhone = $order->get_billing_phone();
}
}
// If a customer has placed an order as a guest, then $customer->get_date_created() == null,
// then we take $order->get_date_created() order
$createdAt = empty($createdAt) ? $order->get_date_created() : $createdAt;
$customerData = [
'createdAt' => $createdAt->date('Y-m-d H:i:s'),
'firstName' => !empty($firstName) ? $firstName : $customer->get_username(),
'lastName' => $lastName,
'email' => $email,
'address' => $this->customer_address->build($customer, $order)->getData()
];
if ($customer->get_id() > 0) {
$customerData['externalId'] = $customer->get_id();
}
// The guest client is unsubscribed by default
if ($customer->get_id() === 0 && $customer->get_date_created() === null) {
$customerData['subscribed'] = false;
}
if ($this->isSubscribed !== null) {
$customerData['subscribed'] = $this->isSubscribed;
}
if (!empty($billingPhone)) {
$customerData['phones'][] = [
'number' => $billingPhone
];
}
// If the client is corporate, set the value isContact.
if ($this->isCorporateEnabled()) {
if ($order !== null) {
$company = $order->get_billing_company();
}
if (empty($company)) {
$company = $customer->get_billing_company();
}
if (!empty($company)) {
$customerData['isContact'] = true;
}
}
if (!empty($this->customFields)) {
foreach ($this->customFields as $metaKey => $customKey) {
$metaValue = $customer->get_meta($metaKey);
if (empty($metaValue)) {
continue;
}
if (strpos($customKey, 'default-crm-field') !== false) {
$crmField = explode('#', $customKey);
if (count($crmField) === 2 && isset($crmField[1])) {
if ($crmField[1] === 'phones') {
$customerData[$crmField[1]][] = ['number' => $metaValue];
} elseif ($crmField[1] === 'tags') {
$customerData['addTags'][] = $metaValue;
} else {
$customerData[$crmField[1]] = $metaValue;
}
} elseif (isset($crmField[1], $crmField[2])) {
// For customer delivery
$customerData[$crmField[1]][$crmField[2]] = $metaValue;
}
} else {
$customerData['customFields'][$customKey] = $metaValue;
}
}
}
$this->customer = apply_filters(
'retailcrm_process_customer',
WC_Retailcrm_Plugin::clearArray($customerData),
$customer
);
}
/**
* Process corporate customer
*
* @param int $crmCustomerId
* @param WC_Customer $customer
* @param \WC_Order $order
*
* @return void
*/
protected function processCorporateCustomer($crmCustomerId, $customer, $order)
{
$data_company = [
'isMain' => true,
'name' => $order->get_billing_company()
];
$data_customer = [
'nickName' => $order->get_billing_company(),
'customerContacts' => [
[
'isMain' => true,
'customer' => [
'id' => $crmCustomerId
]
]
]
];
$corpAddress = new WC_Retailcrm_Customer_Corporate_Address();
$billingAddress = $corpAddress
->setIsMain(true)
->build($customer, $order)
->getData();
if (!empty($billingAddress)) {
$data_company['contragent']['legalAddress'] = implode(
', ',
[
$billingAddress['index'],
$billingAddress['city'],
$billingAddress['region'],
$billingAddress['text']
]
);
}
$this->customerCorporateAddress = $billingAddress;
$this->customerCorporate = apply_filters(
'retailcrm_process_customer_corporate',
WC_Retailcrm_Plugin::clearArray($data_customer),
$customer
);
$this->customerCorporateCompany = apply_filters(
'retailcrm_process_customer_corporate_company',
WC_Retailcrm_Plugin::clearArray($data_company),
$customer
);
}
/**
* @param array $filter Search customer by fields.
*
* @return bool|array
*/
private function searchCustomer($filter)
{
if (isset($filter['externalId'])) {
$search = $this->retailcrm->customersGet($filter['externalId']);
} elseif (isset($filter['email'])) {
if (empty($filter['email'])) {
return false;
}
// If customer not corporate, we need unset this field.
if (empty($filter['isContact'])) {
unset($filter['isContact']);
}
$search = $this->retailcrm->customersList($filter);
}
if (!empty($search) && $search->isSuccessful()) {
$customer = false;
if (isset($search['customers'])) {
if (empty($search['customers'])) {
return false;
}
if (!empty($filter['email'])) {
foreach ($search['customers'] as $finding) {
if (isset($finding['email']) && $finding['email'] == $filter['email']) {
$customer = $finding;
}
}
} else {
$dataCustomers = $search['customers'];
$customer = reset($dataCustomers);
}
} else {
$customer = !empty($search['customer']) ? $search['customer'] : false;
}
return $customer;
}
return false;
}
/**
* Returns customer data by externalId or by email, returns false in case of failure
*
* @param string $customerExternalId Customer externalId.
* @param string $customerEmailOrPhone Customer email or phone.
* @param bool $isContact Customer is the contact person.
*
* @return array|bool
*/
public function findCustomerEmailOrId($customerExternalId, $customerEmailOrPhone, $isContact)
{
$customer = false;
if (!empty($customerExternalId)) {
$customer = $this->searchCustomer(['externalId' => $customerExternalId]);
}
if (!$customer && !empty($customerEmailOrPhone)) {
$customer = $this->searchCustomer(['email' => $customerEmailOrPhone, 'isContact' => $isContact]);
}
return $customer;
}
/**
* Search by provided filter, returns first found customer
*
* @param array $filter
* @param bool $returnGroup Return all customers for group filter instead of first
*
* @return bool|array
*/
public function searchCorporateCustomer($filter, $returnGroup = false)
{
$search = $this->retailcrm->customersCorporateList($filter);
if (!empty($search) && $search->isSuccessful()) {
if (isset($search['customersCorporate'])) {
if (empty($search['customersCorporate'])) {
return false;
}
if ($returnGroup) {
return $search['customersCorporate'];
} else {
$dataCorporateCustomers = $search['customersCorporate'];
$customer = reset($dataCorporateCustomers);
}
} else {
$customer = false;
}
return $customer;
}
return false;
}
/**
* @param WC_Order $order
*
* @return WC_Customer
* @throws Exception
*/
public function buildCustomerFromOrderData($order)
{
$new_customer = new WC_Customer();
foreach ($order->get_address('billing') as $prop => $value) {
if (method_exists($new_customer, 'set_billing_' . $prop)) {
$new_customer->{'set_billing_' . $prop}($value);
}
}
$new_customer->set_first_name($order->get_billing_first_name());
$new_customer->set_last_name($order->get_billing_last_name());
$new_customer->set_email($order->get_billing_email());
$new_customer->set_date_created($order->get_date_created());
return $new_customer;
}
/**
* @param int $customer_id
*
* @return WC_Customer
* @throws \Exception
*/
public function wcCustomerGet($customer_id)
{
return new WC_Customer($customer_id);
}
/**
* @return array
*/
public function getCustomer()
{
return $this->customer;
}
/**
* Returns true if provided WP_User or WC_Customer should be uploaded to CRM
*
* @param \WC_Customer|\WP_User $user
*
* @return bool
*/
public function isCustomer($user)
{
$clientRoles = wp_roles()->get_names();
$clientRoles = apply_filters('retailcrm_customer_roles', WC_Retailcrm_Plugin::clearArray($clientRoles));
if ($user instanceof WP_User) {
$userRole = !empty($user->roles[0]) ? $user->roles[0] : null;
} elseif ($user instanceof WC_Customer) {
$role = $user->get_role();
$userRole = !empty($role) ? $role : null;
} else {
return false;
}
return array_key_exists($userRole, $clientRoles);
}
}
endif;