1
0
mirror of synced 2025-02-11 20:49:27 +03:00

ref #94823 Processing of discount when editing an order (#332)

This commit is contained in:
Kocmonavtik 2024-06-03 13:17:47 +03:00 committed by GitHub
parent 2765e7ecb4
commit 48af9ba5aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 238 additions and 34 deletions

View File

@ -2992,12 +2992,12 @@ class WC_Retailcrm_Client_V5
} }
/** Maximum discount calculation */ /** Maximum discount calculation */
public function calculateDiscountLoyalty(string $site, array $order) public function calculateDiscountLoyalty(string $site, array $order, $bonuses = 0)
{ {
return $this->client->makeRequest( return $this->client->makeRequest(
"/loyalty/calculate", "/loyalty/calculate",
WC_Retailcrm_Request::METHOD_POST, WC_Retailcrm_Request::METHOD_POST,
['site' => $site, 'order' => json_encode($order)] ['site' => $site, 'order' => json_encode($order), 'bonuses' => $bonuses]
); );
} }
@ -3011,12 +3011,12 @@ class WC_Retailcrm_Client_V5
); );
} }
public function cancelBonusOrder(string $site, array $order) public function cancelBonusOrder(array $order)
{ {
return $this->client->makeRequest( return $this->client->makeRequest(
"/orders/loyalty/cancel-bonus-operations", "/orders/loyalty/cancel-bonus-operations",
WC_Retailcrm_Request::METHOD_POST, WC_Retailcrm_Request::METHOD_POST,
['site' => $site, 'order' => json_encode($order)] ['order' => json_encode($order)]
); );
} }

View File

@ -346,12 +346,29 @@ if (!class_exists('WC_Retailcrm_Loyalty')) :
return $discountLp; return $discountLp;
} }
public function isValidOrder($wcUser, $wcOrder) public function isValidOrder($wcCustomer, $wcOrder)
{ {
return !(!$wcUser || (isCorporateUserActivate($this->settings) && isCorporateOrder($wcUser, $wcOrder))); return !(!$wcCustomer || (isCorporateUserActivate($this->settings) && isCorporateOrder($wcCustomer, $wcOrder)));
} }
public function applyLoyaltyDiscount($wcOrder, $discountLp, $createdOrder) 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; $isPercentDiscount = false;
$items = []; $items = [];
@ -378,9 +395,14 @@ if (!class_exists('WC_Retailcrm_Loyalty')) :
$items = $response['order']['items']; $items = $response['order']['items'];
} }
$this->calculateLoyaltyDiscount($wcOrder, $items);
}
public function calculateLoyaltyDiscount($wcOrder, $orderItems)
{
$wcItems = $wcOrder->get_items(); $wcItems = $wcOrder->get_items();
foreach ($items as $item) { foreach ($orderItems as $item) {
$externalId = $item['externalIds'][0]['value']; $externalId = $item['externalIds'][0]['value'];
$externalId = preg_replace('/^\d+\_/m', '', $externalId); $externalId = preg_replace('/^\d+\_/m', '', $externalId);
@ -402,6 +424,37 @@ if (!class_exists('WC_Retailcrm_Loyalty')) :
$wcOrder->calculate_totals(); $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];
}
} }
endif; endif;

View File

@ -44,6 +44,12 @@ if (!class_exists('WC_Retailcrm_Orders')) :
/** @var array */ /** @var array */
private $order = []; private $order = [];
/** @var bool */
private $cancelLoyalty = false;
/** @var string */
private $loyaltyDiscountType = '';
/** @var array */ /** @var array */
private $payment = []; private $payment = [];
@ -94,25 +100,34 @@ if (!class_exists('WC_Retailcrm_Orders')) :
$this->order_payment->resetData(); $this->order_payment->resetData();
$wcOrder = wc_get_order($orderId); $wcOrder = wc_get_order($orderId);
$privilegeType = 'none';
if ($this->loyalty) { if ($this->loyalty) {
$wcCustomer = null;
$privilegeType = 'none';
$discountLp = $this->loyalty->deleteLoyaltyCouponInOrder($wcOrder); $discountLp = $this->loyalty->deleteLoyaltyCouponInOrder($wcOrder);
$wcUser = $wcOrder->get_user(); $dataOrder = $wcOrder->get_data();
if (!$this->loyalty->isValidOrder($wcUser, $wcOrder)) { if (isset($dataOrder['customer_id'])) {
$wcCustomer = new WC_Customer($dataOrder['customer_id']) ?? null;
}
if (!$this->loyalty->isValidOrder($wcCustomer, $wcOrder)) {
if ($discountLp > 0) { if ($discountLp > 0) {
writeBaseLogs('The user does not meet the requirements for working with the loyalty program. Order Id: ' . $orderId); writeBaseLogs('The user does not meet the requirements for working with the loyalty program. Order Id: ' . $orderId);
} }
$discountLp = 0; $discountLp = 0;
} else { $privilegeType = 'none';
} elseif ($this->loyalty->isValidUser($wcCustomer)) {
$privilegeType = 'loyalty_level'; $privilegeType = 'loyalty_level';
} }
} }
$this->processOrder($wcOrder); $this->processOrder($wcOrder);
$this->order['privilegeType'] = $privilegeType;
if (isset($privilegeType)) {
$this->order['privilegeType'] = $privilegeType;
}
$response = $this->retailcrm->ordersCreate($this->order); $response = $this->retailcrm->ordersCreate($this->order);
@ -124,7 +139,7 @@ if (!class_exists('WC_Retailcrm_Orders')) :
} }
if (isset($discountLp) && $discountLp > 0) { if (isset($discountLp) && $discountLp > 0) {
$this->loyalty->applyLoyaltyDiscount($wcOrder, $discountLp, $response['order']); $this->loyalty->applyLoyaltyDiscount($wcOrder, $response['order'], $discountLp);
} }
} catch (Throwable $exception) { } catch (Throwable $exception) {
writeBaseLogs( writeBaseLogs(
@ -302,16 +317,39 @@ if (!class_exists('WC_Retailcrm_Orders')) :
try { try {
$wcOrder = wc_get_order($orderId); $wcOrder = wc_get_order($orderId);
$needRecalculate = false;
$this->processOrder($wcOrder, true); $this->processOrder($wcOrder, true);
$response = $this->retailcrm->ordersEdit($this->order); if ($this->cancelLoyalty) {
$this->cancelLoyalty = false;
$this->order_item->cancelLoyalty = false;
$needRecalculate = true;
// Allows you to verify order changes and perform additional actions 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);
$response = apply_filters('retailcrm_order_update_after', $response, $wcOrder); $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); $this->payment = $this->orderUpdatePaymentType($wcOrder);
if ($needRecalculate) {
$this->loyalty->calculateLoyaltyDiscount($wcOrder, $response['order']['items']);
}
} }
} catch (Throwable $exception) { } catch (Throwable $exception) {
writeBaseLogs( writeBaseLogs(
@ -447,15 +485,32 @@ if (!class_exists('WC_Retailcrm_Orders')) :
} }
$orderData['delivery']['address'] = $this->order_address->build($order)->getData(); $orderData['delivery']['address'] = $this->order_address->build($order)->getData();
$orderItems = []; $orderItems = [];
$crmItems = [];
$wcItems = $order->get_items();
if ($this->loyalty && $update) {
$result = $this->loyalty->getCrmItemsInfo($order->get_id());
if ($result !== []) {
$crmItems = $result['items'];
$this->cancelLoyalty = $this->order_item->isCancelLoyalty($wcItems, $crmItems);
$this->loyaltyDiscountType = $result['discountType'];
}
}
/** @var WC_Order_Item_Product $item */ /** @var WC_Order_Item_Product $item */
foreach ($order->get_items() as $item) { foreach ($wcItems as $id => $item) {
$orderItems[] = $this->order_item->build($item)->getData(); $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['items'] = $orderItems;
$orderData['discountManualAmount'] = 0; $orderData['discountManualAmount'] = 0;
$orderData['discountManualPercent'] = 0; $orderData['discountManualPercent'] = 0;

View File

@ -213,7 +213,7 @@ function isCorporateUserActivate($settings)
return isset($settings['corporate_enabled']) && $settings['corporate_enabled'] === WC_Retailcrm_Base::YES; return isset($settings['corporate_enabled']) && $settings['corporate_enabled'] === WC_Retailcrm_Base::YES;
} }
function isCorporateOrder($wcUser, $wcOrder) function isCorporateOrder($wcCustomer, $wcOrder)
{ {
return !empty($wcUser->get_billing_company()) || !empty($wcOrder->get_billing_company()); 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 = []; protected $settings = [];
/** @var bool */
public $cancelLoyalty = false;
/** /**
* WC_Retailcrm_Order_Item constructor. * WC_Retailcrm_Order_Item constructor.
* *
@ -43,13 +46,13 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data
* *
* @return self * @return self
*/ */
public function build($item) public function build($item, $crmItem = null)
{ {
$decimalPlaces = wc_get_price_decimals(); $decimalPlaces = wc_get_price_decimals();
// Calculate price and discount // Calculate price and discount
$price = $this->calculatePrice($item, $decimalPlaces); $price = $this->calculatePrice($item, $decimalPlaces);
$discountPrice = $this->calculateDiscount($item, $price, $decimalPlaces); $discountPrice = $this->calculateDiscount($item, $price, $decimalPlaces, $crmItem);
$data['productName'] = $item['name']; $data['productName'] = $item['name'];
$data['initialPrice'] = $price; $data['initialPrice'] = $price;
@ -112,12 +115,44 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data
* @param WC_Order_Item_Product $item * @param WC_Order_Item_Product $item
* @param $price * @param $price
* @param int $decimalPlaces Price rounding from WC settings * @param int $decimalPlaces Price rounding from WC settings
* * @param array|null $crmItem Current trade position in CRM
* @return float|int * @return float|int
*/ */
private function calculateDiscount(WC_Order_Item_Product $item, $price, int $decimalPlaces) private function calculateDiscount(
{ WC_Order_Item_Product $item,
$productPrice = $item->get_total() ? $item->get_total() / $item->get_quantity() : 0; $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; $productTax = $item->get_total_tax() ? $item->get_total_tax() / $item->get_quantity() : 0;
$itemPrice = $productPrice + $productTax; $itemPrice = $productPrice + $productTax;
@ -127,7 +162,7 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data
/** /**
* Reset item data. * Reset item data.
*/ */
public function resetData() public function resetData($cancelLoyalty)
{ {
$this->data = [ $this->data = [
'offer' => [], 'offer' => [],
@ -135,5 +170,63 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data
'initialPrice' => 0.00, 'initialPrice' => 0.00,
'quantity' => 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
{
$loyaltyDiscount = 0;
/** If the number of sales items does not match */
if (count($wcItems) !== count($crmItems)) {
$this->cancelLoyalty = true;
return true;
}
foreach ($wcItems as $id => $item) {
/** 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

@ -124,7 +124,7 @@ class WC_Retailcrm_Loyalty_Client_Test extends WC_Retailcrm_Test_Case_Helper
], ],
[ [
'method' => 'cancelBonusOrder', 'method' => 'cancelBonusOrder',
'parameters' => ['site', ['order']] 'parameters' => [['order']]
], ],
]; ];
} }

View File

@ -330,6 +330,7 @@ class WC_Retailcrm_Loyalty_Test extends WC_Retailcrm_Test_Case_Helper
{ {
$products = DataLoyaltyRetailCrm::createProducts(); $products = DataLoyaltyRetailCrm::createProducts();
$user = DataLoyaltyRetailcrm::createUsers()[0]; $user = DataLoyaltyRetailcrm::createUsers()[0];
$discountLoyalty = 50;
$wcOrder = wc_create_order([ $wcOrder = wc_create_order([
'status' => null, 'status' => null,
@ -391,7 +392,7 @@ class WC_Retailcrm_Loyalty_Test extends WC_Retailcrm_Test_Case_Helper
$this->setMockResponse($this->apiMock, 'applyBonusToOrder', $response); $this->setMockResponse($this->apiMock, 'applyBonusToOrder', $response);
$this->loyalty = new WC_Retailcrm_Loyalty($this->apiMock, []); $this->loyalty = new WC_Retailcrm_Loyalty($this->apiMock, []);
$this->loyalty->applyLoyaltyDiscount($wcOrder, 50, $createdCrmOrderResponse); $this->loyalty->applyLoyaltyDiscount($wcOrder, $createdCrmOrderResponse, $discountLoyalty);
foreach ($wcOrder->get_items() as $id => $item) { foreach ($wcOrder->get_items() as $id => $item) {
$this->assertNotEquals($item->get_total(), $currentItemsPrice[$id]); $this->assertNotEquals($item->get_total(), $currentItemsPrice[$id]);
@ -456,7 +457,7 @@ class WC_Retailcrm_Loyalty_Test extends WC_Retailcrm_Test_Case_Helper
] ]
]; ];
$this->loyalty->applyLoyaltyDiscount($wcOrder, 0, $createdCrmOrderResponse); $this->loyalty->applyLoyaltyDiscount($wcOrder, $createdCrmOrderResponse);
foreach ($wcOrder->get_items() as $id => $item) { foreach ($wcOrder->get_items() as $id => $item) {
$this->assertNotEquals($item->get_total(), $currentItemsPrice[$id]); $this->assertNotEquals($item->get_total(), $currentItemsPrice[$id]);

View File

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