1
0
mirror of synced 2025-02-06 18:19:24 +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 */
public function calculateDiscountLoyalty(string $site, array $order)
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)]
['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(
"/orders/loyalty/cancel-bonus-operations",
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;
}
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;
$items = [];
@ -378,9 +395,14 @@ if (!class_exists('WC_Retailcrm_Loyalty')) :
$items = $response['order']['items'];
}
$this->calculateLoyaltyDiscount($wcOrder, $items);
}
public function calculateLoyaltyDiscount($wcOrder, $orderItems)
{
$wcItems = $wcOrder->get_items();
foreach ($items as $item) {
foreach ($orderItems as $item) {
$externalId = $item['externalIds'][0]['value'];
$externalId = preg_replace('/^\d+\_/m', '', $externalId);
@ -402,6 +424,37 @@ if (!class_exists('WC_Retailcrm_Loyalty')) :
$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;

View File

@ -44,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 = [];
@ -94,25 +100,34 @@ if (!class_exists('WC_Retailcrm_Orders')) :
$this->order_payment->resetData();
$wcOrder = wc_get_order($orderId);
$privilegeType = 'none';
if ($this->loyalty) {
$wcCustomer = null;
$privilegeType = 'none';
$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) {
writeBaseLogs('The user does not meet the requirements for working with the loyalty program. Order Id: ' . $orderId);
}
$discountLp = 0;
} else {
$privilegeType = 'none';
} elseif ($this->loyalty->isValidUser($wcCustomer)) {
$privilegeType = 'loyalty_level';
}
}
$this->processOrder($wcOrder);
$this->order['privilegeType'] = $privilegeType;
if (isset($privilegeType)) {
$this->order['privilegeType'] = $privilegeType;
}
$response = $this->retailcrm->ordersCreate($this->order);
@ -124,7 +139,7 @@ if (!class_exists('WC_Retailcrm_Orders')) :
}
if (isset($discountLp) && $discountLp > 0) {
$this->loyalty->applyLoyaltyDiscount($wcOrder, $discountLp, $response['order']);
$this->loyalty->applyLoyaltyDiscount($wcOrder, $response['order'], $discountLp);
}
} catch (Throwable $exception) {
writeBaseLogs(
@ -302,16 +317,39 @@ if (!class_exists('WC_Retailcrm_Orders')) :
try {
$wcOrder = wc_get_order($orderId);
$needRecalculate = false;
$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);
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(
@ -447,15 +485,32 @@ 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'];
$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

@ -213,7 +213,7 @@ function isCorporateUserActivate($settings)
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 = [];
/** @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
{
$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',
'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();
$user = DataLoyaltyRetailcrm::createUsers()[0];
$discountLoyalty = 50;
$wcOrder = wc_create_order([
'status' => null,
@ -391,7 +392,7 @@ class WC_Retailcrm_Loyalty_Test extends WC_Retailcrm_Test_Case_Helper
$this->setMockResponse($this->apiMock, 'applyBonusToOrder', $response);
$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) {
$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) {
$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)
{
$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();
}
}