1
0
mirror of synced 2025-01-29 22:31:42 +03:00

Corporate clients support

* corporate customers support
* skip new payments without type
* extract customer data from order for guests
* extract customer phone and email from order for guests
* set item discount to zero if no discount applies
* create order from back-office
* sync phone via history
* fixed customer squashing
* fixed createdAt crash
* fixed customer id assigning & possible crash after errors in order creation
This commit is contained in:
Pavel 2020-07-10 13:14:03 +03:00 committed by GitHub
parent 53f6327da0
commit 804cbfac37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 7142 additions and 985 deletions

View File

@ -22,7 +22,7 @@ RUN apt-get install -y subversion
RUN apt-get install -y wget
RUN wget -O /usr/bin/phpunit https://phar.phpunit.de/phpunit-7.phar && chmod +x /usr/bin/phpunit
RUN curl --insecure https://getcomposer.org/composer.phar -o /usr/bin/composer && chmod +x /usr/bin/composer
RUN curl --insecure https://getcomposer.org/download/1.9.3/composer.phar -o /usr/bin/composer && chmod +x /usr/bin/composer
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash -
RUN apt-get install -y nodejs

View File

@ -1,6 +1,12 @@
# MySQL host and credentials
DB_NAME=wc_retailcrm_test
DB_USER=wc_retailcrm
DB_PASS=wc_retailcrm
DB_HOST=mysql
# WordPress and WooCommerce versions
WP_VERSION=4.4
WC_VERSION=3.0.0
# Enable this in order to pipe all module log messages (including debug ones) to STDOUT.
MODULE_LOGS_TO_STDOUT=0

View File

@ -1,3 +1,7 @@
## 2020-06-18 4.0.0
* Поддержка корпоративных клиентов
* Поддержка изменения покупателя в заказе
## 2020-06-18 3.6.4
* Передача названия региона / штата / провинции вместо кода

View File

@ -1 +1 @@
3.6.4
4.0.0

View File

@ -9,7 +9,9 @@
}
],
"minimum-stability": "dev",
"require": {},
"require": {
"ext-simplexml": "*"
},
"require-dev": {
"ext-json": "*",
"ext-mbstring": "*",

View File

@ -222,3 +222,9 @@ msgstr "Transferencia de un número de pedido"
msgid "Transferring the payment amount"
msgstr "Transferencia de un monto de pago"
msgid "Corporate customers support"
msgstr "Soporte a clientes corporativos"
msgid "Enabled"
msgstr "Habilitado"

View File

@ -231,3 +231,9 @@ msgstr "Передача номера заказа"
msgid "Transferring the payment amount"
msgstr "Передача суммы оплаты"
msgid "Corporate customers support"
msgstr "Поддержка корпоративных клиентов"
msgid "Enabled"
msgstr "Включено"

View File

@ -15,6 +15,20 @@
<field id="personal_discount" group="customer">personalDiscount</field>
<field id="discount_card_number" group="customer">discountCardNumber</field>
<field id="id" group="customerCorporate">id</field>
<field id="external_id" group="customerCorporate">externalId</field>
<field id="nick_name" group="customerCorporate">nickName</field>
<field id="vip" group="customerCorporate">vip</field>
<field id="bad" group="customerCorporate">bad</field>
<field id="custom_fields" group="customerCorporate">customFields</field>
<field id="personal_discount" group="customerCorporate">personalDiscount</field>
<field id="discount_card_number" group="customerCorporate">discountCardNumber</field>
<field id="manager" group="customerCorporate">manager</field>
<field id="address" group="customerCorporate">address</field>
<field id="main_contact" group="customerCorporate">mainCustomerContact</field>
<field id="company.contragent.i_n_n" group="customerCorporate">companyInn</field>
<field id="main_company" group="customerCorporate">company</field>
<field id="address.index" group="customerAddress">index</field>
<field id="address.country" group="customerAddress">country</field>
<field id="address.region" group="customerAddress">region</field>
@ -45,6 +59,7 @@
<field id="order_method" group="order">orderMethod</field>
<field id="site" group="order">site</field>
<field id="status" group="order">status</field>
<field id="customer" group="order">customer</field>
<field id="manager" group="order">manager</field>
<field id="first_name" group="order">firstName</field>
<field id="last_name" group="order">lastName</field>
@ -62,6 +77,8 @@
<field id="shipment_store" group="order">shipmentStore</field>
<field id="shipment_date" group="order">shipmentDate</field>
<field id="shipped" group="order">shipped</field>
<field id="contact" group="order">contact</field>
<field id="company" group="order">company</field>
<!--<field id="order_product" group="order">item</field>-->
<field id="payment" group="order">payment</field>
<field id="payments.amount" group="order">amount</field>

View File

@ -0,0 +1,86 @@
<?php
/**
* Class WC_Retailcrm_Abstract_Builder.
*/
abstract class WC_Retailcrm_Abstract_Builder implements WC_Retailcrm_Builder_Interface
{
/** @var array|mixed $data */
protected $data;
/**
* @param array|mixed $data
*
* @return \WC_Retailcrm_Abstract_Builder
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
/**
* @return array|mixed
*/
public function getData()
{
return $this->data;
}
/**
* @return $this|\WC_Retailcrm_Builder_Interface
*/
public function reset()
{
$this->data = array();
return $this;
}
/**
* Returns key if it's present in data array (or object which implements ArrayAccess).
* Returns default value if key is not present in data, or data is not accessible as array.
*
* @param string $key
* @param mixed $default
*
* @return mixed
*/
protected function dataValue($key, $default = '')
{
return self::arrayValue($this->data, $key, $default);
}
/**
* Returns key from array if it's present in array
*
* @param array|\ArrayObject $data
* @param mixed $key
* @param string $default
*
* @return mixed|string
*/
protected static function arrayValue($data, $key, $default = '')
{
if (!is_array($data) && !($data instanceof ArrayAccess)) {
return $default;
}
if (array_key_exists($key, $data) && !empty($data[$key])) {
return $data[$key];
}
return $default;
}
/**
* @return \WC_Retailcrm_Builder_Interface
*/
abstract public function build();
/**
* Returns builder result. Should return null if WC_Retailcrm_Abstract_Builder::isBuilt() == false.
*
* @return mixed|null
*/
abstract public function getResult();
}

View File

@ -11,6 +11,19 @@
abstract class WC_Retailcrm_Abstracts_Address extends WC_Retailcrm_Abstracts_Data
{
const ADDRESS_TYPE_BILLING = 'billing';
const ADDRESS_TYPE_SHIPPING = 'shipping';
/** @var string $address_type */
protected $address_type = 'shipping';
/** @var bool $fallback_to_billing */
protected $fallback_to_billing = false;
/** @var bool $fallback_to_shipping */
protected $fallback_to_shipping = false;
/** @var array $data */
protected $data = array(
'index' => '',
'city' => '',
@ -18,6 +31,9 @@ abstract class WC_Retailcrm_Abstracts_Address extends WC_Retailcrm_Abstracts_Dat
'text' => '',
);
/**
* Resets inner state
*/
public function reset_data()
{
$this->data = array(
@ -26,6 +42,86 @@ abstract class WC_Retailcrm_Abstracts_Address extends WC_Retailcrm_Abstracts_Dat
'region' => '',
'text' => '',
);
return $this;
}
/**
* @param bool $fallback_to_billing
*
* @return self
*/
public function setFallbackToBilling($fallback_to_billing)
{
$this->fallback_to_billing = $fallback_to_billing;
return $this;
}
/**
* @param bool $fallback_to_shipping
*
* @return WC_Retailcrm_Abstracts_Address
*/
public function setFallbackToShipping($fallback_to_shipping)
{
$this->fallback_to_shipping = $fallback_to_shipping;
return $this;
}
/**
* Sets woocommerce address type to work with
*
* @param string $addressType
*
* @return self
*/
public function setWCAddressType($addressType = WC_Retailcrm_Abstracts_Address::ADDRESS_TYPE_SHIPPING)
{
$this->address_type = $addressType;
return $this;
}
/**
* Returns address from order. Respects fallback_to_billing parameter.
*
* @param \WC_Order $order
*
* @return array
*/
protected function getOrderAddress($order)
{
$orderAddress = $order->get_address($this->address_type);
if (empty($orderAddress) && $this->address_type === self::ADDRESS_TYPE_BILLING && $this->fallback_to_shipping) {
$orderAddress = $order->get_address(self::ADDRESS_TYPE_SHIPPING);
}
if (empty($orderAddress) && $this->address_type === self::ADDRESS_TYPE_SHIPPING && $this->fallback_to_billing) {
$orderAddress = $order->get_address(self::ADDRESS_TYPE_BILLING);
}
return $orderAddress;
}
/**
* Glue two addresses
*
* @param string $address1
* @param string $address2
*
* @return string
*/
protected function joinAddresses($address1 = '', $address2 = '')
{
if (empty($address1) && empty($address2)) {
return '';
}
if (empty($address2) && !empty($address1)) {
return $address1;
}
return $address1 . ', ' . $address2;
}
/**

View File

@ -58,6 +58,14 @@ abstract class WC_Retailcrm_Abstracts_Data
*/
public function get_data()
{
return apply_filters('retailcrm_before_send_' . $this->filter_name, $this->data);
return apply_filters('retailcrm_before_send_' . $this->filter_name, WC_Retailcrm_Plugin::clearArray($this->data));
}
/**
* @return array
*/
protected function get_data_without_filters()
{
return $this->data;
}
}

View File

@ -122,11 +122,6 @@ abstract class WC_Retailcrm_Abstracts_Settings extends WC_Integration
)
);
$api_version_list = array(
'v4' => 'v4',
'v5' => 'v5'
);
$this->form_fields[] = array(
'title' => __( 'API settings', 'retailcrm' ),
'type' => 'title',
@ -134,23 +129,22 @@ abstract class WC_Retailcrm_Abstracts_Settings extends WC_Integration
'id' => 'api_options'
);
$this->form_fields['api_version'] = array(
'title' => __( 'API version', 'retailcrm' ),
'description' => __( 'Select API version', 'retailcrm' ),
'css' => 'min-width:50px;',
'class' => 'select',
'type' => 'select',
'options' => $api_version_list,
'desc_tip' => true,
);
$this->form_fields['send_payment_amount'] = array(
'title' => __( 'Transferring the payment amount', 'retailcrm' ),
'label' => ' ',
'description' => '',
'class' => 'checkbox',
'type' => 'checkbox',
'desc_tip' => true,
'desc_tip' => true
);
$this->form_fields['corporate_enabled'] = array(
'title' => __('Corporate customers support', 'retailcrm'),
'label' => __('Enabled'),
'description' => '',
'class' => 'checkbox',
'type' => 'checkbox',
'desc_tip' => true
);
$this->form_fields[] = array(
@ -563,40 +557,6 @@ abstract class WC_Retailcrm_Abstracts_Settings extends WC_Integration
return ob_get_clean();
}
/**
* Validate API version
*
* @param string $key
* @param string $value
*
* @return string
*/
public function validate_api_version_field($key, $value)
{
$post = $this->get_post_data();
$versionMap = array(
'v4' => '4.0',
'v5' => '5.0'
);
$api = new WC_Retailcrm_Proxy(
$post[$this->plugin_id . $this->id . '_api_url'],
$post[$this->plugin_id . $this->id . '_api_key']
);
$response = $api->apiVersions();
if (!empty($response) && $response->isSuccessful()) {
if (!in_array($versionMap[$value], $response['versions'])) {
WC_Admin_Settings::add_error( esc_html__( 'The selected API version is unavailable', 'retailcrm' ) );
$value = '';
}
}
return $value;
}
/**
* Validate API url
*
@ -610,7 +570,8 @@ abstract class WC_Retailcrm_Abstracts_Settings extends WC_Integration
$post = $this->get_post_data();
$api = new WC_Retailcrm_Proxy(
$value,
$post[$this->plugin_id . $this->id . '_api_key']
$post[$this->plugin_id . $this->id . '_api_key'],
$this->get_option('corporate_enabled', 'no') === 'yes'
);
$response = $api->apiVersions();
@ -636,7 +597,8 @@ abstract class WC_Retailcrm_Abstracts_Settings extends WC_Integration
$post = $this->get_post_data();
$api = new WC_Retailcrm_Proxy(
$post[$this->plugin_id . $this->id . '_api_url'],
$value
$value,
$this->get_option('corporate_enabled', 'no') === 'yes'
);
$response = $api->apiVersions();
@ -645,7 +607,7 @@ abstract class WC_Retailcrm_Abstracts_Settings extends WC_Integration
$value = '';
}
if (!$response->isSuccessful()) {
if (empty($response) || !$response->isSuccessful()) {
WC_Admin_Settings::add_error( esc_html__( 'Enter the correct API key', 'retailcrm' ) );
$value = '';
}

File diff suppressed because it is too large Load Diff

View File

@ -15,64 +15,127 @@ if ( ! class_exists( 'WC_Retailcrm_Proxy' ) ) :
class WC_Retailcrm_Proxy
{
protected $retailcrm;
protected $logger;
protected $corporateEnabled;
public function __construct($api_url, $api_key, $api_vers = null)
{
$this->logger = new WC_Logger();
if ( ! class_exists( 'WC_Retailcrm_Client_V4' ) ) {
include_once( __DIR__ . '/class-wc-retailcrm-client-v4.php' );
}
public function __construct($api_url, $api_key, $corporateEnabled = false)
{
$this->corporateEnabled = $corporateEnabled;
if ( ! class_exists( 'WC_Retailcrm_Client_V5' ) ) {
include_once( __DIR__ . '/class-wc-retailcrm-client-v5.php' );
}
switch ($api_vers) {
case 'v4':
$this->retailcrm = new WC_Retailcrm_Client_V4($api_url, $api_key, $api_vers);
break;
case 'v5':
$this->retailcrm = new WC_Retailcrm_Client_V5($api_url, $api_key, $api_vers);
break;
case null:
$this->retailcrm = new WC_Retailcrm_Client_V4($api_url, $api_key, $api_vers);
break;
$this->retailcrm = new WC_Retailcrm_Client_V5($api_url, $api_key, 'v5');
}
/**
* getCorporateEnabled
*
* @return bool
*/
public function getCorporateEnabled()
{
return $this->corporateEnabled;
}
private static function reduceErrors($errors)
{
$result = '';
foreach ($errors as $key => $error) {
$result .= " [$key] => $error";
}
return $result;
}
/**
* Response will be omitted in debug logs for those methods
*
* @return string[]
*/
private function methodsWithoutDebugResponse()
{
$methodsList = array('statusesList', 'paymentTypesList', 'deliveryTypesList', 'orderMethodsList');
foreach ($methodsList as $key => $method) {
$method = get_class($this->retailcrm) . '::' . $method;
$methodsList[$key] = $method;
}
return $methodsList;
}
public function __call($method, $arguments)
{
$result = '';
$response = null;
$called = sprintf('%s::%s', get_class($this->retailcrm), $method);
try {
WC_Retailcrm_Logger::debug(
$called,
array(empty($arguments) ? '[no params]' : print_r($arguments, true))
);
/** @var \WC_Retailcrm_Response $response */
$response = call_user_func_array(array($this->retailcrm, $method), $arguments);
if (!empty($response) && $response->isSuccessful()) {
if (is_string($response)) {
WC_Retailcrm_Logger::debug($called, array($response));
return $response;
}
if (empty($response)) {
WC_Retailcrm_Logger::add(sprintf("[%s] null (no response whatsoever)", $called));
return null;
}
if ($response->isSuccessful()) {
// Don't print long lists in debug logs (errors while calling this will be easy to detect anyway)
// Also don't call useless array_map at all while debug mode is off.
if (retailcrm_is_debug()) {
if (in_array(
$called,
$this->methodsWithoutDebugResponse()
)) {
WC_Retailcrm_Logger::debug($called, array('[request was successful, but response is omitted]'));
} else {
WC_Retailcrm_Logger::debug($called, array($response->getRawResponse()));
}
}
$result = ' Ok';
} else {
$result = sprintf(
$method ." : Error: [HTTP-code %s] %s",
$called ." : Error: [HTTP-code %s] %s",
$response->getStatusCode(),
$response->getErrorMsg()
$response->getErrorString()
);
if (isset($response['errors'])) {
foreach ($response['errors'] as $error) {
$result .= " $error";
}
$result .= self::reduceErrors($response['errors']);
}
WC_Retailcrm_Logger::debug($called, array($response->getErrorString()));
WC_Retailcrm_Logger::debug($called, array($response->getRawResponse()));
}
$this->logger->add('retailcrm', sprintf("[%s] %s", $method, $result));
WC_Retailcrm_Logger::add(sprintf("[%s] %s", $called, $result));
} catch (WC_Retailcrm_Exception_Curl $exception) {
$this->logger->add('retailcrm', sprintf("[%s] %s - %s", $method, $exception->getMessage(), $result));
WC_Retailcrm_Logger::debug(get_class($this->retailcrm).'::'.$called, array($exception->getMessage()));
WC_Retailcrm_Logger::debug('', array($exception->getTraceAsString()));
WC_Retailcrm_Logger::add(sprintf("[%s] %s - %s", $called, $exception->getMessage(), $result));
} catch (WC_Retailcrm_Exception_Json $exception) {
$this->logger->add('retailcrm', sprintf("[%s] %s - %s", $method, $exception->getMessage(), $result));
WC_Retailcrm_Logger::debug(get_class($this->retailcrm).'::'.$called, array($exception->getMessage()));
WC_Retailcrm_Logger::debug('', array($exception->getTraceAsString()));
WC_Retailcrm_Logger::add(sprintf("[%s] %s - %s", $called, $exception->getMessage(), $result));
} catch (InvalidArgumentException $exception) {
$this->logger->add('retailcrm', sprintf("[%s] %s - %s", $method, $exception->getMessage(), $result));
WC_Retailcrm_Logger::debug(get_class($this->retailcrm).'::'.$called, array($exception->getMessage()));
WC_Retailcrm_Logger::debug('', array($exception->getTraceAsString()));
WC_Retailcrm_Logger::add(sprintf("[%s] %s - %s", $called, $exception->getMessage(), $result));
}
return $response;
return !empty($response) ? $response : new WC_Retailcrm_Response(900, '{}');
}
}
endif;

View File

@ -24,6 +24,9 @@ class WC_Retailcrm_Response implements \ArrayAccess
// response assoc array
protected $response;
// response raw data
protected $rawResponse;
/**
* ApiResponse constructor.
*
@ -35,6 +38,7 @@ class WC_Retailcrm_Response implements \ArrayAccess
public function __construct($statusCode, $responseBody = null)
{
$this->statusCode = (int) $statusCode;
$this->rawResponse = $responseBody;
if (!empty($responseBody)) {
$response = json_decode($responseBody, true);
@ -166,4 +170,38 @@ class WC_Retailcrm_Response implements \ArrayAccess
return $this->response[$offset];
}
/**
* Returns error string. If there's multiple errors present - they will be squashed into single string.
*
* @return string
*/
public function getErrorString()
{
if ($this->offsetExists('error')) {
return (string) $this->response['error'];
} elseif ($this->offsetExists('errors') && is_array($this->response['errors'])) {
$errorMessage = '';
foreach ($this->response['errors'] as $error) {
$errorMessage .= $error . ' >';
}
if (strlen($errorMessage) > 2) {
return (string) substr($errorMessage, 0, strlen($errorMessage) - 2);
}
return $errorMessage;
}
return '';
}
/**
* @return mixed|null
*/
public function getRawResponse()
{
return $this->rawResponse;
}
}

View File

@ -17,17 +17,30 @@ if (!class_exists('WC_Retailcrm_Base')) {
*/
class WC_Retailcrm_Base extends WC_Retailcrm_Abstracts_Settings
{
/** @var string */
protected $api_url;
/** @var string */
protected $api_key;
/** @var \WC_Retailcrm_Proxy|WC_Retailcrm_Client_V4|WC_Retailcrm_Client_V5|bool */
protected $apiClient;
/** @var mixed */
protected $order_item;
/** @var mixed */
protected $order_address;
/** @var \WC_Retailcrm_Customers */
protected $customers;
/** @var \WC_Retailcrm_Orders */
protected $orders;
/**
* Init and hook in the integration.
* @param $retailcrm (default = false)
* @param \WC_Retailcrm_Proxy|WC_Retailcrm_Client_V4|WC_Retailcrm_Client_V5|bool $retailcrm (default = false)
*/
public function __construct($retailcrm = false) {
parent::__construct();
@ -83,6 +96,8 @@ if (!class_exists('WC_Retailcrm_Base')) {
add_action('admin_print_footer_scripts', array($this, 'ajax_selected_order'), 99);
add_action('woocommerce_created_customer', array($this, 'create_customer'), 10, 1);
add_action('woocommerce_update_customer', array($this, 'update_customer'), 10, 1);
add_action('user_register', array($this, 'create_customer'), 10, 2);
add_action('profile_update', array($this, 'update_customer'), 10, 2);
add_action('wp_print_scripts', array($this, 'initialize_analytics'), 98);
add_action('wp_print_scripts', array($this, 'initialize_daemon_collector'), 99);
add_action('wp_print_footer_scripts', array($this, 'send_analytics'), 99);
@ -207,11 +222,33 @@ if (!class_exists('WC_Retailcrm_Base')) {
$ids = false;
if (isset($_GET['order_ids_retailcrm'])) {
$appendix = array();
$ids = explode(',', $_GET['order_ids_retailcrm']);
foreach ($ids as $key => $id) {
if (stripos($id, '-') !== false) {
$idSplit = explode('-', $id);
if (count($idSplit) == 2) {
$expanded = array();
$first = (int) $idSplit[0];
$last = (int) $idSplit[1];
for ($i = $first; $i <= $last; $i++) {
$expanded[] = $i;
}
$appendix = array_merge($appendix, $expanded);
unset($ids[$key]);
}
}
}
$ids = array_unique(array_merge($ids, $appendix));
}
if ($ids) {
$this->orders->ordersUpload($ids, true);
$this->orders->ordersUpload($ids);
}
}
@ -229,34 +266,64 @@ if (!class_exists('WC_Retailcrm_Base')) {
update_option(static::$option_key, $options);
}
/**
* Create customer in retailCRM
*
* @param int $customer_id
*
* @return void
* @throws \Exception
*/
/**
* Create customer in retailCRM
*
* @param int $customer_id
*
* @return void
* @throws \Exception
*/
public function create_customer($customer_id)
{
if (WC_Retailcrm_Plugin::history_running() === true) {
return;
}
$client = $this->getApiClient();
$client = $this->getApiClient();
if (empty($client)) {
return;
if (empty($client)) {
return;
}
$wcCustomer = new WC_Customer($customer_id);
$email = $wcCustomer->get_billing_email();
if (empty($email)) {
$email = $wcCustomer->get_email();
}
$wcCustomer = new WC_Customer($customer_id);
$response = $client->customersList(array('email' => $wcCustomer->get_billing_email()));
if ((!empty($response) && $response->isSuccessful()) && isset($response['customers']) && count($response['customers']) > 0) {
return;
if (empty($email)) {
return;
} else {
$wcCustomer->set_billing_email($email);
$wcCustomer->save();
}
$this->customers->createCustomer($customer_id);
$response = $client->customersList(array('email' => $email));
if (!empty($response)
&& $response->isSuccessful()
&& isset($response['customers'])
&& count($response['customers']) > 0
) {
$customers = $response['customers'];
$customer = reset($customers);
if (isset($customer['id'])) {
$this->customers->updateCustomerById($customer_id, $customer['id']);
$builder = new WC_Retailcrm_WC_Customer_Builder();
$builder
->setWcCustomer($wcCustomer)
->setPhones(isset($customer['phones']) ? $customer['phones'] : array())
->setAddress(isset($customer['address']) ? $customer['address'] : false)
->build()
->getResult()
->save();
}
} else {
$this->customers->createCustomer($customer_id);
}
}
/**
@ -269,6 +336,10 @@ if (!class_exists('WC_Retailcrm_Base')) {
return;
}
if (empty($customer_id)) {
return;
}
$this->customers->updateCustomer($customer_id);
}
@ -286,7 +357,10 @@ if (!class_exists('WC_Retailcrm_Base')) {
/**
* Edit order in retailCRM
*
* @param int $order_id
*
* @throws \Exception
*/
public function update_order($order_id)
{
@ -359,7 +433,7 @@ if (!class_exists('WC_Retailcrm_Base')) {
return new WC_Retailcrm_Proxy(
$this->get_option('api_url'),
$this->get_option('api_key'),
$this->get_option('api_version')
$this->get_option('corporate_enabled', 'no') === 'yes'
);
}
@ -375,9 +449,8 @@ if (!class_exists('WC_Retailcrm_Base')) {
{
$api_client = $this->getApiClient();
$clientId = get_option('retailcrm_client_id');
$api_version = $this->get_option('api_version');
WC_Retailcrm_Plugin::integration_module($api_client, $clientId, $api_version, false);
WC_Retailcrm_Plugin::integration_module($api_client, $clientId, false);
delete_option('retailcrm_active_in_crm');
}
@ -394,14 +467,14 @@ if (!class_exists('WC_Retailcrm_Base')) {
$client_id = uniqid();
}
if ($settings['api_url'] && $settings['api_key'] && $settings['api_version']) {
if ($settings['api_url'] && $settings['api_key']) {
$api_client = new WC_Retailcrm_Proxy(
$settings['api_url'],
$settings['api_key'],
$settings['api_version']
$settings['corporate_enabled'] === 'yes'
);
$result = WC_Retailcrm_Plugin::integration_module($api_client, $client_id, $settings['api_version']);
$result = WC_Retailcrm_Plugin::integration_module($api_client, $client_id);
if ($result) {
update_option('retailcrm_active_in_crm', true);

View File

@ -12,11 +12,19 @@ if (!class_exists('WC_Retailcrm_Customers')) :
/**
* Class WC_Retailcrm_Customers
*/
class WC_Retailcrm_Customers {
class WC_Retailcrm_Customers
{
/**
* Administrator role
*/
const ADMIN_ROLE = 'administrator';
/**
* Every customer has this role
*/
const CUSTOMER_ROLE = 'customer';
/** @var bool | WC_Retailcrm_Proxy | \WC_Retailcrm_Client_V5 */
/** @var bool | WC_Retailcrm_Proxy | \WC_Retailcrm_Client_V5 */
protected $retailcrm;
/** @var array */
@ -28,11 +36,20 @@ if (!class_exists('WC_Retailcrm_Customers')) :
/** @var array */
private $customer = array();
/** @var array */
private $customerCorporate = array();
/** @var array */
private $customerCorporateCompany = array();
/** @var array */
private $customerCorporateAddress = array();
/**
* WC_Retailcrm_Customers constructor.
*
* @param bool | WC_Retailcrm_Proxy $retailcrm
* @param array $retailcrm_settings
* @param bool | WC_Retailcrm_Proxy $retailcrm
* @param array $retailcrm_settings
* @param WC_Retailcrm_Customer_Address $customer_address
*/
public function __construct($retailcrm = false, $retailcrm_settings, $customer_address)
@ -42,10 +59,41 @@ if (!class_exists('WC_Retailcrm_Customers')) :
$this->customer_address = $customer_address;
}
/**
* setCustomerAddress
*
* @param $address
*
* @return $this
*/
public function setCustomerAddress($address)
{
if ($address instanceof WC_Retailcrm_Customer_Address) {
$this->customer_address = $address;
}
return $this;
}
/**
* Is corporate customers enabled in provided API
*
* @return bool
*/
public function isCorporateEnabled()
{
if (!$this->retailcrm) {
return false;
}
return $this->retailcrm->getCorporateEnabled();
}
/**
* Upload customers to CRM
*
* @param array $ids
*
* @return array mixed
*/
public function customersUpload($ids = array())
@ -58,7 +106,7 @@ if (!class_exists('WC_Retailcrm_Customers')) :
$data_customers = array();
foreach ($users as $user) {
if (!\in_array(self::CUSTOMER_ROLE, $user->roles)) {
if (!static::isCustomer($user)) {
continue;
}
@ -82,9 +130,12 @@ if (!class_exists('WC_Retailcrm_Customers')) :
*
* @param int | WC_Customer $customer
*
* @param \WC_Order|null $order
*
* @return mixed
* @throws \Exception
*/
public function createCustomer($customer)
public function createCustomer($customer, $order = null)
{
if (!$this->retailcrm) {
return null;
@ -98,8 +149,8 @@ if (!class_exists('WC_Retailcrm_Customers')) :
return null;
}
if ($customer->get_role() == self::CUSTOMER_ROLE) {
$this->processCustomer($customer);
if (self::isCustomer($customer)) {
$this->processCustomer($customer, $order);
$response = $this->retailcrm->customersCreate($this->customer);
if ((!empty($response) && $response->isSuccessful()) && isset($response['id'])) {
@ -111,11 +162,12 @@ if (!class_exists('WC_Retailcrm_Customers')) :
}
/**
* Edit customer in CRM
* Update customer in CRM
*
* @param int $customer_id
* @param $customer_id
*
* @return WC_Customer $customer
* @return void|\WC_Customer
* @throws \Exception
*/
public function updateCustomer($customer_id)
{
@ -125,7 +177,7 @@ if (!class_exists('WC_Retailcrm_Customers')) :
$customer = $this->wcCustomerGet($customer_id);
if ($customer->get_role() == self::CUSTOMER_ROLE){
if (self::isCustomer($customer)) {
$this->processCustomer($customer);
$this->retailcrm->customersEdit($this->customer);
}
@ -133,36 +185,288 @@ if (!class_exists('WC_Retailcrm_Customers')) :
return $customer;
}
/**
* Update customer in CRM by ID
*
* @param int $customer_id
* @param int|string $crmCustomerId
*
* @return void|\WC_Customer
* @throws \Exception
*/
public function updateCustomerById($customer_id, $crmCustomerId)
{
if (!$this->retailcrm) {
return;
}
$customer = $this->wcCustomerGet($customer_id);
if (self::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) {
return null;
}
if (is_int($customer)) {
$customer = $this->wcCustomerGet($customer);
}
if (!$customer instanceof WC_Customer) {
return null;
}
if (self::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
*/
public function fillCorporateAddress($corporateId, $customer, $order = null)
{
$found = false;
$builder = new WC_Retailcrm_Customer_Corporate_Address();
$newAddress = $builder
->setFallbackToShipping(true)
->setIsMain(false)
->setExplicitIsMain(false)
->setWCAddressType(WC_Retailcrm_Abstracts_Address::ADDRESS_TYPE_BILLING)
->build($customer, $order)
->get_data();
$addresses = $this->retailcrm->customersCorporateAddresses(
$corporateId,
array(),
null,
100,
'id'
);
if ($addresses && $addresses->isSuccessful() && $addresses->offsetExists('addresses')) {
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()
);
}
}
/**
* 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 (!$response->isSuccessful() || $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'] = array(
'id' => $response['id'],
);
$this->retailcrm->customersCorporateCompaniesCreate(
$customerId,
$this->customerCorporateCompany,
'id',
$this->retailcrm->getSingleSiteForKey()
);
}
return $customerId;
}
/**
* Process customer
*
* @param WC_Customer $customer
* @param WC_Customer $customer
* @param WC_Order|null $order
*
* @return void
* @throws \Exception
*/
protected function processCustomer($customer)
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 = $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($firstName) && empty($lastName)) {
$firstName = $order->get_shipping_first_name();
$lastName = $order->get_shipping_last_name();
}
if (empty($firstName)) {
$firstName = $customer->get_username();
}
if (empty($email)) {
$email = $order->get_billing_email();
}
if (empty($billingPhone)) {
$order->get_billing_phone();
}
}
if (empty($createdAt)) {
$createdAt = new WC_DateTime();
}
$data_customer = array(
'createdAt' => $createdAt->date('Y-m-d H:i:s'),
'firstName' => $firstName ? $firstName : $customer->get_username(),
'lastName' => $customer->get_last_name(),
'lastName' => $lastName,
'email' => $customer->get_billing_email(),
'address' => $this->customer_address->build($customer)->get_data()
'address' => $this->customer_address->build($customer, $order)->get_data()
);
if ($customer->get_id() > 0) {
$data_customer['externalId'] = $customer->get_id();
}
if ($customer->get_billing_phone()) {
if (!empty($billingPhone)) {
$data_customer['phones'][] = array(
'number' => $customer->get_billing_phone()
'number' => $customer->get_billing_phone()
);
}
$this->customer = apply_filters('retailcrm_process_customer', $data_customer, $customer);
$this->customer = apply_filters(
'retailcrm_process_customer',
WC_Retailcrm_Plugin::clearArray($data_customer),
$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 = array(
'isMain' => true,
'name' => $order->get_billing_company()
);
$data_customer = array(
'nickName' => $order->get_billing_company(),
'customerContacts' => array(
array(
'isMain' => true,
'customer' => array(
'id' => $crmCustomerId
)
)
)
);
$orderAddress = new WC_Retailcrm_Order_Address();
$corpAddress = new WC_Retailcrm_Customer_Corporate_Address();
$address = $orderAddress
->setWCAddressType(WC_Retailcrm_Abstracts_Address::ADDRESS_TYPE_BILLING)
->build($order)
->get_data();
$shippingAddress = $corpAddress
->setWCAddressType(WC_Retailcrm_Abstracts_Address::ADDRESS_TYPE_BILLING)
->setFallbackToBilling(true)
->setIsMain(true)
->build($customer, $order)
->get_data();
if (isset($address['text'])) {
$data_company['contragent']['legalAddress'] = $address['text'];
}
$this->customerCorporate = apply_filters(
'retailcrm_process_customer_corporate',
WC_Retailcrm_Plugin::clearArray($data_customer),
$customer
);
$this->customerCorporateAddress = apply_filters(
'retailcrm_process_customer_corporate_address',
WC_Retailcrm_Plugin::clearArray(array_merge(
$shippingAddress,
array('isMain' => true)
)),
$customer
);
$this->customerCorporateCompany = apply_filters(
'retailcrm_process_customer_corporate_company',
WC_Retailcrm_Plugin::clearArray($data_company),
$customer
);
}
/**
@ -170,7 +474,7 @@ if (!class_exists('WC_Retailcrm_Customers')) :
*
* @return bool|array
*/
public function searchCustomer($filter)
private function searchCustomer($filter)
{
if (isset($filter['externalId'])) {
$search = $this->retailcrm->customersGet($filter['externalId']);
@ -183,15 +487,82 @@ if (!class_exists('WC_Retailcrm_Customers')) :
}
if (!empty($search) && $search->isSuccessful()) {
$customer = false;
if (isset($search['customers'])) {
if (empty($search['customers'])) {
return false;
}
$arrayCustumers = $search['customers'];
$customer = reset($arrayCustumers);
if (isset($filter['email']) && count($filter) == 1) {
foreach ($search['customers'] as $finding) {
if (isset($finding['email']) && $finding['email'] == $filter['email']) {
$customer = $finding;
}
}
} else {
$dataCustomers = $search['customers'];
$customer = reset($dataCustomers);
}
} else {
$customer = $search['customer'];
$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 $customerExternalId
* @param $customerEmailOrPhone
*
* @return array|bool
*/
public function findCustomerEmailOrId($customerExternalId, $customerEmailOrPhone)
{
$customer = false;
if (!empty($customerExternalId)) {
$customer = $this->searchCustomer(array('externalId' => $customerExternalId));
}
if (!$customer && !empty($customerEmailOrPhone)) {
$customer = $this->searchCustomer(array('email' => $customerEmailOrPhone));
}
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;
@ -208,7 +579,7 @@ if (!class_exists('WC_Retailcrm_Customers')) :
*/
public function buildCustomerFromOrderData($order)
{
$new_customer = new WC_Customer;
$new_customer = new WC_Customer();
foreach ($order->get_address('billing') as $prop => $value) {
$new_customer->{'set_billing_' . $prop}($value);
@ -226,6 +597,7 @@ if (!class_exists('WC_Retailcrm_Customers')) :
* @param int $customer_id
*
* @return WC_Customer
* @throws \Exception
*/
public function wcCustomerGet($customer_id)
{
@ -239,5 +611,24 @@ if (!class_exists('WC_Retailcrm_Customers')) :
{
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 static function isCustomer($user)
{
if ($user instanceof WC_Customer) {
return $user->get_role() == self::CUSTOMER_ROLE || $user->get_role() == self::ADMIN_ROLE;
} elseif ($user instanceof WP_User) {
return in_array(self::CUSTOMER_ROLE, $user->roles)
|| in_array(self::ADMIN_ROLE, $user->roles);
}
return false;
}
}
endif;

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
*/
class WC_Retailcrm_Orders
{
/** @var bool|WC_Retailcrm_Proxy */
/** @var bool|WC_Retailcrm_Proxy|\WC_Retailcrm_Client_V5|\WC_Retailcrm_Client_V4 */
protected $retailcrm;
/** @var array */
@ -35,6 +35,9 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
/** @var WC_Retailcrm_Order */
protected $orders;
/** @var array */
private $ordersGetRequestCache = array();
/** @var array */
private $order = array();
@ -62,16 +65,23 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
/**
* Upload orders to CRM
*
* @param bool $withCustomers
* @param array $include
*
* @return array $uploadOrders | null
* @throws \Exception
*/
public function ordersUpload($include = array(), $withCustomers = false)
public function ordersUpload($include = array())
{
if (!$this->retailcrm) {
return null;
}
$uploader = new WC_Retailcrm_Customers(
$this->retailcrm,
$this->retailcrm_settings,
new WC_Retailcrm_Customer_Address()
);
$orders = get_posts(array(
'numberposts' => -1,
'post_type' => wc_get_order_types('view-orders'),
@ -79,46 +89,36 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
'include' => $include
));
$orders_data = array();
$regularUploadErrors = array();
$corporateUploadErrors = array();
foreach ($orders as $data_order) {
$order = wc_get_order($data_order->ID);
$this->processOrder($order);
$customer = $order->get_user();
$customers = array();
if ($customer != false) {
$this->order['customer']['externalId'] = $customer->get('ID');
$errorMessage = $this->orderCreate($data_order->ID);
if ($withCustomers === true) {
$customers[] = $customer->get('ID');
if (is_string($errorMessage)) {
if ($this->retailcrm->getCorporateEnabled() && self::isCorporateOrder($order)) {
$corporateUploadErrors[$data_order->ID] = $errorMessage;
} else {
$regularUploadErrors[$data_order->ID] = $errorMessage;
}
}
$orders_data[] = $this->order;
}
if ($withCustomers === true && !empty($customers)) {
$this->customers->customersUpload($customers);
}
static::logOrdersUploadErrors($regularUploadErrors, 'Error while uploading these regular orders');
static::logOrdersUploadErrors($corporateUploadErrors, 'Error while uploading these corporate orders');
$uploadOrders = array_chunk($orders_data, 50);
foreach ($uploadOrders as $uploadOrder) {
$this->retailcrm->ordersUpload($uploadOrder);
time_nanosleep(0, 250000000);
}
return $uploadOrders;
return array();
}
/**
* Create order
* Create order. Returns wc_get_order data or error string.
*
* @param $order_id
* @param $order_id
*
* @return mixed
* @throws Exception
* @return bool|WC_Order|WC_Order_Refund|string
* @throws \Exception
*/
public function orderCreate($order_id)
{
@ -126,59 +126,155 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
return null;
}
$this->order_payment->reset_data();
$wcOrder = wc_get_order($order_id);
$this->processOrder($wcOrder);
try {
$response = $this->retailcrm->ordersCreate($this->order);
if ($response instanceof WC_Retailcrm_Response) {
if ($response->isSuccessful()) {
return $wcOrder;
}
return $response->getErrorString();
}
} catch (InvalidArgumentException $exception) {
return $exception->getMessage();
}
return $wcOrder;
}
/**
* Process order customer data
*
* @param \WC_Order $wcOrder
* @param bool $update
*
* @return bool Returns false if order cannot be processed
* @throws \Exception
*/
protected function processOrderCustomerInfo($wcOrder, $update = false)
{
$customerWasChanged = false;
$wpUser = $wcOrder->get_user();
if ($wpUser instanceof WP_User) {
$wpUserId = (int)$wpUser->get('ID');
$foundCustomer = $this->customers->searchCustomer(array(
'externalId' => $wpUserId
));
if ($update) {
$response = $this->getCrmOrder($wcOrder->get_id());
if (empty($foundCustomer)) {
$foundCustomer = $this->customers->searchCustomer(array(
'email' => $wcOrder->get_billing_email()
));
}
if (empty($foundCustomer)) {
$customerId = $this->customers->createCustomer($wpUserId);
if (!empty($customerId)) {
$this->order['customer']['id'] = $customerId;
}
} else {
if (!empty($foundCustomer['externalId'])) {
$this->order['customer']['externalId'] = $foundCustomer['externalId'];
} else {
$this->order['customer']['id'] = $foundCustomer['id'];
}
}
} else {
$foundCustomer = $this->customers->searchCustomer(array(
'email' => $wcOrder->get_billing_email()
));
if (empty($foundCustomer)) {
$wcCustomer = $this->customers->buildCustomerFromOrderData($wcOrder);
$customerId = $this->customers->createCustomer($wcCustomer);
if (!empty($customerId)) {
$this->order['customer']['id'] = $customerId;
}
} else {
if (!empty($foundCustomer['externalId'])) {
$this->order['customer']['externalId'] = $foundCustomer['externalId'];
} else {
$this->order['customer']['id'] = $foundCustomer['id'];
}
if (!empty($response)) {
$customerWasChanged = self::isOrderCustomerWasChanged($wcOrder, $response);
}
}
$this->retailcrm->ordersCreate($this->order);
if ($wpUser instanceof WP_User) {
if (!WC_Retailcrm_Customers::isCustomer($wpUser)) {
return false;
}
return $wcOrder;
$wpUserId = (int) $wpUser->get('ID');
if (!$update || ($update && $customerWasChanged)) {
$this->fillOrderCreate($wpUserId, $wpUser->get('billing_email'), $wcOrder);
}
} else {
$wcCustomer = $this->customers->buildCustomerFromOrderData($wcOrder);
if (!$update || ($update && $customerWasChanged)) {
$this->fillOrderCreate(0, $wcCustomer->get_billing_email(), $wcOrder);
}
}
if ($update && $customerWasChanged) {
$this->order['firstName'] = $wcOrder->get_shipping_first_name();
$this->order['lastName'] = $wcOrder->get_shipping_last_name();
}
return true;
}
/**
* Fill order on create
*
* @param int $wcCustomerId
* @param string $wcCustomerEmail
* @param \WC_Order $wcOrder
*
* @throws \Exception
*/
protected function fillOrderCreate($wcCustomerId, $wcCustomerEmail, $wcOrder)
{
$foundCustomerId = '';
$foundCustomer = $this->customers->findCustomerEmailOrId($wcCustomerId, $wcCustomerEmail);
if (empty($foundCustomer)) {
$foundCustomerId = $this->customers->createCustomer($wcCustomerId, $wcOrder);
if (!empty($foundCustomerId)) {
$this->order['customer']['id'] = $foundCustomerId;
}
} else {
$this->order['customer']['id'] = $foundCustomer['id'];
$foundCustomerId = $foundCustomer['id'];
}
$this->order['contragent']['contragentType'] = 'individual';
if ($this->retailcrm->getCorporateEnabled() && static::isCorporateOrder($wcOrder)) {
unset($this->order['contragent']['contragentType']);
$crmCorporate = $this->customers->searchCorporateCustomer(array(
'contactIds' => array($foundCustomerId),
'companyName' => $wcOrder->get_billing_company()
));
if (empty($crmCorporate)) {
$crmCorporate = $this->customers->searchCorporateCustomer(array(
'companyName' => $wcOrder->get_billing_company()
));
}
if (empty($crmCorporate)) {
$corporateId = $this->customers->createCorporateCustomerForOrder(
$foundCustomerId,
$wcCustomerId,
$wcOrder
);
$this->order['customer']['id'] = $corporateId;
} else {
$this->customers->fillCorporateAddress(
$crmCorporate['id'],
new WC_Customer($wcCustomerId),
$wcOrder
);
$this->order['customer']['id'] = $crmCorporate['id'];
}
$companiesResponse = $this->retailcrm->customersCorporateCompanies(
$this->order['customer']['id'],
array(),
null,
null,
'id'
);
if (!empty($companiesResponse) && $companiesResponse->isSuccessful()) {
foreach ($companiesResponse['companies'] as $company) {
if ($company['name'] == $wcOrder->get_billing_company()) {
$this->order['company'] = array(
'id' => $company['id'],
'name' => $company['name']
);
break;
}
}
}
$this->order['contact']['id'] = $foundCustomerId;
}
}
/**
@ -187,6 +283,7 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
* @param int $order_id
*
* @return WC_Order $order | null
* @throws \Exception
*/
public function updateOrder($order_id)
{
@ -194,20 +291,16 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
return null;
}
$order = wc_get_order($order_id);
$this->processOrder($order, true);
if ($this->retailcrm_settings['api_version'] == 'v4') {
$this->order['paymentType'] = $this->retailcrm_settings[$order->get_payment_method()];
}
$wcOrder = wc_get_order($order_id);
$this->processOrder($wcOrder, true);
$response = $this->retailcrm->ordersEdit($this->order);
if ((!empty($response) && $response->isSuccessful()) && $this->retailcrm_settings['api_version'] == 'v5') {
$this->payment = $this->orderUpdatePaymentType($order);
if (!empty($response) && $response->isSuccessful()) {
$this->payment = $this->orderUpdatePaymentType($wcOrder);
}
return $order;
return $wcOrder;
}
/**
@ -223,11 +316,9 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
return null;
}
$response = $this->retailcrm->ordersGet($order->get_id());
if (!empty($response) && $response->isSuccessful()) {
$retailcrmOrder = $response['order'];
$retailcrmOrder = $this->getCrmOrder($order->get_id());
if (!empty($retailcrmOrder)) {
foreach ($retailcrmOrder['payments'] as $payment_data) {
$payment_external_id = explode('-', $payment_data['externalId']);
@ -260,9 +351,10 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
* process to combine order data
*
* @param WC_Order $order
* @param boolean $update
* @param boolean $update
*
* @return void
* @throws \Exception
*/
protected function processOrder($order, $update = false)
{
@ -313,8 +405,12 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
}
}
$order_data['delivery']['address'] = $this->order_address->build($order)->get_data();
$order_items = array();
$order_data['delivery']['address'] = $this->order_address
->setFallbackToBilling(true)
->setWCAddressType(WC_Retailcrm_Abstracts_Address::ADDRESS_TYPE_SHIPPING)
->build($order)
->get_data();
/** @var WC_Order_Item_Product $item */
foreach ($order->get_items() as $item) {
@ -324,12 +420,19 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
$order_data['items'] = $order_items;
if ($this->retailcrm_settings['api_version'] == 'v5' && !$update) {
if (!$update) {
$this->order_payment->is_new = true;
$order_data['payments'][] = $this->order_payment->build($order)->get_data();
}
$this->order = apply_filters('retailcrm_process_order', $order_data, $order);
$this->order = WC_Retailcrm_Plugin::clearArray($order_data);
$this->processOrderCustomerInfo($order, $update);
$this->order = apply_filters(
'retailcrm_process_order',
WC_Retailcrm_Plugin::clearArray($this->order),
$order
);
}
/**
@ -355,6 +458,31 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
return $payment;
}
/**
* ordersGet wrapper with cache (in order to minimize request count).
*
* @param int|string $orderId
* @param bool $cached
*
* @return array
*/
protected function getCrmOrder($orderId, $cached = true)
{
if ($cached && isset($this->ordersGetRequestCache[$orderId])) {
return (array) $this->ordersGetRequestCache[$orderId];
}
$crmOrder = array();
$response = $this->retailcrm->ordersGet($orderId);
if (!empty($response) && $response->isSuccessful() && isset($response['order'])) {
$crmOrder = (array) $response['order'];
$this->ordersGetRequestCache[$orderId] = $crmOrder;
}
return $crmOrder;
}
/**
* @return array
*/
@ -370,5 +498,106 @@ if ( ! class_exists( 'WC_Retailcrm_Orders' ) ) :
{
return $this->payment;
}
/**
* Returns true if provided order is for corporate customer
*
* @param WC_Order $order
*
* @return bool
*/
public static function isCorporateOrder($order)
{
$billingCompany = $order->get_billing_company();
return !empty($billingCompany);
}
/**
* Returns true if passed crm order is corporate
*
* @param array|\ArrayAccess $order
*
* @return bool
*/
public static function isCorporateCrmOrder($order)
{
return (is_array($order) || $order instanceof ArrayAccess)
&& isset($order['customer'])
&& isset($order['customer']['type'])
&& $order['customer']['type'] == 'customer_corporate';
}
/**
* Returns true if customer in order was changed. `true` will be returned if one of these four conditions is met:
*
* 1. If CMS order is corporate and retailCRM order is not corporate or vice versa, then customer obviously
* needs to be updated in retailCRM.
* 2. If billing company from CMS order is not the same as the one in the retailCRM order,
* then company needs to be updated.
* 3. If contact person or individual externalId is different from customer ID in the CMS order, then
* contact person or customer in retailCRM should be updated (even if customer id in the order is not set).
* 4. If contact person or individual email is not the same as the CMS order billing email, then
* contact person or customer in retailCRM should be updated.
*
* @param \WC_Order $wcOrder
* @param array|\ArrayAccess $crmOrder
*
* @return bool
*/
public static function isOrderCustomerWasChanged($wcOrder, $crmOrder)
{
if (!isset($crmOrder['customer'])) {
return false;
}
$customerWasChanged = self::isCorporateOrder($wcOrder) != self::isCorporateCrmOrder($crmOrder);
$synchronizableUserData = self::isCorporateCrmOrder($crmOrder)
? $crmOrder['contact'] : $crmOrder['customer'];
if (!$customerWasChanged) {
if (self::isCorporateCrmOrder($crmOrder)) {
$currentCrmCompany = isset($crmOrder['company']) ? $crmOrder['company']['name'] : '';
if (!empty($currentCrmCompany) && $currentCrmCompany != $wcOrder->get_billing_company()) {
$customerWasChanged = true;
}
}
if (isset($synchronizableUserData['externalId'])
&& $synchronizableUserData['externalId'] != $wcOrder->get_customer_id()
) {
$customerWasChanged = true;
} elseif (isset($synchronizableUserData['email'])
&& $synchronizableUserData['email'] != $wcOrder->get_billing_email()
) {
$customerWasChanged = true;
}
}
return $customerWasChanged;
}
/**
* Logs orders upload errors with prefix log message.
* Array keys must be orders ID's in WooCommerce, values must be strings (error messages).
*
* @param array $errors
* @param string $prefix
*/
public static function logOrdersUploadErrors($errors, $prefix = 'Errors while uploading these orders')
{
if (empty($errors)) {
return;
}
WC_Retailcrm_Logger::add($prefix);
foreach ($errors as $orderId => $error) {
WC_Retailcrm_Logger::add(sprintf("[%d] => %s", $orderId, $error));
}
WC_Retailcrm_Logger::add('==================================');
}
}
endif;

View File

@ -53,6 +53,12 @@ class WC_Retailcrm_Plugin {
}
public function activate() {
if (!class_exists( 'WC_Integration' ) ) {
add_action('admin_notices', array(new WC_Integration_Retailcrm(), 'woocommerce_missing_notice'));
return;
}
if (!class_exists('WC_Retailcrm_Icml')) {
require_once (dirname(__FILE__) . '/class-wc-retailcrm-icml.php');
}
@ -84,13 +90,13 @@ class WC_Retailcrm_Plugin {
/**
* Edit configuration in CRM
*
* @param WC_Retailcrm_Proxy $api_client
* @param WC_Retailcrm_Proxy|\WC_Retailcrm_Client_V4|\WC_Retailcrm_Client_V5 $api_client
* @param string $client_id
* @param bool $active
*
* @return boolean
*/
public static function integration_module($api_client, $client_id, $api_version, $active = true) {
public static function integration_module($api_client, $client_id, $active = true) {
if (!$api_client) {
return false;
@ -103,18 +109,12 @@ class WC_Retailcrm_Plugin {
'active' => $active,
);
if ($api_version == 'v4') {
$configuration['configurationUrl'] = get_site_url();
$configuration['integrationCode'] = self::INTEGRATION_CODE;
$configuration['baseUrl'] = get_site_url();
$configuration['clientId'] = $client_id;
$configuration['accountUrl'] = get_site_url();
$response = $api_client->marketplaceSettingsEdit($configuration);
} else {
$configuration['integrationCode'] = self::INTEGRATION_CODE;
$configuration['baseUrl'] = get_site_url();
$configuration['clientId'] = $client_id;
$configuration['accountUrl'] = get_site_url();
$response = $api_client->integrationModulesEdit($configuration);
}
$response = $api_client->integrationModulesEdit($configuration);
if (!$response) {
return false;
@ -127,6 +127,53 @@ class WC_Retailcrm_Plugin {
return false;
}
/**
* Unset empty fields
*
* @param array $arr input array
*
* @return array
*/
public static function clearArray(array $arr)
{
if (!is_array($arr)) {
return $arr;
}
$result = array();
foreach ($arr as $index => $node) {
$result[$index] = (is_array($node))
? self::clearArray($node)
: $node;
if ($result[$index] === ''
|| $result[$index] === null
|| (is_array($result[$index]) && count($result[$index]) < 1)
) {
unset($result[$index]);
}
}
return $result;
}
/**
* Returns WC_Customer by id. Returns null if there's no such customer.
*
* @param int $id
*
* @return \WC_Customer|null
*/
public static function getWcCustomerById($id)
{
try {
return new WC_Customer($id);
} catch (\Exception $exception) {
return null;
}
}
/**
* Check running history
*
@ -137,4 +184,3 @@ class WC_Retailcrm_Plugin {
return self::$history_run;
}
}

View File

@ -0,0 +1,318 @@
<?php
/**
* Class WC_Retailcrm_Customer_Switcher
* This component provides builder-like interface in order to make it easier to change customer & customer data
* in the order via retailCRM history.
*/
class WC_Retailcrm_Customer_Switcher implements WC_Retailcrm_Builder_Interface
{
/**
* @var \WC_Retailcrm_Customer_Switcher_State $data
*/
private $data;
/**
* @var \WC_Retailcrm_Customer_Switcher_Result|null $result
*/
private $result;
/**
* WC_Retailcrm_Customer_Data_Replacer constructor.
*/
public function __construct()
{
$this->reset();
}
/**
* In fact, this will execute customer change in provided order.
* This will not produce any new entities.
*
* @return $this|\WC_Retailcrm_Builder_Interface
* @throws \WC_Data_Exception
*/
public function build()
{
$this->data->validate();
WC_Retailcrm_Logger::debug(__METHOD__, array('state', $this->data));
$newCustomer = $this->data->getNewCustomer();
$newContact = $this->data->getNewContact();
$newCompany = $this->data->getNewCompanyName();
$companyAddress = $this->data->getCompanyAddress();
if (!empty($newCustomer)) {
WC_Retailcrm_Logger::debug(
__METHOD__,
array(
'Changing to individual customer for order',
$this->data->getWcOrder()->get_id()
)
);
$this->processChangeToRegular($this->data->getWcOrder(), $newCustomer, false);
$this->data->getWcOrder()->set_billing_company('');
} else {
if (!empty($newContact)) {
WC_Retailcrm_Logger::debug(
__METHOD__,
array(
'Changing to contact person customer for order',
$this->data->getWcOrder()->get_id()
)
);
$this->processChangeToRegular($this->data->getWcOrder(), $newContact, true);
}
if (!empty($newCompany)) {
WC_Retailcrm_Logger::debug(
__METHOD__,
array(sprintf(
'Replacing old order id=`%d` company `%s` with new company `%s`',
$this->data->getWcOrder()->get_id(),
$this->data->getWcOrder()->get_billing_company(),
$newCompany
))
);
$this->processCompanyChange();
}
if (!empty($companyAddress)) {
$this->processCompanyAddress();
}
}
return $this;
}
/**
* Change order customer to regular one
*
* @param \WC_Order $wcOrder
* @param array $newCustomer
* @param bool $isContact
*
* @throws \WC_Data_Exception
*/
public function processChangeToRegular($wcOrder, $newCustomer, $isContact)
{
$wcCustomer = null;
WC_Retailcrm_Logger::debug(
__METHOD__,
array(
'Switching in order',
$wcOrder->get_id(),
'to',
$newCustomer
)
);
if (isset($newCustomer['externalId'])) {
$wcCustomer = WC_Retailcrm_Plugin::getWcCustomerById($newCustomer['externalId']);
if (!empty($wcCustomer)) {
$wcOrder->set_customer_id($wcCustomer->get_id());
WC_Retailcrm_Logger::debug(
__METHOD__,
array(
'Set customer to',
$wcCustomer->get_id(),
'in order',
$wcOrder->get_id()
)
);
}
} else {
$wcOrder->set_customer_id(0);
WC_Retailcrm_Logger::debug(
__METHOD__,
array(
'Set customer to 0 (guest) in order',
$wcOrder->get_id()
)
);
}
$fields = array(
'billing_first_name' => self::arrayValue($newCustomer, 'firstName'),
'billing_last_name' => self::arrayValue($newCustomer, 'lastName'),
'billing_email' => self::arrayValue($newCustomer, 'email')
);
foreach ($fields as $field => $value) {
$wcOrder->{'set_' . $field}($value);
}
$address = self::arrayValue($newCustomer, 'address', array());
if ($isContact) {
self::setShippingAddressToOrder($wcOrder, $address);
} else {
self::setBillingAddressToOrder($wcOrder, $address);
self::setShippingAddressToOrder($wcOrder, $address);
}
$wcOrder->set_billing_phone(self::singleCustomerPhone($newCustomer));
$this->result = new WC_Retailcrm_Customer_Switcher_Result($wcCustomer, $wcOrder);
}
/**
* Process company address.
*
* @throws \WC_Data_Exception
*/
protected function processCompanyAddress()
{
$wcOrder = $this->data->getWcOrder();
$companyAddress = $this->data->getCompanyAddress();
if (!empty($companyAddress)) {
self::setBillingAddressToOrder($wcOrder, $companyAddress);
}
if (empty($this->result)) {
$this->result = new WC_Retailcrm_Customer_Switcher_Result(null, $wcOrder);
}
}
/**
* This will update company field in order and create result if it's not set (happens when only company was changed).
*
* @throws \WC_Data_Exception
*/
public function processCompanyChange()
{
$this->data->getWcOrder()->set_billing_company($this->data->getNewCompanyName());
if (empty($this->result)) {
$this->result = new WC_Retailcrm_Customer_Switcher_Result(null, $this->data->getWcOrder());
}
}
/**
* @return $this|\WC_Retailcrm_Builder_Interface
*/
public function reset()
{
$this->data = new WC_Retailcrm_Customer_Switcher_State();
$this->result = null;
return $this;
}
/**
* Set initial state into component
*
* @param \WC_Retailcrm_Customer_Switcher_State $data
*
* @return $this|\WC_Retailcrm_Builder_Interface
*/
public function setData($data)
{
if (!($data instanceof WC_Retailcrm_Customer_Switcher_State)) {
throw new \InvalidArgumentException('Invalid data type');
}
$this->data = $data;
return $this;
}
/**
* @return \WC_Retailcrm_Customer_Switcher_Result|null
*/
public function getResult()
{
return $this->result;
}
/**
* @return \WC_Retailcrm_Customer_Switcher_State
*/
public function getData()
{
return $this->data;
}
/**
* Sets billing address properties in order
*
* @param \WC_Order $wcOrder
* @param array $address
*
* @throws \WC_Data_Exception
*/
private static function setBillingAddressToOrder($wcOrder, $address)
{
$wcOrder->set_billing_state(self::arrayValue($address, 'region', '--'));
$wcOrder->set_billing_postcode(self::arrayValue($address, 'index', '--'));
$wcOrder->set_billing_country(self::arrayValue($address, 'country', '--'));
$wcOrder->set_billing_city(self::arrayValue($address, 'city', '--'));
$wcOrder->set_billing_address_1(self::arrayValue($address, 'text', '--'));
}
/**
* Sets shipping address properties in order
*
* @param \WC_Order $wcOrder
* @param array $address
*
* @throws \WC_Data_Exception
*/
private static function setShippingAddressToOrder($wcOrder, $address)
{
$wcOrder->set_shipping_state(self::arrayValue($address, 'region', '--'));
$wcOrder->set_shipping_postcode(self::arrayValue($address, 'index', '--'));
$wcOrder->set_shipping_country(self::arrayValue($address, 'country', '--'));
$wcOrder->set_shipping_city(self::arrayValue($address, 'city', '--'));
$wcOrder->set_shipping_address_1(self::arrayValue($address, 'text', '--'));
}
/**
* @param array|\ArrayObject|\ArrayAccess $arr
* @param string $key
* @param string $def
*
* @return mixed|string
*/
private static function arrayValue($arr, $key, $def = '')
{
if (!is_array($arr) && !($arr instanceof ArrayObject) && !($arr instanceof ArrayAccess)) {
return $def;
}
if (!array_key_exists($key, $arr) && !empty($arr[$key])) {
return $def;
}
return isset($arr[$key]) ? $arr[$key] : $def;
}
/**
* Returns first phone from order data or null
*
* @param array $customerData
*
* @return string|null
*/
private static function singleCustomerPhone($customerData)
{
if (!array_key_exists('phones', $customerData)) {
return '';
}
if (empty($customerData['phones']) || !is_array($customerData['phones'])) {
return '';
}
$phones = $customerData['phones'];
$phone = reset($phones);
if (!isset($phone['number'])) {
return '';
}
return (string) $phone['number'];
}
}

View File

@ -0,0 +1,402 @@
<?php
/**
* Class WC_Retailcrm_History_Assembler
* Assembles history records into list which closely resembles orders & customers list output from API.
*/
class WC_Retailcrm_History_Assembler
{
/**
* Assembles orders list from history data
*
* @param array $orderHistory
*
* @return array
*/
public static function assemblyOrder($orderHistory)
{
$fields = self::getMappingValues();
$orders = array();
$orderHistory = self::filterHistory($orderHistory, 'order');
foreach ($orderHistory as $change) {
$change['order'] = self::removeEmpty($change['order']);
if(isset($change['order']['items']) && $change['order']['items']) {
$items = array();
foreach($change['order']['items'] as $item) {
if(isset($change['created'])) {
$item['create'] = 1;
}
$items[$item['id']] = $item;
}
$change['order']['items'] = $items;
}
if(isset($change['order']['contragent']['contragentType']) && $change['order']['contragent']['contragentType']) {
$change['order']['contragentType'] = $change['order']['contragent']['contragentType'];
unset($change['order']['contragent']);
}
if (!empty($orders) && isset($orders[$change['order']['id']])) {
$orders[$change['order']['id']] = array_merge($orders[$change['order']['id']], $change['order']);
} else {
$orders[$change['order']['id']] = $change['order'];
}
if (isset($change['item']) && $change['item']) {
if(isset($orders[$change['order']['id']]['items'][$change['item']['id']])) {
$orders[$change['order']['id']]['items'][$change['item']['id']] = array_merge($orders[$change['order']['id']]['items'][$change['item']['id']], $change['item']);
} else {
$orders[$change['order']['id']]['items'][$change['item']['id']] = $change['item'];
}
if ($change['oldValue'] === null
&& $change['field'] == 'order_product'
) {
$orders[$change['order']['id']]['items'][$change['item']['id']]['create'] = true;
}
if ($change['newValue'] === null
&& $change['field'] == 'order_product'
) {
$orders[$change['order']['id']]['items'][$change['item']['id']]['delete'] = true;
}
if (!isset($orders[$change['order']['id']]['items'][$change['item']['id']]['create'])
&& isset($fields['item'][$change['field']])
&& $fields['item'][$change['field']]
) {
$orders[$change['order']['id']]['items'][$change['item']['id']][$fields['item'][$change['field']]] = $change['newValue'];
}
} elseif ($change['field'] == 'payments' && isset($change['payment'])) {
if ($change['newValue'] !== null) {
$orders[$change['order']['id']]['payments'][] = self::newValue($change['payment']);
}
} else {
if (isset($fields['delivery'][$change['field']]) && $fields['delivery'][$change['field']] == 'service') {
$orders[$change['order']['id']]['delivery']['service']['code'] = self::newValue($change['newValue']);
} elseif (isset($fields['delivery'][$change['field']]) && $fields['delivery'][$change['field']]) {
$orders[$change['order']['id']]['delivery'][$fields['delivery'][$change['field']]] = self::newValue($change['newValue']);
} elseif (isset($fields['orderAddress'][$change['field']]) && $fields['orderAddress'][$change['field']]) {
$orders[$change['order']['id']]['delivery']['address'][$fields['orderAddress'][$change['field']]] = $change['newValue'];
} elseif (isset($fields['integrationDelivery'][$change['field']]) && $fields['integrationDelivery'][$change['field']]) {
$orders[$change['order']['id']]['delivery']['service'][$fields['integrationDelivery'][$change['field']]] = self::newValue($change['newValue']);
} elseif (isset($fields['customerContragent'][$change['field']]) && $fields['customerContragent'][$change['field']]) {
$orders[$change['order']['id']][$fields['customerContragent'][$change['field']]] = self::newValue($change['newValue']);
} elseif (strripos($change['field'], 'custom_') !== false) {
$orders[$change['order']['id']]['customFields'][str_replace('custom_', '', $change['field'])] = self::newValue($change['newValue']);
} elseif (isset($fields['order'][$change['field']]) && $fields['order'][$change['field']]) {
$orders[$change['order']['id']][$fields['order'][$change['field']]] = self::newValue($change['newValue']);
}
if (isset($change['created'])) {
$orders[$change['order']['id']]['create'] = 1;
}
if (isset($change['deleted'])) {
$orders[$change['order']['id']]['deleted'] = 1;
}
}
}
return $orders;
}
/**
* Assembles customers list from history changes
*
* @param array $customerHistory
*
* @return array
*/
public static function assemblyCustomer($customerHistory)
{
$customers = array();
$fields = self::getMappingValues(array('customer'));
$fieldsAddress = self::getMappingValues(array('customerAddress'));
$customerHistory = self::filterHistory($customerHistory, 'customer');
foreach ($customerHistory as $change) {
$change['customer'] = self::removeEmpty($change['customer']);
if (isset($change['deleted'])
&& $change['deleted']
&& isset($customers[$change['customer']['id']])
) {
$customers[$change['customer']['id']]['deleted'] = true;
continue;
}
if ($change['field'] == 'id') {
$customers[$change['customer']['id']] = $change['customer'];
}
if (isset($customers[$change['customer']['id']])) {
$customers[$change['customer']['id']] = array_merge($customers[$change['customer']['id']], $change['customer']);
} else {
$customers[$change['customer']['id']] = $change['customer'];
}
if (isset($fields['customer'][$change['field']])
&& $fields['customer'][$change['field']]
) {
$customers[
$change['customer']['id']
][
$fields['customer'][$change['field']]
] = self::newValue($change['newValue']);
}
if (isset($fieldsAddress['customerAddress'][$change['field']])
&& $fieldsAddress['customerAddress'][$change['field']]
) {
if (!isset($customers[$change['customer']['id']]['address'])) {
$customers[$change['customer']['id']]['address'] = array();
}
$customers[
$change['customer']['id']
][
'address'
][
$fieldsAddress['customerAddress'][$change['field']]
] = self::newValue($change['newValue']);
}
// email_marketing_unsubscribed_at old value will be null and new value will be datetime in
// `Y-m-d H:i:s` format if customer was marked as unsubscribed in retailCRM
if (isset($change['customer']['id']) &&
$change['field'] == 'email_marketing_unsubscribed_at'
) {
if ($change['oldValue'] == null && is_string(self::newValue($change['newValue']))) {
$customers[$change['customer']['id']]['subscribed'] = false;
} elseif (is_string($change['oldValue']) && self::newValue($change['newValue']) == null) {
$customers[$change['customer']['id']]['subscribed'] = true;
}
}
}
return $customers;
}
/**
* Assembles corporate customers list from changes
*
* @param array $customerHistory
*
* @return array
*/
public static function assemblyCorporateCustomer($customerHistory)
{
$fields = self::getMappingValues(array('customerCorporate', 'customerAddress'));
$customersCorporate = array();
foreach ($customerHistory as $change) {
$change['customer'] = self::removeEmpty($change['customer']);
if (isset($change['deleted'])
&& $change['deleted']
&& isset($customersCorporate[$change['customer']['id']])
) {
$customersCorporate[$change['customer']['id']]['deleted'] = true;
continue;
}
if (isset($customersCorporate[$change['customer']['id']])) {
if (isset($customersCorporate[$change['customer']['id']]['deleted'])
&& $customersCorporate[$change['customer']['id']]['deleted']
) {
continue;
}
$customersCorporate[$change['customer']['id']] = array_merge(
$customersCorporate[$change['customer']['id']],
$change['customer']
);
} else {
$customersCorporate[$change['customer']['id']] = $change['customer'];
}
if (isset($fields['customerCorporate'][$change['field']])
&& $fields['customerCorporate'][$change['field']]
) {
$customersCorporate[
$change['customer']['id']
][
$fields['customerCorporate'][$change['field']]
] = self::newValue($change['newValue']);
}
if (isset($fields['customerAddress'][$change['field']])
&& $fields['customerAddress'][$change['field']]
) {
if (empty($customersCorporate[$change['customer']['id']]['address'])) {
$customersCorporate[$change['customer']['id']]['address'] = array();
}
$customersCorporate[
$change['customer']['id']
][
'address'
][
$fields['customerAddress'][$change['field']]
] = self::newValue($change['newValue']);
}
if ($change['field'] == 'address') {
$customersCorporate[
$change['customer']['id']
]['address'] = array_merge($change['address'], self::newValue($change['newValue']));
}
}
foreach ($customersCorporate as $id => &$customer) {
if (empty($customer['id']) && !empty($id)) {
$customer['id'] = $id;
$customer['deleted'] = true;
}
}
return $customersCorporate;
}
/**
* Returns mapping data for retailCRM entities. Used to assembly entities from history.
*
* @param array $groupFilter
*
* @return array
*/
private static function getMappingValues($groupFilter = array())
{
$fields = array();
$mappingFile = realpath(implode(
DIRECTORY_SEPARATOR,
array(__DIR__, '..', '..', 'config', 'objects.xml')
));
if (file_exists($mappingFile)) {
$objects = simplexml_load_file($mappingFile);
foreach($objects->fields->field as $object) {
if (empty($groupFilter) || in_array($object["group"], $groupFilter)) {
$fields[(string)$object["group"]][(string)$object["id"]] = (string)$object;
}
}
}
return $fields;
}
/**
* Value accessor
*
* @param array $value
*
* @return mixed
*/
private static function newValue($value)
{
if (isset($value['code'])) {
return $value['code'];
} else {
return $value;
}
}
/**
* Returns array without values which are considered empty
*
* @param array|\ArrayAccess $inputArray
*
* @return array
*/
private static function removeEmpty($inputArray)
{
$outputArray = array();
if (!empty($inputArray)) {
foreach ($inputArray as $key => $element) {
if(!empty($element) || $element === 0 || $element === '0'){
if (is_array($element)) {
$element = self::removeEmpty($element);
}
$outputArray[$key] = $element;
}
}
}
return $outputArray;
}
/**
* Filters out history by these terms:
* - Changes from current API key will be added only if CMS changes are more actual than history.
* - All other changes will be merged as usual.
* It fixes these problems:
* - Changes from current API key are merged when it's not needed.
* - Changes from CRM can overwrite more actual changes from CMS due to ignoring current API key changes.
*
* @param array $historyEntries Raw history from CRM
* @param string $recordType Entity field name, e.g. `customer` or `order`.
*
* @return array
*/
private static function filterHistory($historyEntries, $recordType)
{
$history = array();
$organizedHistory = array();
$notOurChanges = array();
foreach ($historyEntries as $entry) {
if (!isset($entry[$recordType]['externalId'])) {
if ($entry['source'] == 'api'
&& isset($change['apiKey']['current'])
&& $entry['apiKey']['current'] == true
&& $entry['field'] != 'externalId'
) {
continue;
} else {
$history[] = $entry;
}
continue;
}
$externalId = $entry[$recordType]['externalId'];
$field = $entry['field'];
if (!isset($organizedHistory[$externalId])) {
$organizedHistory[$externalId] = array();
}
if (!isset($notOurChanges[$externalId])) {
$notOurChanges[$externalId] = array();
}
if ($entry['source'] == 'api'
&& isset($entry['apiKey']['current'])
&& $entry['apiKey']['current'] == true
) {
if (isset($notOurChanges[$externalId][$field]) || $entry['field'] == 'externalId') {
$organizedHistory[$externalId][] = $entry;
} else {
continue;
}
} else {
$organizedHistory[$externalId][] = $entry;
$notOurChanges[$externalId][$field] = true;
}
}
unset($notOurChanges);
foreach ($organizedHistory as $historyChunk) {
$history = array_merge($history, $historyChunk);
}
return $history;
}
}

View File

@ -0,0 +1,125 @@
<?php
/**
* RetailCRM Integration.
*
* @package WC_Retailcrm_Customers
* @category Integration
* @author RetailCRM
*/
if (!class_exists('WC_Retailcrm_Logger') && class_exists('WC_Log_Levels')):
/**
* Class WC_Retailcrm_Logger
*/
class WC_Retailcrm_Logger
{
/** @var string */
const HANDLE = 'retailcrm';
/**
* @var \WC_Logger_Interface $instance
*/
private static $instance;
/**
* @var array $additionalHandlers
*/
private static $additionalHandlers;
/**
* WC_Retailcrm_Logger constructor.
*/
private function __construct() {}
/**
* Instantiates logger with file handler.
*
* @return \WC_Logger_Interface
*/
private static function getInstance()
{
if (empty(static::$instance)) {
static::$instance = new WC_Logger(self::$additionalHandlers);
}
return static::$instance;
}
/**
* @param array $additionalHandlers
*/
public static function setAdditionalHandlers($additionalHandlers)
{
self::$additionalHandlers = $additionalHandlers;
}
/**
* Regular logging
*
* @param string $message
* @param string $level
*/
public static function add($message, $level = WC_Log_Levels::NOTICE)
{
self::getInstance()->add(self::HANDLE, $message, $level);
}
/**
* Regular logging with caller prefix
*
* @param string $caller
* @param string $message
* @param string $level
*/
public static function addCaller($caller, $message, $level = WC_Log_Levels::NOTICE)
{
self::add(sprintf('<%s> => %s', $caller, $message), $level);
}
/**
* Log error
*
* @param string $message
*/
public static function error($message)
{
self::add($message, WC_Log_Levels::ERROR);
}
/**
* Debug logging. Contains a lot of debug data like full requests & responses.
* This log will work only if debug mode is enabled (see retailcrm_is_debug() for details).
* Caller should be specified, or message will be ignored at all.
*
* @param string $method
* @param array|string $messages
*/
public static function debug($method, $messages)
{
if (retailcrm_is_debug()) {
if (!empty($method) && !empty($messages)) {
$result = is_array($messages) ? substr(
array_reduce(
$messages,
function ($carry, $item) {
$carry .= ' ' . print_r($item, true);
return $carry;
}
),
1
) : $messages;
self::getInstance()->add(
self::HANDLE . '_debug',
sprintf(
'<%s> => %s',
$method,
$result
),
WC_Log_Levels::DEBUG
);
}
}
}
}
endif;

View File

@ -0,0 +1,188 @@
<?php
/**
* Class WC_Retailcrm_Paginated_Request
* It will merge request with pagination data into one big monstrous array.
* Use with caution, it can cause problems with very large data sets (due to memory exhaustion, obviously).
*/
class WC_Retailcrm_Paginated_Request
{
/**
* @var \WC_Retailcrm_Proxy|\WC_Retailcrm_Client_V5
*/
private $api;
/**
* @var string
*/
private $method;
/**
* @var array
*/
private $params;
/**
* @var string
*/
private $dataKey;
/**
* @var int
*/
private $limit;
/**
* @var array
*/
private $data;
/**
* WC_Retailcrm_Paginated_Request constructor.
*/
public function __construct()
{
$this->reset();
}
/**
* Sets retailCRM api client to request
*
* @param \WC_Retailcrm_Proxy|\WC_Retailcrm_Client_V5 $api
*
* @return self
*/
public function setApi($api)
{
$this->api = $api;
return $this;
}
/**
* Sets API client method to request
*
* @param string $method
*
* @return self
*/
public function setMethod($method)
{
$this->method = $method;
return $this;
}
/**
* Sets method params for API client (leave `{{page}}` instead of page and `{{limit}}` instead of limit)
*
* @param array $params
*
* @return self
*/
public function setParams($params)
{
$this->params = $params;
return $this;
}
/**
* Sets dataKey (key with data in response)
*
* @param string $dataKey
*
* @return self
*/
public function setDataKey($dataKey)
{
$this->dataKey = $dataKey;
return $this;
}
/**
* Sets record limit per request
*
* @param int $limit
*
* @return self
*/
public function setLimit($limit)
{
$this->limit = $limit;
return $this;
}
/**
* Executes request
*
* @return $this
*/
public function execute()
{
$this->data = array();
$response = true;
$page = 1;
do {
$response = call_user_func_array(
array($this->api, $this->method),
$this->buildParams($this->params, $page)
);
if ($response instanceof WC_Retailcrm_Response && $response->offsetExists($this->dataKey)) {
$this->data = array_merge($response[$this->dataKey]);
$page = $response['pagination']['currentPage'] + 1;
}
time_nanosleep(0, 300000000);
} while ($response && (isset($response['pagination'])
&& $response['pagination']['currentPage'] < $response['pagination']['totalPageCount']));
return $this;
}
/**
* Returns data
*
* @return array
*/
public function getData()
{
return $this->data;
}
/**
* Reset paginated request
*
* @return $this
*/
public function reset()
{
$this->method = '';
$this->limit = 100;
$this->data = array();
return $this;
}
/**
* buildParams
*
* @param array $placeholderParams
* @param int $currentPage
*
* @return mixed
*/
private function buildParams($placeholderParams, $currentPage)
{
foreach ($placeholderParams as $key => $param) {
if ($param == '{{page}}') {
$placeholderParams[$key] = $currentPage;
}
if ($param == '{{limit}}') {
$placeholderParams[$key] = $this->limit;
}
}
return $placeholderParams;
}
}

View File

@ -17,19 +17,32 @@ class WC_Retailcrm_Customer_Address extends WC_Retailcrm_Abstracts_Address
protected $filter_name = 'customer_address';
/**
* @param WC_Customer $customer
* @param WC_Customer $customer
* @param \WC_Order|null $order
*
* @return self
*/
public function build($customer)
public function build($customer, $order = null)
{
$data = array(
'index' => $customer->get_billing_postcode(),
'countryIso' => $customer->get_billing_country(),
'region' => $this->get_state_name($customer->get_billing_country(), $customer->get_billing_state()),
'city' => $customer->get_billing_city(),
'text' => $customer->get_billing_address_1() . ', ' . $customer->get_billing_address_2()
);
$customerBillingAddress = $customer->get_billing_address();
if ($order instanceof WC_Order && empty($customerBillingAddress)) {
$data = array(
'index' => $order->get_billing_postcode(),
'countryIso' => $order->get_billing_country(),
'region' => $this->get_state_name($order->get_billing_country(), $order->get_billing_state()),
'city' => $order->get_billing_city(),
'text' => $order->get_billing_address_1() . ', ' . $order->get_billing_address_2()
);
} else {
$data = array(
'index' => $customer->get_billing_postcode(),
'countryIso' => $customer->get_billing_country(),
'region' => $this->get_state_name($customer->get_billing_country(), $customer->get_billing_state()),
'city' => $customer->get_billing_city(),
'text' => $customer->get_billing_address_1() . ', ' . $customer->get_billing_address_2()
);
}
$this->set_data_fields($data);

View File

@ -0,0 +1,158 @@
<?php
/**
* PHP version 5.3
*
* @category Integration
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
/**
* Class WC_Retailcrm_Customer_Address
*/
class WC_Retailcrm_Customer_Corporate_Address extends WC_Retailcrm_Abstracts_Address
{
/** @var string $filter_name */
protected $filter_name = 'customer_address';
/** @var bool $isMain */
protected $isMain = true;
/** @var bool $explicitIsMain */
protected $explicitIsMain;
/**
* Sets woocommerce address type to work with
*
* @param string $addressType
*
* @return \WC_Retailcrm_Customer_Corporate_Address
*/
public function setWCAddressType($addressType = WC_Retailcrm_Abstracts_Address::ADDRESS_TYPE_SHIPPING)
{
$this->address_type = $addressType;
return $this;
}
/**
* @param bool $fallback_to_shipping
*
* @return WC_Retailcrm_Customer_Corporate_Address
*/
public function setFallbackToShipping($fallback_to_shipping)
{
$this->fallback_to_shipping = $fallback_to_shipping;
return $this;
}
/**
* @param bool $isMain
*
* @return WC_Retailcrm_Customer_Corporate_Address
*/
public function setIsMain($isMain)
{
$this->isMain = $isMain;
return $this;
}
/**
* @param bool $explicitIsMain
*
* @return WC_Retailcrm_Customer_Corporate_Address
*/
public function setExplicitIsMain($explicitIsMain)
{
$this->explicitIsMain = $explicitIsMain;
return $this;
}
/**
* @param WC_Customer $customer
* @param \WC_Order|null $order
*
* @return self
*/
public function build($customer, $order = null)
{
if ($order instanceof WC_Order) {
$address = $this->getOrderAddress($order);
$data = array(
'index' => $address['postcode'],
'countryIso' => $address['country'],
'region' => $address['state'],
'city' => $address['city'],
'name' => $address['company'],
'text' => $this->joinAddresses($address['address_1'], $address['address_2'])
);
} else if (self::ADDRESS_TYPE_SHIPPING === $this->address_type) {
$data = $this->getCustomerShippingAddress($customer);
if (empty($address) && $this->fallback_to_billing) {
$data = $this->getCustomerBillingAddress($customer);
}
} elseif (self::ADDRESS_TYPE_BILLING === $this->address_type) {
$data = $this->getCustomerBillingAddress($customer);
if (empty($address) && $this->fallback_to_shipping) {
$data = $this->getCustomerShippingAddress($customer);
}
}
if ($this->isMain) {
$data['isMain'] = true;
} elseif ($this->explicitIsMain) {
$data['isMain'] = false;
}
$this->set_data_fields($data);
return $this;
}
/**
* Returns built customer billing address
*
* @param \WC_Customer|\WP_User $customer
*
* @return array
*/
public function getCustomerBillingAddress($customer)
{
return array(
'index' => $customer->get_billing_postcode(),
'countryIso' => $customer->get_billing_country(),
'region' => $customer->get_billing_state(),
'city' => $customer->get_billing_city(),
'name' => $customer->get_billing_company(),
'text' => $this->joinAddresses(
$customer->get_billing_address_1(),
$customer->get_billing_address_2()
)
);
}
/**
* Returns built customer shipping address
*
* @param \WC_Customer|\WP_User $customer
*
* @return array
*/
public function getCustomerShippingAddress($customer)
{
return array(
'index' => $customer->get_shipping_postcode(),
'countryIso' => $customer->get_shipping_country(),
'region' => $customer->get_shipping_state(),
'city' => $customer->get_shipping_city(),
'name' => $customer->get_shipping_company(),
'text' => $this->joinAddresses(
$customer->get_shipping_address_1(),
$customer->get_shipping_address_2()
)
);
}
}

View File

@ -0,0 +1,237 @@
<?php
/**
* Class WC_Retailcrm_WC_Customer_Builder
* It converts retailCRM customer data (array) into WC_Customer
*/
class WC_Retailcrm_WC_Customer_Builder extends WC_Retailcrm_Abstract_Builder
{
/**
* @var \WC_Customer $customer
*/
private $customer;
/**
* WC_Retailcrm_WC_Customer_Builder constructor.
*/
public function __construct()
{
$this->reset();
}
/**
* @param string $firstName
*
* @return $this
*/
public function setFirstName($firstName)
{
$this->data['firstName'] = $firstName;
return $this;
}
/**
* @param string $lastName
*
* @return $this
*/
public function setLastName($lastName)
{
$this->data['lastName'] = $lastName;
return $this;
}
/**
* @param string $email
*
* @return $this
*/
public function setEmail($email)
{
$this->data['email'] = $email;
return $this;
}
/**
* @param string $externalId
*
* @return $this
*/
public function setExternalId($externalId)
{
$this->data['externalId'] = $externalId;
return $this;
}
/**
* @param array $phones
*
* @return $this
*/
public function setPhones($phones)
{
if (self::isPhonesArrayValid($phones)) {
$this->data['phones'] = $phones;
}
return $this;
}
/**
* @param array $address
*
* @return $this
*/
public function setAddress($address)
{
if (is_array($address)) {
$this->data['address'] = $address;
}
return $this;
}
/**
* @param \WC_Customer $customer
*
* @return $this
*/
public function setWcCustomer($customer)
{
if ($customer instanceof WC_Customer) {
$this->customer = $customer;
}
return $this;
}
/**
* Sets provided externalId and loads associated customer from DB (it it exists there).
* Returns true if everything went find; returns false if customer wasn't found.
*
* @param string $externalId
*
* @return bool
* @throws \Exception
*/
public function loadExternalId($externalId)
{
try {
$wcCustomer = new WC_Customer($externalId);
} catch (\Exception $exception) {
return false;
}
$this->setExternalId($externalId);
$this->setWcCustomer($wcCustomer);
return true;
}
public function reset()
{
parent::reset();
$this->customer = new WC_Customer();
return $this;
}
/**
* Fill WC_Customer fields with customer data from retailCRM.
* If field is not present in retailCRM customer - it will remain unchanged.
*
* @return $this|\WC_Retailcrm_Builder_Interface
*/
public function build()
{
$this->checkBuilderValidity();
WC_Retailcrm_Logger::debug(__METHOD__, array('Building WC_Customer from data:', $this->data));
$this->customer->set_first_name($this->dataValue('firstName', $this->customer->get_first_name()));
$this->customer->set_last_name($this->dataValue('lastName', $this->customer->get_last_name()));
$this->customer->set_billing_email($this->dataValue('email', $this->customer->get_billing_email()));
$phones = $this->dataValue('phones', array());
if ((is_array($phones) || $phones instanceof Countable) && count($phones) > 0) {
$phoneData = reset($phones);
if (is_array($phoneData) && isset($phoneData['number'])) {
$this->customer->set_billing_phone($phoneData['number']);
}
} elseif (is_string($phones) || is_numeric($phones)) {
$this->customer->set_billing_phone($phones);
}
$address = $this->dataValue('address');
if (!empty($address)) {
$this->customer->set_billing_state(self::arrayValue(
$address,
'region',
$this->customer->get_billing_state())
);
$this->customer->set_billing_postcode(self::arrayValue(
$address,
'index',
$this->customer->get_billing_postcode())
);
$this->customer->set_billing_country(self::arrayValue(
$address,
'country',
$this->customer->get_billing_country())
);
$this->customer->set_billing_city(self::arrayValue(
$address,
'city',
$this->customer->get_billing_city())
);
}
return $this;
}
/**
* @return mixed|\WC_Customer|null
*/
public function getResult()
{
return $this->customer;
}
/**
* Throws an exception if internal state is not ready for data building.
*
* @throws \RuntimeException
*/
private function checkBuilderValidity()
{
if (empty($this->data)) {
throw new \RuntimeException('Empty data');
}
if (!is_array($this->data)) {
throw new \RuntimeException('Data must be an array');
}
}
/**
* Returns true if provided variable contains array with customer phones.
*
* @param mixed $phones
*
* @return bool
*/
private static function isPhonesArrayValid($phones)
{
if (!is_array($phones)) {
return false;
}
foreach ($phones as $phone) {
if (!is_array($phone) || count($phone) != 1 || !array_key_exists('number', $phone)) {
return false;
}
}
return true;
}
}

View File

@ -67,7 +67,7 @@ function get_wc_shipping_methods() {
);
}
return apply_filters('retailcrm_shipping_list', $result);
return apply_filters('retailcrm_shipping_list', WC_Retailcrm_Plugin::clearArray($result));
}
function retailcrm_get_delivery_service($method_id, $instance_id) {
@ -98,3 +98,13 @@ function retailcrm_get_wc_product($id, $settings) {
return wc_get_product($id);
}
/**
* Returns true if either wordpress debug mode or module debugging is enabled
*
* @return bool
*/
function retailcrm_is_debug() {
return (defined('WP_DEBUG') && WP_DEBUG == true)
|| (defined('RCRM_DEBUG') && RCRM_DEBUG == true);
}

View File

@ -0,0 +1,48 @@
<?php
/**
* Interface WC_Retailcrm_Builder_Interface.
* Main interface for builders. All builders
*/
interface WC_Retailcrm_Builder_Interface {
/**
* Sets data into builder
*
* @param array|mixed $data
*
* @return self
*/
public function setData($data);
/**
* Returns data present in the builder
*
* @return array|mixed
*/
public function getData();
/**
* This method should build result with data present in the builder.
* It should return builder instance in spite of actual building result.
* Any exception can be thrown in case of error. It should be processed accordingly.
*
* @return self
* @throws \Exception
*/
public function build();
/**
* This method should reset builder state.
* In other words, after calling reset() builder inner state should become identical to newly created builder's state.
*
* @return self
*/
public function reset();
/**
* Returns builder result. Can be anything (depends on builder).
*
* @return mixed|null
*/
public function getResult();
}

View File

@ -0,0 +1,77 @@
<?php
/**
* Class WC_Retailcrm_Customer_Switcher_Result
* Holds modified order and customer which was set in the order.
* If customer is null, then only order props was updated. Previous customer (if it was registered)
* will be detached from this order.
*/
class WC_Retailcrm_Customer_Switcher_Result
{
/** @var \WC_Customer|null */
private $wcCustomer;
/** @var \WC_Order $wcOrder */
private $wcOrder;
/**
* WC_Retailcrm_Customer_Switcher_Result constructor.
*
* @param \WC_Customer|null $wcCustomer
* @param \WC_Order $wcOrder
*/
public function __construct($wcCustomer, $wcOrder)
{
$this->wcCustomer = $wcCustomer;
$this->wcOrder = $wcOrder;
if ((!is_null($this->wcCustomer) && !($this->wcCustomer instanceof WC_Customer))
|| !($this->wcOrder instanceof WC_Order)
) {
throw new \InvalidArgumentException(sprintf('Incorrect data provided to %s', __CLASS__));
}
}
/**
* @return \WC_Customer|null
*/
public function getWcCustomer()
{
return $this->wcCustomer;
}
/**
* @return \WC_Order
*/
public function getWcOrder()
{
return $this->wcOrder;
}
/**
* Save customer (if exists) and order.
*
* @return $this
*/
public function save()
{
WC_Retailcrm_Logger::debug(
__METHOD__,
array(
'Saving customer and order:',
$this->wcCustomer,
$this->wcOrder
)
);
if (!empty($this->wcCustomer) && $this->wcCustomer->get_id()) {
$this->wcCustomer->save();
}
if (!empty($this->wcOrder) && $this->wcOrder->get_id()) {
$this->wcOrder->save();
}
return $this;
}
}

View File

@ -0,0 +1,178 @@
<?php
/**
* Class WC_Retailcrm_Customer_Switcher_State
* Holds WC_Retailcrm_Customer_Switcher state. It exists only because we need to comply with builder interface.
*/
class WC_Retailcrm_Customer_Switcher_State
{
/** @var \WC_Order $wcOrder */
private $wcOrder;
/** @var array */
private $newCustomer;
/** @var array */
private $newContact;
/** @var string $newCompanyName */
private $newCompanyName;
/** @var array $companyAddress */
private $companyAddress;
/**
* @return \WC_Order
*/
public function getWcOrder()
{
return $this->wcOrder;
}
/**
* @param \WC_Order $wcOrder
*
* @return WC_Retailcrm_Customer_Switcher_State
*/
public function setWcOrder($wcOrder)
{
$this->wcOrder = $wcOrder;
return $this;
}
/**
* @return array
*/
public function getNewCustomer()
{
return $this->newCustomer;
}
/**
* @param array $newCustomer
*
* @return WC_Retailcrm_Customer_Switcher_State
*/
public function setNewCustomer($newCustomer)
{
$this->newCustomer = $newCustomer;
return $this;
}
/**
* @return array
*/
public function getNewContact()
{
return $this->newContact;
}
/**
* @param array $newContact
*
* @return WC_Retailcrm_Customer_Switcher_State
*/
public function setNewContact($newContact)
{
$this->newContact = $newContact;
return $this;
}
/**
* @return string
*/
public function getNewCompanyName()
{
return $this->newCompanyName;
}
/**
* @param string $newCompanyName
*
* @return WC_Retailcrm_Customer_Switcher_State
*/
public function setNewCompanyName($newCompanyName)
{
$this->newCompanyName = $newCompanyName;
return $this;
}
/**
* @return array
*/
public function getCompanyAddress()
{
return $this->companyAddress;
}
/**
* @param array $companyAddress
*
* @return WC_Retailcrm_Customer_Switcher_State
*/
public function setCompanyAddress($companyAddress)
{
$this->companyAddress = $companyAddress;
return $this;
}
/**
* @param array $newCompany
*
* @return WC_Retailcrm_Customer_Switcher_State
*/
public function setNewCompany($newCompany)
{
if (isset($newCompany['name'])) {
$this->setNewCompanyName($newCompany['name']);
}
if (isset($newCompany['address']) && !empty($newCompany['address'])) {
$this->setCompanyAddress($newCompany['address']);
}
return $this;
}
/**
* Returns true if current state may be processable (e.g. when customer or related data was changed).
* It doesn't guarantee state validity.
*
* @return bool
*/
public function feasible()
{
return !(empty($this->newCustomer) && empty($this->newContact) && empty($this->newCompanyName));
}
/**
* Throws an exception if state is not valid
*
* @throws \InvalidArgumentException
* @return void
*/
public function validate()
{
if (empty($this->wcOrder)) {
throw new \InvalidArgumentException('Empty WC_Order.');
}
if (empty($this->newCustomer) && empty($this->newContact) && empty($this->newCompanyName)) {
throw new \InvalidArgumentException('New customer, new contact and new company is empty.');
}
if (!empty($this->newCustomer) && !empty($this->newContact)) {
WC_Retailcrm_Logger::debug(
__METHOD__,
array(
'State data (customer and contact):' . PHP_EOL,
$this->getNewCustomer(),
$this->getNewContact()
)
);
throw new \InvalidArgumentException(
'Too much data in state - cannot determine which customer should be used.'
);
}
}
}

View File

@ -11,6 +11,7 @@
class WC_Retailcrm_Order_Address extends WC_Retailcrm_Abstracts_Address
{
/** @var string $filter_name */
protected $filter_name = 'order_address';
/**
@ -20,7 +21,7 @@ class WC_Retailcrm_Order_Address extends WC_Retailcrm_Abstracts_Address
*/
public function build($order)
{
$address = $order->get_address('shipping');
$address = $this->getOrderAddress($order);
if (!empty($address)) {
$data = array(

View File

@ -65,12 +65,7 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data
$this->set_data_fields($data);
$this->set_offer($item);
if ($this->settings['api_version'] == 'v5' && round($discount_price, 2)) {
$this->set_data_field('discountManualAmount',round($discount_price, 2));
} elseif ($this->settings['api_version'] == 'v4' && round($discount_price, 2)) {
$this->set_data_field('discount', round($discount_price, 2));
}
$this->set_data_field('discountManualAmount', (float) round($discount_price, 2));
return $this;
}
@ -114,6 +109,7 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data
* @param $price
*
* @return float|int
* @todo Rounding issues with prices in pennies / cents should be expected here.
*/
private function calculate_discount(WC_Order_Item_Product $item, $price)
{

View File

@ -50,6 +50,7 @@ class WC_Retailcrm_Order_Payment extends WC_Retailcrm_Abstracts_Data
*/
public function build($order, $externalId = false)
{
$this->reset_data();
$data = array();
if (!empty($this->settings['send_payment_amount'])
@ -79,6 +80,8 @@ class WC_Retailcrm_Order_Payment extends WC_Retailcrm_Abstracts_Data
if ($this->is_new) {
if (isset($this->settings[$order->get_payment_method()])) {
$data['type'] = $this->settings[$order->get_payment_method()];
} else {
$data = array();
}
}
@ -87,6 +90,28 @@ class WC_Retailcrm_Order_Payment extends WC_Retailcrm_Abstracts_Data
return $this;
}
/**
* Returns false if payment doesn't have method
*
* @return array
*/
public function get_data()
{
$data = parent::get_data();
if (empty($data['type'])) {
return array();
}
if (!empty($this->settings['send_payment_amount'])
&& $this->settings['send_payment_amount'] === WC_Retailcrm_Base::NO
) {
unset($data['amount']);
}
return $data;
}
public function reset_data()
{
$this->data = array(

View File

@ -64,13 +64,14 @@ class WC_Retailcrm_Order extends WC_Retailcrm_Abstracts_Data
'countryIso' => $order->get_shipping_country()
);
if ($data['countryIso'] == '--') {
$countries = new WC_Countries();
$data['countryIso'] = $countries->get_base_country();
}
$this->set_data_fields($data);
$this->set_number($order);
if ($this->settings['api_version'] != 'v5') {
$this->set_payment_data($order);
}
if (isset($this->settings[$order->get_status()])) {
$this->set_data_field('status', $this->settings[$order->get_status()]);
}

Binary file not shown.

Binary file not shown.

View File

@ -45,6 +45,10 @@ API-ключ должен быть для отдельного магазина
2. В появившихся списках справочников настройте соответствие способов доставки и оплаты, а так же статусов заказов. Отметьте галочку "Выгружать остатки", если хотите выгружать остатки из Retailcrm в магазин (подробнее смотрите в описании).
== Changelog ==
= 4.0.0 =
* Поддержка корпоративных клиентов
* Поддержка изменения покупателя в заказе
= 3.6.4 =
* Передача названия региона / штата / провинции вместо кода

View File

@ -1,6 +1,6 @@
<?php
/**
* Version: 3.6.4
* Version: 4.0.0
* WC requires at least: 3.0
* WC tested up to: 3.9.3
* Plugin Name: WooCommerce retailCRM
@ -21,6 +21,8 @@ if (!class_exists( 'WC_Integration_Retailcrm')) :
* Class WC_Integration_Retailcrm
*/
class WC_Integration_Retailcrm {
const WOOCOMMERCE_SLUG = 'woocommerce';
const WOOCOMMERCE_PLUGIN_PATH = 'woocommerce/woocommerce.php';
private static $instance;
@ -38,17 +40,8 @@ if (!class_exists( 'WC_Integration_Retailcrm')) :
public function __construct() {
$this->load_plugin_textdomain();
if (class_exists( 'WC_Integration' ) ) {
require_once(dirname(__FILE__ ) . '/include/abstracts/class-wc-retailcrm-abstracts-settings.php');
require_once(dirname(__FILE__ ) . '/include/abstracts/class-wc-retailcrm-abstracts-data.php');
require_once(dirname(__FILE__ ) . '/include/abstracts/class-wc-retailcrm-abstracts-address.php');
require_once(dirname(__FILE__ ) . '/include/order/class-wc-retailcrm-order.php');
require_once(dirname(__FILE__ ) . '/include/order/class-wc-retailcrm-order-payment.php');
require_once(dirname(__FILE__ ) . '/include/order/class-wc-retailcrm-order-item.php');
require_once(dirname(__FILE__ ) . '/include/order/class-wc-retailcrm-order-address.php');
require_once(dirname(__FILE__ ) . '/include/customer/class-wc-retailcrm-customer-address.php');
require_once(dirname(__FILE__ ) . '/include/class-wc-retailcrm-base.php');
require_once(dirname(__FILE__ ) . '/include/functions.php');
if (class_exists( 'WC_Integration' )) {
self::load_module();
add_filter('woocommerce_integrations', array( $this, 'add_integration'));
} else {
add_action('admin_notices', array($this, 'woocommerce_missing_notice'));
@ -56,7 +49,30 @@ if (!class_exists( 'WC_Integration_Retailcrm')) :
}
public function woocommerce_missing_notice() {
echo '<div class="error"><p>Woocommerce is not installed</p></div>';
if (static::isWooCommerceInstalled()) {
if (!is_plugin_active(static::WOOCOMMERCE_PLUGIN_PATH)) {
echo '
<div class="error">
<p>
Activate WooCommerce in order to enable retailCRM integration!
<a href="' . wp_nonce_url(admin_url('plugins.php')) . '" aria-label="Activate WooCommerce">
Click here to open plugins manager
</a>
</p>
</div>
';
}
} else {
echo '
<div class="error">
<p>
<a href="'
. static::generatePluginInstallationUrl(static::WOOCOMMERCE_SLUG)
. '" aria-label="Install WooCommerce">Install WooCommerce</a> in order to enable retailCRM integration!
</p>
</div>
';
}
}
public function load_plugin_textdomain() {
@ -74,6 +90,78 @@ if (!class_exists( 'WC_Integration_Retailcrm')) :
$integrations[] = 'WC_Retailcrm_Base';
return $integrations;
}
/**
* Loads module classes.
*/
public static function load_module()
{
require_once(dirname(__FILE__) . '/include/interfaces/class-wc-retailcrm-builder-interface.php');
require_once(dirname(__FILE__) . '/include/models/class-wc-retailcrm-customer-switcher-state.php');
require_once(dirname(__FILE__) . '/include/models/class-wc-retailcrm-customer-switcher-result.php');
require_once(dirname(__FILE__ ) . '/include/components/class-wc-retailcrm-logger.php');
require_once(dirname(__FILE__ ) . '/include/components/class-wc-retailcrm-history-assembler.php');
require_once(dirname(__FILE__ ) . '/include/components/class-wc-retailcrm-paginated-request.php');
require_once(dirname(__FILE__) . '/include/components/class-wc-retailcrm-customer-switcher.php');
require_once(dirname(__FILE__ ) . '/include/abstracts/class-wc-retailcrm-abstract-builder.php');
require_once(dirname(__FILE__ ) . '/include/abstracts/class-wc-retailcrm-abstracts-settings.php');
require_once(dirname(__FILE__ ) . '/include/abstracts/class-wc-retailcrm-abstracts-data.php');
require_once(dirname(__FILE__ ) . '/include/abstracts/class-wc-retailcrm-abstracts-address.php');
require_once(dirname(__FILE__ ) . '/include/customer/woocommerce/class-wc-retailcrm-wc-customer-builder.php');
require_once(dirname(__FILE__ ) . '/include/order/class-wc-retailcrm-order.php');
require_once(dirname(__FILE__ ) . '/include/order/class-wc-retailcrm-order-payment.php');
require_once(dirname(__FILE__ ) . '/include/order/class-wc-retailcrm-order-item.php');
require_once(dirname(__FILE__ ) . '/include/order/class-wc-retailcrm-order-address.php');
require_once(dirname(__FILE__ ) . '/include/customer/class-wc-retailcrm-customer-address.php');
require_once(dirname(__FILE__ ) . '/include/customer/class-wc-retailcrm-customer-corporate-address.php');
require_once(dirname(__FILE__ ) . '/include/class-wc-retailcrm-base.php');
require_once(dirname(__FILE__ ) . '/include/functions.php');
}
/**
* Returns true if WooCommerce was found in plugin cache
*
* @return bool
*/
private function isWooCommerceInstalled()
{
$plugins = wp_cache_get( 'plugins', 'plugins' );
if (!$plugins) {
$plugins = get_plugins();
} elseif (isset($plugins[''])) {
$plugins = $plugins[''];
}
if (!isset($plugins[static::WOOCOMMERCE_PLUGIN_PATH])) {
return false;
}
return true;
}
/**
* Generate plugin installation url
*
* @param $pluginSlug
*
* @return string
*/
private function generatePluginInstallationUrl($pluginSlug)
{
$action = 'install-plugin';
return wp_nonce_url(
add_query_arg(
array(
'action' => $action,
'plugin' => $pluginSlug
),
admin_url( 'update.php' )
),
$action.'_'.$pluginSlug
);
}
}
if (!class_exists('WC_Retailcrm_Plugin')) {

View File

@ -15,7 +15,7 @@
*
*
* @link https://wordpress.org/plugins/woo-retailcrm/
* @version 3.6.4
* @version 4.0.0
*
* @package RetailCRM
*/

0
tests/bin/install.sh Executable file → Normal file
View File

View File

@ -1,6 +1,7 @@
<?php
$_tests_dir = getenv('WP_TESTS_DIR');
$_output_logs_to_stdout = getenv('MODULE_LOGS_TO_STDOUT');
$_wcOldBootstrap = '/tmp/woocommerce/tests/bootstrap.php';
$_wcNewBootstrap = '/tmp/woocommerce/tests/legacy/bootstrap.php';
@ -32,6 +33,13 @@ if (file_exists($_wcOldBootstrap)) {
}
$plugin_dir = dirname(dirname(__FILE__)) . '/';
// helpers
require $plugin_dir . 'src/include/components/class-wc-retailcrm-logger.php';
require $plugin_dir . 'tests/helpers/class-wc-retailcrm-response-helper.php';
require $plugin_dir . 'tests/helpers/class-wc-retailcrm-test-case-helper.php';
require $plugin_dir . 'tests/helpers/class-wc-retailcrm-log-handler-stdout.php';
if (!empty($_output_logs_to_stdout) && $_output_logs_to_stdout == '1') {
WC_Retailcrm_Logger::setAdditionalHandlers(array(new WC_Retailcrm_Log_Handler_Stdout()));
}

View File

@ -0,0 +1,88 @@
<?php
/**
* PHP version 5.3
*
* @category Integration
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
class WC_Retailcrm_Customer_Switcher_State_Test extends WC_Retailcrm_Test_Case_Helper
{
public function test_feasible()
{
$state = new WC_Retailcrm_Customer_Switcher_State();
$this->assertFalse($state->feasible());
$state->setNewCustomer(array())
->setNewContact(array())
->setNewCompanyName('');
$this->assertFalse($state->feasible());
$state->setNewCustomer(array('id' => 1));
$this->assertTrue($state->feasible());
$state->setNewCustomer(array())
->setNewContact(array('id' => 1));
$this->assertTrue($state->feasible());
$state->setNewCustomer(array())
->setNewContact(array())
->setNewCompanyName('test');
$this->assertTrue($state->feasible());
$state->setNewCustomer(array())
->setNewContact(array())
->setNewCompany(array('name' => 'test'));
$this->assertTrue($state->feasible());
$state->setNewCustomer(array())
->setNewContact(array())
->setNewCompanyName('');
$this->assertFalse($state->feasible());
}
/**
* @expectedException \InvalidArgumentException
*/
public function test_validate_empty()
{
$state = new WC_Retailcrm_Customer_Switcher_State();
$state->validate();
}
/**
* @expectedException \InvalidArgumentException
*/
public function test_validate_order()
{
$state = new WC_Retailcrm_Customer_Switcher_State();
$state->setWcOrder(new WC_Order())
->validate();
}
/**
* @expectedException \InvalidArgumentException
*/
public function test_validate_customer_and_contact_set()
{
$state = new WC_Retailcrm_Customer_Switcher_State();
$state->setWcOrder(new WC_Order())
->setNewCustomer(array('id' => 1))
->setNewContact(array('id' => 1))
->validate();
}
/**
* @@doesNotPerformAssertions
*/
public function test_validate_ok()
{
$state = new WC_Retailcrm_Customer_Switcher_State();
$state->setWcOrder(new WC_Order())
->setNewCustomer(array('id' => 1))
->validate();
}
}

View File

@ -0,0 +1,137 @@
<?php
/**
* PHP version 5.3
*
* @category Integration
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
class WC_Retailcrm_WC_Customer_Builder_Test extends WC_Retailcrm_Test_Case_Helper
{
/**
* @expectedException \RuntimeException
*/
public function test_empty()
{
$builder = new WC_Retailcrm_WC_Customer_Builder();
$builder->build();
}
/**
* @expectedException \RuntimeException
*/
public function test_empty_array()
{
$builder = new WC_Retailcrm_WC_Customer_Builder();
$builder->setData(array())->build();
}
/**
* @expectedException \RuntimeException
*/
public function test_not_array()
{
$builder = new WC_Retailcrm_WC_Customer_Builder();
$builder->setData(new stdClass())->build();
}
/**
* @dataProvider customerData
*
* @param array $customerData
*/
public function test_build($customerData)
{
$builder = new WC_Retailcrm_WC_Customer_Builder();
$wcCustomer = $builder->setData($customerData)->build()->getResult();
$this->assertInstanceOf('\WC_Customer', $wcCustomer);
if (isset($customerData['firstName'])) {
$this->assertEquals($customerData['firstName'], $wcCustomer->get_first_name());
}
if (isset($customerData['lastName'])) {
$this->assertEquals($customerData['lastName'], $wcCustomer->get_last_name());
}
if (isset($customerData['email'])) {
$this->assertEquals($customerData['email'], $wcCustomer->get_billing_email());
}
if (isset($customerData['phones']) && count($customerData['phones']) > 0) {
$this->assertEquals($customerData['phones'][0]['number'], $wcCustomer->get_billing_phone());
}
if (isset($customerData['address']) && !empty($customerData['address'])) {
$address = $customerData['address'];
if (isset($address['region'])) {
$this->assertEquals($address['region'], $wcCustomer->get_billing_state());
}
if (isset($address['index'])) {
$this->assertEquals($address['index'], $wcCustomer->get_billing_postcode());
}
if (isset($address['country'])) {
$this->assertEquals($address['country'], $wcCustomer->get_billing_country());
}
if (isset($address['city'])) {
$this->assertEquals($address['city'], $wcCustomer->get_billing_city());
}
}
}
public function customerData()
{
return array(
array(
'customer' => array(
'type' => 'customer',
'id' => 4228,
'externalId' => '2',
)
),
array(
'customer' => array(
'type' => 'customer',
'id' => 4228,
'externalId' => '2',
'isContact' => false,
'createdAt' => '2020-06-01 15:31:46',
'managerId' => 27,
'vip' => false,
'bad' => false,
'site' => 'bitrix-test',
'contragent' => array(
'contragentType' => 'individual',
),
'tags' => array(),
'marginSumm' => 9412,
'totalSumm' => 9412,
'averageSumm' => 9412,
'ordersCount' => 1,
'costSumm' => 0,
'customFields' => array(),
'personalDiscount' => 0,
'cumulativeDiscount' => 0,
'address' => array(
'id' => 3132,
'text' => 'ул. Пушкина дом Колотушкина',
),
'segments' => array(),
'firstName' => 'tester001',
'lastName' => 'tester001',
'email' => 'tester001@example.com',
'emailMarketingUnsubscribedAt' => '2020-06-01 15:34:23',
'phones' => array(array('number' => '2354708915097'))
)
)
);
}
}

View File

@ -0,0 +1,117 @@
<?php
if (!defined( 'ABSPATH' ) ) {
exit;
}
/**
* Handles log entries by writing to a stdout
*
* @class WC_Retailcrm_Log_Handler_Stdout
*/
class WC_Retailcrm_Log_Handler_Stdout extends WC_Log_Handler
{
/**
* @var array
*/
protected $cached_logs = array();
/**
* Constructor for the logger.
*/
public function __construct()
{
add_action('plugins_loaded', array($this, 'write_cached_logs'));
}
/**
* @param int $timestamp
* @param string $level
* @param string $message
* @param array $context
*
* @return bool
*/
public function handle( $timestamp, $level, $message, $context )
{
if (isset( $context['source'] ) && $context['source']) {
$handle = $context['source'];
} else {
$handle = 'log';
}
$entry = self::format_entry($timestamp, $level, $message, $context);
return $this->add($entry, $handle);
}
/**
* @param int $timestamp
* @param string $level
* @param string $message
* @param array $context
*
* @return string
*/
protected static function format_entry($timestamp, $level, $message, $context)
{
if (isset($context['_legacy'] ) && true === $context['_legacy']) {
if (isset($context['source']) && $context['source']) {
$handle = $context['source'];
} else {
$handle = 'log';
}
$message = apply_filters('woocommerce_logger_add_message', $message, $handle);
$time = date_i18n('m-d-Y @ H:i:s');
$entry = sprintf('%s - %s', $time, $message);
} else {
$entry = parent::format_entry( $timestamp, $level, $message, $context );
}
return $entry;
}
/**
* @param string $entry Log entry text.
* @param string $handle Log entry handle.
*
* @return bool True if write was successful.
*/
protected function add($entry, $handle)
{
$result = false;
if (is_resource(STDOUT)) {
$result = fwrite(STDOUT, $entry . PHP_EOL);
} else {
$this->cache_log($entry, $handle);
}
return false !== $result;
}
/**
* Cache log to write later.
*
* @param string $entry Log entry text.
* @param string $handle Log entry handle.
*/
protected function cache_log($entry, $handle)
{
$this->cached_logs[] = array(
'entry' => $entry,
'handle' => $handle,
);
}
/**
* Write cached logs.
*/
public function write_cached_logs()
{
foreach ($this->cached_logs as $log) {
$this->add($log['entry'], $log['handle']);
}
}
}

View File

@ -6,20 +6,19 @@
class WC_Retailcrm_Test_Case_Helper extends WC_Unit_Test_Case
{
/**
* @param string $apiVersion
*
* @return array
*/
protected function setOptions($apiVersion = 'v5')
protected function setOptions()
{
$options = array(
'api_url' => 'https://example.retailcrm.ru',
'api_key' => 'dhsHJGYdjkHHJKJSGjhasjhgajsgJGHsg',
'api_version' => $apiVersion,
'api_version' => 'v5',
'p_draft' => 'no',
'p_pending' => 'no',
'p_private' => 'no',
'p_publish' => 'no',
'send_payment_amount' => 'yes',
'order_methods' => '',
'flat_rate_shipping' => 'delivery',
'free_shipping' => 'delivery2',
@ -48,6 +47,43 @@ class WC_Retailcrm_Test_Case_Helper extends WC_Unit_Test_Case
return $options;
}
/**
* Removes all data from the DB.
*/
protected function deleteAllData()
{
if (function_exists('_delete_all_data')) {
_delete_all_data();
} else {
global $wpdb;
foreach ( array(
$wpdb->posts,
$wpdb->postmeta,
$wpdb->comments,
$wpdb->commentmeta,
$wpdb->term_relationships,
$wpdb->termmeta,
) as $table ) {
//phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$wpdb->query( "DELETE FROM {$table}" );
}
foreach ( array(
$wpdb->terms,
$wpdb->term_taxonomy,
) as $table ) {
//phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$wpdb->query( "DELETE FROM {$table} WHERE term_id != 1" );
}
$wpdb->query( "UPDATE {$wpdb->term_taxonomy} SET count = 0" );
$wpdb->query( "DELETE FROM {$wpdb->users} WHERE ID != 1" );
$wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE user_id != 1" );
}
}
/**
* @return array
*/

View File

@ -0,0 +1,51 @@
<?php
/**
* PHP version 5.3
*
* @category Integration
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
class WC_Retailcrm_Customer_Switcher_Result_Test extends WC_Retailcrm_Test_Case_Helper
{
/**
* @expectedException \InvalidArgumentException
*/
public function test_invalid_both()
{
new WC_Retailcrm_Customer_Switcher_Result(new stdClass(), new stdClass());
}
/**
* @expectedException \InvalidArgumentException
*/
public function test_invalid_customer()
{
new WC_Retailcrm_Customer_Switcher_Result(new stdClass(), new WC_Order());
}
/**
* @expectedException \InvalidArgumentException
*/
public function test_invalid_order()
{
new WC_Retailcrm_Customer_Switcher_Result(new WC_Customer(), new stdClass());
}
public function test_valid()
{
$result = new WC_Retailcrm_Customer_Switcher_Result(new WC_Customer(), new WC_Order());
$this->assertInstanceOf('\WC_Customer', $result->getWcCustomer());
$this->assertInstanceOf('\WC_Order', $result->getWcOrder());
}
public function test_valid_no_customer()
{
$result = new WC_Retailcrm_Customer_Switcher_Result(null, new WC_Order());
$this->assertEmpty($result->getWcCustomer())
; $this->assertInstanceOf('\WC_Order', $result->getWcOrder());
}
}

View File

@ -26,8 +26,11 @@ class WC_Retailcrm_Order_Address_Test extends WC_Retailcrm_Test_Case_Helper
public function test_build()
{
$order_address = new WC_Retailcrm_Order_Address;
$data = $order_address->build($this->order)->get_data();
$order_address = new WC_Retailcrm_Order_Address();
$data = $order_address
->setWCAddressType(WC_Retailcrm_Abstracts_Address::ADDRESS_TYPE_BILLING)
->build($this->order)
->get_data();
$this->assertArrayHasKey('index', $data);
$this->assertArrayHasKey('city', $data);

View File

@ -19,6 +19,7 @@ class WC_Retailcrm_Order_Payment_Test extends WC_Retailcrm_Test_Case_Helper
parent::setUp();
$this->order = WC_Helper_Order::create_order();
$this->setOptions();
}
/**
@ -28,11 +29,18 @@ class WC_Retailcrm_Order_Payment_Test extends WC_Retailcrm_Test_Case_Helper
*/
public function test_build($externalId)
{
$order_payment = new WC_Retailcrm_Order_Payment($this->getOptions());
$settings = $this->getOptions();
$settings['send_payment_amount'] = 'no';
$order_payment = new WC_Retailcrm_Order_Payment($settings);
$data = $order_payment->build($this->order, $externalId)->get_data();
$this->assertArrayHasKey('externalId', $data);
$this->assertNotEmpty($data);
if (!empty($externalId)) {
$this->assertArrayHasKey('externalId', $data);
}
$this->assertArrayHasKey('type', $data);
$this->assertArrayNotHasKey('amount', $data);
$this->assertArrayHasKey('order', $data);
@ -51,8 +59,13 @@ class WC_Retailcrm_Order_Payment_Test extends WC_Retailcrm_Test_Case_Helper
$data = $order_payment->build($this->order, $externalId)->get_data();
$this->assertArrayHasKey('externalId', $data);
$this->assertArrayHasKey('type', $data);
$this->assertNotEmpty($data);
if (!empty($externalId)) {
$this->assertArrayHasKey('externalId', $data);
}
$this->assertArrayHasKey('type', $data);
$this->assertArrayHasKey('amount', $data);
$this->assertArrayHasKey('order', $data);
}

View File

@ -45,7 +45,6 @@ class WC_Retailcrm_Base_Test extends WC_Retailcrm_Test_Case_Helper
$this->assertInternalType('array', $this->unit->form_fields);
$this->assertArrayHasKey('api_url', $this->unit->form_fields);
$this->assertArrayHasKey('api_key', $this->unit->form_fields);
$this->assertArrayHasKey('api_version', $this->unit->form_fields);
foreach (get_post_statuses() as $key => $status) {
$this->assertArrayHasKey('p_' . $key, $this->unit->form_fields);

View File

@ -38,6 +38,8 @@ class WC_Retailcrm_Customers_Test extends WC_Retailcrm_Test_Case_Helper
->willReturn($this->responseMock);
$this->customer = new WC_Customer();
$this->customer->set_first_name('Tester');
$this->customer->set_last_name('Tester');
$this->customer->set_email(uniqid(md5(date('Y-m-d H:i:s'))) . '@mail.com');
$this->customer->set_billing_email($this->customer->get_email());
$this->customer->set_password('password');
@ -124,13 +126,15 @@ class WC_Retailcrm_Customers_Test extends WC_Retailcrm_Test_Case_Helper
/**
* @param $retailcrm
*
* @dataProvider dataProviderApiClient
* @throws \Exception
*/
public function test_create_customer_empty_response($retailcrm)
{
$this->responseMock = null;
$this->apiMock = null;
$retailcrm_customer = $this->getRetailcrmCustomer($retailcrm);
$id = $retailcrm_customer->createCustomer($this->customer->get_id());

File diff suppressed because it is too large Load Diff

View File

@ -28,12 +28,11 @@ class WC_Retailcrm_Orders_Test extends WC_Retailcrm_Test_Case_Helper
/**
* @param $retailcrm
* @param $apiVersion
* @dataProvider dataProviderRetailcrm
*/
public function test_order_upload($retailcrm, $apiVersion)
public function test_order_upload($retailcrm)
{
$this->options = $this->setOptions($apiVersion);
$this->options = $this->setOptions();
$retailcrm_orders = $this->getRetailcrmOrders($retailcrm);
$upload_orders = $retailcrm_orders->ordersUpload();
@ -46,10 +45,9 @@ class WC_Retailcrm_Orders_Test extends WC_Retailcrm_Test_Case_Helper
/**
* @param $retailcrm
* @param $apiVersion
* @dataProvider dataProviderRetailcrm
*/
public function test_order_create($retailcrm, $apiVersion)
public function test_order_create($retailcrm)
{
if ($retailcrm) {
$responseMock = $this->getMockBuilder('\WC_Retailcrm_Response_Helper')
@ -82,7 +80,7 @@ class WC_Retailcrm_Orders_Test extends WC_Retailcrm_Test_Case_Helper
}
$this->createTestOrder();
$this->options = $this->setOptions($apiVersion);
$this->options = $this->setOptions();
$retailcrm_orders = $this->getRetailcrmOrders($retailcrm);
$order = $retailcrm_orders->orderCreate($this->order->get_id());
$order_send = $retailcrm_orders->getOrder();
@ -114,16 +112,11 @@ class WC_Retailcrm_Orders_Test extends WC_Retailcrm_Test_Case_Helper
$this->assertEquals('WooCity', $order_send['delivery']['address']['city']);
$this->assertEquals('delivery', $order_send['delivery']['code']);
if ($apiVersion == 'v4') {
$this->assertArrayHasKey('paymentType', $order_send);
$this->assertEquals('payment1', $order_send['paymentType']);
} elseif ($apiVersion == 'v5') {
$this->assertArrayHasKey('payments', $order_send);
$this->assertInternalType('array', $order_send['payments']);
$this->assertArrayHasKey('type', $order_send['payments'][0]);
$this->assertArrayHasKey('externalId', $order_send['payments'][0]);
$this->assertEquals('payment1', $order_send['payments'][0]['type']);
}
$this->assertArrayHasKey('payments', $order_send);
$this->assertInternalType('array', $order_send['payments']);
$this->assertArrayHasKey('type', $order_send['payments'][0]);
$this->assertArrayHasKey('externalId', $order_send['payments'][0]);
$this->assertEquals('payment1', $order_send['payments'][0]['type']);
} else {
$this->assertEquals(null, $order);
}
@ -132,15 +125,15 @@ class WC_Retailcrm_Orders_Test extends WC_Retailcrm_Test_Case_Helper
/**
* @param $isSuccessful
* @param $retailcrm
* @param $apiVersion
* @dataProvider dataProviderUpdateOrder
*/
public function test_update_order($isSuccessful, $retailcrm, $apiVersion)
public function test_update_order($isSuccessful, $retailcrm)
{
$this->createTestOrder();
$this->options = $this->setOptions($apiVersion);
$this->options = $this->setOptions();
if ($retailcrm && $apiVersion == 'v5') {
if ($retailcrm) {
$responseMock = $this->getMockBuilder('\WC_Retailcrm_Response_Helper')
->disableOriginalConstructor()
->setMethods(array(
@ -200,24 +193,19 @@ class WC_Retailcrm_Orders_Test extends WC_Retailcrm_Test_Case_Helper
$this->assertEquals('WooCity', $order_send['delivery']['address']['city']);
$this->assertEquals('delivery', $order_send['delivery']['code']);
if ($apiVersion == 'v4') {
$this->assertArrayHasKey('paymentType', $order_send);
$this->assertEquals('payment1', $order_send['paymentType']);
} elseif ($apiVersion == 'v5') {
$payment = $retailcrm_orders->getPayment();
$this->assertInternalType('array', $payment);
$payment = $retailcrm_orders->getPayment();
$this->assertInternalType('array', $payment);
if (!empty($payment)) {
$this->assertArrayHasKey('type', $payment);
$this->assertArrayHasKey('order', $payment);
$this->assertArrayHasKey('externalId', $payment);
$this->assertEquals('payment1', $payment['type']);
if (!empty($payment)) {
$this->assertArrayHasKey('type', $payment);
$this->assertArrayHasKey('order', $payment);
$this->assertArrayHasKey('externalId', $payment);
$this->assertEquals('payment1', $payment['type']);
if (!empty($this->options['send_payment_amount']) && $this->options['send_payment_amount'] == 'yes') {
$this->assertArrayHasKey('amount', $payment);
} else {
$this->assertArrayNotHasKey('amount', $payment);
}
if (!empty($this->options['send_payment_amount']) && $this->options['send_payment_amount'] == 'yes') {
$this->assertArrayHasKey('amount', $payment);
} else {
$this->assertArrayNotHasKey('amount', $payment);
}
}
} else {
@ -232,43 +220,19 @@ class WC_Retailcrm_Orders_Test extends WC_Retailcrm_Test_Case_Helper
return array(
array(
'is_successful' => true,
'retailcrm' => $this->apiMock,
'api_version' => 'v5'
'retailcrm' => $this->apiMock
),
array(
'is_successful' => true,
'retailcrm' => false,
'api_version' => 'v5'
'retailcrm' => false
),
array(
'is_successful' => false,
'retailcrm' => false,
'api_version' => 'v5'
'retailcrm' => false
),
array(
'is_successful' => false,
'retailcrm' => $this->apiMock,
'api_version' => 'v5'
),
array(
'is_successful' => false,
'retailcrm' => $this->apiMock,
'api_version' => 'v4'
),
array(
'is_successful' => true,
'retailcrm' => $this->apiMock,
'api_version' => 'v4'
),
array(
'is_successful' => false,
'retailcrm' => false,
'api_version' => 'v4'
),
array(
'is_successful' => true,
'retailcrm' => false,
'api_version' => 'v4'
'retailcrm' => $this->apiMock
)
);
}
@ -279,20 +243,10 @@ class WC_Retailcrm_Orders_Test extends WC_Retailcrm_Test_Case_Helper
return array(
array(
'retailcrm' => $this->apiMock,
'api_version' => 'v4'
'retailcrm' => $this->apiMock
),
array(
'retailcrm' => false,
'api_version' => 'v4'
),
array(
'retailcrm' => $this->apiMock,
'api_version' => 'v5'
),
array(
'retailcrm' => false,
'api_version' => 'v5'
'retailcrm' => false
)
);
}

View File

@ -26,14 +26,13 @@ class WC_Retailcrm_Plugin_Test extends WC_Retailcrm_Test_Case_Helper
/**
* @param $retailcrm
* @param $response
* @param $apiVersion
*
* @dataProvider dataProviderIntegrationModule
*/
public function test_integration_module($retailcrm,$response, $apiVersion)
public function test_integration_module($retailcrm, $response)
{
$client_id = uniqid();
$result = WC_Retailcrm_Plugin::integration_module($retailcrm, $client_id, $apiVersion);
$result = WC_Retailcrm_Plugin::integration_module($retailcrm, $client_id);
if (!$retailcrm || $response['success'] == false) {
$this->assertEquals(false, $result);
@ -58,22 +57,12 @@ class WC_Retailcrm_Plugin_Test extends WC_Retailcrm_Test_Case_Helper
private function getResponseData()
{
return array(
'v4' => array(
"true" => array(
"success" => true
),
"false" => array(
"success" => false
)
"true" => array(
"success" => true
),
'v5' => array(
"true" => array(
"success" => true
),
"false" => array(
"success" => false,
"errorMsg" => "Forbidden"
)
"false" => array(
"success" => false,
"errorMsg" => "Forbidden"
)
);
}
@ -81,64 +70,29 @@ class WC_Retailcrm_Plugin_Test extends WC_Retailcrm_Test_Case_Helper
public function dataProviderIntegrationModule()
{
$this->setUp();
$responseData = $this->getResponseData();
return array(
array(
'retailcrm' => $this->getApiMock(
'v4',
$this->getResponseData['v4']['true']
),
'response' => $this->getResponseData['v4']['true'],
'apiVersion' => 'v4'
'retailcrm' => $this->getApiMock($responseData['true']),
'response' => $responseData['true']
),
array(
'retailcrm' => false,
'response' => $this->getResponseData['v4']['true'],
'apiVersion' => 'v4'
'response' => $responseData['true']
),
array(
'retailcrm' => $this->getApiMock(
'v4',
$this->getResponseData['v4']['false']
),
'response' => $this->getResponseData['v4']['false'],
'apiVersion' => 'v4'
'retailcrm' => $this->getApiMock($responseData['false']),
'response' => $responseData['false']
),
array(
'retailcrm' => false,
'response' => $this->getResponseData['v4']['false'],
'apiVersion' => 'v4'
),
array(
'retailcrm' => $this->getApiMock(
'v5',
$this->getResponseData['v5']['true']
),
'response' => $this->getResponseData['v5']['true'],
'apiVersion' => 'v5'
),
array(
'retailcrm' => false,
'response' => $this->getResponseData['v5']['true'],
'apiVersion' => 'v5'
),
array(
'retailcrm' => $this->getApiMock(
'v5',
$this->getResponseData['v5']['false']
),
'response' => $this->getResponseData['v5']['false'],
'apiVersion' => 'v5'
),
array(
'retailcrm' => false,
'response' => $this->getResponseData['v5']['false'],
'apiVersion' => 'v5'
'response' => $responseData['false']
)
);
}
private function getApiMock($apiVersion, $response)
private function getApiMock($response)
{
$responseMock = $this->getMockBuilder('\WC_Retailcrm_Response_Helper')
->disableOriginalConstructor()
@ -159,29 +113,16 @@ class WC_Retailcrm_Plugin_Test extends WC_Retailcrm_Test_Case_Helper
$responseMock->setResponse($response);
if ($apiVersion == 'v4') {
$apiMock = $this->getMockBuilder('\WC_Retailcrm_Proxy')
->disableOriginalConstructor()
->setMethods(array(
'marketplaceSettingsEdit'
))
->getMock();
$apiMock->expects($this->any())
->method('marketplaceSettingsEdit')
->willReturn($responseMock);
} else {
$apiMock = $this->getMockBuilder('\WC_Retailcrm_Proxy')
$apiMock = $this->getMockBuilder('\WC_Retailcrm_Proxy')
->disableOriginalConstructor()
->setMethods(array(
'integrationModulesEdit'
))
->getMock();
$apiMock->expects($this->any())
->method('integrationModulesEdit')
->willReturn($responseMock);
}
$apiMock->expects($this->any())
->method('integrationModulesEdit')
->willReturn($responseMock);
return $apiMock;
}