PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/catalog/lib/integration/report/storestock/storestocksale.php

https://gitlab.com/alexprowars/bitrix
PHP | 292 lines | 243 code | 49 blank | 0 comment | 11 complexity | 667526b9e77f23568ec859fc30ccf247 MD5 | raw file
  1. <?php
  2. namespace Bitrix\Catalog\Integration\Report\StoreStock;
  3. use Bitrix\Sale\Internals\ShipmentItemStoreTable;
  4. use Bitrix\Sale\Internals\ShipmentItemTable;
  5. use Bitrix\Sale\Internals\ShipmentTable;
  6. use Bitrix\Sale\Internals\BasketTable;
  7. use Bitrix\Catalog\StoreProductTable;
  8. use Bitrix\Catalog\ProductTable;
  9. use Bitrix\Currency\CurrencyManager;
  10. use Bitrix\Currency\CurrencyTable;
  11. use Bitrix\Main\Type\DateTime;
  12. use Bitrix\Main\ORM\Fields\Relations\Reference;
  13. use Bitrix\Main\ORM\Query\Join;
  14. class StoreStockSale
  15. {
  16. protected const DEFAULT_DATE_INTERVAL = '-30D';
  17. protected static $defaultCurrency;
  18. protected static $productPrice;
  19. public static function getStoreStockSaleData(bool $isOneField, array $filter): array
  20. {
  21. $filter = self::prepareFilter($filter);
  22. $shippedData = self::getShippedData($filter);
  23. $reservedData = self::getReservedData($filter);
  24. $productIds = self::combineUniqueColumnElements([$shippedData, $reservedData], 'PRODUCT_ID');
  25. self::initProductPrice($productIds);
  26. $storeIds =
  27. $filter['STORES']
  28. ?? self::combineUniqueColumnElements([$shippedData, $reservedData], 'STORE_ID')
  29. ;
  30. $storesData = [];
  31. if ($isOneField)
  32. {
  33. $storesData = self::formField($shippedData, $reservedData);
  34. $storesData['STORE_IDS'] = $storeIds;
  35. }
  36. else
  37. {
  38. $storesPositionData = array_fill_keys(
  39. $storeIds,
  40. [
  41. 'shippedData' => [],
  42. 'reservedData' => [],
  43. ]
  44. );
  45. foreach ($shippedData as $shippedPosition)
  46. {
  47. $storesPositionData[$shippedPosition['STORE_ID']]['shippedData'][] = $shippedPosition;
  48. }
  49. foreach ($reservedData as $reservedPosition)
  50. {
  51. $storesPositionData[$reservedPosition['STORE_ID']]['reservedData'][] = $reservedPosition;
  52. }
  53. foreach ($storesPositionData as $storeId => $fieldData)
  54. {
  55. $storesData[] = self::formField($fieldData['shippedData'], $fieldData['reservedData'], $storeId);
  56. }
  57. }
  58. return $storesData;
  59. }
  60. public static function getDefaultReportInterval(): array
  61. {
  62. $currentDate = new DateTime();
  63. $intervalStartDate = new DateTime();
  64. $intervalStartDate->add(self::DEFAULT_DATE_INTERVAL);
  65. return [
  66. 'FROM' => $intervalStartDate->toString(),
  67. 'TO' => $currentDate->toString(),
  68. ];
  69. }
  70. public static function getProductPrice(int $productId): float
  71. {
  72. if (!isset(self::$productPrice[$productId]))
  73. {
  74. self::initProductPrice([$productId]);
  75. self::$productPrice[$productId] ??= 0;
  76. }
  77. return self::$productPrice[$productId];
  78. }
  79. private static function prepareFilter(array $filter): array
  80. {
  81. if
  82. (
  83. !isset($filter['REPORT_INTERVAL'])
  84. || !isset($filter['REPORT_INTERVAL']['FROM'])
  85. || !isset($filter['REPORT_INTERVAL']['TO'])
  86. )
  87. {
  88. $filter['REPORT_INTERVAL'] = self::getDefaultReportInterval();
  89. }
  90. return $filter;
  91. }
  92. protected static function getShippedData(array $filter): array
  93. {
  94. $shipmentData = ShipmentItemTable::getList([
  95. 'select' => [
  96. 'QUANTITY' => 'QUANTITY',
  97. 'STORE_ID' => 'S_BARCODE.STORE_ID',
  98. 'PRODUCT_ID' => 'BASKET.PRODUCT_ID',
  99. ],
  100. 'filter' => self::formShipmentDataFilter($filter),
  101. 'runtime' => [
  102. (new Reference(
  103. 'S_BARCODE',
  104. ShipmentItemStoreTable::class,
  105. Join::on('this.ID', 'ref.ORDER_DELIVERY_BASKET_ID')
  106. ))->configureJoinType(Join::TYPE_LEFT),
  107. (new Reference(
  108. 'SHIPMENT',
  109. ShipmentTable::class,
  110. Join::on('this.ORDER_DELIVERY_ID', 'ref.ID')
  111. ))->configureJoinType(Join::TYPE_LEFT),
  112. (new Reference(
  113. 'BASKET',
  114. BasketTable::class,
  115. Join::on('this.BASKET_ID', 'ref.ID')
  116. ))->configureJoinType(Join::TYPE_LEFT),
  117. ],
  118. ])->fetchAll();
  119. return $shipmentData;
  120. }
  121. private static function formShipmentDataFilter(array $filter): array
  122. {
  123. $formedFilter = [
  124. '=SHIPMENT.DEDUCTED' => 'Y',
  125. ];
  126. if (isset($filter['STORES']))
  127. {
  128. $formedFilter['=S_BARCODE.STORE_ID'] = $filter['STORES'];
  129. }
  130. if (isset($filter['PRODUCTS']))
  131. {
  132. $formedFilter['=BASKET.PRODUCT_ID'] = $filter['PRODUCTS'];
  133. }
  134. if (isset($filter['REPORT_INTERVAL']))
  135. {
  136. $formedFilter['>=SHIPMENT.DATE_DEDUCTED'] = new DateTime($filter['REPORT_INTERVAL']['FROM']);
  137. $formedFilter['<=SHIPMENT.DATE_DEDUCTED'] = new DateTime($filter['REPORT_INTERVAL']['TO']);
  138. }
  139. return $formedFilter;
  140. }
  141. protected static function getReservedData(array $filter): array
  142. {
  143. $reservedData = StoreProductTable::getList([
  144. 'select' => [
  145. 'PRODUCT_ID',
  146. 'STORE_ID',
  147. 'QUANTITY' => 'AMOUNT',
  148. ],
  149. 'filter' => self::formReservedDataFilter($filter),
  150. ])->fetchAll();
  151. return $reservedData;
  152. }
  153. private static function formReservedDataFilter(array $filter): array
  154. {
  155. $formedFilter = [];
  156. if (isset($filter['STORES']))
  157. {
  158. $formedFilter['=STORE_ID'] = $filter['STORES'];
  159. }
  160. else
  161. {
  162. $formedFilter['!=QUANTITY'] = 0;
  163. }
  164. if (isset($filter['PRODUCTS']))
  165. {
  166. $formedFilter['=PRODUCT_ID'] = $filter['PRODUCTS'];
  167. }
  168. return $formedFilter;
  169. }
  170. private static function combineUniqueColumnElements(array $arraysList, string $columnKey): array
  171. {
  172. $combineColumnElements = [];
  173. foreach ($arraysList as $item)
  174. {
  175. $columnElements = array_column($item, $columnKey);
  176. array_push($combineColumnElements, ...$columnElements);
  177. }
  178. return array_unique($combineColumnElements);
  179. }
  180. protected static function formField(array $storeSoldData, array $storeReservedData, int $storeId = null): array
  181. {
  182. $soldSum = 0.0;
  183. $storedSum = 0.0;
  184. foreach ($storeSoldData as $basketPosition)
  185. {
  186. $soldSum += self::getPositionPrice($basketPosition['PRODUCT_ID'], $basketPosition['QUANTITY']);
  187. }
  188. foreach ($storeReservedData as $storePosition)
  189. {
  190. $storedSum += self::getPositionPrice($storePosition['PRODUCT_ID'], $storePosition['QUANTITY']);
  191. }
  192. $storedPercent = ($storedSum + $soldSum) > 0 ? ($storedSum / ($storedSum + $soldSum)) * 100 : 0;
  193. $soldPercent = ($storedSum + $soldSum) > 0 ? ($soldSum / ($storedSum + $soldSum)) * 100 : 0;
  194. $result = [
  195. 'SUM_STORED' => $storedSum,
  196. 'SUM_STORED_PERCENT' => $storedPercent,
  197. 'SUM_SOLD' => $soldSum,
  198. 'SUM_SOLD_PERCENT' => $soldPercent,
  199. ];
  200. if ($storeId !== null)
  201. {
  202. $result['STORE_ID'] = $storeId;
  203. }
  204. return $result;
  205. }
  206. protected static function getPositionPrice(int $productId, float $productCount): float
  207. {
  208. return self::getProductPrice($productId) * $productCount;
  209. }
  210. protected static function initProductPrice(array $productIds): void
  211. {
  212. $defaultCurrency = CurrencyManager::getBaseCurrency();
  213. $productsData = ProductTable::getList([
  214. 'select' => [
  215. 'ID',
  216. 'PURCHASING_PRICE',
  217. 'PURCHASING_CURRENCY',
  218. 'PURCHASING_CURRENCY_AMOUNT' => 'CURRENCY_TABLE.CURRENT_BASE_RATE',
  219. ],
  220. 'filter' => [
  221. '=ID' => $productIds,
  222. ],
  223. 'runtime' => [
  224. (new Reference(
  225. 'CURRENCY_TABLE',
  226. CurrencyTable::class,
  227. Join::on('this.PURCHASING_CURRENCY', 'ref.CURRENCY')
  228. ))->configureJoinType(Join::TYPE_LEFT),
  229. ],
  230. ])->fetchAll();
  231. foreach ($productsData as $product)
  232. {
  233. self::$productPrice[$product['ID']] = (float)$product['PURCHASING_PRICE'];
  234. if ($product['PURCHASING_CURRENCY'] !== $defaultCurrency)
  235. {
  236. $defaultCurrencyAmount = (float)\CCurrency::getCurrency($defaultCurrency)['CURRENT_BASE_RATE'];
  237. $currentCurrencyAmount = (float)$product['PURCHASING_CURRENCY_AMOUNT'];
  238. self::$productPrice[$product['ID']] *= $currentCurrencyAmount;
  239. self::$productPrice[$product['ID']] /= $defaultCurrencyAmount;
  240. }
  241. }
  242. }
  243. }