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

ref #71456 Added loyalty program support (#338)

This commit is contained in:
Kocmonavtik 2024-07-15 11:57:57 +03:00 committed by Uryvskiy Dima
parent 69ef4e24c5
commit 01fe3c9741
31 changed files with 3218 additions and 224 deletions

View File

@ -1,3 +1,6 @@
## 2024-07-15 4.8.0
* Added loyalty program
## 2024-06-27 4.7.9
* Fixed undefined array key number in order history

View File

@ -1 +1 @@
4.7.9
4.8.0

View File

@ -445,8 +445,149 @@ msgstr "Se requiere clave API con acceso a una tienda"
msgid "The currency of the site differs from the currency of the store in CRM. For the integration to work correctly, the currencies in CRM and CMS must match"
msgstr "La moneda del sitio web es distinto a la tienda del CRM. Para el funcionamiento correcto de la integración, las monedas del CMS y CRM deben coincid"
msgid "Loyalty program"
msgstr "Programa de fidelización"
msgid "Activate program loyalty"
msgstr "Activar programa de fidelización"
msgid "Enable this setting for activate program loyalty on site"
msgstr "Activa esta configuración para activar programa de fidelización en la página web"
msgid "Terms of loyalty program"
msgstr "Condiciones del programa de fidelización"
msgid "Insert the terms and conditions of the loyalty program"
msgstr "Introduce las condiciones del programa de fidelización"
msgid "Conditions of personal data processing"
msgstr "Condiciones de procesamiento de datos personales"
msgid "Insert the terms and conditions for processing personal data"
msgstr "Introduce las condiciones para el procesamiento de datos personales"
msgid "To activate the loyalty program it is necessary to activate the <a href='?page=wc-settings'>'enable use of coupons option'</a>"
msgstr "Para activar el programa de fidelización es necesario activar la opción <a href='?page=wc-settings'>'habilitar el uso de cupones'</a>"
msgid "Bonus account"
msgstr "Cuenta de bonos"
msgid "Participation ID: "
msgstr "ID de participación: "
msgid "Current level: "
msgstr "Nivel actual: "
msgid "Bonuses on the account: "
msgstr "Bonos en la cuenta: "
msgid "Bonus card number: "
msgstr "Número de tarjeta de bonos: "
msgid "Date of registration: "
msgstr "Fecha de registro: "
msgid "Current level rules"
msgstr "Reglas del nivel actual"
msgid "Required amount of purchases to move to the next level: "
msgstr "Cantidad de compras necesarias para pasar al siguiente nivel: "
msgid "Activate participation in the loyalty program"
msgstr "Activar participación en el programa de fidelización"
msgid "Send"
msgstr "Enviar"
msgid "To register in the loyalty program, fill in the form:"
msgstr "Para registrarse en el programa de fidelización, rellena el formulario:"
msgid " I agree with "
msgstr " Estoy de acuerdo con "
msgid "loyalty program terms"
msgstr "términos del programa de fidelización"
msgid "terms of personal data processing"
msgstr "términos de procesamiento de datos personales"
msgid "Phone"
msgstr "Teléfono"
msgid "Error while registering in the loyalty program. Try again later"
msgstr "Error al registrarse en el programa de fidelización. Inténtalo de nuevo más tarde"
msgid "The card is not linked"
msgstr "La tarjeta no está vinculada"
msgid "Error while retrieving data. Try again later"
msgstr "Error al recuperar los datos. Inténtalo de nuevo más tarde"
msgid "Error when activating the loyalty program. Try again later"
msgstr "Error al activar el programa de fidelización. Inténtalo de nuevo más tarde"
msgid "Enter the correct phone number"
msgstr "Introduce el número de teléfono correcto"
msgid "Close"
msgstr "Cerrar"
msgid "Ordinary products: accrual of 1 bonus for each %s %s"
msgstr "Productos ordinarios: acumulación de 1 bono por cada %s %s"
msgid "Promotional products: accrual of 1 bonus for each %s %s"
msgstr "Productos promocionales: acumulación de 1 bono por cada %s %s"
msgid "Ordinary products: bonus accrual in the amount of %s%% of the purchase amount"
msgstr "Productos ordinarios: acumulación de bonos en la cantidad de %s%% de la suma de la compra"
msgid "Promotional products: bonus accrual in the amount of %s%% of the purchase amount"
msgstr "Productos promocionales: acumulación de bonos en la cantidad de %s%% de la suma de la compra"
msgid "Ordinary products: %s%% discount"
msgstr "Productos ordinarios: %s%% de descuento"
msgid "Promotional products: %s%% discount"
msgstr "Productos promocionales: %s%% de descuento"
msgid "Uploading services"
msgstr "Descarga de servicios"
msgstr "Subida de servicios"
msgid "Goods with the 'virtual' option enabled will be uploaded to Simla as services"
msgstr "Los bienes con la opción 'virtual' activada se cargarán en Simla como servicios"
msgstr "Los productos con la opción 'virtual' activada se subirán a Simla como servicios"
msgid "User not found in the system"
msgstr "Usuario no encontrado en el sistema"
msgid "Error when searching for participation in loyalty programs"
msgstr "Error al buscar la participación en programas de fidelización"
msgid "No active participation in the loyalty program was detected"
msgstr "No se detectó la participación activa en el programa de fidelización"
msgid "No bonuses for debiting"
msgstr "No hay bonos para debitar"
msgid "Loyalty program not found"
msgstr "Programa de fidelización no encontrado"
msgid "Loyalty program is not active"
msgstr "Programa de fidelización no está activo"
msgid "Loyalty program blocked"
msgstr "Programa de fidelización bloqueado"
msgid "This user is a corporate person"
msgstr "Este usuario es una persona jurídica"
msgid "It is possible to write off"
msgstr "Es posible debitar"
msgid "bonuses"
msgstr "bonos"
msgid "Use coupon:"
msgstr "Utiliza el cupón:"
msgid "Points will be awarded upon completion of the order:"
msgstr "Los puntos se concederán al finalizar el pedido:"

View File

@ -454,8 +454,149 @@ msgstr "Требуется API ключ с доступом к одному ма
msgid "The currency of the site differs from the currency of the store in CRM. For the integration to work correctly, the currencies in CRM and CMS must match"
msgstr "Валюта сайта отличается от валюты магазина в CRM. Для корректной работы интеграции, валюты в CRM и CMS должны совпадать"
msgid "Loyalty program"
msgstr "Программа лояльности"
msgid "Activate program loyalty"
msgstr "Активировать программу лояльности"
msgid "Enable this setting for activate program loyalty on site"
msgstr "Активируйте эту настройку для активации программы лояльности на сайте"
msgid "Terms of loyalty program"
msgstr "Условия программы лояльности"
msgid "Insert the terms and conditions of the loyalty program"
msgstr "Вставьте условия участия в программе лояльности"
msgid "Conditions of personal data processing"
msgstr "Условия обработки персональных данных"
msgid "Insert the terms and conditions for processing personal data"
msgstr "Вставьте условия обработки персональных данных"
msgid "To activate the loyalty program it is necessary to activate the <a href='?page=wc-settings'>'enable use of coupons option'</a>"
msgstr "Для активации программы лояльности необходимо активировать опцию <a href='?page=wc-settings'>'включить использование купонов'</a>"
msgid "Bonus account"
msgstr "Бонусный счёт"
msgid "Participation ID: "
msgstr "ID участия: "
msgid "Current level: "
msgstr "Текущий уровень: "
msgid "Bonuses on the account: "
msgstr "Бонусов на счёте: "
msgid "Bonus card number: "
msgstr "Номер бонусной карты: "
msgid "Date of registration: "
msgstr "Дата регистрации: "
msgid "Current level rules"
msgstr "Правила текущего уровня"
msgid "Required amount of purchases to move to the next level: "
msgstr "Необходимая сумма покупок для перехода на следующий уровень: "
msgid "Activate participation in the loyalty program"
msgstr "Активировать участие в программе лояльности"
msgid "Send"
msgstr "Отправить"
msgid "To register in the loyalty program, fill in the form:"
msgstr "Для регистрации в программе лояльности заполните форму:"
msgid " I agree with "
msgstr " Я согласен с "
msgid "loyalty program terms"
msgstr "условиями программы лояльности"
msgid "terms of personal data processing"
msgstr "условиями обработки персональных данных"
msgid "Phone"
msgstr "Телефон"
msgid "Error while registering in the loyalty program. Try again later"
msgstr "Ошибка при регистрации в программе лояльности. Попробуйте позже"
msgid "The card is not linked"
msgstr "Карта не привязана"
msgid "Error while retrieving data. Try again later"
msgstr "Ошибка при получении данных. Попробуйте позже"
msgid "Error when activating the loyalty program. Try again later"
msgstr "Ошибка при активации программы лояльности. Попробуйте позже"
msgid "Enter the correct phone number"
msgstr "Введите корректный номер телефона"
msgid "Close"
msgstr "Закрыть"
msgid "Ordinary products: accrual of 1 bonus for each %s %s"
msgstr "Обычные товары: начисление 1 бонуса за каждые %s %s"
msgid "Promotional products: accrual of 1 bonus for each %s %s"
msgstr "Акционные товары: начисление 1 бонуса за каждые %s %s"
msgid "Ordinary products: bonus accrual in the amount of %s%% of the purchase amount"
msgstr "Обычные товары: начисление бонусов в размере %s%% от суммы покупки"
msgid "Promotional products: bonus accrual in the amount of %s%% of the purchase amount"
msgstr "Акционные товары: начисление бонусов в размере %s%% от суммы покупки"
msgid "Ordinary products: %s%% discount"
msgstr "Обычные товары: %s%% скидка"
msgid "Promotional products: %s%% discount"
msgstr "Акционные товары: %s%% скидка"
msgid "Uploading services"
msgstr "Выгрузка услуг"
msgid "Goods with the 'virtual' option enabled will be uploaded to Simla as services"
msgstr "Товары с включенной опцией 'виртуальные' будут выгружаться в CRM как услуги"
msgid "User not found in the system"
msgstr "Пользователь не найден в системе"
msgid "Error when searching for participation in loyalty programs"
msgstr "Ошибка при поиске участия в программах лояльности"
msgid "No active participation in the loyalty program was detected"
msgstr "Не обнаружено активного участия в программе лояльности"
msgid "No bonuses for debiting"
msgstr "Нет бонусов для списания"
msgid "Loyalty program not found"
msgstr "Программа лояльности не найдена"
msgid "Loyalty program is not active"
msgstr "Программа лояльности не активна"
msgid "Loyalty program blocked"
msgstr "Программа лояльности заблокирована"
msgid "This user is a corporate person"
msgstr "Данный пользователь является корпоратиным лицом"
msgid "It is possible to write off"
msgstr "Возможно списать"
msgid "bonuses"
msgstr "бонусов"
msgid "Use coupon:"
msgstr "Используйте купон:"
msgid "Points will be awarded upon completion of the order:"
msgstr "По завершению заказа будет начислено баллов:"

View File

@ -0,0 +1,34 @@
.popup-fade-loyalty {
display: none;
}
.popup-fade-loyalty:before {
content: '';
background: #000;
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0.7;
z-index: 9999;
}
.popup-loyalty {
position: fixed;
top: 20%;
left: 50%;
padding: 20px;
width: 1000px;
margin-left: -500px;
height: 50%;
background: #fff;
border: 1px solid orange;
border-radius: 4px;
z-index: 99999;
opacity: 1;
overflow: auto;
}
.popup-close-loyalty {
position: absolute;
top: 10px;
right: 10px;
}

View File

@ -0,0 +1,121 @@
jQuery(function() {
jQuery('#loyaltyRegisterForm').on("submit", function (event) {
var termsCheck = jQuery('#termsLoyalty');
var privacyCheck = jQuery('#privacyLoyalty');
if (termsCheck.length) {
if (!termsCheck.is(':checked')) {
event.preventDefault();
return false;
}
}
if (privacyCheck.length) {
if (!privacyCheck.is(':checked')) {
event.preventDefault();
return false;
}
}
let phone = jQuery('#phoneLoyalty');
if (!phone.val().match(/(?:\+|\d)[\d\-\(\) ]{7,}\d/)) {
if (!jQuery('#warningLoyaltyPhone').length) {
phone.parent().append('<span style="color: red" id="warningLoyaltyPhone">' + messagePhone + '</span>')
}
event.preventDefault();
return false;
} else {
jQuery('#warningLoyaltyPhone').remove();
}
jQuery.ajax({
url: loyaltyUrl.url + '/admin-ajax.php?action=register_customer_loyalty',
method: 'POST',
timeout: 0,
data: {ajax: 1, phone: phone.val(), userId: customerId},
dataType: 'json'
})
.done(function (response) {
if (response.hasOwnProperty('error')) {
jQuery('#loyaltyRegisterForm').append('<p style="color: red">'+ response.error + '</p>')
event.preventDefault();
return false;
} else {
location.reload();
}
})
event.preventDefault();
});
jQuery('#loyaltyActivateForm').on("submit", function (event) {
var activateCheckbox = jQuery('#loyaltyActiveCheckbox');
if (activateCheckbox.length) {
if (!activateCheckbox.is(':checked')) {
event.preventDefault();
return false;
}
}
jQuery.ajax({
url: loyaltyUrl.url + '/admin-ajax.php?action=activate_customer_loyalty',
method: 'POST',
timeout: 0,
data: {ajax: 1, loyaltyId: loyaltyId},
dataType: 'json'
})
.done(function (response) {
if (response.hasOwnProperty('error')) {
jQuery('#loyaltyRegisterForm').append('<p style="color: red">'+ response.error + '</p>')
event.preventDefault();
return false;
} else {
location.reload();
}
})
event.preventDefault();
});
jQuery('.popup-open-loyalty').click(function() {
if (jQuery(this).attr('id') === 'terms-popup') {
jQuery('#popup-loyalty-text').html(termsLoyalty);
} else {
jQuery('#popup-loyalty-text').html(privacyLoyalty);
}
jQuery('.popup-fade-loyalty').fadeIn();
return false;
});
jQuery('.popup-close-loyalty').click(function() {
jQuery(this).parents('.popup-fade-loyalty').fadeOut();
return false;
});
jQuery(document).keydown(function(e) {
if (e.keyCode === 27) { // Key Escape
e.stopPropagation();
jQuery('.popup-fade-loyalty').fadeOut();
}
});
jQuery('.popup-fade-loyalty').click(function(e) {
if (jQuery(e.target).closest('.popup-loyalty').length == 0) {
jQuery(this).fadeOut();
}
});
jQuery('#phoneLoyalty').keydown(function (e) {
let key = e.key;
return (key >= '0' && key <= '9') || key == '+' || key == '(' || key == ')'|| key == '-' ||
key == 'ArrowLeft' || key == 'ArrowRight' || key == 'Delete' || key == 'Backspace';
});
});

View File

@ -0,0 +1,36 @@
jQuery(function() {
if (jQuery('#woocommerce_integration-retailcrm_loyalty').is(':checked')) {
checkActiveCoupon();
}
jQuery('#woocommerce_integration-retailcrm_loyalty').change(function () {
if (this.checked) {
checkActiveCoupon();
}
})
function checkActiveCoupon()
{
jQuery.ajax({
url: AdminUrl.url + '/admin-ajax.php?action=get_status_coupon',
method: 'POST',
timeout: 0,
data: {ajax: 1},
dataType: 'json'
})
.done(function (response) {
if (response.coupon_status !== 'yes') {
var checkElement = jQuery('#woocommerce_integration-retailcrm_loyalty');
checkElement.parent().css('color', 'red');
checkElement.css('border-color', 'red');
checkElement.prop('checked', false);
if (!jQuery('#coupon_warning').length) {
checkElement.parent().parent().append(
"<p id='coupon_warning' style='color: red'>" + response.translate.coupon_warning + "</p>"
);
}
}
})
}
});

View File

@ -117,6 +117,7 @@ abstract class WC_Retailcrm_Abstracts_Settings extends WC_Integration
$this->form_fields['online_assistant'] = [
'title' => __('Online assistant', 'retailcrm'),
'css' => 'width:400px; height:215px; resize: horizontal;',
'type' => 'textarea',
'id' => 'online_assistant',
'placeholder' => __('Insert the Online consultant code here', 'retailcrm')
@ -593,6 +594,41 @@ abstract class WC_Retailcrm_Abstracts_Settings extends WC_Integration
'description' => __('WhatsApp chat will be opened with this contact', 'retailcrm')
];
/**
* Loyalty Program
*/
$this->form_fields[] = [
'title' => __('Loyalty program', 'retailcrm'),
'type' => 'heading',
'description' => '',
'id' => 'loyalty_options'
];
$this->form_fields['loyalty'] = [
'label' => __('Activate program loyalty', 'retailcrm'),
'title' => __('Loyalty program', 'retailcrm'),
'class' => 'checkbox',
'type' => 'checkbox',
'description' => __('Enable this setting for activate program loyalty on site', 'retailcrm')
];
$this->form_fields['loyalty_terms'] = [
'title' => __('Terms of loyalty program', 'retailcrm'),
'css' => 'width:400px; height:215px; resize: horizontal;',
'type' => 'textarea',
'id' => 'loyalty_terms',
'placeholder' => __('Insert the terms and conditions of the loyalty program', 'retailcrm')
];
$this->form_fields['loyalty_personal'] = [
'title' => __('Conditions of personal data processing', 'retailcrm'),
'css' => 'width:400px; height:215px; resize: horizontal;',
'type' => 'textarea',
'id' => 'loyalty_personal',
'placeholder' => __('Insert the terms and conditions for processing personal data', 'retailcrm')
];
/**
* Generate icml file
*/

View File

@ -169,17 +169,7 @@ class WC_Retailcrm_Client_V5
*/
public function customersCorporateList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/customers-corporate',
@ -273,17 +263,7 @@ class WC_Retailcrm_Client_V5
*/
public function customersCorporateNotesList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/customers-corporate/notes',
@ -421,17 +401,8 @@ class WC_Retailcrm_Client_V5
) {
$this->checkIdParameter($by);
$parameters = ['by' => $by];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
$parameters['by'] = $by;
return $this->client->makeRequest(
"/customers-corporate/$id/addresses",
@ -537,17 +508,8 @@ class WC_Retailcrm_Client_V5
) {
$this->checkIdParameter($by);
$parameters = ['by' => $by];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
$parameters['by'] = $by;
return $this->client->makeRequest(
"/customers-corporate/$id/companies",
@ -640,17 +602,8 @@ class WC_Retailcrm_Client_V5
) {
$this->checkIdParameter($by);
$parameters = ['by' => $by];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
$parameters['by'] = $by;
return $this->client->makeRequest(
"/customers-corporate/$id/contacts",
@ -769,17 +722,7 @@ class WC_Retailcrm_Client_V5
*/
public function usersList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/users',
@ -840,17 +783,7 @@ class WC_Retailcrm_Client_V5
*/
public function segmentsList(array $filter = [], $limit = null, $page = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/segments',
@ -870,17 +803,7 @@ class WC_Retailcrm_Client_V5
*/
public function customFieldsList(array $filter = [], $limit = null, $page = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/custom-fields',
@ -994,17 +917,7 @@ class WC_Retailcrm_Client_V5
*/
public function customDictionariesList(array $filter = [], $limit = null, $page = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/custom-fields/dictionaries',
@ -1101,17 +1014,7 @@ class WC_Retailcrm_Client_V5
*/
public function ordersList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/orders',
@ -1445,17 +1348,7 @@ class WC_Retailcrm_Client_V5
*/
public function customersList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/customers',
@ -1666,16 +1559,8 @@ class WC_Retailcrm_Client_V5
*/
public function customersNotesList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/customers/notes',
WC_Retailcrm_Request::METHOD_GET,
@ -1748,17 +1633,7 @@ class WC_Retailcrm_Client_V5
*/
public function ordersPacksList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/orders/packs',
@ -1899,17 +1774,7 @@ class WC_Retailcrm_Client_V5
*/
public function tasksList(array $filter = [], $limit = null, $page = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/tasks',
@ -2008,17 +1873,7 @@ class WC_Retailcrm_Client_V5
*/
public function storeProductsGroups(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/store/product-groups',
@ -2042,17 +1897,7 @@ class WC_Retailcrm_Client_V5
*/
public function storeInventories(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/store/inventories',
@ -2180,17 +2025,7 @@ class WC_Retailcrm_Client_V5
*/
public function storeProducts(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
$parameters = $this->buildParameters($filter, $page, $limit);
return $this->client->makeRequest(
'/store/products',
@ -3008,6 +2843,183 @@ class WC_Retailcrm_Client_V5
);
}
/** Loyalty program */
/** Customer registration in the loyalty program */
public function createLoyaltyAccount(array $parameters, $site = null)
{
if ([] === $parameters) {
throw new InvalidArgumentException(
'Parameter `parameters` must contains a data'
);
}
return $this->client->makeRequest(
'/loyalty/account/create',
WC_Retailcrm_Request::METHOD_POST,
$this->fillSite($site, ['loyaltyAccount' => json_encode($parameters)])
);
}
/** Receiving information about participation in the loyalty program */
public function getLoyaltyClientInfo(int $clientIdLoyalty)
{
return $this->client->makeRequest(
"/loyalty/account/$clientIdLoyalty",
WC_Retailcrm_Request::METHOD_GET
);
}
/** Activation of participation in the loyalty program */
public function activateLoyaltyAccount(int $clientIdLoyalty)
{
return $this->client->makeRequest(
"/loyalty/account/$clientIdLoyalty/activate",
WC_Retailcrm_Request::METHOD_POST
);
}
/** Editing participation in the loyalty program */
public function editLoyaltyAccount(int $clientIdLoyalty, array $parameters)
{
return $this->client->makeRequest(
"/loyalty/account/$clientIdLoyalty/edit",
WC_Retailcrm_Request::METHOD_POST,
['id' => $clientIdLoyalty, 'loyaltyAccount' => json_encode($parameters)]
);
}
/** List of participation in the loyalty program */
public function getLoyaltyAccountList(array $filter = [], $limit = null, $page = null)
{
$parameters = $this->buildParameters($filter, $limit, $page);
return $this->client->makeRequest(
'/loyalty/accounts',
WC_Retailcrm_Request::METHOD_GET,
$parameters
);
}
/** List of loyalty programs */
public function getListLoyalty(array $filter = [], $limit = null, $page = null)
{
$parameters = $this->buildParameters($filter, $limit, $page);
return $this->client->makeRequest(
'/loyalty/loyalties',
WC_Retailcrm_Request::METHOD_GET,
$parameters
);
}
/** Receiving information about the loyalty program */
public function getLoyalty(int $idLoyalty)
{
return $this->client->makeRequest(
"/loyalty/loyalties/$idLoyalty",
WC_Retailcrm_Request::METHOD_GET
);
}
/** Charge bonus */
public function chargeBonusLoyalty(int $clientIdLoyalty, float $amount, string $comment = '')
{
return $this->client->makeRequest(
"/loyalty/account/$clientIdLoyalty/bonus/charge",
WC_Retailcrm_Request::METHOD_POST,
['amount' => $amount, 'comment' => $comment]
);
}
public function creditBonusLoyalty(int $clientIdLoyalty, array $parameters)
{
if (!isset($parameters['amount'])) {
throw new InvalidArgumentException(
'Parameter `amount` does not exist'
);
}
return $this->client->makeRequest(
"/loyalty/account/$clientIdLoyalty/bonus/credit",
WC_Retailcrm_Request::METHOD_POST,
$parameters
);
}
/** History of the client's bonus account */
public function getClientBonusHistory(int $clientIdLoyalty, array $filter = [], $limit = null, $page = null)
{
$parameters = $this->buildParameters($filter, $limit, $page);
$parameters['id'] = $clientIdLoyalty;
return $this->client->makeRequest(
"/loyalty/account/$clientIdLoyalty/bonus/operations",
WC_Retailcrm_Request::METHOD_GET,
$parameters
);
}
public function getDetailClientBonus(
int $clientIdLoyalty,
string $status,
array $filter = [],
$limit = null,
$page = null
) {
$parameters = $this->buildParameters($filter, $limit, $page);
$parameters['id'] = $clientIdLoyalty;
$parameters['status'] = $status;
return $this->client->makeRequest(
"/loyalty/account/$clientIdLoyalty/bonus/$status/details",
WC_Retailcrm_Request::METHOD_GET,
$parameters
);
}
/** Bonus account history for all participants */
public function getBonusHistory(string $cursor, array $filter = [], $limit = null)
{
$parameters = $this->buildParameters($filter, $limit);
$parameters['cursor'] = $cursor;
return $this->client->makeRequest(
"/loyalty/bonus/operations",
WC_Retailcrm_Request::METHOD_GET,
$parameters
);
}
/** Maximum discount calculation */
public function calculateDiscountLoyalty(string $site, array $order, $bonuses = 0)
{
return $this->client->makeRequest(
"/loyalty/calculate",
WC_Retailcrm_Request::METHOD_POST,
['site' => $site, 'order' => json_encode($order), 'bonuses' => $bonuses]
);
}
/** Application of bonuses under the loyalty program */
public function applyBonusToOrder(string $site, array $order, float $bonuses)
{
return $this->client->makeRequest(
"/orders/loyalty/apply",
WC_Retailcrm_Request::METHOD_POST,
['site' => $site, 'order' => json_encode($order), 'bonuses' => $bonuses]
);
}
public function cancelBonusOrder(array $order)
{
return $this->client->makeRequest(
"/orders/loyalty/cancel-bonus-operations",
WC_Retailcrm_Request::METHOD_POST,
['order' => json_encode($order)]
);
}
/**
* Update CRM basic statistic
*
@ -3121,4 +3133,23 @@ class WC_Retailcrm_Client_V5
return $params;
}
protected function buildParameters(array $filter = [], $limit = null, $page = null): array
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (isset($page)) {
$parameters['page'] = (int) $page;
}
if (isset($limit)) {
$parameters['limit'] = (int) $limit;
}
return $parameters;
}
}

View File

@ -33,6 +33,12 @@ if (!class_exists('WC_Retailcrm_Base')) {
/** @var WC_Retailcrm_Cart */
protected $cart;
/** @var WC_Retailcrm_Loyalty */
protected $loyalty;
/** @var array */
protected $updatedOrderId = [];
/**
* Init and hook in the integration.
*
@ -86,6 +92,7 @@ if (!class_exists('WC_Retailcrm_Base')) {
add_action('wp_ajax_generate_icml', [$this, 'generate_icml']);
add_action('wp_ajax_upload_selected_orders', [$this, 'upload_selected_orders']);
add_action('wp_ajax_clear_cron_tasks', [$this, 'clear_cron_tasks']);
add_action('wp_ajax_get_status_coupon', [$this, 'get_status_coupon']);
add_action('admin_print_footer_scripts', [$this, 'ajax_generate_icml'], 99);
add_action('woocommerce_update_customer', [$this, 'update_customer'], 10, 1);
add_action('user_register', [$this, 'create_customer'], 10, 2);
@ -99,6 +106,42 @@ if (!class_exists('WC_Retailcrm_Base')) {
add_action('admin_enqueue_scripts', [$this, 'include_files_for_admin'], 101);
add_action('woocommerce_new_order', [$this, 'create_order'], 11, 1);
if (isLoyaltyActivate($this->settings)) {
add_action('wp_ajax_register_customer_loyalty', [$this, 'register_customer_loyalty']);
add_action('wp_ajax_activate_customer_loyalty', [$this, 'activate_customer_loyalty']);
add_action('init', [$this, 'add_loyalty_endpoint'], 11, 1);
add_action('woocommerce_account_menu_items', [$this, 'add_loyalty_item'], 11, 1);
add_action('woocommerce_account_loyalty_endpoint', [$this, 'show_loyalty'], 11, 1);
// Add coupon hooks for loyalty program
add_action('woocommerce_cart_coupon', [$this, 'coupon_info'], 11, 1);
//Remove coupons when cart changes
add_action('woocommerce_add_to_cart', [$this, 'refresh_loyalty_coupon'], 11, 1);
add_action('woocommerce_after_cart_item_quantity_update', [$this, 'refresh_loyalty_coupon'], 11, 1);
add_action('woocommerce_cart_item_removed', [$this, 'refresh_loyalty_coupon'], 11, 1);
add_action('woocommerce_before_cart_empted', [$this, 'clear_loyalty_coupon'], 11, 1);
add_action('woocommerce_removed_coupon', [$this, 'remove_coupon'], 11, 1);
add_action('woocommerce_applied_coupon', [$this, 'apply_coupon'], 11, 1);
add_action('woocommerce_review_order_before_payment', [$this, 'reviewCreditBonus'], 11, 1);
add_action('wp_trash_post', [$this, 'trash_order_action'], 10, 1);
if (
!$this->get_option('deactivate_update_order')
|| $this->get_option('deactivate_update_order') == static::NO
) {
add_action('woocommerce_update_order', [$this, 'take_update_order'], 11, 1);
add_action('shutdown', [$this, 'update_order_loyalty'], -1);
add_action('woocommerce_saved_order_items', [$this, 'update_order_items'], 10, 1);
}
} elseif (
!$this->get_option('deactivate_update_order')
|| $this->get_option('deactivate_update_order') == static::NO
) {
add_action('woocommerce_update_order', [$this, 'update_order'], 10, 1);
}
// Subscribed hooks
add_action('register_form', [$this, 'subscribe_register_form'], 99);
add_action('woocommerce_register_form', [$this, 'subscribe_woocommerce_register_form'], 99);
@ -111,13 +154,6 @@ if (!class_exists('WC_Retailcrm_Base')) {
);
}
if (
!$this->get_option('deactivate_update_order')
|| $this->get_option('deactivate_update_order') == static::NO
) {
add_action('woocommerce_update_order', [$this, 'update_order'], 11, 1);
}
if ($this->get_option('abandoned_carts_enabled') === static::YES) {
$this->cart = new WC_Retailcrm_Cart($this->apiClient, $this->settings);
@ -127,6 +163,8 @@ if (!class_exists('WC_Retailcrm_Base')) {
add_action('woocommerce_cart_emptied', [$this, 'clear_cart']);
}
$this->loyalty = new WC_Retailcrm_Loyalty($this->apiClient, $this->settings);
// Deactivate hook
add_action('retailcrm_deactivate', [$this, 'deactivate']);
@ -523,6 +561,15 @@ if (!class_exists('WC_Retailcrm_Base')) {
}
}
public function update_order($orderId)
{
if (WC_Retailcrm_Plugin::history_running() === true) {
return;
}
$this->orders->updateOrder($orderId);
}
/**
* Edit order in retailCRM
*
@ -532,13 +579,38 @@ if (!class_exists('WC_Retailcrm_Base')) {
*
* @throws \Exception
*/
public function update_order($order_id)
public function take_update_order($order_id)
{
if (WC_Retailcrm_Plugin::history_running() === true) {
if (
WC_Retailcrm_Plugin::history_running() === true
|| did_action('woocommerce_checkout_order_processed')
|| did_action('woocommerce_new_order')
) {
return;
}
$this->orders->updateOrder($order_id);
$this->updatedOrderId[$order_id] = $order_id;
}
public function update_order_loyalty()
{
if ($this->updatedOrderId !== []) {
foreach ($this->updatedOrderId as $orderId) {
$this->orders->updateOrder($orderId);
}
}
}
public function update_order_items($orderId)
{
$this->orders->updateOrder($orderId);
}
public function trash_order_action($id)
{
if ('shop_order' == get_post_type($id)) {
$this->orders->updateOrder($id, true);
}
}
/**
@ -607,6 +679,126 @@ if (!class_exists('WC_Retailcrm_Base')) {
$this->include_js_scripts_for_admin();
}
public function get_status_coupon()
{
echo json_encode(
[
'coupon_status' => get_option('woocommerce_enable_coupons'),
'translate' => [
'coupon_warning' => __(
"To activate the loyalty program it is necessary to activate the <a href='?page=wc-settings'>'enable use of coupons option'</a>",
'retailcrm'
)
]
]);
wp_die();
}
public function register_customer_loyalty()
{
$phone = filter_input(INPUT_POST, 'phone');
$userId = filter_input(INPUT_POST, 'userId');
$site = $this->apiClient->getSingleSiteForKey();
$isSuccessful = false;
if (!empty($site) && $userId && $phone) {
$isSuccessful = $this->loyalty->registerCustomer($userId, $phone, $site);
}
if (!$isSuccessful) {
writeBaseLogs('Errors when registering a loyalty program. Passed parameters: ' .
json_encode(['site' => $site, 'userId' => $userId, 'phone' => $phone])
);
echo json_encode(['error' => __('Error while registering in the loyalty program. Try again later', 'retailcrm')]);
} else {
echo json_encode(['isSuccessful' => true]);
}
wp_die();
}
public function activate_customer_loyalty()
{
$loyaltyId = filter_input(INPUT_POST, 'loyaltyId');
$isSuccessful = false;
if ($loyaltyId) {
$isSuccessful = $this->loyalty->activateLoyaltyCustomer($loyaltyId);
}
if (!$isSuccessful) {
writeBaseLogs('Errors when activate loyalty program. Passed parameters: ' . json_encode(['loyaltyId' => $loyaltyId]));
echo json_encode(['error' => __('Error when activating the loyalty program. Try again later', 'retailcrm')]);
} else {
echo json_encode(['isSuccessful' => true]);
}
wp_die();
}
public function coupon_info()
{
try {
$result = $this->loyalty->createLoyaltyCoupon();
if ($result) {
echo $result;
}
} catch (Throwable $exception) {
writeBaseLogs($exception->getMessage());
}
}
public function refresh_loyalty_coupon()
{
try {
$this->loyalty->createLoyaltyCoupon(true);
} catch (Throwable $exception) {
writeBaseLogs($exception->getMessage());
}
}
public function clear_loyalty_coupon()
{
try {
$this->loyalty->clearLoyaltyCoupon();
} catch (Throwable $exception) {
writeBaseLogs($exception->getMessage());
}
}
public function remove_coupon($couponCode)
{
try {
if (!$this->loyalty->deleteLoyaltyCoupon($couponCode)) {
$this->loyalty->createLoyaltyCoupon(true);
}
} catch (Throwable $exception) {
writeBaseLogs($exception->getMessage());
}
}
public function apply_coupon($couponCode)
{
try {
if (!$this->loyalty->isLoyaltyCoupon($couponCode)) {
$this->loyalty->createLoyaltyCoupon(true);
}
} catch (Throwable $exception) {
writeBaseLogs($exception->getMessage());
}
}
public function reviewCreditBonus()
{
$resultHtml = $this->loyalty->getCreditBonuses();
if ($resultHtml) {
echo $resultHtml;
}
}
/**
* In this method we include CSS file
*
@ -645,9 +837,10 @@ if (!class_exists('WC_Retailcrm_Base')) {
'retailcrm-cron-info',
'retailcrm-meta-fields',
'retailcrm-module-settings',
'retailcrm-loyalty'
];
$wpAdminUrl = ['url' => get_admin_url()];
$wpAdminUrl = ['url' => get_admin_url()];
$jsScriptsPath = plugins_url() . '/woo-retailcrm/assets/js/';
foreach ($jsScripts as $scriptName) {
@ -803,6 +996,53 @@ if (!class_exists('WC_Retailcrm_Base')) {
wp_die();
}
public function add_loyalty_item($items)
{
$items['loyalty'] = __('Loyalty program', 'retailcrm');
return $items;
}
public function add_loyalty_endpoint()
{
add_rewrite_endpoint('loyalty', EP_PAGES);
}
public function show_loyalty()
{
$userId = get_current_user_id();
if (!isset($userId)) {
return;
}
$jsScript = 'retailcrm-loyalty-actions';
$loyaltyUrl = ['url' => get_admin_url()];
$jsScriptsPath = plugins_url() . '/woo-retailcrm/assets/js/';
$cssPath = plugins_url() . '/woo-retailcrm/assets/css/';
$messagePhone = __('Enter the correct phone number', 'retailcrm');
wp_register_script($jsScript, $jsScriptsPath . $jsScript . '.js', false, '0.1');
wp_enqueue_script($jsScript, $jsScriptsPath . $jsScript . '.js', '', '', true);
wp_localize_script($jsScript, 'loyaltyUrl', $loyaltyUrl);
wp_localize_script($jsScript, 'customerId', $userId);
wp_localize_script($jsScript, 'messagePhone', $messagePhone);
wp_localize_script($jsScript, 'termsLoyalty', $this->settings['loyalty_terms']);
wp_localize_script($jsScript, 'privacyLoyalty', $this->settings['loyalty_personal']);
wp_register_style('retailcrm-loyalty-style', $cssPath . 'retailcrm-loyalty-style.css', false, '0.1');
wp_enqueue_style('retailcrm-loyalty-style');
$result = $this->loyalty->getForm($userId);
if ([] === $result) {
echo '<p style="color: red">'. __('Error while retrieving data. Try again later', 'retailcrm') . '</p>';
} else {
wp_localize_script($jsScript, 'loyaltyId', $result['loyaltyId'] ?? null);
echo $result['form'];
}
}
/**
* Get custom fields with CRM
*

View File

@ -0,0 +1,539 @@
<?php
if (!class_exists('WC_Retailcrm_Loyalty')) :
if (!class_exists('WC_Retailcrm_Loyalty_Form')) {
include_once(WC_Integration_Retailcrm::checkCustomFile('include/components/class-wc-retailcrm-loyalty-form.php'));
}
/**
* PHP version 7.0
*
* Class WC_Retailcrm_Loyalty - Allows transfer data carts with CMS.
*
* @category Integration
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
class WC_Retailcrm_Loyalty
{
/** @var WC_Retailcrm_Client_V5 */
protected $apiClient;
protected $dateFormat;
protected $settings;
/** @var WC_Retailcrm_Loyalty_Form */
protected $loyaltyForm;
protected $validator;
public function __construct($apiClient, $settings)
{
$this->apiClient = $apiClient;
$this->settings = $settings;
$this->dateFormat = 'Y-m-d H:i:sP';
$this->loyaltyForm = new WC_Retailcrm_Loyalty_Form();
$this->validator = new WC_Retailcrm_Loyalty_Validator(
$this->apiClient,
isCorporateUserActivate($this->settings)
);
}
public function getForm(int $userId)
{
$result = [];
try {
$response = $this->getLoyaltyAccounts($userId);
} catch (Throwable $exception) {
writeBaseLogs('Exception get loyalty accounts: ' . $exception->getMessage());
return $result;
}
$loyaltyAccount = $response['loyaltyAccounts'][0] ?? null;
if ($loyaltyAccount && (int) $loyaltyAccount['customer']['externalId'] === $userId) {
if ($loyaltyAccount['active'] === true) {
$result['form'] = $this->loyaltyForm->getInfoLoyalty($loyaltyAccount);
} else {
$result['form'] = $this->loyaltyForm->getActivationForm();
$result['loyaltyId'] = $loyaltyAccount['id'];
}
} else {
$result['form'] = $this->loyaltyForm->getRegistrationForm();
}
return $result;
}
public function registerCustomer(int $userId, string $phone, string $site): bool
{
$parameters = [
'phoneNumber' => $phone,
'customer' => [
'externalId' => $userId
]
];
try {
$response = $this->apiClient->createLoyaltyAccount($parameters, $site);
if (!$response->isSuccessful()) {
writeBaseLogs('Error while registering in the loyalty program: ' . $response->getRawResponse());
}
return $response->isSuccessful();
} catch (Throwable $exception) {
writeBaseLogs('Exception while registering in the loyalty program: ' . $exception->getMessage());
return false;
}
}
public function activateLoyaltyCustomer(int $loyaltyId)
{
try {
$response = $this->apiClient->activateLoyaltyAccount($loyaltyId);
if (!$response->isSuccessful()) {
writeBaseLogs('Error while registering in the loyalty program: ' . $response->getRawResponse());
}
return $response->isSuccessful();
} catch (Throwable $exception) {
writeBaseLogs('Exception while activate loyalty account: ' . $exception->getMessage());
return false;
}
}
private function getDiscountLoyalty($cartItems, $site, $customerId)
{
$response = $this->calculateDiscountLoyalty($cartItems, $site, $customerId);
if ($response === 0) {
return 0;
}
$discount = 0;
//Checking if the loyalty discount is a percent discount
foreach ($response['order']['items'] as $item) {
if (!isset($item['discounts'])) {
continue;
}
foreach ($item['discounts'] as $discountItem) {
if ($discountItem['type'] === 'loyalty_level') {
$discount += $discountItem['amount'];
}
}
}
//If the discount has already been given, do not work with points deduction
if ($discount === 0) {
foreach ($response['calculations'] as $calculate) {
if ($calculate['privilegeType'] !== 'loyalty_level') {
continue;
}
$discount = $calculate['maxChargeBonuses'];
}
}
return $discount;
}
private function getLoyaltyAccounts(int $userId)
{
$response = $this->apiClient->customersGet($userId);
if (!isset($response['customer']['id'])) {
return [];
}
$filter['customerId'] = $response['customer']['id'];
$response = $this->apiClient->getLoyaltyAccountList($filter);
if (!$response->isSuccessful() || !$response->offsetExists('loyaltyAccounts')) {
return null;
}
return $response;
}
public function createLoyaltyCoupon($refreshCoupon = false)
{
global $woocommerce;
$site = $this->apiClient->getSingleSiteForKey();
$cartItems = $woocommerce->cart->get_cart();
$customerId = $woocommerce->customer ? $woocommerce->customer->get_id() : null;
$resultString = '';
if (!$customerId || !$cartItems) {
return null;
}
$couponsLp = [];
// Check exists used loyalty coupons
foreach ($woocommerce->cart->get_coupons() as $code => $coupon) {
if ($this->isLoyaltyCoupon($code)) {
$couponsLp[] = $code;
}
}
// if you need to refresh coupon that does not exist
if (count($couponsLp) === 0 && $refreshCoupon) {
return null;
}
//If one loyalty coupon is used, not generate a new one
if (count($couponsLp) === 1 && !$refreshCoupon) {
return null;
}
// if more than 1 loyalty coupon is used, delete all coupons
if (count($couponsLp) > 1 || $refreshCoupon) {
foreach ($couponsLp as $code) {
$woocommerce->cart->remove_coupon($code);
$coupon = new WC_Coupon($code);
$coupon->delete(true);
}
$woocommerce->cart->calculate_totals();
}
if (!$this->validator->checkAccount($customerId)) {
return null;
}
$lpDiscountSum = $this->getDiscountLoyalty($woocommerce->cart->get_cart(), $site, $customerId);
if ($lpDiscountSum === 0) {
return null;
}
//Check the existence of loyalty coupons and delete them
$coupons = $this->getCouponLoyalty($woocommerce->customer->get_email());
foreach ($coupons as $item) {
$coupon = new WC_Coupon($item['code']);
$coupon->delete(true);
}
//Generate new coupon
$coupon = new WC_Coupon();
$coupon->set_usage_limit(0);
$coupon->set_amount($lpDiscountSum);
$coupon->set_email_restrictions($woocommerce->customer->get_email());
$coupon->set_code('loyalty' . mt_rand());
$coupon->save();
if ($refreshCoupon) {
$woocommerce->cart->apply_coupon($coupon->get_code());
return $resultString;
}
//If a percentage discount, automatically apply a loyalty coupon
if ($this->validator->loyaltyAccount['level']['type'] === 'discount') {
$woocommerce->cart->apply_coupon($coupon->get_code());
return $resultString;
}
$resultString .= ' <div style="text-align: left; line-height: 3"><b>' . __('It is possible to write off', 'retailcrm') . ' ' . $lpDiscountSum . ' ' . __('bonuses', 'retailcrm') . '</b></div>';
return $resultString. '<div style="text-align: left;"><b>' . __('Use coupon:', 'retailcrm') . ' <u><i>' . $coupon->get_code() . '</i></u></i></b></div>';
}
public function clearLoyaltyCoupon()
{
global $woocommerce;
foreach ($woocommerce->cart->get_coupons() as $code => $coupon) {
if ($this->isLoyaltyCoupon($code)) {
$woocommerce->cart->remove_coupon($code);
$coupon = new WC_Coupon($code);
$coupon->delete(true);
}
}
}
public function deleteLoyaltyCoupon($couponCode)
{
if ($this->isLoyaltyCoupon($couponCode)) {
$coupon = new WC_Coupon($couponCode);
$coupon->delete(true);
return true;
}
return false;
}
public function isLoyaltyCoupon($couponCode): bool
{
return preg_match('/^loyalty\d+$/m', $couponCode) === 1;
}
public function getCouponLoyalty($email)
{
global $wpdb;
return $wpdb->get_results(
$wpdb->prepare(
"SELECT posts.post_name code FROM {$wpdb->prefix}posts AS posts
LEFT JOIN {$wpdb->prefix}postmeta AS postmeta ON posts.ID = postmeta.post_id
WHERE posts.post_type = 'shop_coupon' AND posts.post_name LIKE 'loyalty%'
AND postmeta.meta_key = 'customer_email' AND postmeta.meta_value LIKE %s",
'%' . $email . '%'
), ARRAY_A
);
}
public function deleteLoyaltyCouponInOrder($wcOrder)
{
$discountLp = 0;
$coupons = $wcOrder->get_coupons();
foreach ($coupons as $coupon) {
$code = $coupon->get_code();
if ($this->isLoyaltyCoupon($code)) {
$discountLp = $coupon->get_discount();
$wcOrder->remove_coupon($code);
$objectCoupon = new WC_Coupon($code);
$objectCoupon->delete(true);
$wcOrder->recalculate_coupons();
break;
}
}
return $discountLp;
}
public function isValidOrder($wcCustomer, $wcOrder)
{
return !(!$wcCustomer || (isCorporateUserActivate($this->settings) && isCorporateOrder($wcCustomer, $wcOrder)));
}
public function isValidUser($wcCustomer)
{
if (empty($wcCustomer)) {
return false;
}
try {
$response = $this->getLoyaltyAccounts($wcCustomer->get_id());
} catch (Throwable $exception) {
writeBaseLogs('Exception get loyalty accounts: ' . $exception->getMessage());
return false;
}
return isset($response['loyaltyAccounts'][0]);
}
public function applyLoyaltyDiscount($wcOrder, $createdOrder, $discountLp = 0)
{
$isPercentDiscount = false;
$items = [];
// Verification of automatic creation of the percentage discount of the loyalty program
foreach ($createdOrder['items'] as $item) {
foreach ($item['discounts'] as $discount) {
if ($discount['type'] === 'loyalty_level') {
$isPercentDiscount = true;
$items = $createdOrder['items'];
break 2;
}
}
}
if (!$isPercentDiscount) {
$response = $this->apiClient->applyBonusToOrder($createdOrder['site'], ['externalId' => $createdOrder['externalId']], (float) $discountLp);
if (!$response instanceof WC_Retailcrm_Response || !$response->isSuccessful()) {
return $response->getErrorString();
}
$items = $response['order']['items'];
}
$this->calculateLoyaltyDiscount($wcOrder, $items);
}
public function calculateLoyaltyDiscount($wcOrder, $orderItems)
{
$wcItems = $wcOrder->get_items();
foreach ($orderItems as $item) {
$externalId = $item['externalIds'][0]['value'];
$externalId = preg_replace('/^\d+\_/m', '', $externalId);
if (isset($wcItems[(int) $externalId])) {
$discountLoyaltyTotal = 0;
foreach ($item['discounts'] as $discount) {
if (in_array($discount['type'], ['bonus_charge', 'loyalty_level'])) {
$discountLoyaltyTotal += $discount['amount'];
}
}
$wcItem = $wcItems[(int) $externalId];
$wcItem->set_total($wcItem->get_total() - $discountLoyaltyTotal);
$wcItem->calculate_taxes();
$wcItem->save();
}
}
$wcOrder->calculate_totals();
}
public function getCrmItemsInfo($orderExternalId)
{
$discountType = null;
$crmItems = [];
$response = $this->apiClient->ordersGet($orderExternalId);
if (!$response instanceof WC_Retailcrm_Response || !$response->isSuccessful() || !isset($response['order'])) {
writeBaseLogs('Process order: Error when receiving an order from the CRM. Order Id: ' . $orderExternalId);
return [];
}
foreach ($response['order']['items'] as $item) {
$externalId = $item['externalIds'][0]['value'];
$externalId = preg_replace('/^\d+\_/m', '', $externalId);
$crmItems[$externalId] = $item;
if (!$discountType) {
foreach ($item['discounts'] as $discount) {
if (in_array($discount['type'], ['bonus_charge', 'loyalty_level'])) {
$discountType = $discount['type'];
break;
}
}
}
}
return ['items' => $crmItems, 'discountType' => $discountType];
}
public function calculateDiscountLoyalty($cartItems, $site, $customerId, $bonuses = 0)
{
$order = [
'site' => $site,
'customer' => ['externalId' => $customerId],
'privilegeType' => 'loyalty_level'
];
$useXmlId = isset($this->settings['bind_by_sku']) && $this->settings['bind_by_sku'] === WC_Retailcrm_Base::YES;
foreach ($cartItems as $item) {
$product = $item['data'];
$order['items'][] = [
'offer' => $useXmlId ? ['xmlId' => $product->get_sku()] : ['externalId' => $product->get_id()],
'quantity' => $item['quantity'],
'initialPrice' => wc_get_price_including_tax($product),
'discountManualAmount' => ($item['line_subtotal'] - $item['line_total']) / $item['quantity']
];
}
$response = $this->apiClient->calculateDiscountLoyalty($site, $order, (float) $bonuses);
if (!$response->isSuccessful() || !isset($response['calculations'])) {
return 0;
}
return $response;
}
public function getCreditBonuses(): string
{
global $woocommerce;
$customerId = $woocommerce->customer ? $woocommerce->customer->get_id() : null;
$site = $this->apiClient->getSingleSiteForKey();
if (
!$customerId
|| !$woocommerce->cart
|| !$woocommerce->cart->get_cart()
|| !$site
|| !$this->validator->checkAccount($customerId)
) {
return '';
}
$loyaltyCoupon = null;
$coupons = $woocommerce->cart->get_applied_coupons();
foreach ($coupons as $coupon) {
if ($this->isLoyaltyCoupon($coupon)) {
$loyaltyCoupon = new WC_Coupon($coupon);
$woocommerce->cart->remove_coupon($coupon);
$woocommerce->cart->calculate_totals();
break;
}
}
if ($loyaltyCoupon) {
$chargeBonuses = $loyaltyCoupon->get_amount();
}
$cartItems = $woocommerce->cart->get_cart();
$response = $this->calculateDiscountLoyalty($cartItems, $site, $customerId, $chargeBonuses ?? 0);
if ($loyaltyCoupon) {
$coupon = new WC_Coupon();
$coupon->set_usage_limit(0);
$coupon->set_amount($loyaltyCoupon->get_amount());
$coupon->set_email_restrictions($loyaltyCoupon->get_email_restrictions());
$coupon->set_code($loyaltyCoupon->get_code());
$coupon->save();
$woocommerce->cart->apply_coupon($coupon->get_code());
$woocommerce->cart->calculate_totals();
}
if ($response === 0) {
return '';
}
$creditBonuses = $response['order']['bonusesCreditTotal'];
if ($creditBonuses) {
return $this->getHtmlCreditBonuses($creditBonuses);
}
return '';
}
private function getHtmlCreditBonuses($creditBonuses)
{
return '<b style="font-size: large">' . __("Points will be awarded upon completion of the order:", 'retailcrm') . ' <u style="color: green"><i>' . $creditBonuses . '</u></i></b>';
}
}
endif;

View File

@ -17,6 +17,9 @@ if (!class_exists('WC_Retailcrm_Orders')) :
/** @var bool|WC_Retailcrm_Proxy|WC_Retailcrm_Client_V5 */
protected $retailcrm;
/** @var WC_Retailcrm_Loyalty|null */
protected $loyalty = null;
/** @var array */
protected $retailcrm_settings;
@ -41,6 +44,12 @@ if (!class_exists('WC_Retailcrm_Orders')) :
/** @var array */
private $order = [];
/** @var bool */
private $cancelLoyalty = false;
/** @var string */
private $loyaltyDiscountType = '';
/** @var array */
private $payment = [];
@ -64,6 +73,10 @@ if (!class_exists('WC_Retailcrm_Orders')) :
$this->orders = $orders;
$this->order_payment = $order_payment;
if (isLoyaltyActivate($retailcrm_settings)) {
$this->loyalty = new WC_Retailcrm_Loyalty($retailcrm, $retailcrm_settings);
}
if (!empty($retailcrm_settings['order-meta-data-retailcrm'])) {
$this->customFields = json_decode($retailcrm_settings['order-meta-data-retailcrm'], true);
}
@ -88,8 +101,34 @@ if (!class_exists('WC_Retailcrm_Orders')) :
$wcOrder = wc_get_order($orderId);
if ($this->loyalty) {
$wcCustomer = null;
$privilegeType = 'none';
$discountLp = $this->loyalty->deleteLoyaltyCouponInOrder($wcOrder);
$dataOrder = $wcOrder->get_data();
if (isset($dataOrder['customer_id'])) {
$wcCustomer = new WC_Customer($dataOrder['customer_id']) ?? null;
}
if (!$this->loyalty->isValidOrder($wcCustomer, $wcOrder)) {
if ($discountLp > 0) {
writeBaseLogs('The user does not meet the requirements for working with the loyalty program. Order Id: ' . $orderId);
}
$discountLp = 0;
$privilegeType = 'none';
} elseif ($this->loyalty->isValidUser($wcCustomer)) {
$privilegeType = 'loyalty_level';
}
}
$this->processOrder($wcOrder);
if (isset($privilegeType)) {
$this->order['privilegeType'] = $privilegeType;
}
$response = $this->retailcrm->ordersCreate($this->order);
// Allows you to verify order creation and perform additional actions
@ -98,6 +137,10 @@ if (!class_exists('WC_Retailcrm_Orders')) :
if (!$response instanceof WC_Retailcrm_Response || !$response->isSuccessful()) {
return $response->getErrorString();
}
if (isset($discountLp) && $discountLp > 0) {
$this->loyalty->applyLoyaltyDiscount($wcOrder, $response['order'], $discountLp);
}
} catch (Throwable $exception) {
writeBaseLogs(
sprintf(
@ -266,7 +309,7 @@ if (!class_exists('WC_Retailcrm_Orders')) :
* @return WC_Order $order | null
* @throws \Exception
*/
public function updateOrder($orderId)
public function updateOrder($orderId, $statusTrash = false)
{
if (!$this->retailcrm instanceof WC_Retailcrm_Proxy) {
return null;
@ -274,16 +317,39 @@ if (!class_exists('WC_Retailcrm_Orders')) :
try {
$wcOrder = wc_get_order($orderId);
$needRecalculate = false;
$this->processOrder($wcOrder, true);
$this->processOrder($wcOrder, true, $statusTrash);
if ($this->cancelLoyalty) {
$this->cancelLoyalty = false;
$this->order_item->cancelLoyalty = false;
$needRecalculate = true;
if ($this->loyaltyDiscountType === 'loyalty_level') {
$this->order['privilegeType'] = 'none';
}
if ($this->loyaltyDiscountType === 'bonus_charge') {
$responseCancelBonus = $this->retailcrm->cancelBonusOrder(['externalId' => $this->order['externalId']]);
if (!$responseCancelBonus instanceof WC_Retailcrm_Response || !$responseCancelBonus->isSuccessful()) {
writeBaseLogs('Error when canceling bonuses');
return null;
}
}
}
$response = $this->retailcrm->ordersEdit($this->order);
// Allows you to verify order changes and perform additional actions
$response = apply_filters('retailcrm_order_update_after', $response, $wcOrder);
if ($response instanceof WC_Retailcrm_Response && $response->isSuccessful()) {
if ($response instanceof WC_Retailcrm_Response && $response->isSuccessful() && isset($response['order'])) {
$this->payment = $this->orderUpdatePaymentType($wcOrder);
if ($needRecalculate) {
$this->loyalty->calculateLoyaltyDiscount($wcOrder, $response['order']['items']);
}
}
} catch (Throwable $exception) {
writeBaseLogs(
@ -356,7 +422,7 @@ if (!class_exists('WC_Retailcrm_Orders')) :
* @return void
* @throws \Exception
*/
protected function processOrder($order, $update = false)
protected function processOrder($order, $update = false, $statusTrash = false)
{
if (!$order instanceof WC_Order) {
return;
@ -419,15 +485,44 @@ if (!class_exists('WC_Retailcrm_Orders')) :
}
$orderData['delivery']['address'] = $this->order_address->build($order)->getData();
$orderItems = [];
$crmItems = [];
$wcItems = $order->get_items();
if ($this->loyalty && $update) {
$result = $this->loyalty->getCrmItemsInfo($order->get_id());
if ($result !== []) {
$crmItems = $result['items'];
if (
$statusTrash
|| (
$result['discountType'] !== null
&& in_array($order->get_status(), ['cancelled', 'refunded'])
)
) {
$this->cancelLoyalty = true;
$this->order_item->cancelLoyalty = true;
} else {
$this->cancelLoyalty = $this->order_item->isCancelLoyalty($wcItems, $crmItems);
}
$this->loyaltyDiscountType = $result['discountType'];
}
}
/** @var WC_Order_Item_Product $item */
foreach ($order->get_items() as $item) {
$orderItems[] = $this->order_item->build($item)->getData();
foreach ($wcItems as $id => $item) {
$crmItem = $crmItems[$id] ?? null;
$orderItems[] = $this->order_item->build($item, $crmItem)->getData();
$this->order_item->resetData();
$this->order_item->resetData($this->cancelLoyalty);
}
unset($crmItems, $crmItem);
$orderData['items'] = $orderItems;
$orderData['discountManualAmount'] = 0;
$orderData['discountManualPercent'] = 0;

View File

@ -0,0 +1,90 @@
<?php
if (!class_exists('WC_Retailcrm_Loyalty')) :
class WC_Retailcrm_Loyalty_Form
{
public function getRegistrationForm()
{
return sprintf(
'
<form id="loyaltyRegisterForm" method="post">
<p>%s</p>
<p><input type="checkbox" name="terms" id="termsLoyalty" required>%s<a id="terms-popup" class="popup-open-loyalty" href="#">%s</a>.</p>
<p><input type="checkbox" name="privacy" id="privacyLoyalty" required>%s<a id="privacy-popup" class="popup-open-loyalty" href="#">%s</a>.</p>
<p><input type="text" name="phone" id="phoneLoyalty" placeholder="%s" required></p>
<p><input type="submit" value="%s"></p>
</form>
<div class="popup-fade-loyalty">
<div class="popup-loyalty">
<a class="popup-close-loyalty" href="#">%s</a>
<br>
<div id="popup-loyalty-text"></div>
</div>
</div>
',
__('To register in the loyalty program, fill in the form:', 'retailcrm'),
__(' I agree with ', 'retailcrm'),
__('loyalty program terms', 'retailcrm'),
__(' I agree with ', 'retailcrm'),
__('terms of personal data processing', 'retailcrm'),
__('Phone', 'retailcrm'),
__('Send', 'retailcrm'),
__('Close', 'retailcrm')
);
}
public function getActivationForm()
{
return sprintf('
<form id="loyaltyActivateForm" method="post">
<p><input type="checkbox" id="loyaltyActiveCheckbox" name="loyaltyCheckbox" required> %s</p>
<input type="submit" value="%s">
</form>',
__('Activate participation in the loyalty program', 'retailcrm'),
__('Send', 'retailcrm')
);
}
public function getInfoLoyalty(array $loyaltyAccount)
{
$data = [
'<b>' . __('Bonus account', 'retailcrm') . '</b>',
__('Participation ID: ', 'retailcrm') . $loyaltyAccount['id'],
__('Current level: ', 'retailcrm') . $loyaltyAccount['level']['name'],
__('Bonuses on the account: ', 'retailcrm') . $loyaltyAccount['amount'],
__('Bonus card number: ' , 'retailcrm') . ($loyaltyAccount['cardNumber'] ?? __('The card is not linked', 'retailcrm')),
__('Date of registration: ', 'retailcrm') . $loyaltyAccount['activatedAt'],
'<br>',
'<b>' . __('Current level rules', 'retailcrm') . '</b>',
__('Required amount of purchases to move to the next level: ', 'retailcrm') . $loyaltyAccount['nextLevelSum'] . ' ' . $loyaltyAccount['loyalty']['currency']
];
switch ($loyaltyAccount['level']['type']) {
case 'bonus_converting':
$data[] = sprintf(__('Ordinary products: accrual of 1 bonus for each %s %s', 'retailcrm'), $loyaltyAccount['level']['privilegeSize'], $loyaltyAccount['loyalty']['currency']);
$data[] = sprintf(__('Promotional products: accrual of 1 bonus for each %s %s', 'retailcrm'), $loyaltyAccount['level']['privilegeSizePromo'], $loyaltyAccount['loyalty']['currency']);
break;
case 'bonus_percent':
$data[] = sprintf(__('Ordinary products: bonus accrual in the amount of %s%% of the purchase amount', 'retailcrm'), $loyaltyAccount['level']['privilegeSize']);
$data[] = sprintf(__('Promotional products: bonus accrual in the amount of %s%% of the purchase amount', 'retailcrm'), $loyaltyAccount['level']['privilegeSizePromo']);
break;
case 'discount':
$data[] = sprintf(__('Ordinary products: %s%% discount', 'retailcrm'), $loyaltyAccount['level']['privilegeSize']);
$data[] = sprintf(__('Promotional products: %s%% discount', 'retailcrm'), $loyaltyAccount['level']['privilegeSizePromo']);
break;
}
$result = '';
foreach ($data as $line) {
$result .= "<p style='line-height: 1'>$line</p>";
}
return $result;
}
}
endif;

View File

@ -202,3 +202,18 @@ function useHpos()
return class_exists(Automattic\WooCommerce\Utilities\OrderUtil::class)
&& Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled();
}
function isLoyaltyActivate($settings)
{
return isset($settings['loyalty']) && $settings['loyalty'] === WC_Retailcrm_Base::YES;
}
function isCorporateUserActivate($settings)
{
return isset($settings['corporate_enabled']) && $settings['corporate_enabled'] === WC_Retailcrm_Base::YES;
}
function isCorporateOrder($wcCustomer, $wcOrder)
{
return !empty($wcCustomer->get_billing_company()) || !empty($wcOrder->get_billing_company());
}

View File

@ -28,6 +28,9 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data
*/
protected $settings = [];
/** @var bool */
public $cancelLoyalty = false;
/**
* WC_Retailcrm_Order_Item constructor.
*
@ -43,13 +46,13 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data
*
* @return self
*/
public function build($item)
public function build($item, $crmItem = null)
{
$decimalPlaces = wc_get_price_decimals();
// Calculate price and discount
$price = $this->calculatePrice($item, $decimalPlaces);
$discountPrice = $this->calculateDiscount($item, $price, $decimalPlaces);
$discountPrice = $this->calculateDiscount($item, $price, $decimalPlaces, $crmItem);
$data['productName'] = $item['name'];
$data['initialPrice'] = $price;
@ -112,12 +115,44 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data
* @param WC_Order_Item_Product $item
* @param $price
* @param int $decimalPlaces Price rounding from WC settings
*
* @param array|null $crmItem Current trade position in CRM
* @return float|int
*/
private function calculateDiscount(WC_Order_Item_Product $item, $price, int $decimalPlaces)
{
$productPrice = $item->get_total() ? $item->get_total() / $item->get_quantity() : 0;
private function calculateDiscount(
WC_Order_Item_Product $item,
$price,
int $decimalPlaces,
$crmItem = null
) {
if ($crmItem) {
$loyaltyDiscount = 0;
foreach ($crmItem['discounts'] as $discount) {
if (in_array($discount['type'], ['bonus_charge', 'loyalty_level'])) {
$loyaltyDiscount += $discount['amount'];
break;
}
}
/**
* The loyalty program discount is calculated within the CRM system. It must be deleted during transfer to avoid duplication.
*/
$productPrice = ($item->get_total() / $item->get_quantity()) + ($loyaltyDiscount / $crmItem['quantity']);
if ($this->cancelLoyalty) {
if ($item->get_total() + $loyaltyDiscount <= $item->get_subtotal()) {
$item->set_total($item->get_total() + $loyaltyDiscount);
$item->calculate_taxes();
$item->save();
}
$productPrice = $item->get_total() / $item->get_quantity();
}
} else {
$productPrice = $item->get_total() ? $item->get_total() / $item->get_quantity() : 0;
}
$productTax = $item->get_total_tax() ? $item->get_total_tax() / $item->get_quantity() : 0;
$itemPrice = $productPrice + $productTax;
@ -127,7 +162,7 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data
/**
* Reset item data.
*/
public function resetData()
public function resetData($cancelLoyalty)
{
$this->data = [
'offer' => [],
@ -135,5 +170,63 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data
'initialPrice' => 0.00,
'quantity' => 0.00
];
$this->cancelLoyalty = $cancelLoyalty;
}
/**
* Checking whether the loyalty program discount needs to be canceled. (Changing the sales items in the order)
*
* @param array $wcItems
* @param array $crmItems
*
* @return bool
*/
public function isCancelLoyalty($wcItems, $crmItems): bool
{
/** If the number of sales items does not match */
if (count($wcItems) !== count($crmItems)) {
$this->cancelLoyalty = true;
return true;
}
foreach ($wcItems as $id => $item) {
$loyaltyDiscount = 0;
/** If a trading position has been added/deleted */
if (!isset($crmItems[$id])) {
$this->cancelLoyalty = true;
return true;
}
/** If the quantity of goods in a trade item does not match */
if ($item->get_quantity() !== $crmItems[$id]['quantity']) {
$this->cancelLoyalty = true;
return true;
}
foreach ($crmItems[$id]['discounts'] as $discount) {
if (in_array($discount['type'], ['bonus_charge', 'loyalty_level'])) {
$loyaltyDiscount = $discount['amount'];
break;
}
}
/**
*If the sum of the trade item including discounts and loyalty program discount exceeds the cost without discounts.
* (Occurs when recalculating an order, deleting/adding coupons)
*/
if (($item->get_total() + $loyaltyDiscount) > $item->get_subtotal()) {
$this->cancelLoyalty = true;
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,33 @@
<?php
if (!class_exists('WC_Retailcrm_Loyalty_Constraint')) :
/**
* PHP version 7.0
*
* Class WC_Retailcrm_Loyalty_Constraint - Constraint for CRM loyalty.
*
* @category Integration
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
class WC_Retailcrm_Loyalty_Constraint
{
public $notFoundCrmUser = 'User not found in the system';
public $errorFoundLoyalty = 'Error when searching for participation in loyalty programs';
public $notFoundActiveParticipation = 'No active participation in the loyalty program was detected';
public $notExistBonuses = 'No bonuses for debiting';
public $notFoundLoyalty = 'Loyalty program not found';
public $loyaltyInactive = 'Loyalty program is not active';
public $loyaltyBlocked = 'Loyalty program blocked';
public $isCorporateUser = 'This user is a corporate person';
}
endif;

View File

@ -0,0 +1,120 @@
<?php
if (!class_exists('WC_Retailcrm_Loyalty_Validator')) :
/**
* PHP version 7.0
*
* Class WC_Retailcrm_Loyalty_Validator - Validate CRM loyalty.
*
* @category Integration
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
class WC_Retailcrm_Loyalty_Validator extends WC_Retailcrm_Loyalty_Constraint
{
/** @var WC_Retailcrm_Client_V5 */
protected $apiClient;
protected $isActiveCorp;
public $customer;
public $loyaltyAccount;
public function __construct($apiClient, $isActiveCorp)
{
$this->apiClient = $apiClient;
$this->isActiveCorp = $isActiveCorp;
}
public function checkAccount(int $userId): bool
{
try {
$this->checkUser($userId);
$this->checkLoyaltyAccount($this->customer['id']);
$this->checkActiveLoyalty($this->loyaltyAccount['loyalty']['id']);
return true;
} catch (ValidatorException $exception) {
WC_Admin_Settings::add_error((esc_html__($exception->getMessage(), 'retailcrm')) . "userId: $userId");
} catch (Throwable $exception) {
WC_Admin_Settings::add_error($exception->getMessage());
}
return false;
}
/**
* @throws ValidatorException
*/
private function checkUser($userId)
{
$responseUser = $this->apiClient->customersGet($userId);
if (!isset($responseUser['customer']['id'])) {
throw new ValidatorException($this->notFoundCrmUser, 400);
}
$customer = new WC_Customer($userId);
if ($this->isActiveCorp && !empty($customer->get_billing_company())) {
throw new ValidatorException($this->isCorporateUser, 400);
}
$this->customer = $responseUser['customer'];
}
/**
* @throws ValidatorException
*/
private function checkLoyaltyAccount($customerId)
{
$filter['customerId'] = $customerId;
$responseLoyalty = $this->apiClient->getLoyaltyAccountList($filter);
if (!$responseLoyalty->isSuccessful() || !$responseLoyalty->offsetExists('loyaltyAccounts')) {
throw new ValidatorException($this->errorFoundLoyalty, 400);
}
$actualAccount = null;
foreach ($responseLoyalty['loyaltyAccounts'] as $loyaltyAccount) {
if ($loyaltyAccount['active'] === true) {
$actualAccount = $loyaltyAccount;
}
}
if (!isset($actualAccount)) {
throw new ValidatorException($this->notFoundActiveParticipation, 400);
}
if ($actualAccount['amount'] === 0 && $actualAccount['level']['type'] !== 'discount') {
throw new ValidatorException($this->notExistBonuses, 400);
}
$this->loyaltyAccount = $actualAccount;
}
/**
* @throws ValidatorException
*/
private function checkActiveLoyalty($idLoyalty)
{
$responseLoyalty = $this->apiClient->getLoyalty($idLoyalty);
if (!$responseLoyalty->isSuccessful() || !$responseLoyalty->offsetExists('loyalty')) {
throw new ValidatorException($this->notFoundLoyalty, 400);
}
if ($responseLoyalty['loyalty']['active'] !== true) {
throw new ValidatorException($this->loyaltyInactive, 400);
}
if ($responseLoyalty['loyalty']['blocked'] === true) {
throw new ValidatorException($this->loyaltyBlocked, 400);
}
}
}
endif;

View File

@ -199,7 +199,55 @@ return [
"La moneda del sitio web es distinto a la tienda del CRM. Para el funcionamiento correcto de la integración, las monedas del CMS y CRM deben coincid",
"Uploading services" => "Descarga de servicios",
"Goods with the 'virtual' option enabled will be uploaded to Simla as services" =>
"Los bienes con la opción 'virtual' activada se cargarán en Simla como servicios"
"Los bienes con la opción 'virtual' activada se cargarán en Simla como servicios",
"Loyalty program" => "Programa de fidelización",
"Activate program loyalty" => "Activar programa de fidelización",
"Enable this setting for activate program loyalty on site" => "Activa esta configuración para activar el programa de fidelización en la página web",
"Terms of loyalty program" => "Condiciones del programa de fidelización",
"Insert the terms and conditions of the loyalty program" => "Inserte las condiciones del programa de fidelización",
"Conditions of personal data processing" => "Condiciones de procesamiento de datos personales",
"Insert the terms and conditions for processing personal data" => "Inserte las condiciones para el procesamiento de datos personales",
"To activate the loyalty program it is necessary to activate the <a href='?page=wc-settings'>'enable use of coupons option'</a>" =>
"Para activar el programa de fidelización es necesario activar la opción <a href='?page=wc-settings'>'habilitar el uso de cupones'</a>",
"Bonus account" => "Cuenta de bonificación",
"Participation ID: " => "ID de participación: ",
"Current level: " => "Nivel actual: ",
"Bonuses on the account: " => "Bonificaciones en la cuenta: ",
"Bonus card number: " => "Número de tarjeta de bonificación: ",
"Date of registration: " => "Fecha de registro: ",
"Current level rules" => "Reglas del nivel actual",
"Required amount of purchases to move to the next level: " => "Cantidad de compras necesarias para pasar al siguiente nivel: ",
"Activate participation in the loyalty program" => "Activar participación en el programa de fidelización",
"Send" => "Enviar",
"To register in the loyalty program, fill in the form:" => "Para registrarse en el programa de fidelización, rellene el formulario:",
" I agree with " => " Acepto ",
"loyalty program terms" => "términos del programa de fidelización",
"terms of personal data processing" => "términos de procesamiento de datos personales",
"Phone" => "Teléfono",
"Error while registering in the loyalty program. Try again later" => "Error al registrarse en el programa de fidelización. Inténtalo de nuevo más tarde",
"The card is not linked" => "La tarjeta no está vinculada",
"Error while retrieving data. Try again later" => "Error al recuperar los datos. Inténtalo de nuevo más tarde",
"Error when activating the loyalty program. Try again later" => "Error al activar el programa de fidelización. Inténtalo de nuevo más tarde",
"Enter the correct phone number" => "Introduce el número de teléfono correcto",
"Close" => "Cerrar",
"Ordinary products: accrual of 1 bonus for each %s %s" => "Productos ordinarios: acumulación de 1 bonificación por cada %s %s",
"Promotional products: accrual of 1 bonus for each %s %s" => "Productos promocionales: acumulación de 1 bonificación por cada %s %s",
"Ordinary products: bonus accrual in the amount of %s%% of the purchase amount" => "Productos ordinarios: acumulación de bonificación en la cantidad de %s%% del monto de la compra",
"Promotional products: bonus accrual in the amount of %s%% of the purchase amount" => "Productos promocionales: acumulación de bonificación en la cantidad de %s%% del monto de la compra",
"Ordinary products: %s%% discount" => "Productos ordinarios: descuento del %s%%",
"Promotional products: %s%% discount" => "Productos promocionales: descuento del %s%%",
"User not found in the system" => "Usuario no encontrado en el sistema",
"Error when searching for participation in loyalty programs" => "Error al buscar la participación en programas de fidelización",
"No active participation in the loyalty program was detected" => "No se detectó participación activa en el programa de fidelización",
"No bonuses for debiting" => "No hay bonificaciones para debitar",
"Loyalty program not found" => "Programa de fidelización no encontrado",
"Loyalty program is not active" => "El programa de fidelización no está activo",
"Loyalty program blocked" => "Programa de fidelización bloqueado",
"This user is a corporate person" => "Este usuario es una persona jurídica",
"It is possible to write off" => "Es posible debitar",
"bonuses" => "bonificaciones",
"Use coupon:" => "Utiliza el cupón:",
"Points will be awarded upon completion of the order:" => "Los puntos se concederán al finalizar el pedido:",
],
"language" => "es",
"x-generator" => "GlotPress/2.4.0-alpha",

Binary file not shown.

View File

@ -198,7 +198,55 @@ return [
"Валюта сайта отличается от валюты магазина в CRM. Для корректной работы интеграции, валюты в CRM и CMS должны совпадать",
"Uploading services" => "Выгрузка услуг",
"Goods with the 'virtual' option enabled will be uploaded to Simla as services" =>
"Товары с включенной опцией 'виртуальные' будут выгружаться в CRM как услуги"
"Товары с включенной опцией 'виртуальные' будут выгружаться в CRM как услуги",
"Loyalty program" => "Программа лояльности",
"Activate program loyalty" => "Активировать программу лояльности",
"Enable this setting for activate program loyalty on site" => "Активируйте эту настройку для активации программы лояльности на сайте",
"Terms of loyalty program" => "Условия программы лояльности",
"Insert the terms and conditions of the loyalty program" => "Вставьте условия участия в программе лояльности",
"Conditions of personal data processing" => "Условия обработки персональных данных",
"Insert the terms and conditions for processing personal data" => "Вставьте условия обработки персональных данных",
"To activate the loyalty program it is necessary to activate the <a href='?page=wc-settings'>'enable use of coupons option'</a>" =>
"Для активации программы лояльности необходимо активировать опцию <a href='?page=wc-settings'>'включить использование купонов'</a>",
"Bonus account" => "Бонусный счет",
"Participation ID: " => "ID участия: ",
"Current level: " => "Текущий уровень: ",
"Bonuses on the account: " => "Бонусов на счету: ",
"Bonus card number: " => "Номер бонусной карты: ",
"Date of registration: " => "Дата регистрации: ",
"Current level rules" => "Правила текущего уровня",
"Required amount of purchases to move to the next level: " => "Необходимая сумма покупок для перехода на следующий уровень: ",
"Activate participation in the loyalty program" => "Активировать участие в программе лояльности",
"Send" => "Отправить",
"To register in the loyalty program, fill in the form:" => "Для регистрации в программе лояльности заполните форму:",
" I agree with " => " Я согласен с ",
"loyalty program terms" => "условиями программы лояльности",
"terms of personal data processing" => "условиями обработки персональных данных",
"Phone" => "Телефон",
"Error while registering in the loyalty program. Try again later" => "Ошибка при регистрации в программе лояльности. Попробуйте позже",
"The card is not linked" => "Карта не привязана",
"Error while retrieving data. Try again later" => "Ошибка при получении данных. Попробуйте позже",
"Error when activating the loyalty program. Try again later" => "Ошибка при активации программы лояльности. Попробуйте позже",
"Enter the correct phone number" => "Введите корректный номер телефона",
"Close" => "Закрыть",
"Ordinary products: accrual of 1 bonus for each %s %s" => "Обычные товары: начисление 1 бонуса за каждые %s %s",
"Promotional products: accrual of 1 bonus for each %s %s" => "Акционные товары: начисление 1 бонуса за каждые %s %s",
"Ordinary products: bonus accrual in the amount of %s%% of the purchase amount" => "Обычные товары: начисление бонуса в размере %s%% от суммы покупки",
"Promotional products: bonus accrual in the amount of %s%% of the purchase amount" => "Акционные товары: начисление бонуса в размере %s%% от суммы покупки",
"Ordinary products: %s%% discount" => "Обычные товары: скидка %s%%",
"Promotional products: %s%% discount" => "Акционные товары: скидка %s%%",
"User not found in the system" => "Пользователь не найден в системе",
"Error when searching for participation in loyalty programs" => "Ошибка при поиске участия в программах лояльности",
"No active participation in the loyalty program was detected" => "Не обнаружено активного участия в программе лояльности",
"No bonuses for debiting" => "Нет бонусов для списания",
"Loyalty program not found" => "Программа лояльности не найдена",
"Loyalty program is not active" => "Программа лояльности не активна",
"Loyalty program blocked" => "Программа лояльности заблокирована",
"This user is a corporate person" => "Этот пользователь является корпоративным лицом",
"It is possible to write off" => "Возможно списать",
"bonuses" => "бонусов",
"Use coupon:" => "Используйте купон:",
"Points will be awarded upon completion of the order:" => "По завершению заказа будет начислено баллов:",
],
"language" => "ru",
"x-generator" => "GlotPress/2.4.0-alpha",

Binary file not shown.

View File

@ -5,7 +5,7 @@ Tags: Интеграция, Simla.com, simla
Requires PHP: 7.0
Requires at least: 5.3
Tested up to: 6.5
Stable tag: 4.7.9
Stable tag: 4.8.0
License: GPLv1 or later
License URI: http://www.gnu.org/licenses/gpl-1.0.html
@ -82,6 +82,9 @@ Asegúrate de tener una clave API específica para cada tienda. Las siguientes i
== Changelog ==
= 4.8.0 =
* Added loyalty program
= 4.7.9 =
* Fixed undefined array key number in order history

View File

@ -5,7 +5,7 @@
* Description: Integration plugin for WooCommerce & Simla.com
* Author: RetailDriver LLC
* Author URI: http://retailcrm.pro/
* Version: 4.7.9
* Version: 4.8.0
* Tested up to: 6.5
* Requires Plugins: woocommerce
* WC requires at least: 5.4
@ -121,6 +121,7 @@ if (!class_exists( 'WC_Integration_Retailcrm')) :
require_once(self::checkCustomFile('include/icml/class-wc-retailcrm-icml-writer.php'));
require_once(self::checkCustomFile('include/class-wc-retailcrm-orders.php'));
require_once(self::checkCustomFile('include/class-wc-retailcrm-cart.php'));
require_once(self::checkCustomFile('include/class-wc-retailcrm-loyalty.php'));
require_once(self::checkCustomFile('include/class-wc-retailcrm-customers.php'));
require_once(self::checkCustomFile('include/class-wc-retailcrm-inventories.php'));
require_once(self::checkCustomFile('include/class-wc-retailcrm-history.php'));
@ -132,6 +133,9 @@ if (!class_exists( 'WC_Integration_Retailcrm')) :
require_once(self::checkCustomFile('include/validators/url-validator/class-wc-retailcrm-url-constraint.php'));
require_once(self::checkCustomFile('include/validators/url-validator/class-wc-retailcrm-url-validator.php'));
require_once(self::checkCustomFile('include/validators/class-wc-retailcrm-validator-exception.php'));
require_once(self::checkCustomFile('include/components/class-wc-retailcrm-loyalty-form.php'));
require_once(self::checkCustomFile('include/validators/loyalty-validator/class-wc-retailcrm-loyalty-constraint.php'));
require_once(self::checkCustomFile('include/validators/loyalty-validator/class-wc-retailcrm-loyalty-validator.php'));
}
/**

View File

@ -16,7 +16,7 @@
*
* @link https://wordpress.org/plugins/woo-retailcrm/
*
* @version 4.7.9
* @version 4.8.0
*
* @package RetailCRM
*/

View File

@ -0,0 +1,345 @@
<?php
/**
* PHP version 7.0
*
* Class DataLoyaltyRetailCrm - Data set for WC_Retailcrm_Loyalty_Test.
*
* @category Integration
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
class DataLoyaltyRetailCrm
{
public static function getDataLoyalty()
{
return [
[
'isSuccessful' => true,
'body' => json_encode(['loyaltyAccounts' => []]),
'expected' => 'id="loyaltyRegisterForm"'
],
[
'isSuccessful' => true,
'body' => json_encode(
[
'loyaltyAccounts' => [
0 => [
'active' => false,
'customer' => [
'externalId' => 1
],
'id' => 1
]
]
]
),
'expected' => 'id="loyaltyActivateForm"'
],
[
'isSuccessful' => true,
'body' => json_encode(
[
'loyaltyAccounts' => [
0 => [
'id' => 1,
'level' => [
'name' => 'Test level',
'privilegeSize' => 5,
'privilegeSizePromo' => 3,
'type' => 'bonus_converting'
],
'amount' => 1000,
'cardNumber' => '12345',
'activatedAt' => '2024-04-10 15:00:00',
'nextLevelSum' => 15000,
'loyalty' => [
'currency' => 'USD'
],
'customer' => [
'externalId' => 1
],
'active' => true
]
]
]
),
'expected' => 'Ordinary products: accrual of 1 bonus for each'
],
];
}
public static function dataCheckUser()
{
return [
[
'responseApiMethod' => [],
'wcUserType' => 'individual',
'throwMessage' => 'User not found in the system',
'isCorpActive' => false
],
[
'responseApiMethod' => ['customer' => ['id' => 1]],
'wcUserType' => 'corp',
'throwMessage' => 'This user is a corporate person',
'isCorpActive' => true
],
[
'responseApiMethod' => ['customer' => ['id' => 1]],
'wcUserType' => 'corp',
'throwMessage' => null,
'isCorpActive' => false
],
[
'responseApiMethod' => ['customer' => ['id' => 1]],
'wcUserType' => 'individual',
'throwMessage' => null,
'isCorpActive' => true
],
];
}
public static function dataLoyaltyAccount()
{
return [
[
'responseMock' => ['success' => true],
'throwMessage' => 'Error when searching for participation in loyalty programs'
],
[
'responseMock' => ['success' => true, 'loyaltyAccounts' => []],
'throwMessage' => 'No active participation in the loyalty program was detected'
],
[
'responseMock' => ['success' => true, 'loyaltyAccounts' => [['active' => true, 'amount' => 0, 'level' => ['type' => 'bonus_converting']]]],
'throwMessage' => 'No bonuses for debiting'
],
[
'responseMock' => ['success' => true, 'loyaltyAccounts' => [['active' => true, 'amount' => 0, 'level' => ['type' => 'discount']]]],
'throwMessage' => null
],
[
'responseMock' => ['success' => true, 'loyaltyAccounts' => [['active' => true, 'amount' => 100, 'level' => ['type' => 'bonus_converting']]]],
'throwMessage' => null
],
];
}
public static function dataCheckActiveLoyalty()
{
return [
[
'responseMock' => ['success' => true],
'throwMessage' => 'Loyalty program not found'
],
[
'responseMock' => ['success' => true, 'loyalty' => ['active' => false]],
'throwMessage' => 'Loyalty program is not active'
],
[
'responseMock' => ['success' => true, 'loyalty' => ['active' => true, 'blocked' => true]],
'throwMessage' => 'Loyalty program blocked'
],
[
'responseMock' => ['success' => true, 'loyalty' => ['active' => true, 'blocked' => false]],
'throwMessage' => null
]
];
}
public static function createProducts()
{
$product1 = new WC_Product();
$product1->set_name('Test product 1');
$product1->set_sku('test1' . mt_rand());
$product1->set_regular_price('200');
$product1->set_sale_price('200');
$product1->save();
$product2 = new WC_Product();
$product2->set_name('Test product 2');
$product2->set_sku('test2' . mt_rand());
$product2->set_regular_price('100');
$product2->set_sale_price('50');
$product2->save();
return [$product1, $product2];
}
public static function getDataCalculation()
{
return [
[
'response' => [
'calculations' => [],
'order' => [
'items' => [
[
'discounts' => [
[
'type' => 'loyalty_level',
'amount' => 20
],
]
],
[
'discounts' => [
[
'type' => 'loyalty_level',
'amount' => 20
],
]
]
]
]
],
'expected' => 40
],
[
'response' => [
'calculations' => [
[
'privilegeType' => 'test'
],
[
'privilegeType' => 'loyalty_level',
'maxChargeBonuses' => 50
]
],
'order' => [
'items' => [['discounts' => null], ['discounts' => null]],
]
],
'expected' => 50
]
];
}
public static function dataCheckLoyaltyCoupon()
{
return [
[
'code' => 'loyalty49844894548',
'expected' => true
],
[
'code' => '56556446548484',
'expected' => false
],
[
'code' => 'dfhdfh54655pl',
'expected' => false
],
[
'code' => '654844pl18498',
'expected' => false
]
];
}
public static function dataGetEmailsForPersonalCoupon()
{
return [
[
'email' => 'test1@gmail.com',
'code' => 'loyalty' . mt_rand()
],
[
'email' => 'test2@gmail.com',
'code' => 'loyalty' . mt_rand()
],
[
'email' => 'test3@gmail.com',
'expectedCode' => false
]
];
}
public static function dataValidUser()
{
$users = self::createUsers();
return [
[
'customer' => $users[0],
'corporate_enabled' => 'yes',
'expected' => true,
'orderCorporate' => false
],
[
'customer' => $users[1],
'corporate_enabled' => 'yes',
'expected' => false,
'orderCorporate' => false
],
[
'customer' => $users[1],
'corporate_enabled' => 'no',
'expected' => true,
'orderCorporate' => false
],
[
'customer' => null,
'corporate_enabled' => 'yes',
'expected' => false,
'orderCorporate' => false
],
[
'customer' => $users[0],
'corporate_enabled' => 'yes',
'expected' => false,
'orderCorporate' => true
]
];
}
public static function createUsers()
{
$customer = new WC_Customer();
$customer->set_first_name('Tester 1');
$customer->set_last_name('Tester 1');
$customer->set_email(uniqid(md5(date('Y-m-d H:i:s'))) . '@mail.com');
$customer->set_password('password');
$customer->set_billing_phone('89000000000');
$customer->set_date_created(date('Y-m-d H:i:s'));
$customer->save();
$customer1 = new WC_Customer();
$customer1->set_first_name('Tester 1');
$customer1->set_last_name('Tester 1');
$customer1->set_email(uniqid(md5(date('Y-m-d H:i:s'))) . '@mail.com');
$customer1->set_password('password');
$customer1->set_billing_phone('89000000000');
$customer1->set_date_created(date('Y-m-d H:i:s'));
$customer1->set_billing_company('OOO TEST');
$customer1->save();
return [$customer, $customer1];
}
public static function createCoupons($email1 = 'test1@gmail.com', $email2 = 'test2@gmail.com')
{
$coupon = new WC_Coupon();
$coupon->set_usage_limit(0);
$coupon->set_amount(100);
$coupon->set_email_restrictions($email1);
$coupon->set_code('loyalty' . mt_rand());
$coupon->save();
$coupon1 = new WC_Coupon();
$coupon1->set_usage_limit(0);
$coupon1->set_amount(100);
$coupon1->set_email_restrictions($email2);
$coupon1->set_code('loyalty' . mt_rand());
$coupon1->save();
return [$coupon, $coupon1];
}
}

View File

@ -80,7 +80,10 @@ class WC_Retailcrm_Test_Case_Helper extends WC_Unit_Test_Case
'product_description' => 'full',
'stores_for_uploading' => ['woocommerce', 'main'],
'woo_coupon_apply_field' => 'testField',
'icml_unload_services' => 'yes'
'icml_unload_services' => 'yes',
'loyalty' => 'yes',
'loyalty_terms' => 'Test terms',
'loyalty_personal' => 'Test privacy'
];
update_option(WC_Retailcrm_Base::$option_key, $options);

View File

@ -0,0 +1,131 @@
<?php
if (!class_exists('WC_Retailcrm_Client_V5')) {
include_once(WC_Integration_Retailcrm::checkCustomFile('include/api/class-wc-retailcrm-client-v5.php'));
}
/**
* PHP version 7.0
*
* Class WC_Retailcrm_Loyalty_Client_Test
*
* @category Integration
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
class WC_Retailcrm_Loyalty_Client_Test extends WC_Retailcrm_Test_Case_Helper
{
protected $responseMock;
protected $apiMock;
/** @var \WC_Retailcrm_Client_V5 */
protected $clientMock;
public function setUp()
{
$this->responseMock = $this->getMockBuilder('\WC_Retailcrm_Response_Helper')
->disableOriginalConstructor()
->setMethods(['isSuccessful'])
->getMock()
;
$this->responseMock->setResponse(['success' => true]);
$this->setMockResponse($this->responseMock, 'isSuccessful', true);
$this->apiMock = $this->getMockBuilder('\WC_Retailcrm_Request')
->disableOriginalConstructor()
->setMethods(['makeRequest'])
->getMock()
;
$this->setMockResponse($this->apiMock, 'makeRequest', $this->responseMock);
$this->clientMock = new \WC_Retailcrm_Client_V5('https://test@retailcrm.ru', 'test', 'test');
$reflection = new ReflectionClass($this->clientMock);
$reflection_property = $reflection->getProperty('client');
$reflection_property->setAccessible(true);
$reflection_property->setValue($this->clientMock, $this->apiMock);
}
/**
* @dataProvider requestLoyaltyData
*/
public function testLoyaltyRequest($method, $parameters)
{
/** @var WC_Retailcrm_Response $test */
$test = call_user_func([$this->clientMock, $method], ...$parameters);
$this->assertTrue($test->isSuccessful());
}
public function requestLoyaltyData()
{
return [
[
'method' => 'createLoyaltyAccount',
'parameters' => [['test'], 'testSite']
],
[
'method' => 'getLoyaltyClientInfo',
'parameters' => [1]
],
[
'method' => 'activateLoyaltyAccount',
'parameters' => [1]
],
[
'method' => 'editLoyaltyAccount',
'parameters' => [1, ['test']]
],
[
'method' => 'getLoyaltyAccountList',
'parameters' => [['filter'], 20, 1]
],
[
'method' => 'getListLoyalty',
'parameters' => [['filter'], 20, 1]
],
[
'method' => 'getLoyalty',
'parameters' => [1]
],
[
'method' => 'chargeBonusLoyalty',
'parameters' => [1, 100, 'test']
],
[
'method' => 'creditBonusLoyalty',
'parameters' => [1, ['amount' => 100]]
],
[
'method' => 'getClientBonusHistory',
'parameters' => [1, ['filter'], 20, 1]
],
[
'method' => 'getDetailClientBonus',
'parameters' => [1, 'status', ['filter'], 20, 1]
],
[
'method' => 'getBonusHistory',
'parameters' => ['cursor', ['filter'], 20]
],
[
'method' => 'calculateDiscountLoyalty',
'parameters' => ['site', ['order']]
],
[
'method' => 'applyBonusToOrder',
'parameters' => ['site', ['order'], 100]
],
[
'method' => 'cancelBonusOrder',
'parameters' => [['order']]
],
];
}
}

View File

@ -132,6 +132,11 @@ class WC_Retailcrm_Base_Test extends WC_Retailcrm_Test_Case_Helper
$this->assertArrayHasKey('bind_by_sku', $this->baseRetailcrm->form_fields);
$this->assertArrayHasKey('update_number', $this->baseRetailcrm->form_fields);
$this->assertArrayHasKey('product_description', $this->baseRetailcrm->form_fields);
//loyalty
$this->assertArrayHasKey('loyalty', $this->baseRetailcrm->form_fields);
$this->assertArrayHasKey('loyalty_terms', $this->baseRetailcrm->form_fields);
$this->assertArrayHasKey('loyalty_personal', $this->baseRetailcrm->form_fields);
}
public function test_retailcrm_form_fields_value()

View File

@ -0,0 +1,495 @@
<?php
use DataLoyaltyRetailCrm;
if (!class_exists('WC_Retailcrm_Loyalty')) {
include_once(WC_Integration_Retailcrm::checkCustomFile('class-wc-retailcrm-loyalty.php'));
}
if (!class_exists('WC_Retailcrm_Response')) {
include_once(WC_Integration_Retailcrm::checkCustomFile('include/api/class-wc-retailcrm-response.php'));
}
/**
* PHP version 7.0
*
* Class WC_Retailcrm_Loyalty_Test
*
* @category Integration
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
class WC_Retailcrm_Loyalty_Test extends WC_Retailcrm_Test_Case_Helper
{
protected $responseMock;
protected $apiMock;
/** @var \WC_Retailcrm_Loyalty */
protected $loyalty;
public function setUp()
{
$this->responseMock = $this->getMockBuilder('\WC_Retailcrm_Response_Helper')
->disableOriginalConstructor()
->setMethods(['isSuccessful'])
->getMock()
;
$this->responseMock->setResponse(['success' => true]);
$this->setMockResponse($this->responseMock, 'isSuccessful', true);
$this->apiMock = $this->getMockBuilder('\WC_Retailcrm_Client_V5')
->disableOriginalConstructor()
->setMethods(['customersGet', 'getLoyaltyAccountList', 'createLoyaltyAccount', 'activateLoyaltyAccount', 'calculateDiscountLoyalty', 'getSingleSiteForKey', 'applyBonusToOrder'])
->getMock()
;
$this->setMockResponse($this->apiMock, 'customersGet', ['customer' => ['id' => 1]]);
$this->setMockResponse($this->apiMock, 'createLoyaltyAccount', $this->responseMock);
$this->setMockResponse($this->apiMock, 'activateLoyaltyAccount', $this->responseMock);
$this->setMockResponse($this->apiMock, 'getSingleSiteForKey', 'woo');
$this->loyalty = new WC_Retailcrm_Loyalty($this->apiMock, []);
}
/**
* @dataProvider DataLoyaltyRetailCrm::getDataLoyalty()
*/
public function testGetForm($isSuccessful, $body, $expected)
{
$response = new WC_Retailcrm_Response($isSuccessful ? 200 : 400, $body);
$this->setMockResponse($this->apiMock, 'getLoyaltyAccountList', $response);
$this->loyalty = new WC_Retailcrm_Loyalty($this->apiMock, []);
$result = $this->loyalty->getForm(1);
if (isset($result['form'])) {
$this->assertTrue((bool) stripos($result['form'], $expected));
}
}
public function testRegistrationLoyalty()
{
$result = $this->loyalty->registerCustomer(1, '89999999999', 'test');
$this->assertTrue($result);
}
public function testActivateLoyalty()
{
$result = $this->loyalty->activateLoyaltyCustomer(1);
$this->assertTrue($result);
}
/**
* @dataProvider DataLoyaltyRetailCrm::getDataCalculation()
*/
public function testGetDiscountLoyalty($response, $expected)
{
$responseMock = new WC_Retailcrm_Response(200, json_encode($response));
$this->setMockResponse($this->apiMock, 'calculateDiscountLoyalty', $responseMock);
$cartItems = $this->createCart();
$this->loyalty = new WC_Retailcrm_Loyalty($this->apiMock, []);
$method = $this->getPrivateMethod('getDiscountLoyalty', $this->loyalty);
$discount = $method->invokeArgs($this->loyalty, [$cartItems, 'test', 1]);
$this->assertEquals($expected, $discount);
foreach ($cartItems as $items) {
$items['data']->delete(true);
}
}
/**
* @dataProvider DataLoyaltyRetailCrm::dataCheckLoyaltyCoupon()
*/
public function testIsLoyaltyCoupon($code, $expected)
{
$this->assertEquals($expected, $this->loyalty->isLoyaltyCoupon($code));
}
/**
* @dataProvider DataLoyaltyRetailCrm::dataValidUser();
*/
public function testIsValidOrder($customer, $corporate_enabled, $expected, $orderCorporate)
{
$this->loyalty = new WC_Retailcrm_Loyalty($this->apiMock, ['corporate_enabled' => $corporate_enabled]);
$wcOrder = new WC_Order();
if ($orderCorporate) {
$wcOrder->set_billing_company('OOO TEST');
}
$this->assertEquals($expected, $this->loyalty->isValidOrder($customer, $wcOrder));
}
/**
* @dataProvider DataLoyaltyRetailCrm::dataGetEmailsForPersonalCoupon()
*/
public function testGetCouponLoyalty($email, $code)
{
if ($code) {
$coupon = new WC_Coupon();
$coupon->set_usage_limit(0);
$coupon->set_amount(100);
$coupon->set_email_restrictions($email);
$coupon->set_code($code);
$coupon->save();
}
$coupons = $this->loyalty->getCouponLoyalty($email);
if (!$coupons && $code === false) {
$this->assertTrue(true);
} else {
$result = false;
foreach ($coupons as $item) {
$coupon = new WC_Coupon($item['code']);
$result = true;
$this->assertTrue($code === $item['code']);
$coupon->delete(true);
}
$this->assertTrue($result);
}
}
public function testCreateLoyaltyCouponWithoutAppliedCoupon()
{
$products = DataLoyaltyRetailCrm::createProducts();
$user = DataLoyaltyRetailcrm::createUsers()[0];
$cart = new WC_Cart();
$cart->add_to_cart($products[0]->get_id());
$cart->add_to_cart($products[1]->get_id());
$woocommerce = wc();
$woocommerce->cart = $cart;
$woocommerce->customer = $user;
$validatorMock = $this->getMockBuilder('\WC_Retailcrm_Loyalty_Validator')
->disableOriginalConstructor()
->getMock()
;
$this->setMockResponse($validatorMock, 'checkAccount', true);
$validatorMock->loyaltyAccount['level']['type'] = 'loyalty_level';
$responseCalculation = DataLoyaltyRetailCrm::getDataCalculation()[1];
$responseMock = new WC_Retailcrm_Response(200, json_encode($responseCalculation['response']));
$this->setMockResponse($this->apiMock, 'calculateDiscountLoyalty', $responseMock);
$this->loyalty = new WC_Retailcrm_Loyalty($this->apiMock, []);
$reflection = new \ReflectionClass($this->loyalty);
$reflection_property = $reflection->getProperty('validator');
$reflection_property->setAccessible(true);
$reflection_property->setValue($this->loyalty, $validatorMock);
$GLOBALS['woocommerce'] = $woocommerce;
$result = $this->loyalty->createLoyaltyCoupon();
$this->assertNotEmpty($result);
$this->assertNotEmpty($this->loyalty->getCouponLoyalty($woocommerce->customer->get_email()));
}
public function testCreateLoyaltyCouponWithPercentDiscount()
{
$products = DataLoyaltyRetailCrm::createProducts();
$user = DataLoyaltyRetailcrm::createUsers()[0];
$cart = new WC_Cart();
$cart->add_to_cart($products[0]->get_id());
$cart->add_to_cart($products[1]->get_id());
$woocommerce = wc();
$woocommerce->cart = $cart;
$woocommerce->customer = $user;
$validatorMock = $this->getMockBuilder('\WC_Retailcrm_Loyalty_Validator')
->disableOriginalConstructor()
->getMock()
;
$this->setMockResponse($validatorMock, 'checkAccount', true);
$validatorMock->loyaltyAccount['level']['type'] = 'discount';
$responseCalculation = DataLoyaltyRetailCrm::getDataCalculation()[1];
$responseMock = new WC_Retailcrm_Response(200, json_encode($responseCalculation['response']));
$this->setMockResponse($this->apiMock, 'calculateDiscountLoyalty', $responseMock);
$this->loyalty = new WC_Retailcrm_Loyalty($this->apiMock, []);
$reflection = new \ReflectionClass($this->loyalty);
$reflection_property = $reflection->getProperty('validator');
$reflection_property->setAccessible(true);
$reflection_property->setValue($this->loyalty, $validatorMock);
$GLOBALS['woocommerce'] = $woocommerce;
$result = $this->loyalty->createLoyaltyCoupon();
$this->assertEmpty($result);
$this->assertNotEmpty($this->loyalty->getCouponLoyalty($woocommerce->customer->get_email()));
$this->assertNotEmpty($woocommerce->cart->get_coupons());
}
public function testCreateLoyaltyCouponWithRefreshCoupon()
{
$products = DataLoyaltyRetailCrm::createProducts();
$user = DataLoyaltyRetailcrm::createUsers()[0];
$cart = new WC_Cart();
$cart->add_to_cart($products[0]->get_id());
$cart->add_to_cart($products[1]->get_id());
$coupon = new WC_Coupon();
$coupon->set_usage_limit(0);
$coupon->set_amount('50');
$coupon->set_email_restrictions($user->get_email());
$coupon->set_code('loyalty' . mt_rand());
$coupon->save();
$cart->apply_coupon($coupon->get_code());
$couponCode = $coupon->get_code();
$woocommerce = wc();
$woocommerce->cart = $cart;
$woocommerce->customer = $user;
$validatorMock = $this->getMockBuilder('\WC_Retailcrm_Loyalty_Validator')
->disableOriginalConstructor()
->getMock()
;
$this->setMockResponse($validatorMock, 'checkAccount', true);
$validatorMock->loyaltyAccount['level']['type'] = 'discount';
$responseCalculation = DataLoyaltyRetailCrm::getDataCalculation()[1];
$responseMock = new WC_Retailcrm_Response(200, json_encode($responseCalculation['response']));
$this->setMockResponse($this->apiMock, 'calculateDiscountLoyalty', $responseMock);
$this->loyalty = new WC_Retailcrm_Loyalty($this->apiMock, []);
$reflection = new \ReflectionClass($this->loyalty);
$reflection_property = $reflection->getProperty('validator');
$reflection_property->setAccessible(true);
$reflection_property->setValue($this->loyalty, $validatorMock);
$GLOBALS['woocommerce'] = $woocommerce;
$result = $this->loyalty->createLoyaltyCoupon(true);
$this->assertEmpty($result);
$this->assertNotEmpty($this->loyalty->getCouponLoyalty($woocommerce->customer->get_email()));
$coupons = $woocommerce->cart->get_coupons();
$this->assertNotEmpty($coupons);
$this->assertFalse(isset($coupons[$couponCode]));
}
public function testDeleteLoyaltyCouponInOrder()
{
$products = DataLoyaltyRetailCrm::createProducts();
$user = DataLoyaltyRetailcrm::createUsers()[0];
$coupon = new WC_Coupon();
$coupon->set_usage_limit(0);
$coupon->set_amount('50');
$coupon->set_email_restrictions($user->get_email());
$coupon->set_code('loyalty' . mt_rand());
$coupon->save();
$wcOrder = wc_create_order([
'status' => null,
'customer_id' => $user->get_id(),
'customer_note' => null,
'parent' => null,
'created_via' => null,
'cart_hash' => null,
'order_id' => 0,
]);
$wcOrder->add_product($products[0]);
$wcOrder->add_product($products[1]);
$wcOrder->apply_coupon($coupon->get_code());
$wcOrder->calculate_totals();
$wcOrder->save();
$this->assertNotEmpty($wcOrder->get_coupons());
$this->loyalty->deleteLoyaltyCouponInOrder($wcOrder);
$this->assertEmpty($wcOrder->get_coupons());
$wcOrder->delete(true);
}
public function testApplyLoyaltyDiscountWithBonuses()
{
$products = DataLoyaltyRetailCrm::createProducts();
$user = DataLoyaltyRetailcrm::createUsers()[0];
$discountLoyalty = 50;
$wcOrder = wc_create_order([
'status' => null,
'customer_id' => $user->get_id(),
'customer_note' => null,
'parent' => null,
'created_via' => null,
'cart_hash' => null,
'order_id' => 0,
]);
$wcOrder->add_product($products[0]);
$wcOrder->add_product($products[1]);
$wcOrder->calculate_totals();
$wcOrder->save();
foreach ($wcOrder->get_items() as $id => $item) {
$currentItemsPrice[$id] = $item->get_total();
$itemIds[] = $id;
}
$createdCrmOrderResponse = ['site' => 'test', 'externalId' => 1, 'items' => [['discounts' => [],], ['discounts' => []]]];
$response = new WC_Retailcrm_Response(
200,
json_encode([
'order' => [
'items' => [
[
'externalIds' => [
[
'value' => '11_' . $itemIds[0]
]
],
'discounts' => [
[
'type' => 'bonus_charge',
'amount' => 25
]
]
],
[
'externalIds' => [
[
'value' => '22_' . $itemIds[1]
]
],
'discounts' => [
[
'type' => 'bonus_charge',
'amount' => 25
]
]
]
]
]
])
);
$this->setMockResponse($this->apiMock, 'applyBonusToOrder', $response);
$this->loyalty = new WC_Retailcrm_Loyalty($this->apiMock, []);
$this->loyalty->applyLoyaltyDiscount($wcOrder, $createdCrmOrderResponse, $discountLoyalty);
foreach ($wcOrder->get_items() as $id => $item) {
$this->assertNotEquals($item->get_total(), $currentItemsPrice[$id]);
}
}
public function testApplyLoyaltyDiscountWithPercentDiscount()
{
$products = DataLoyaltyRetailCrm::createProducts();
$user = DataLoyaltyRetailcrm::createUsers()[0];
$wcOrder = wc_create_order([
'status' => null,
'customer_id' => $user->get_id(),
'customer_note' => null,
'parent' => null,
'created_via' => null,
'cart_hash' => null,
'order_id' => 0,
]);
$wcOrder->add_product($products[0]);
$wcOrder->add_product($products[1]);
$wcOrder->calculate_totals();
$wcOrder->save();
foreach ($wcOrder->get_items() as $id => $item) {
$currentItemsPrice[$id] = $item->get_total();
$itemIds[] = $id;
}
$createdCrmOrderResponse = [
'site' => 'test',
'externalId' => 1,
'items' => [
[
'externalIds' => [
[
'value' => '11_' . $itemIds[0]
]
],
'discounts' => [
[
'type' => 'loyalty_level',
'amount' => 25
]
]
],
[
'externalIds' => [
[
'value' => '22_' . $itemIds[1]
]
],
'discounts' => [
[
'type' => 'loyalty_level',
'amount' => 25
]
]
]
]
];
$this->loyalty->applyLoyaltyDiscount($wcOrder, $createdCrmOrderResponse);
foreach ($wcOrder->get_items() as $id => $item) {
$this->assertNotEquals($item->get_total(), $currentItemsPrice[$id]);
}
}
private function getPrivateMethod($method, $class)
{
$reflection = new ReflectionClass($class);
$method = $reflection->getMethod($method);
$method->setAccessible(true);
return $method;
}
private function createCart()
{
$products = DataLoyaltyRetailCrm::createProducts();
return [
[
'data' => $products[0],
'quantity' => 1,
'line_subtotal' => $products[0]->get_regular_price(),
'line_total' => $products[0]->get_regular_price(),//When sale_price identical regular price, sale is empty
],
[
'data' => $products[1],
'quantity' => 3,
'line_subtotal' => $products[1]->get_regular_price() * 3,
'line_total' => $products[1]->get_sale_price() * 3,
],
];
}
}

View File

@ -642,9 +642,12 @@ class WC_Retailcrm_Orders_Test extends WC_Retailcrm_Test_Case_Helper
*/
private function getRetailcrmOrders($retailcrm)
{
$options = $this->getOptions();
unset($options['loyalty']);
return new WC_Retailcrm_Orders(
$retailcrm,
$this->getOptions(),
$options,
new WC_Retailcrm_Order_Item($this->getOptions()),
new WC_Retailcrm_Order_Address(),
new WC_Retailcrm_Customers(
@ -669,4 +672,3 @@ class WC_Retailcrm_Orders_Test extends WC_Retailcrm_Test_Case_Helper
->getMock();
}
}

View File

@ -0,0 +1,142 @@
<?php
use DataLoyaltyRetailCrm;
class WC_Retailcrm_Loyalty_Validator_Test extends WC_Retailcrm_Test_Case_Helper
{
protected $responseMock;
protected $apiMock;
protected $individualClient;
protected $corpClient;
public function setUp()
{
$this->individualClient = new WC_Customer();
$this->individualClient->set_first_name('Test');
$this->individualClient->set_last_name('Test');
$this->individualClient->set_email(uniqid(md5(date('Y-m-d H:i:s'))) . '@mail.com');
$this->individualClient->set_billing_email( $this->individualClient->get_email());
$this->individualClient->set_password('password');
$this->individualClient->set_billing_phone('89000000000');
$this->individualClient->set_date_created(date('Y-m-d H:i:s'));
$this->individualClient->save();
$this->corpClient = new WC_Customer();
$this->corpClient->set_first_name('Test');
$this->corpClient->set_last_name('Test');
$this->corpClient->set_email(uniqid(md5(date('Y-m-d H:i:s'))) . '@mail.com');
$this->corpClient->set_billing_email($this->corpClient->get_email());
$this->corpClient->set_password('password');
$this->corpClient->set_billing_phone('89000000000');
$this->corpClient->set_date_created(date('Y-m-d H:i:s'));
$this->corpClient->set_billing_company('TEST COMPANY');
$this->corpClient->save();
}
/**
* @dataProvider DataLoyaltyRetailCrm::dataCheckUser()
*/
public function testCheckUser($responseApiMethod, $wcUserType, $throwMessage, $isCorpActive)
{
$this->setResponseMock();
$this->setApiMock('customersGet', $responseApiMethod);
$validator = new WC_Retailcrm_Loyalty_Validator($this->apiMock, $isCorpActive);
$method = $this->getPrivateMethod('checkUser', $validator);
$wcUserId = $wcUserType === 'individual' ? $this->individualClient->get_id() : $this->corpClient->get_id();
try {
$method->invokeArgs($validator, [$wcUserId]);
if ($throwMessage) {
$this->fail('ValidatorException was not thrown');
} else {
$this->assertTrue(true);
}
} catch (ValidatorException $exception) {
$this->assertEquals($throwMessage, $exception->getMessage());
}
}
/** @dataProvider DataLoyaltyRetailCrm::dataLoyaltyAccount() */
public function testGetLoyaltyAccount($responseMock, $throwMessage)
{
$this->setResponseMock($responseMock);
$this->setApiMock('getLoyaltyAccountList', $this->responseMock);
$validator = new WC_Retailcrm_Loyalty_Validator($this->apiMock, true);
$method = $this->getPrivateMethod('checkLoyaltyAccount', $validator);
try {
$method->invokeArgs($validator, [777]);
if ($throwMessage) {
$this->fail('ValidatorException was not thrown');
} else {
$this->assertTrue(true);
}
} catch (ValidatorException $exception) {
$this->assertEquals($throwMessage, $exception->getMessage());
}
}
/** @dataProvider DataLoyaltyRetailCrm::dataCheckActiveLoyalty() */
public function testCheckActivateLoyalty($responseMock, $throwMessage)
{
$this->setResponseMock($responseMock);
$this->setApiMock('getLoyalty', $this->responseMock);
$validator = new WC_Retailcrm_Loyalty_Validator($this->apiMock, true);
$method = $this->getPrivateMethod('checkActiveLoyalty', $validator);
try {
$method->invokeArgs($validator, [1]);
if ($throwMessage) {
$this->fail('ValidatorException was not thrown');
} else {
$this->assertTrue(true);
}
} catch (ValidatorException $exception) {
$this->assertEquals($throwMessage, $exception->getMessage());
}
}
private function setResponseMock($response = ['success' => true])
{
$this->responseMock = $this->getMockBuilder('\WC_Retailcrm_Response_Helper')
->disableOriginalConstructor()
->setMethods(['isSuccessful'])
->getMock()
;
$this->responseMock->setResponse($response);
$this->setMockResponse($this->responseMock, 'isSuccessful', true);
}
private function setApiMock($method, $response)
{
$this->apiMock = $this->getMockBuilder('\WC_Retailcrm_Client_V5')
->disableOriginalConstructor()
->setMethods([$method])
->getMock()
;
$this->setMockResponse($this->apiMock, $method, $response);
}
private function getPrivateMethod($method, $class)
{
$reflection = new ReflectionClass($class);
$method = $reflection->getMethod($method);
$method->setAccessible(true);
return $method;
}
public function tearDown()
{
$this->individualClient->delete();
$this->corpClient->delete();
}
}