ref #96244 Fixed duplicating items on history sync (#222)

This commit is contained in:
gleemand 2024-07-02 14:59:53 +02:00 committed by GitHub
parent bf9f07a0a8
commit d6862bad58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 89 additions and 99 deletions

View File

@ -1,3 +1,6 @@
## v3.6.5
* Исправлено дублирование товаров при обратной синхронизации
## v3.6.4 ## v3.6.4
* Добавлена передача услуг через ICML каталог * Добавлена передача услуг через ICML каталог

View File

@ -1 +1 @@
3.6.4 3.6.5

View File

@ -393,11 +393,11 @@ class RetailcrmHistory
self::saveCarrier($prestashopOrder->id, $deliveryType, $crmOrder['delivery']['cost']); self::saveCarrier($prestashopOrder->id, $deliveryType, $crmOrder['delivery']['cost']);
$quantities = self::createOrderDetails($crmOrder, $prestashopOrder); $isStockEnough = self::createOrderDetails($crmOrder, $prestashopOrder, true);
self::setOutOfStockStatusInPrestashop($crmOrder, $prestashopOrder, $quantities); self::setOutOfStockStatusInPrestashop($crmOrder, $prestashopOrder, $isStockEnough);
self::setOutOfStockStatusInCrm($crmOrder, $prestashopOrder, $quantities); self::setOutOfStockStatusInCrm($crmOrder, $prestashopOrder, $isStockEnough);
// collect order ids for single fix request // collect order ids for single fix request
self::$orderFix[] = ['id' => $crmOrder['id'], 'externalId' => $prestashopOrder->id]; self::$orderFix[] = ['id' => $crmOrder['id'], 'externalId' => $prestashopOrder->id];
@ -438,13 +438,13 @@ class RetailcrmHistory
$crmOrder = self::cleanDeletedItems($crmOrder, $prestashopOrder); $crmOrder = self::cleanDeletedItems($crmOrder, $prestashopOrder);
$quantities = self::createOrderDetails($crmOrder, $prestashopOrder); $isStockEnoughForNewItems = self::createOrderDetails($crmOrder, $prestashopOrder);
$isStockEnoughForExistingItems = self::checkItemsQuantityAndDiscount($crmOrder, $prestashopOrder);
$isStockEnough = $isStockEnoughForNewItems && $isStockEnoughForExistingItems;
self::setOutOfStockStatusInPrestashop($crmOrder, $prestashopOrder, $quantities); self::setOutOfStockStatusInPrestashop($crmOrder, $prestashopOrder, $isStockEnough);
self::setOutOfStockStatusInCrm($crmOrder, $prestashopOrder, $isStockEnough);
self::setOutOfStockStatusInCrm($crmOrder, $prestashopOrder, $quantities); self::switchPrestashopOrderStatusByCrmStatus($crmOrder, $prestashopOrder, $isStockEnough);
self::switchPrestashopOrderStatusByCrmStatus($crmOrder, $prestashopOrder, $quantities);
// update order number in PS if receiveOrderNumber option (CRM->PS) enabled // update order number in PS if receiveOrderNumber option (CRM->PS) enabled
if (isset($crmOrder['number']) && self::$receiveOrderNumber) { if (isset($crmOrder['number']) && self::$receiveOrderNumber) {
@ -1137,50 +1137,45 @@ class RetailcrmHistory
} }
/** /**
* @return bool Returns if stock of all items is enough
*
* @throws PrestaShopDatabaseException * @throws PrestaShopDatabaseException
* @throws PrestaShopException * @throws PrestaShopException
*/ */
private static function createOrderDetails($crmOrder, $prestashopOrder) private static function createOrderDetails($crmOrder, $prestashopOrder, $isCreating = false)
{ {
$newItemsIds = []; $newItemsIds = [];
$quantities = []; $isNewItemsExist = false;
$isStockEnough = true;
if (empty($crmOrder['items'])) { if (empty($crmOrder['items'])) {
RetailcrmLogger::writeDebug(__METHOD__, 'Empty order items'); RetailcrmLogger::writeDebug(__METHOD__, 'Empty order items');
return $quantities; return $isStockEnough;
} }
foreach ($crmOrder['items'] as $item) { foreach ($crmOrder['items'] as $key => $item) {
if (!isset($item['offer']['externalId'])) {
continue;
}
$externalId = $item['offer']['externalId'];
$product = new Product((int) $externalId, false, self::$default_lang);
$product_id = $externalId;
if (RetailcrmOrderBuilder::isGiftItem($item)) { if (RetailcrmOrderBuilder::isGiftItem($item)) {
continue; continue;
} }
$product_attribute_id = 0; $isNewItem = isset($item['create']);
if (false !== strpos($externalId, '#')) { if (!$isNewItem && !$isCreating) {
$externalIds = explode('#', $externalId); continue;
$product_id = $externalIds[0];
$product_attribute_id = $externalIds[1];
} }
$orderDetail = self::createOrderDetail($item, $product, $product_attribute_id, $prestashopOrder); $parsedExtId = static::parseItemExternalId($item);
$availableInStockOld = StockAvailable::getQuantityAvailableByProduct($product_id); $product_id = $parsedExtId['product_id'];
$product_attribute_id = $parsedExtId['product_attribute_id'];
$quantities[$product_id]['old'] = $availableInStockOld; $product = new Product((int) $product_id, false, self::$default_lang);
if (isset($item['initialPrice'])) { $orderDetail = self::createOrderDetail($item, $product, $parsedExtId, $prestashopOrder);
$deltaQuantity = -1 * $orderDetail->product_quantity;
} else { $isStockEnough = $product->checkQty($orderDetail->product_quantity);
$deltaQuantity = -1 * ($item['quantity'] - $orderDetail->product_quantity);
} // переменная используется для передачи разницы количества товара в метод StockAvailable::updateQuantity
$deltaQuantity = -1 * $orderDetail->product_quantity;
StockAvailable::updateQuantity( StockAvailable::updateQuantity(
$product_id, $product_id,
@ -1189,37 +1184,32 @@ class RetailcrmHistory
Context::getContext()->shop->id Context::getContext()->shop->id
); );
$availableInStockNew = StockAvailable::getQuantityAvailableByProduct($product_id);
$quantities[$product_id]['new'] = $availableInStockNew;
if (!isset($item['initialPrice'])) {
$orderDetail->product_quantity = $orderDetail->product_quantity - $deltaQuantity;
$orderDetail->total_price_tax_incl = $orderDetail->product_price * $orderDetail->product_quantity;
}
try { try {
self::loadInPrestashop($orderDetail, 'save'); self::loadInPrestashop($orderDetail, 'save');
$newItemsIds[Db::getInstance()->Insert_ID()] = $item['id']; $newItemsIds[Db::getInstance()->Insert_ID()] = $item['id'];
} catch (Exception $e) { } catch (Exception $e) {
} }
$isNewItemsExist = true;
unset($crmOrder['items'][$key]);
} }
// update order items ids in crm // update order items ids in crm
self::$newItemsIdsByOrderId[$prestashopOrder->id] = $newItemsIds; if ($isNewItemsExist) {
self::$newItemsIdsByOrderId[$prestashopOrder->id] = $newItemsIds;
}
return $quantities; return $isStockEnough;
} }
private static function switchPrestashopOrderStatusByCrmStatus($crmOrder, $prestashopOrder, $quantities) private static function switchPrestashopOrderStatusByCrmStatus($crmOrder, $prestashopOrder, $isStockEnough)
{ {
if (!isset($crmOrder['status'])) { if (!isset($crmOrder['status'])) {
return; return;
} }
$orderStatus = $crmOrder['status']; $orderStatus = $crmOrder['status'];
$outOfStockItems = self::getOutOfStockItems($quantities); if (!$isStockEnough) {
if (0 < count($outOfStockItems)) {
$orderStatus = self::getOutOfStockStatus($crmOrder, $prestashopOrder); $orderStatus = self::getOutOfStockStatus($crmOrder, $prestashopOrder);
} }
@ -1414,19 +1404,19 @@ class RetailcrmHistory
continue; continue;
} }
$orderPayment = new OrderPayment();
$paymentType = self::getPaymentType($payment); $paymentType = self::getPaymentType($payment);
if (!$paymentType) {
if ($paymentType) { continue;
$orderToUpdate->payment = $paymentType;
$orderPayment->payment_method = $paymentType;
} }
$orderToUpdate->payment = $paymentType;
$orderPayment = new OrderPayment();
$orderPayment->payment_method = $paymentType;
$orderPayment->order_reference = $orderToUpdate->reference; $orderPayment->order_reference = $orderToUpdate->reference;
$orderPayment->id_currency = (int) Configuration::get('PS_CURRENCY_DEFAULT'); $orderPayment->id_currency = (int) Configuration::get('PS_CURRENCY_DEFAULT');
$orderPayment->amount = isset($payment['amount']) ? $payment['amount'] : $orderToUpdate->total_paid; $orderPayment->amount = $payment['amount'] ?? $orderToUpdate->total_paid;
$orderPayment->date_add = isset($payment['paidAt']) ? $payment['paidAt'] : date('Y-m-d H:i:s'); $orderPayment->date_add = $payment['paidAt'] ?? date('Y-m-d H:i:s');
RetailcrmLogger::writeDebug( RetailcrmLogger::writeDebug(
__METHOD__, __METHOD__,
@ -1577,7 +1567,7 @@ class RetailcrmHistory
return $crmOrder; return $crmOrder;
} }
foreach ($crmOrder['items'] as $item) { foreach ($crmOrder['items'] as $key => $item) {
if (!isset($item['delete']) || true != $item['delete']) { if (!isset($item['delete']) || true != $item['delete']) {
continue; continue;
} }
@ -1606,6 +1596,8 @@ class RetailcrmHistory
$product_attribute_id, $product_attribute_id,
$id_order_detail $id_order_detail
); );
unset($crmOrder['items'][$key]);
} }
return $crmOrder; return $crmOrder;
@ -1613,9 +1605,16 @@ class RetailcrmHistory
private static function checkItemsQuantityAndDiscount($crmOrder, $prestashopOrder) private static function checkItemsQuantityAndDiscount($crmOrder, $prestashopOrder)
{ {
$itemQuantities = []; $isStockEnough = true;
if (empty($crmOrder['items'])) {
RetailcrmLogger::writeDebug(__METHOD__, 'Empty order items');
return $isStockEnough;
}
foreach ($prestashopOrder->getProductsDetail() as $orderItem) { foreach ($prestashopOrder->getProductsDetail() as $orderItem) {
foreach ($crmOrder['items'] as $crmItem) { foreach ($crmOrder['items'] as $key => $crmItem) {
if (RetailcrmOrderBuilder::isGiftItem($crmItem)) { if (RetailcrmOrderBuilder::isGiftItem($crmItem)) {
continue; continue;
} }
@ -1625,10 +1624,16 @@ class RetailcrmHistory
$product_attribute_id = $parsedExtId['product_attribute_id']; $product_attribute_id = $parsedExtId['product_attribute_id'];
$isExistingItem = !isset($crmItem['create']); $isExistingItem = !isset($crmItem['create']);
if (!$isExistingItem || $product_id != $orderItem['product_id'] || $product_attribute_id != $orderItem['product_attribute_id']) { if (
!$isExistingItem
|| $product_id != $orderItem['product_id']
|| $product_attribute_id != $orderItem['product_attribute_id']
) {
continue; continue;
} }
$product = new Product((int) $product_id, false, self::$default_lang);
$orderDetailId = !empty($parsedExtId['id_order_detail']) $orderDetailId = !empty($parsedExtId['id_order_detail'])
? $parsedExtId['id_order_detail'] : $orderItem['id_order_detail']; ? $parsedExtId['id_order_detail'] : $orderItem['id_order_detail'];
$orderDetail = new OrderDetail($orderDetailId); $orderDetail = new OrderDetail($orderDetailId);
@ -1645,7 +1650,7 @@ class RetailcrmHistory
$orderDetail->product_quantity = $crmItem['quantity']; $orderDetail->product_quantity = $crmItem['quantity'];
$orderDetail->product_quantity_in_stock = $crmItem['quantity']; $orderDetail->product_quantity_in_stock = $crmItem['quantity'];
$itemQuantities[$product_id] = StockAvailable::getQuantityAvailableByProduct($product_id); $isStockEnough = 0 > $deltaQuantity || $product->checkQty(-1 * $deltaQuantity);
StockAvailable::updateQuantity( StockAvailable::updateQuantity(
$product_id, $product_id,
@ -1659,10 +1664,11 @@ class RetailcrmHistory
? $prestashopOrder->id_warehouse : 0; ? $prestashopOrder->id_warehouse : 0;
self::loadInPrestashop($orderDetail, 'update'); self::loadInPrestashop($orderDetail, 'update');
unset($crmOrder['items'][$key]);
} }
} }
return $itemQuantities; return $isStockEnough;
} }
private static function getInternalOrderStatus($state) private static function getInternalOrderStatus($state)
@ -1761,16 +1767,15 @@ class RetailcrmHistory
return $orderToUpdate; return $orderToUpdate;
} }
private static function setOutOfStockStatusInPrestashop($crmOrder, $prestashopOrder, $quantities) private static function setOutOfStockStatusInPrestashop($crmOrder, $prestashopOrder, $isStockEnough)
{ {
if (!isset($crmOrder['items'])) { if (!isset($crmOrder['items'])) {
return false; return false;
} }
$outOfStockItems = self::getOutOfStockItems($quantities);
$newStatus = self::getOutOfStockStatus($crmOrder, $prestashopOrder); $newStatus = self::getOutOfStockStatus($crmOrder, $prestashopOrder);
if (0 < count($outOfStockItems) && $newStatus) { if (!$isStockEnough && $newStatus) {
self::createOrderHistory($prestashopOrder, $newStatus); self::createOrderHistory($prestashopOrder, $newStatus);
$prestashopOrder->current_state = self::$statuses[$newStatus]; $prestashopOrder->current_state = self::$statuses[$newStatus];
self::loadInPrestashop($prestashopOrder, 'save'); self::loadInPrestashop($prestashopOrder, 'save');
@ -1865,19 +1870,13 @@ class RetailcrmHistory
return $cart; return $cart;
} }
private static function setOutOfStockStatusInCrm($crmOrder, $prestashopOrder, $quantities = null) private static function setOutOfStockStatusInCrm($crmOrder, $prestashopOrder, $isStockEnough)
{ {
if (!isset($crmOrder['items']) || !is_array($crmOrder['items'])) { if (!isset($crmOrder['items']) || !is_array($crmOrder['items'])) {
return false; return false;
} }
if (null === $quantities) { if (!$isStockEnough) {
$quantities = self::checkItemsQuantityAndDiscount($crmOrder, $prestashopOrder);
}
$outOfStockItems = self::getOutOfStockItems($quantities);
if (0 < count($outOfStockItems)) {
$crmOrder['status'] = self::getOutOfStockStatus($crmOrder, $prestashopOrder); $crmOrder['status'] = self::getOutOfStockStatus($crmOrder, $prestashopOrder);
self::$api->ordersEdit($crmOrder, 'id'); self::$api->ordersEdit($crmOrder, 'id');
@ -1912,20 +1911,10 @@ class RetailcrmHistory
$orderHistory->changeIdOrderState(self::$statuses[$orderStatus], $prestashopOrder->id, true); $orderHistory->changeIdOrderState(self::$statuses[$orderStatus], $prestashopOrder->id, true);
} }
private static function getOutOfStockItems($quantities) private static function createOrderDetail($item, $product, $parsedExtId, $prestashopOrder)
{ {
return array_filter($quantities, function ($value) { $product_id = $parsedExtId['product_id'];
if (0 > $value['new'] || $value['new'] == $value['old']) { $product_attribute_id = $parsedExtId['product_attribute_id'];
return true;
}
return false;
});
}
private static function createOrderDetail($item, $product, $product_attribute_id, $prestashopOrder)
{
$product_id = $item['offer']['externalId'];
if (0 != $product_attribute_id) { if (0 != $product_attribute_id) {
$productName = htmlspecialchars( $productName = htmlspecialchars(
@ -1935,16 +1924,11 @@ class RetailcrmHistory
$productName = htmlspecialchars(strip_tags($product->name)); $productName = htmlspecialchars(strip_tags($product->name));
} }
if (isset($item['initialPrice'])) { $parsedExtId = static::parseItemExternalId($item);
$productPrice = round($item['initialPrice'], 2); $orderDetail = new OrderDetail($parsedExtId['id_order_detail'] ?? null);
$orderDetail = new OrderDetail();
$orderDetail->product_quantity = (int) $item['quantity']; $orderDetail->product_quantity = (int) $item['quantity'];
$orderDetail->product_quantity_in_stock = (int) $item['quantity']; $orderDetail->product_quantity_in_stock = (int) $item['quantity'];
} else {
$parsedExtId = static::parseItemExternalId($item);
$orderDetail = new OrderDetail($parsedExtId['id_order_detail']);
$productPrice = $orderDetail->product_price;
}
static::setOrderDetailProductName($orderDetail, $productName); static::setOrderDetailProductName($orderDetail, $productName);
@ -1956,13 +1940,16 @@ class RetailcrmHistory
$orderDetail->product_attribute_id = (int) $product_attribute_id; $orderDetail->product_attribute_id = (int) $product_attribute_id;
$orderDetail->product_reference = implode('', ['\'', $product->reference, '\'']); $orderDetail->product_reference = implode('', ['\'', $product->reference, '\'']);
$productPrice = $item['initialPrice'];
$orderDetail->product_price = $productPrice; $orderDetail->product_price = $productPrice;
$orderDetail->original_product_price = $productPrice; $orderDetail->original_product_price = $productPrice;
$orderDetail->total_price_tax_incl = $productPrice * $orderDetail->product_quantity; $orderDetail->total_price_tax_incl = $productPrice * $orderDetail->product_quantity;
$orderDetail->unit_price_tax_incl = $productPrice; $orderDetail->unit_price_tax_incl = $productPrice;
$orderDetail->id_warehouse = !empty($prestashopOrder->id_warehouse) ? $prestashopOrder->id_warehouse : 0; $orderDetail->id_warehouse = $prestashopOrder->id_warehouse ?? 0;
$orderDetail->id_order_detail = $parsedExtId['id_order_detail'] ?? null;
return $orderDetail; return $orderDetail;
} }

View File

@ -48,7 +48,7 @@ require_once dirname(__FILE__) . '/bootstrap.php';
class RetailCRM extends Module class RetailCRM extends Module
{ {
const VERSION = '3.6.4'; const VERSION = '3.6.5';
const API_URL = 'RETAILCRM_ADDRESS'; const API_URL = 'RETAILCRM_ADDRESS';
const API_KEY = 'RETAILCRM_API_TOKEN'; const API_KEY = 'RETAILCRM_API_TOKEN';