Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/wbs24.exchange1c/lib/ |
Upload File : |
<?php namespace Wbs24\Exchange1c; use Bitrix\Main\Type\DateTime; class Orders implements Interfaces\Orders { protected $wrappers; protected $Catalog; public const LIMIT = 50; public function __construct($objects = []) { $this->wrappers = new Wrappers($objects); $this->Catalog = $objects['Catalog'] ?? new Catalog($objects); $this->Db = $objects['Db'] ?? new Db($objects); } public function getOrders(array $param = []): array { $orders = $this->getOrdersByParam($param); $convertedOrders = $this->convertOrders($orders); return $convertedOrders; } public function getOrdersByShippingDate(array $param = []): array { $orders = []; $from = $param['from'] ?? false; $to = $param['to'] ?? false; $shippingDatePropertyId = $param['shipping_date_property_id'] ?? false; if ($from && $to && $shippingDatePropertyId) { $ids = $this->getOrdersIdsByShippingDate($from, $to, $shippingDatePropertyId); if ($ids) { unset($param['from']); unset($param['to']); $param['orderIds'] = $ids; $orders = $this->getOrders($param); $orders = $this->validateOrders($orders); } } return $orders; } protected function getOrdersIdsByShippingDate(string $from, string $to, int $shippingDatePropertyId): array { $ids = []; $dateList = $this->getDateListByRange($from, $to); $ids = $this->getOrdersIdsByDateList($dateList, $shippingDatePropertyId); return $ids; } // дополнительная валидация заказов // не отправлять те, которые отменены до 8:00 даты отгрузки protected function validateOrders($orders) { $validatedOrders = []; foreach ($orders as $order) { $canceled = $order['canceled'] ?? false; $canceledTimestamp = $order['canceledTimestamp'] ?? false; $shipmentDate = $order['properties']['shipmentDate'] ?? false; if ( $canceled == 'Y' && $canceledTimestamp && $shipmentDate ) { $shipmentDateTimestamp = strtotime($shipmentDate.' 08:00:00'); if ($canceledTimestamp < $shipmentDateTimestamp) { continue; } } $validatedOrders[] = $order; } return $validatedOrders; } protected function getDateListByRange(string $from, string $to): array { $list = []; $maxList = 31; // максимальное возможное кол-во дат на выходе не более 31 (искусственное ограничение для оптимизации нагрузки) $dateFormat = 'd.m.Y'; if ($from && $to) { $currentTimestamp = $fromTimestamp = strtotime($from); $toTimestamp = strtotime($to); for ($i = 0; $i < $maxList; $i++) { $currentDate = date($dateFormat, $currentTimestamp); $list[] = $currentDate; if ($currentDate == date($dateFormat, $toTimestamp)) break; $currentTimestamp += 86400; } } return $list; } // прямой запрос к БД, т.к. через D7 нет возможности реализовать protected function getOrdersIdsByDateList(array $dateList, int $shippingDatePropertyId): array { $ids = []; $result = $this->Db->get( 'b_sale_order_props_value', [ 'ORDER_PROPS_ID' => $shippingDatePropertyId, 'VALUE' => $dateList, ] ); foreach ($result as $item) { $id = $item['ORDER_ID'] ?? false; if ($id) $ids[] = $id; } return $ids; } protected function getOrdersByParam(array $param = []): array { $orders = []; $select = array_merge(['ID', 'XML_ID', 'STATUS_ID', 'USER_ID', 'DATE_CANCELED', 'CANCELED', 'DATE_STATUS', 'DATE_INSERT', 'PRICE_DELIVERY'], $param['select'] ?? []); $siteId = $param['siteId'] ?? false; $fromTime = $param['fromTime'] ?? time(); $from = $param['from'] ?? false; $to = $param['to'] ?? false; $orderIds = $param['orderIds'] ?? false; $offset = $param['offset'] ?? 0; $limit = $param['limit'] ?? self::LIMIT; $notCheckPermissions = $param['not_check_permissions'] ?? false; $i = 0; $foundAllowedProducts = false; //$allowedProductIds = $this->Catalog->getAllowedProductIds(); // получить ids разрешенных товаров $filter = []; if ($siteId) { $filter['LID'] = $siteId; } if ($from || $to) { // режим получения данных по дате в формате дд.мм.гггг чч:мм:сс if ($from) $filter['>=DATE_INSERT'] = $from; if ($to) $filter['<=DATE_INSERT'] = $to; } elseif ($orderIds) { // режим получения заказов по списку ID $filter['ID'] = $orderIds; } else { // режим получения данных по timestamp $filter = [ '>=DATE_INSERT' => DateTime::createFromTimestamp($fromTime), ]; } if ($limit > 500) $limit = 500; $result = $this->wrappers->Order->getList([ 'select' => $select, 'filter' => $filter, 'order' => ['ID' => 'ASC'], 'limit' => $limit, 'offset' => $offset, ]); while ($orderFields = $result->fetch()) { $orderId = $orderFields['ID']; $i++; $orderFields['customer'] = $this->getCustomerInfo($orderId); $orderFields['customer']['customerId'] = $this->getCustomerId($orderFields['customer']); $orderFields['customer']['userId'] = $orderFields['USER_ID'] ?? ''; $orderFields['properties'] = $this->getOrderInfo($orderId); $orderFields['source'] = $this->getSource($orderId); $products = $this->getProductsWithKitElementsByOrderId($orderId); // вместе с инфо о составе комплектов //$products = $this->getProductsByOrderId($orderId); // без инфо о комплектах $products = $this->addStocksInfoFromAvailableQuantity($products); // для работы с доступным кол-вом //$products = $this->addStocksInfo($products, $orderFields); // для даботы с складами // добавить доставку как товар, если есть $deliveryPrice = intval($orderFields['PRICE_DELIVERY']); if ($deliveryPrice) { $products[] = [ 'ID' => NULL, 'PRODUCT_ID' => NULL, 'SET_PARENT_ID' => NULL, 'NAME' => 'Доставка', 'PRICE' => $deliveryPrice, 'QUANTITY' => '1', 'sku' => 'DELIVERY', 'stocks' => ['quantity' => 1], ]; } $orderFields['products'] = $products; // var_export($orderFields); exit; // debug // для фильтрации заказов по разрешенным товарам /* if ( !$notCheckPermissions && !$this->hasAllowedProducts($products, $allowedProductIds) ) continue; */ $orders[] = $orderFields; } return $orders; } protected function getSource($orderId) { $order = $this->wrappers->Order->load($orderId); $code = 'site'; $xmlId = $order->getField('XML_ID'); if (substr($xmlId, 0, 9) == 'YAMARKET_') $code = 'yamarket_marketp_dbs'; if (substr($xmlId, 0, 4) == 'OZON') $code = 'ozon_market'; if (substr($xmlId, 0, 6) == 'SBERMM') $code = 'sbermm'; if (substr($xmlId, 0, 2) == 'WB') $code = 'wildberries'; return $code; } protected function getProductsByOrderId(int $orderId): array { $products = []; $result = $this->wrappers->Basket->getList([ 'select' => ['ID', 'PRODUCT_ID', 'NAME', 'PRICE', 'QUANTITY'], 'filter' => [ 'ORDER_ID' => $orderId, 'SET_PARENT_ID' => '', ], ]); while($productInBasket = $result->fetch()) { $products[] = $this->addProductMeta($productInBasket); } return $products; } protected function getProductsWithKitElementsByOrderId(int $orderId): array { $products = []; $subProducts = []; $result = $this->wrappers->Basket->getList([ 'select' => ['ID', 'PRODUCT_ID', 'SET_PARENT_ID', 'NAME', 'PRICE', 'QUANTITY'], 'filter' => [ 'ORDER_ID' => $orderId, ], ]); while ($productInBasket = $result->fetch()) { $basketId = $productInBasket['ID']; $parentId = $productInBasket['SET_PARENT_ID'] ?? ''; if ($parentId) { $subProducts[$basketId] = $this->addProductMeta($productInBasket); } else { $products[$basketId] = $this->addProductMeta($productInBasket); } } // переместить продукты из состава комплекта в комплект-родитель foreach ($subProducts as $basketId => $subProd) { $parentId = $subProd['SET_PARENT_ID'] ?? ''; if (isset($products[$parentId])) { $products[$parentId]['kitElements'][$basketId] = $subProd; } } return $products; } protected function addProductMeta($productInBasket) { $productId = $productInBasket['PRODUCT_ID']; $productMeta = $this->Catalog->getProductMeta($productId); foreach ($productMeta as $key => $value) { if ($value) $productInBasket[$key] = $value; } return $productInBasket; } protected function addStocksInfoFromAvailableQuantity(array $products): array { $productsWithStocks = []; foreach ($products as $product) { $product['stocks'] = [ 'quantity' => intval($product['QUANTITY']), ]; $productsWithStocks[] = $product; } return $productsWithStocks; } protected function addStocksInfo(array $products, array $orderFields): array { $warehouseInfo = $this->getWarehouseInfo($products, $orderFields); $productsWithStocks = []; foreach ($products as $product) { $product['stocks'] = [ 'stockId' => $warehouseInfo['warehouseId'] ?? 0, 'stockName' => $warehouseInfo['warehouseName'] ?? '', 'quantity' => intval($product['QUANTITY']), ]; $productsWithStocks[] = $product; } return $productsWithStocks; } protected function getCustomerInfo(int $orderId): array { $propCodeToType = [ 'FIO' => 'name', 'EMAIL' => 'email', 'PHONE' => 'phone', 'CITY' => 'city', 'ADDRESS' => 'address', ]; return $this->getProperties($orderId, $propCodeToType); } /* CREATE TABLE `wbs24_exchange1c_customers` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `phone` varchar(255) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name_phone` (`name`,`phone`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; */ protected function getCustomerId(array $customerInfo): int { $table = 'wbs24_exchange1c_customers'; $name = trim(mb_strtoupper($customerInfo['name'] ?? '')); $phone = str_replace( ['+', ' ', '(', ')', '-'], '', $customerInfo['phone'] ?? '' ); if (!$name && !$phone) return false; $customerId = false; $foundCustomer = $this->Db->getSingle($table, [ 'name' => $name, 'phone' => $phone, ]); if ($foundCustomer) { $customerId = $foundCustomer['id']; } else { $this->Db->set($table, [ 'name' => $name, 'phone' => $phone, ]); $foundCustomer = $this->Db->getSingle($table, [ 'name' => $name, 'phone' => $phone, ]); if ($foundCustomer) $customerId = $foundCustomer['id']; } return $customerId; } protected function getOrderInfo(int $orderId): array { $propCodeToType = [ 'EXTERNAL_ID' => 'orderExternalId', // символьный код свойства заказа - внешний номер заказа 'SHIPPING_DATE' => 'shipmentDate', // символьный код свойства заказа - дата откгрузки ]; return $this->getProperties($orderId, $propCodeToType); } protected function getProperties(int $orderId, array $propCodeToType): array { $info = []; $result = $this->wrappers->PropertyValueCollection->getList([ 'select' => ['CODE', 'VALUE'], 'filter' => [ '=ORDER_ID' => $orderId, ], ]); while ($prop = $result->fetch()) { $code = $prop['CODE']; $value = $prop['VALUE']; $type = $propCodeToType[$code] ?? false; if ($type && $value) { $info[$type] = $value; } } return $info; } protected function hasAllowedProducts(array $products, array $allowedProductIds): bool { $has = false; foreach ($products as $product) { $productId = $product['PRODUCT_ID'] ?? false; if (in_array($productId, $allowedProductIds)) { $has = true; break; } } return $has; } /** * Получение ID склада. * При добавлении складов, нужно добавить логику в эту функцию. */ protected function getWarehouseInfo(array $products, array $orderFields): array { $xmlId = $orderFields['XML_ID'] ?? ''; $warehousesInfo = $this->Catalog->getWarehousesInfo(); // temp - для тестового режима (если разрешены склады 3 и 4) if (isset($warehousesInfo[3]) && isset($warehousesInfo[4])) { $warehousesInfo[1] = $warehousesInfo[3]; $warehousesInfo[2] = $warehousesInfo[4]; } $warehouseId = 2; // по умолчанию для маркетплейсов if (substr($xmlId, 0, 3) == 'bx_') $warehouseId = 1; // для заказов с сайта return [ 'warehouseId' => $warehousesInfo[$warehouseId]['XML_ID'] ?? '', 'warehouseName' => $warehousesInfo[$warehouseId]['TITLE'] ?? '', ]; } protected function convertOrders(array $orders): array { $convertedOrders = []; foreach ($orders as $order) { [ 'products' => $products, 'orderSum' => $orderSum, ] = $this->convertProducts($order['products']); $convertedOrder = [ 'orderId' => $order['ID'], 'xmlId' => $order['XML_ID'], 'status' => $order['STATUS_ID'], 'statusTimestamp' => $this->getStringFromDateTimeObject($order['DATE_STATUS'] ?? false), 'canceled' => $order['CANCELED'], 'canceledTimestamp' => $this->getStringFromDateTimeObject($order['DATE_CANCELED'] ?? false), 'createdTimestamp' => $this->getStringFromDateTimeObject($order['DATE_INSERT'] ?? false), 'orderSum' => $orderSum, 'properties' => $order['properties'], 'customer' => $order['customer'], 'source' => $order['source'], 'products' => $products, ]; $convertedOrders[] = $convertedOrder; } return $convertedOrders; } protected function convertProducts(array $products, bool $useKitElements = false): array { $convertedProducts = []; $convertProductFields = [ 'productId' => 'PRODUCT_ID', 'sku' => 'sku', //'color' => 'color', //'size' => 'size', //'barcode' => 'barcode', //'packageRatio' => 'packageRatio', ]; if ($useKitElements) { $convertProductFields['price'] = 'PRICE'; } else { $convertProductFields['salePrice'] = 'PRICE'; } $orderSum = 0; foreach ($products as $product) { $setProduct = []; foreach ($convertProductFields as $setKey => $key) { $value = $product[$key] ?? ''; //if ($setKey == 'salePrice') $value = intval($value); $setProduct[$setKey] = $value; } $setProduct['stocks']['quantity'] = intval($product['stocks']['quantity'] ?? $product['QUANTITY']); if (!$useKitElements) { $productSum = $setProduct['salePrice'] * $setProduct['stocks']['quantity']; $setProduct['salePriceSum'] = $productSum; $orderSum += $productSum; } // рекурсия для комплектов $kitElements = $product['kitElements'] ?? false; if ($kitElements) { $flagOfUseKitElements = true; [ 'products' => $convertedKitElements, ] = $this->convertProducts($kitElements, $flagOfUseKitElements); $setProduct['kitElements'] = $convertedKitElements; } $convertedProducts[] = $setProduct; } return [ 'products' => $convertedProducts, 'orderSum' => $orderSum, ]; } protected function getTimestampFromDateTimeObject($obj) { $timestamp = ''; if ($obj && $obj instanceof DateTime) { $timestamp = $obj->getTimestamp(); } return $timestamp; } protected function getStringFromDateTimeObject($obj) { $timestamp = $this->getTimestampFromDateTimeObject($obj); return $timestamp ? date('d.m.Y H:i:s', $timestamp) : ''; } }