1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
storestocksale.php
См. документацию.
1<?php
2
3namespace Bitrix\Catalog\Integration\Report\StoreStock;
4
5\Bitrix\Main\Loader::includeModule('sale');
6
7use Bitrix\Catalog\Access\AccessController;
8use Bitrix\Catalog\Access\ActionDictionary;
9use Bitrix\Catalog\Integration\Report\StoreStock\Entity\ProductInfo;
10use Bitrix\Catalog\Integration\Report\StoreStock\Entity\Store\StoreInfo;
11use Bitrix\Catalog\Integration\Report\StoreStock\Entity\Store\StoreWithProductsInfo;
12use Bitrix\Catalog\StoreBatchDocumentElementTable;
13use Bitrix\Catalog\StoreDocumentTable;
14use Bitrix\Main\ORM\Fields\ExpressionField;
15use Bitrix\Sale\Internals\ShipmentItemStoreTable;
16use Bitrix\Sale\Internals\ShipmentItemTable;
17use Bitrix\Catalog\StoreProductTable;
18use Bitrix\Catalog\ProductTable;
19use Bitrix\Currency\CurrencyManager;
20use Bitrix\Currency\CurrencyTable;
21use Bitrix\Main\Type\DateTime;
22use Bitrix\Main\ORM\Fields\Relations\Reference;
23use Bitrix\Main\ORM\Query\Join;
24
26final class StoreStockSale
27{
28 protected const DEFAULT_DATE_INTERVAL = '-30D';
29
30 protected static $defaultCurrency;
31
32 protected static $productPrice;
33
34 public static function getProductsSoldAmountForStores($filter = []): array
35 {
36 $soldProductsDbResult = self::getProductsSoldAmountFromShipmentsList($filter);
37 $result = [];
38 while ($soldProduct = $soldProductsDbResult->fetch())
39 {
40 $storeId = (int)$soldProduct['STORE_ID'];
41 if (!isset($result[$storeId]))
42 {
43 $result[$storeId] = [];
44 }
45
46 $measureId = (int)$soldProduct['MEASURE_ID'] ?: \CCatalogMeasure::getDefaultMeasure(true)['ID'];
47 if (!isset($result[$storeId][$measureId]))
48 {
49 $result[$storeId][$measureId] = 0.0;
50 }
51
52 $result[$storeId][$measureId] += (float)$soldProduct['QUANTITY_SUM'];
53 }
54
55 return $result;
56 }
57
58 public static function getProductsSoldAmountForProducts($filter = []): array
59 {
60 $shipmentsDbResult = self::getProductsSoldAmountFromShipmentsList($filter);
61 $result = [];
62
63 while ($row = $shipmentsDbResult->fetch())
64 {
65 $result[$row['PRODUCT_ID']] ??= 0;
66 $result[$row['PRODUCT_ID']] += (float)$row['QUANTITY_SUM'];
67 }
68
69 return $result;
70 }
71 public static function getProductsSoldAmountForProductsOnStore(int $storeId, $filter = []): array
72 {
73 $filter['STORES'] = $storeId;
74
76 }
77
78 public static function getProductsSoldPricesForStores(array $filter = []): array
79 {
81 $result = [];
82
83 foreach ($soldProductsDbResult as $item)
84 {
85 $storeId = (int)$item['STORE_ID'];
86 $result[$storeId] ??= [];
87 $batchCurrencyId = $item['BATCH_CURRENCY'];
88
89 $result[$storeId][$batchCurrencyId] ??= [];
90 $result[$storeId][$batchCurrencyId]['COST_PRICE'] ??= 0.0;
91 $result[$storeId][$batchCurrencyId]['TOTAL_SOLD'] ??= 0.0;
92 $result[$storeId][$batchCurrencyId]['COST_PRICE'] +=
93 (float)$item['COST_PRICE']
94 * (float)$item['BASKET_QUANTITY']
95 * (-1)
96 ;
97
98 $basketCurrencyId = $item['BASKET_CURRENCY'];
99
100 $result[$storeId][$basketCurrencyId] ??= [];
101 $result[$storeId][$basketCurrencyId]['COST_PRICE'] ??= 0.0;
102 $result[$storeId][$basketCurrencyId]['TOTAL_SOLD'] ??= 0.0;
103 $result[$storeId][$basketCurrencyId]['TOTAL_SOLD'] +=
104 (float)$item['BASKET_PRICE']
105 * (float)$item['BASKET_QUANTITY']
106 * (-1)
107 ;
108 }
109
110 return $result;
111 }
112
113 public static function getProductsSoldPricesForProducts($filter = []): array
114 {
116 $result = [];
117
118 foreach ($shipmentsDbResult as $item)
119 {
120 $batchCurrencyId = $item['BATCH_CURRENCY'];
121
122 $result[$item['PRODUCT_ID']][$batchCurrencyId] ??= [];
123 $result[$item['PRODUCT_ID']][$batchCurrencyId]['COST_PRICE'] ??= 0.0;
124 $result[$item['PRODUCT_ID']][$batchCurrencyId]['TOTAL_SOLD'] ??= 0.0;
125 $result[$item['PRODUCT_ID']][$batchCurrencyId]['COST_PRICE'] +=
126 (float)$item['COST_PRICE']
127 * (float)$item['BASKET_QUANTITY']
128 * (-1)
129 ;
130
131 $basketCurrencyId = $item['BASKET_CURRENCY'];
132
133 $result[$item['PRODUCT_ID']][$basketCurrencyId] ??= [];
134 $result[$item['PRODUCT_ID']][$basketCurrencyId]['COST_PRICE'] ??= 0.0;
135 $result[$item['PRODUCT_ID']][$basketCurrencyId]['TOTAL_SOLD'] ??= 0.0;
136 $result[$item['PRODUCT_ID']][$basketCurrencyId]['TOTAL_SOLD'] +=
137 (float)$item['BASKET_PRICE']
138 * (float)$item['BASKET_QUANTITY']
139 * (-1)
140 ;
141 }
142
143 return $result;
144 }
145
146 public static function getProductsSoldPricesForProductsOnStore(int $storeId, $filter = []): array
147 {
148 $filter['=STORES'] = $storeId;
149
151 }
152
154 {
155 $getListParameters = self::getShippedDataListParameters($filter);
156
157 $getListParameters['select']['BASKET_CURRENCY'] = 'BASKET.CURRENCY';
158 $getListParameters['select']['BASKET_PRICE'] = 'BASKET.PRICE';
159 $getListParameters['select']['DATE_DEDUCTED'] = 'DELIVERY.DATE_DEDUCTED';
160
161 $getListParameters['runtime'][] = new Reference(
162 'S_PRODUCT_BATCH_SHIPMENT',
163 StoreBatchDocumentElementTable::class,
164 Join::on('this.S_BARCODE.ID', 'ref.SHIPMENT_ITEM_STORE_ID')
165 );
166
167 $getListParameters['select']['COST_PRICE'] = 'S_PRODUCT_BATCH_SHIPMENT.BATCH_PRICE';
168 $getListParameters['select']['BASKET_QUANTITY'] = 'S_PRODUCT_BATCH_SHIPMENT.AMOUNT';
169 $getListParameters['select']['BATCH_CURRENCY'] = 'S_PRODUCT_BATCH_SHIPMENT.BATCH_CURRENCY';
170
171 return ShipmentItemTable::getList($getListParameters)->fetchAll();
172 }
173
178 private static function getProductsSoldAmountFromShipmentsList(array $filter = []): \Bitrix\Main\ORM\Query\Result
179 {
180 $getListParameters = self::getShippedDataListParameters($filter);
181
182 $getListParameters['select']['MEASURE_ID'] = 'BASKET.PRODUCT.MEASURE';
183 $getListParameters['select'][] = 'QUANTITY_SUM';
184 $getListParameters['group'] = ['BASKET.PRODUCT_ID', 'S_BARCODE.STORE_ID'];
185 $getListParameters['runtime'][] = new ExpressionField(
186 'QUANTITY_SUM',
187 'SUM(%s)',
188 ['QUANTITY']
189 );
190
191 return ShipmentItemTable::getList($getListParameters);
192 }
193
194 public static function getStoreStockSaleData(bool $isOneField, array $filter): array
195 {
196 $filter = self::prepareFilter($filter);
197 $reservedData = self::getReservedData($filter);
198
199 $productIds = array_column($reservedData, 'PRODUCT_ID');
200 self::initProductPrice($productIds);
201
202 $storeIds =
203 $filter['STORES']
204 ?? array_column($reservedData, 'STORE_ID')
205 ;
206
207 $storesData = [];
208 if ($isOneField)
209 {
210 $storesData = self::formField($reservedData);
211 $storesData['STORE_IDS'] = $storeIds;
212 }
213 else
214 {
215 $storesPositionData = array_fill_keys(
216 $storeIds,
217 [
218 'reservedData' => [],
219 ]
220 );
221
222 foreach ($reservedData as $reservedPosition)
223 {
224 $storesPositionData[$reservedPosition['STORE_ID']]['reservedData'][] = $reservedPosition;
225 }
226
227 foreach ($storesPositionData as $storeId => $fieldData)
228 {
229 $storesData[] = self::formField($fieldData['reservedData'], $storeId);
230 }
231 }
232
233 return $storesData;
234 }
235
236 public static function getDefaultReportInterval(): array
237 {
238 $currentDate = new DateTime();
239 $intervalStartDate = new DateTime();
240 $intervalStartDate->add(self::DEFAULT_DATE_INTERVAL);
241
242 return [
243 'FROM' => $intervalStartDate->toString(),
244 'TO' => $currentDate->toString(),
245 ];
246 }
247
248 private static function getProductPrice(int $productId): float
249 {
250 if (!isset(self::$productPrice[$productId]))
251 {
252 self::initProductPrice([$productId]);
253 self::$productPrice[$productId] ??= 0;
254 }
255
256 return self::$productPrice[$productId];
257 }
258
259 private static function prepareFilter(array $filter): array
260 {
261 if (isset($filter['REPORT_INTERVAL_from']) && isset($filter['REPORT_INTERVAL_to']))
262 {
263 $filter['REPORT_INTERVAL'] = [
264 'FROM' => $filter['REPORT_INTERVAL_from'],
265 'TO' => $filter['REPORT_INTERVAL_to'],
266 ];
267 }
268
269 $accessController = AccessController::getCurrent();
270 if (!$accessController->checkCompleteRight(ActionDictionary::ACTION_STORE_VIEW))
271 {
272 $availableStores = $accessController->getPermissionValue(ActionDictionary::ACTION_STORE_VIEW) ?? [];
273
274 if (isset($filter['STORES']) && is_array($filter['STORES']))
275 {
276 $filter['STORES'] = array_values(array_intersect($availableStores, $filter['STORES']));
277 }
278 else
279 {
280 $filter['STORES'] = $availableStores;
281 }
282 }
283
284 if
285 (
286 !isset($filter['REPORT_INTERVAL'])
287 || !isset($filter['REPORT_INTERVAL']['FROM'])
288 || !isset($filter['REPORT_INTERVAL']['TO'])
289 )
290 {
291 $filter['REPORT_INTERVAL'] = self::getDefaultReportInterval();
292 }
293
294 $filter['INNER_MOVEMENT'] = (bool)($filter['INNER_MOVEMENT'] ?? true);
295
296 return $filter;
297 }
298
299
308 public static function getShippedData(array $filter): array
309 {
310 $getListParameters = self::getShippedDataListParameters($filter);
311 $getListParameters['select']['QUANTITY'] = 'QUANTITY';
312
313 return self::formStoresListFromStoresData($filter, ShipmentItemTable::getList($getListParameters)->fetchAll());
314 }
315
316 protected static function formStoresListFromStoresData(array $filter, array $storesData): array
317 {
318 ProductInfo::initBasePrice(...array_column($storesData, 'PRODUCT_ID'));
319 StoreInfo::loadStoreName(...array_column($storesData, 'STORE_ID'));
320
321 $storesInfo = [];
322
323 if (isset($filter['STORES']))
324 {
325 $storesInfo = array_fill_keys($filter['STORES'], []);
326 }
327 foreach ($storesData as $shipmentItem)
328 {
329 $storeId = $shipmentItem['STORE_ID'];
330 $productId = $shipmentItem['PRODUCT_ID'];
331 if (!isset($storesInfo[$storeId]))
332 {
333 $storesInfo[$storeId] = [];
334 }
335
336 if (!isset($storesInfo[$storeId][$productId]))
337 {
338 $storesInfo[$storeId][$productId] = (float)$shipmentItem['QUANTITY'];
339 }
340 else
341 {
342 $storesInfo[$storeId][$productId] += (float)$shipmentItem['QUANTITY'];
343 }
344 }
345
346 $stores = [];
347 foreach ($storesInfo as $storeId => $storeInfo)
348 {
349 $store = new StoreWithProductsInfo($storeId);
350 foreach ($storeInfo as $productId => $quantity)
351 {
352 $store->addProduct(new ProductInfo($productId, $quantity));
353 }
354 $stores[] = $store;
355 }
356
357 return $stores;
358 }
359
361 {
362 $filter = self::prepareFilter($filter);
363
364 return [
365 'select' => [
366 'STORE_ID' => 'S_BARCODE.STORE_ID',
367 'PRODUCT_ID' => 'BASKET.PRODUCT_ID',
368 ],
369
370 'filter' => self::formShipmentDataFilter($filter),
371
372 'runtime' => [
373 (new Reference(
374 'S_BARCODE',
375 ShipmentItemStoreTable::class,
376 Join::on('this.ID', 'ref.ORDER_DELIVERY_BASKET_ID')
377 ))->configureJoinType(Join::TYPE_LEFT),
378 ],
379 ];
380 }
381
382 private static function formShipmentDataFilter(array $filter): array
383 {
384 $formedFilter = [
385 '=DELIVERY.DEDUCTED' => 'Y',
386 '>S_BARCODE.STORE_ID' => 0,
387 ];
388
389 if (isset($filter['STORES']))
390 {
391 $formedFilter['=S_BARCODE.STORE_ID'] = $filter['STORES'];
392 }
393
394 if (isset($filter['=STORES']))
395 {
396 $formedFilter['=S_BARCODE.STORE_ID'] = $filter['=STORES'];
397 }
398
399 if (isset($filter['PRODUCTS']))
400 {
401 $formedFilter['=BASKET.PRODUCT_ID'] = $filter['PRODUCTS'];
402 }
403
404 if (isset($filter['REPORT_INTERVAL']))
405 {
406 $formedFilter['>=DELIVERY.DATE_DEDUCTED'] = new DateTime($filter['REPORT_INTERVAL']['FROM']);
407 $formedFilter['<=DELIVERY.DATE_DEDUCTED'] = new DateTime($filter['REPORT_INTERVAL']['TO']);
408 }
409
410 return $formedFilter;
411 }
412
419 public static function getArrivedData(array $filter): array
420 {
421 $getListParameters = self::getArrivedDataListParameters($filter);
422 return self::formStoresListFromStoresData($filter, StoreDocumentTable::getList($getListParameters)->fetchAll());
423 }
424
433 public static function computeSoldPercent(float $shippedSum, float $arrivedSum, int $precision = 2): float
434 {
435
436 if ($shippedSum === 0.0)
437 {
438 $soldPercent = 0;
439 }
440 elseif ($arrivedSum === 0.0)
441 {
442 $soldPercent = 100;
443 }
444 else
445 {
446 $soldPercent = ($shippedSum / $arrivedSum) * 100;
447 }
448
449 return round($soldPercent, $precision);
450 }
451
453 {
454 $filter = self::prepareFilter($filter);
455
456 return [
457 'select' => [
458 'PRODUCT_ID' => 'ELEMENTS.ELEMENT_ID',
459 'QUANTITY' => 'ELEMENTS.AMOUNT',
460 'STORE_ID' => 'ELEMENTS.STORE_TO',
461 ],
462
463 'filter' => self::formArrivedDataFilter($filter),
464 ];
465 }
466
467 private static function formArrivedDataFilter(array $filter): array
468 {
470 if ($filter['INNER_MOVEMENT'])
471 {
473 }
474 $formedFilter = [
475 '=DOC_TYPE' => $docTypes,
476 '=STATUS' => 'Y',
477 '>ELEMENTS.ELEMENT_ID' => 0,
478 ];
479
480 if (isset($filter['STORES']))
481 {
482 $formedFilter['=ELEMENTS.STORE_TO'] = $filter['STORES'];
483 }
484
485 if (isset($filter['PRODUCTS']))
486 {
487 $formedFilter['=ELEMENTS.ELEMENT_ID'] = $filter['PRODUCTS'];
488 }
489
490 if (isset($filter['REPORT_INTERVAL']))
491 {
492 $formedFilter['>=DATE_STATUS'] = new DateTime($filter['REPORT_INTERVAL']['FROM']);
493 $formedFilter['<=DATE_STATUS'] = new DateTime($filter['REPORT_INTERVAL']['TO']);
494 }
495
496 return $formedFilter;
497 }
498
499 public static function getReservedData(array $filter): array
500 {
501 $reservedData = StoreProductTable::getList([
502 'select' => [
503 'PRODUCT_ID',
504 'STORE_ID',
505 'QUANTITY' => 'AMOUNT',
506 ],
507 'filter' => self::formReservedDataFilter($filter),
508 ])->fetchAll();
509
510 return self::formStoresListFromStoresData($filter, $reservedData);
511 }
512
513 private static function formReservedDataFilter(array $filter): array
514 {
515 $formedFilter = [
516 '>STORE_ID' => 0,
517 ];
518
519 $filter = self::prepareFilter($filter);
520 if (isset($filter['STORES']))
521 {
522 $formedFilter['=STORE_ID'] = $filter['STORES'];
523 }
524 else
525 {
526 $formedFilter['!=QUANTITY'] = 0;
527 }
528
529 if (isset($filter['PRODUCTS']))
530 {
531 $formedFilter['=PRODUCT_ID'] = $filter['PRODUCTS'];
532 }
533
534 return $formedFilter;
535 }
536
537 private static function combineUniqueColumnElements(array $arraysList, string $columnKey): array
538 {
539 $combineColumnElements = [];
540 foreach ($arraysList as $item)
541 {
542 $columnElements = array_column($item, $columnKey);
543 array_push($combineColumnElements, ...$columnElements);
544 }
545
546 return array_unique($combineColumnElements);
547 }
548
549 protected static function formField(array $storeReservedData, int $storeId = null): array
550 {
551 $storedSum = 0.0;
552 foreach ($storeReservedData as $storePosition)
553 {
554 $storedSum += self::getPositionPrice($storePosition['PRODUCT_ID'], $storePosition['QUANTITY']);
555 }
556
557 $result = [
558 'SUM_STORED' => $storedSum,
559 ];
560
561 if ($storeId !== null)
562 {
563 $result['STORE_ID'] = $storeId;
564 }
565
566 return $result;
567 }
568
569 protected static function getPositionPrice(int $productId, float $productCount): float
570 {
571 return self::getProductPrice($productId) * $productCount;
572 }
573
574 protected static function initProductPrice(array $productIds): void
575 {
576 $defaultCurrency = CurrencyManager::getBaseCurrency();
577 $productsData = ProductTable::getList([
578 'select' => [
579 'ID',
580 'PURCHASING_PRICE',
581 'PURCHASING_CURRENCY',
582 'PURCHASING_CURRENCY_AMOUNT' => 'CURRENCY_TABLE.CURRENT_BASE_RATE',
583 ],
584 'filter' => [
585 '=ID' => $productIds,
586 ],
587 'runtime' => [
588 (new Reference(
589 'CURRENCY_TABLE',
590 CurrencyTable::class,
591 Join::on('this.PURCHASING_CURRENCY', 'ref.CURRENCY')
592 ))->configureJoinType(Join::TYPE_LEFT),
593 ],
594 ])->fetchAll();
595
596 foreach ($productsData as $product)
597 {
598 self::$productPrice[$product['ID']] = (float)$product['PURCHASING_PRICE'];
599 if ($product['PURCHASING_CURRENCY'] !== $defaultCurrency)
600 {
601 $defaultCurrencyAmount = (float)\CCurrency::getCurrency($defaultCurrency)['CURRENT_BASE_RATE'];
602 $currentCurrencyAmount = (float)$product['PURCHASING_CURRENCY_AMOUNT'];
603
604 self::$productPrice[$product['ID']] *= $currentCurrencyAmount;
605 self::$productPrice[$product['ID']] /= $defaultCurrencyAmount;
606 }
607 }
608 }
609}
$accessController
Определения options.php:23
static getPositionPrice(int $productId, float $productCount)
Определения storestocksale.php:569
static computeSoldPercent(float $shippedSum, float $arrivedSum, int $precision=2)
Определения storestocksale.php:433
static getProductsSoldPricesForDeductedPeriod(array $filter=[])
Определения storestocksale.php:153
static formField(array $storeReservedData, int $storeId=null)
Определения storestocksale.php:549
static getStoreStockSaleData(bool $isOneField, array $filter)
Определения storestocksale.php:194
static getProductsSoldPricesForProductsOnStore(int $storeId, $filter=[])
Определения storestocksale.php:146
static getProductsSoldAmountForProductsOnStore(int $storeId, $filter=[])
Определения storestocksale.php:71
static formStoresListFromStoresData(array $filter, array $storesData)
Определения storestocksale.php:316
static getProductsSoldPricesForStores(array $filter=[])
Определения storestocksale.php:78
static getList(array $parameters=array())
Определения datamanager.php:431
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
$filter
Определения iblock_catalog_list.php:54
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$precision
Определения template.php:403