Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
usestore.php
1<?php
2
4
14use Bitrix\Crm\Order\TradingPlatform;
15use Bitrix\Crm\Component\EntityDetails\ProductList;
16use Bitrix\Crm\Order\Internals\ShipmentRealizationTable;
18
19Loc::loadMessages(__FILE__);
20
21final class UseStore
22{
23 protected const CATEGORY_NAME = "use_store";
24
25 public const URL_PARAM_STORE_MASTER_HIDE = "STORE_MASTER_HIDE";
26
27 public static function isUsed(): bool
28 {
29 return State::isUsedInventoryManagement();
30 }
31
32 public static function isUsedOneC(): bool
33 {
34 return Option::get('catalog', 'once_inventory_management') === 'Y';
35 }
36
37 public static function enableOnec(): bool
38 {
39 Option::set('catalog', 'once_inventory_management', 'Y');
40
41 return true;
42 }
43
44 public static function disableOnec(): bool
45 {
46 Option::set('catalog', 'once_inventory_management', 'N');
47
48 return true;
49 }
50
51 public static function isPlanRestricted(): bool
52 {
53 return !Catalog\Config\Feature::isInventoryManagementEnabled();
54 }
55
59 protected static function isCrmExists(): bool
60 {
61 return Loader::includeModule('crm');
62 }
63
67 protected static function shouldManageQuantityTrace(): bool
68 {
69 if (!self::isCrmExists())
70 {
71 return false;
72 }
73
74 return !\CCrmSaleHelper::isWithOrdersMode();
75 }
76
80 protected static function isSeparateSku(): bool
81 {
82 return Option::get('catalog', 'show_catalog_tab_with_offers') === 'Y';
83 }
84
85 protected static function checkEnablingConditions(): bool
86 {
87 return (
88 self::isCrmExists()
89 && !\CCrmSaleHelper::isWithOrdersMode()
90 && !self::isUsedOneC()
91 );
92 }
93
94 protected static function enableOptions(): void
95 {
96 Option::set('catalog', 'default_quantity_trace', 'Y');
97 Option::set('catalog', 'default_can_buy_zero', 'Y');
98 Option::set('catalog', 'allow_negative_amount', 'Y');
99 Option::set('catalog', 'default_use_store_control', 'Y');
100 Option::set('catalog', 'enable_reservation', 'Y');
101 }
102
107 public static function enable(): bool
108 {
109 if (!self::checkEnablingConditions())
110 {
111 return false;
112 }
113
115
118 self::resetStoreBatch();
120 self::resetCrmReserve();
121
122 self::installRealizationDocumentTradingPlatform();
123
125
126 self::showEntityProductGridColumns();
127
128 return true;
129 }
130
131 protected static function registerEventsHandlers()
132 {
133 $eventManager = EventManager::getInstance();
134
135 $eventManager->registerEventHandler('sale', 'onBeforeSaleShipmentSetField', 'crm', '\Bitrix\Crm\Order\EventsHandler\Shipment', 'onBeforeSetField');
136 }
137
138 protected static function unRegisterEventsHandlers()
139 {
140 $eventManager = EventManager::getInstance();
141
142 $eventManager->unRegisterEventHandler('sale', 'onBeforeSaleShipmentSetField', 'crm', '\Bitrix\Crm\Order\EventsHandler\Shipment', 'onBeforeSetField');
143 }
144
149 public static function enableWithoutResetting(): bool
150 {
151 if (!self::checkEnablingConditions())
152 {
153 return false;
154 }
155
157
158 self::installRealizationDocumentTradingPlatform();
159
160 self::showEntityProductGridColumns();
161
162 return true;
163 }
164
165 public static function disable(): bool
166 {
167 if (!self::isCrmExists())
168 {
169 return false;
170 }
171
172 Option::set('catalog', 'default_use_store_control', 'N');
173 Option::set('catalog', 'default_quantity_trace', 'N');
174
175 if (self::shouldManageQuantityTrace())
176 {
181 }
182
183 self::deactivateRealizationDocumentTradingPlatform();
184
186
187 if (Loader::includeModule('pull'))
188 {
189 \CPullWatch::AddToStack(
190 'CATALOG_INVENTORY_MANAGEMENT_CHANGED',
191 [
192 'module_id' => 'crm',
193 'command' => 'onCatalogInventoryManagementDisabled',
194 ],
195 );
196 }
197
198 return true;
199 }
200
201 protected static function resetQuantity(): void
202 {
204 $conn->queryExecute('truncate table b_catalog_store_product');
205 $conn->queryExecute('delete from b_catalog_store_barcode where ORDER_ID is null and STORE_ID > 0');
206 unset($conn);
207 }
208
216
217 protected static function resetQuantityTraceMainTypes(): void
218 {
219 $mainTypes = implode(
220 ', ',
221 [
225 ]
226 );
227
228 Application::getConnection()->queryExecute("
229 update b_catalog_product
230 set
231 QUANTITY = 0,
232 QUANTITY_RESERVED = 0,
233 QUANTITY_TRACE = '" . ProductTable::STATUS_DEFAULT . "',
234 CAN_BUY_ZERO = '" . ProductTable::STATUS_DEFAULT . "',
235 NEGATIVE_AMOUNT_TRACE = '" . ProductTable::STATUS_DEFAULT . "',
236 AVAILABLE = '" . ProductTable::STATUS_YES . "'
237 where
238 TYPE in (" . $mainTypes . ")
239 ");
240 }
241
242 protected static function resetQuantityTraceSku(): void
243 {
244 if (!self::isCloud() && self::isSeparateSku())
245 {
246 Application::getConnection()->queryExecute("
247 update b_catalog_product
248 set
249 QUANTITY = 0,
250 QUANTITY_RESERVED = 0,
251 QUANTITY_TRACE = '" . ProductTable::STATUS_DEFAULT . "',
252 CAN_BUY_ZERO = '" . ProductTable::STATUS_DEFAULT . "',
253 NEGATIVE_AMOUNT_TRACE = '" . ProductTable::STATUS_DEFAULT . "',
254 AVAILABLE = '" . ProductTable::STATUS_YES . "'
255 where
256 TYPE = " . ProductTable::TYPE_SKU
257 );
258 }
259 else
260 {
261 Application::getConnection()->queryExecute("
262 update b_catalog_product
263 set
264 QUANTITY = 0,
265 QUANTITY_RESERVED = 0,
266 QUANTITY_TRACE = '" . ProductTable::STATUS_NO . "',
267 CAN_BUY_ZERO = '" . ProductTable::STATUS_YES . "',
268 NEGATIVE_AMOUNT_TRACE = '" . ProductTable::STATUS_YES . "',
269 AVAILABLE = '" . ProductTable::STATUS_YES . "'
270 where
271 TYPE = " . ProductTable::TYPE_SKU
272 );
273 }
274 }
275
276 protected static function resetQuantityTraceEmptySku(): void
277 {
278 if (!self::isCloud())
279 {
280 return;
281 }
282 Application::getConnection()->queryExecute("
283 update b_catalog_product
284 set
285 QUANTITY = 0,
286 QUANTITY_TRACE = '" . ProductTable::STATUS_YES . "',
287 CAN_BUY_ZERO = '" . ProductTable::STATUS_NO . "',
288 NEGATIVE_AMOUNT_TRACE = '" . ProductTable::STATUS_NO . "',
289 AVAILABLE = '" . ProductTable::STATUS_NO . "',
290 TYPE = " . ProductTable::TYPE_PRODUCT . "
291 where
293 );
294 }
295
296 protected static function resetQuantityTraceSets(): void
297 {
298 Application::getConnection()->queryExecute("
299 update b_catalog_product
300 set
301 QUANTITY = 0,
302 QUANTITY_TRACE = '" . ProductTable::STATUS_NO . "',
303 CAN_BUY_ZERO = '" . ProductTable::STATUS_YES . "',
304 NEGATIVE_AMOUNT_TRACE = '" . ProductTable::STATUS_YES . "',
305 AVAILABLE = '" . ProductTable::STATUS_YES . "'
306 where
307 TYPE = " . ProductTable::TYPE_SET
308 );
309 }
310
311 protected static function resetSaleReserve(): void
312 {
313 if (Loader::includeModule('sale'))
314 {
316
317 $conn->queryExecute("update b_sale_order_dlv_basket set RESERVED_QUANTITY = 0 where 1 = 1");
318 $conn->queryExecute("update b_sale_order_delivery set RESERVED='N' where 1 = 1");
319
320 $conn->queryExecute("truncate table b_sale_basket_reservation_history");
321 $conn->queryExecute("truncate table b_sale_basket_reservation");
322 }
323 }
324
325 private static function resetCrmReserve(): void
326 {
327 if (Loader::includeModule('crm'))
328 {
330 $conn->queryExecute("truncate table b_crm_product_row_reservation");
331 $conn->queryExecute("truncate table b_crm_product_reservation_map");
332 }
333 }
334
335 private static function resetStoreBatch(): void
336 {
337 Application::getConnection()->queryExecute('truncate table b_catalog_store_batch');
338 Application::getConnection()->queryExecute('truncate table b_catalog_store_batch_docs_element');
339 }
340
346 private static function resetCrmRealizations(): void
347 {
348 if (Loader::includeModule('crm'))
349 {
350 $realizations = ShipmentRealizationTable::getList([
351 'filter' => [
352 '=IS_REALIZATION' => 'Y',
353 ],
354 ]);
355 foreach ($realizations as $realization)
356 {
357 ShipmentRealizationTable::delete($realization['ID']);
358 ShipmentTable::deleteWithItems($realization['SHIPMENT_ID']);
359 }
360 }
361 }
362
368 private static function resetStoreDocuments(): void
369 {
370 global $USER_FIELD_MANAGER;
371
372 $fileIds = Catalog\StoreDocumentFileTable::getList(['select' => ['FILE_ID']])->fetchAll();
373 $fileIds = array_column($fileIds, 'FILE_ID');
374
375 foreach ($fileIds as $fileId)
376 {
377 \CFile::Delete($fileId);
378 }
379
380 $documents = Catalog\StoreDocumentTable::getList(['select' => ['ID', 'DOC_TYPE']])->fetchAll();
381 foreach ($documents as $document)
382 {
383 $typeTableClass = Catalog\Document\StoreDocumentTableManager::getTableClassByType($document['DOC_TYPE']);
384 if ($typeTableClass)
385 {
386 $USER_FIELD_MANAGER->Delete($typeTableClass::getUfId(), $document['ID']);
387 }
388 }
389
391
392 $conn->queryExecute('truncate table b_catalog_store_docs');
393 $conn->queryExecute('truncate table b_catalog_docs_element');
394 $conn->queryExecute('truncate table b_catalog_docs_barcode');
395 $conn->queryExecute('truncate table b_catalog_store_document_file');
396
397 if (Loader::includeModule('crm'))
398 {
399 \Bitrix\Crm\Timeline\TimelineEntry::deleteByAssociatedEntityType(\CCrmOwnerType::StoreDocument);
400
401 $conn->queryExecute("truncate table b_crm_store_document_contractor");
402 }
403 }
404
410 public static function resetDocuments(): void
411 {
412 self::resetStoreDocuments();
413 self::resetCrmRealizations();
414 }
415
416 protected static function disableQuantityTraceMainTypes(): void
417 {
418 $mainTypes = implode(
419 ', ',
420 [
424 ]
425 );
426
427 Application::getConnection()->queryExecute("
428 update b_catalog_product
429 set
430 QUANTITY_TRACE = '" . ProductTable::STATUS_DEFAULT . "',
431 CAN_BUY_ZERO = '" . ProductTable::STATUS_DEFAULT . "',
432 NEGATIVE_AMOUNT_TRACE = '" . ProductTable::STATUS_DEFAULT . "',
433 AVAILABLE = '" . ProductTable::STATUS_YES . "'
434 where
435 TYPE in (" . $mainTypes . ")
436 ");
437 }
438
439 protected static function disableQuantityTraceSku(): void
440 {
441 Application::getConnection()->queryExecute("
442 update b_catalog_product
443 set
444 QUANTITY_TRACE = '" . ProductTable::STATUS_DEFAULT . "',
445 CAN_BUY_ZERO = '" . ProductTable::STATUS_DEFAULT . "',
446 NEGATIVE_AMOUNT_TRACE = '" . ProductTable::STATUS_DEFAULT . "',
447 AVAILABLE = '" . ProductTable::STATUS_YES . "'
448 where
449 TYPE = " . ProductTable::TYPE_SKU
450 );
451 }
452
453 protected static function disableQuantityTraceEmptySku(): void
454 {
455 if (!self::isCloud())
456 {
457 return;
458 }
459 Application::getConnection()->queryExecute("
460 update b_catalog_product
461 set
462 QUANTITY = 0,
463 QUANTITY_TRACE = '" . ProductTable::STATUS_YES . "',
464 CAN_BUY_ZERO = '" . ProductTable::STATUS_NO . "',
465 NEGATIVE_AMOUNT_TRACE = '" . ProductTable::STATUS_NO . "',
466 AVAILABLE = '" . ProductTable::STATUS_NO . "'
467 where
469 );
470 }
471
472 protected static function disableQuantityTraceSets(): void
473 {
474 Application::getConnection()->queryExecute("
475 update b_catalog_product
476 set
477 QUANTITY_TRACE = '" . ProductTable::STATUS_NO . "',
478 CAN_BUY_ZERO = '" . ProductTable::STATUS_YES . "',
479 NEGATIVE_AMOUNT_TRACE = '" . ProductTable::STATUS_YES . "',
480 AVAILABLE = '" . ProductTable::STATUS_YES . "'
481 where
482 TYPE = " . ProductTable::TYPE_SET
483 );
484 }
485
492 public static function isQuantityInconsistent(): bool
493 {
494 $connection = Application::getConnection();
495
496 $productTypes = new SqlExpression('(?i, ?i)', ProductTable::TYPE_PRODUCT, ProductTable::TYPE_OFFER);
497 $query = $connection->query("
498 select cp.ID, (cp.QUANTITY - (sum(csp.AMOUNT) - sum(csp.QUANTITY_RESERVED))) as QUANTITY_DIFFERENCE from b_catalog_product cp
499 inner join b_catalog_store_product csp on cp.ID = csp.PRODUCT_ID
500 inner join b_catalog_store cs on cs.ID = csp.STORE_ID
501 where cp.TYPE in {$productTypes} and (cs.ACTIVE = 'Y')
502 group by cp.ID
503 having QUANTITY_DIFFERENCE != 0
504 limit 1
505 ");
506
507 if ($query->fetch())
508 {
509 return true;
510 }
511
512 $query = $connection->query("
513 select cp.ID, cp.QUANTITY from b_catalog_product cp
514 left outer join b_catalog_store_product csp on cp.ID = csp.PRODUCT_ID
515 where cp.TYPE in {$productTypes} and csp.PRODUCT_ID is null and cp.QUANTITY != 0
516 limit 1
517 ");
518
519 if ($query->fetch())
520 {
521 return true;
522 }
523
524 return false;
525 }
526
527 public static function doNonEmptyProductsExist(): bool
528 {
529 $connection = Application::getConnection();
530
531 $productTypes = new SqlExpression('(?i, ?i)', ProductTable::TYPE_PRODUCT, ProductTable::TYPE_OFFER);
532 $query = $connection->query("
533 select ID from b_catalog_product cp
534 where TYPE in {$productTypes} and (QUANTITY != 0 or QUANTITY_RESERVED != 0)
535 limit 1
536 ");
537
538 if ($query->fetch())
539 {
540 return true;
541 }
542
543 $query = $connection->query("
544 select ID from b_catalog_store_product csp
545 where AMOUNT != 0 or QUANTITY_RESERVED != 0
546 limit 1
547 ");
548
549 if ($query->fetch())
550 {
551 return true;
552 }
553
554 return false;
555 }
556
557 public static function conductedDocumentsExist(): bool
558 {
559 $iterator = Catalog\StoreDocumentTable::getList([
560 'select' => [
561 'ID',
562 ],
563 'filter' => [
564 '=STATUS' => 'Y',
565 ],
566 'limit' => 1,
567 ]);
568 $row = $iterator->fetch();
569 unset($iterator);
570
571 return !empty($row);
572 }
573
574 public static function isEmpty(): bool
575 {
576 return self::catalogIsEmpty() && self::storeIsEmpty();
577 }
578
579 private static function catalogIsEmpty(): bool
580 {
581 global $DB;
582 $str = "SELECT ID FROM b_catalog_product WHERE QUANTITY > 0 or QUANTITY_RESERVED > 0 limit 1";
583 $r = $DB->query($str);
584
585 return !($r->fetch());
586 }
587
588 private static function storeIsEmpty(): bool
589 {
590 global $DB;
591 $str = "SELECT ID FROM b_catalog_store_product WHERE AMOUNT > 0 limit 1";
592 $r = $DB->query($str);
593
594 return !($r->fetch());
595 }
596
597 private static function getPortalZone(): string
598 {
599 $portalZone = '';
600
601 if (Loader::includeModule('bitrix24'))
602 {
603 if (method_exists('CBitrix24', 'getLicensePrefix'))
604 {
605 $licensePrefix = \CBitrix24::getLicensePrefix();
606 if ($licensePrefix !== false)
607 {
608 $portalZone = (string)$licensePrefix;
609 }
610 }
611 }
612 elseif (Loader::includeModule('intranet'))
613 {
614 if (method_exists('CIntranetUtils', 'getPortalZone'))
615 {
616 $portalZone = \CIntranetUtils::getPortalZone();
617 }
618 }
619
620 return $portalZone;
621 }
622
623 private static function installRealizationDocumentTradingPlatform(): void
624 {
625 if (Loader::includeModule('crm'))
626 {
627 $platformCode = TradingPlatform\RealizationDocument::TRADING_PLATFORM_CODE;
628 $platform = TradingPlatform\RealizationDocument::getInstanceByCode($platformCode);
629 if (!$platform->isInstalled())
630 {
631 $platform->install();
632 }
633 }
634 }
635
636 private static function showEntityProductGridColumns(): void
637 {
638 if (!Loader::includeModule('crm'))
639 {
640 return;
641 }
642
643 $headers = [
644 'STORE_INFO',
645 'STORE_AVAILABLE',
646 'RESERVE_INFO',
647 'ROW_RESERVED',
648 'DEDUCTED_INFO',
649 ];
650
651 $allHeaderMap = ProductList::getHeaderDefaultMap();
652 $allHeaders = array_keys($allHeaderMap);
653
654 $gridId = ProductList::DEFAULT_GRID_ID;
655 $connection = Application::getConnection();
656 $sqlHelper = $connection->getSqlHelper();
657 $queryResult = $connection->query(
658 "SELECT ID, VALUE FROM b_user_option WHERE CATEGORY = 'main.interface.grid' AND NAME = '{$sqlHelper->forSql($gridId)}'"
659 );
660
661 $resetCache = false;
662 while ($gridSettings = $queryResult->fetch())
663 {
664 $optionID = (int)$gridSettings['ID'];
665 $value = $gridSettings['VALUE'];
666 if (!$value)
667 {
668 continue;
669 }
670
671 $options = unserialize($value, ['allowed_classes' => false]);
672 if (
673 !is_array($options)
674 || empty($options)
675 || !isset($options['views'])
676 || !is_array($options['views'])
677 )
678 {
679 continue;
680 }
681
682 $changed = false;
683 foreach ($options['views'] as &$view)
684 {
685 if (!isset($view['columns']) || $view['columns'] === '')
686 {
687 continue;
688 }
689
690 $allUsedColumns = explode(',', $view['columns']);
691 $currentHeadersInDefaultPosition = array_values(
692 array_intersect($allHeaders, array_merge($allUsedColumns, $headers))
693 );
694 $headers = array_values(array_intersect($allHeaders, $headers));
695
696 foreach ($headers as $header)
697 {
698 if (in_array($header, $allUsedColumns, true))
699 {
700 continue;
701 }
702
703 $insertPosition = array_search($header, $currentHeadersInDefaultPosition, true);
704 array_splice($allUsedColumns, $insertPosition, 0, $header);
705 $changed = true;
706 }
707
708 if ($changed)
709 {
710 $view['columns'] = implode(',', $allUsedColumns);
711 }
712 }
713 unset($view);
714
715 if ($changed)
716 {
717 $sqlValue = $sqlHelper->forSql(serialize($options));
718 $connection->queryExecute(
719 "UPDATE b_user_option SET VALUE = '{$sqlValue}' WHERE ID ='{$optionID}'"
720 );
721 $resetCache = true;
722 }
723 }
724
725 if ($resetCache)
726 {
727 Application::getInstance()->getManagedCache()->cleanDir('user_option');
728 }
729
730 if (Loader::includeModule('pull'))
731 {
732 \CPullWatch::AddToStack(
733 'CATALOG_INVENTORY_MANAGEMENT_CHANGED',
734 [
735 'module_id' => 'crm',
736 'command' => 'onCatalogInventoryManagementEnabled',
737 ],
738 );
739 }
740 }
741
742 private static function deactivateRealizationDocumentTradingPlatform()
743 {
744 if (Loader::includeModule('crm'))
745 {
746 $platformCode = TradingPlatform\RealizationDocument::TRADING_PLATFORM_CODE;
747 $platform = TradingPlatform\RealizationDocument::getInstanceByCode($platformCode);
748 if ($platform->isInstalled())
749 {
750 $platform->unsetActive();
751 }
752 }
753 }
754
755 public static function needShowSlider(): bool
756 {
757 return !self::isUsed();
758 }
759
760 public static function isCloud(): bool
761 {
762 return Loader::includeModule('bitrix24');
763 }
764}
static getConnection($name="")
static loadMessages($file)
Definition loc.php:64