Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
product.php
1<?php
3
11
12class Product extends Entity
13{
15 protected const ACTION_CHANGE_PARENT_AVAILABLE = 'SKU_AVAILABLE';
17 protected const ACTION_CHANGE_PARENT_TYPE = 'PARENT_TYPE';
19 protected const ACTION_RECALCULATE_SETS = 'SETS';
21 protected const ACTION_SEND_NOTIFICATIONS = 'SUBSCRIPTION';
22
24 private static ?bool $separateSkuMode = null;
26 private static ?bool $saleIncluded = null;
28 private static ?string $queryElementDate = null;
29
35 public static function getTabletClassName(): string
36 {
37 return '\Bitrix\Catalog\ProductTable';
38 }
39
45 protected static function getDefaultCachedFieldList(): array
46 {
47 return [
48 'ID',
49 'TYPE',
50 'AVAILABLE',
51 'QUANTITY',
52 'QUANTITY_TRACE' => 'QUANTITY_TRACE_ORIG',
53 'CAN_BUY_ZERO' => 'CAN_BUY_ZERO_ORIG',
54 'SUBSCRIBE' => 'SUBSCRIBE_ORIG'
55 ];
56 }
57
64 public static function delete($id): ORM\Data\DeleteResult
65 {
66 return parent::deleteNoDemands($id);
67 }
68
77 protected static function prepareForAdd(ORM\Data\AddResult $result, $id, array &$data): void
78 {
79 if (isset($data['fields']['ID']))
80 $id = $data['fields']['ID'];
81 $id = (int)$id;
82 if ($id <= 0)
83 {
84 $result->addError(new ORM\EntityError(
85 Loc::getMessage('BX_CATALOG_MODEL_PRODUCT_ERR_WRONG_PRODUCT_ID')
86 ));
87 return;
88 }
89
90 $iblockId = 0;
91 if (isset($data['external_fields']['IBLOCK_ID']))
92 $iblockId = (int)$data['external_fields']['IBLOCK_ID'];
93 if ($iblockId <= 0)
94 $iblockId = \CIBlockElement::GetIBlockByID($id);
95 if (empty($iblockId))
96 {
97 $result->addError(new ORM\EntityError(
98 Loc::getMessage('BX_CATALOG_MODEL_PRODUCT_ERR_ELEMENT_NOT_EXISTS')
99 ));
100 return;
101 }
102 $iblockData = \CCatalogSku::GetInfoByIBlock($iblockId);
103 if (empty($iblockData))
104 {
105 $result->addError(new ORM\EntityError(
106 Loc::getMessage('BX_CATALOG_MODEL_PRODUCT_ERR_SIMPLE_IBLOCK')
107 ));
108 return;
109 }
110 $data['external_fields']['IBLOCK_ID'] = $iblockId;
111
112 $allowedTypes = self::getProductTypes($iblockData['CATALOG_TYPE']);
113
114 $fields = $data['fields'];
115 parent::prepareForAdd($result, $id, $fields);
116 if (!$result->isSuccess())
117 return;
118
119 if (self::$separateSkuMode === null)
120 {
121 self::$separateSkuMode = Main\Config\Option::get('catalog', 'show_catalog_tab_with_offers') === 'Y';
122 }
123
124 static $defaultValues = null,
125 $blackList = null,
126 $paymentPeriods = null,
127 $tripleFields = null,
128 $booleanFields = null,
129 $nullFields = null,
130 $sizeFields = null;
131 if ($defaultValues === null)
132 {
133 $defaultValues = [
134 'QUANTITY' => 0,
135 'QUANTITY_RESERVED' => 0,
136 'QUANTITY_TRACE' => Catalog\ProductTable::STATUS_DEFAULT,
137 'CAN_BUY_ZERO' => Catalog\ProductTable::STATUS_DEFAULT,
138 'WEIGHT' => 0,
139 'PRICE_TYPE' => Catalog\ProductTable::PAYMENT_TYPE_SINGLE,
140 'RECUR_SCHEME_LENGTH' => null,
141 'RECUR_SCHEME_TYPE' => Catalog\ProductTable::PAYMENT_PERIOD_DAY,
142 'TRIAL_PRICE_ID' => null,
143 'WITHOUT_ORDER' => Catalog\ProductTable::STATUS_NO,
144 'SELECT_BEST_PRICE' => Catalog\ProductTable::STATUS_NO,
145 'VAT_ID' => null,
146 'VAT_INCLUDED' => Catalog\ProductTable::STATUS_NO,
147 'BARCODE_MULTI' => Catalog\ProductTable::STATUS_NO,
148 'SUBSCRIBE' => Catalog\ProductTable::STATUS_DEFAULT,
149 'BUNDLE' => Catalog\ProductTable::STATUS_NO,
150 'PURCHASING_PRICE' => null,
151 'PURCHASING_CURRENCY' => null,
152 'TMP_ID' => null,
153 'MEASURE' => null,
154 'WIDTH' => null,
155 'LENGTH' => null,
156 'HEIGHT' => null,
157 ];
158
159 $blackList = [
160 'NEGATIVE_AMOUNT_TRACE' => true
161 ];
162
163 $paymentPeriods = Catalog\ProductTable::getPaymentPeriods(false);
164 $tripleFields = ['QUANTITY_TRACE', 'CAN_BUY_ZERO', 'SUBSCRIBE'];
165 $booleanFields = ['WITHOUT_ORDER', 'SELECT_BEST_PRICE', 'VAT_INCLUDED', 'BARCODE_MULTI', 'BUNDLE'];
166 $nullFields = ['MEASURE', 'TRIAL_PRICE_ID', 'VAT_ID', 'RECUR_SCHEME_LENGTH'];
167 $sizeFields = ['WIDTH', 'LENGTH', 'HEIGHT'];
168 }
169 $defaultValues['TYPE'] = self::getDefaultProductType($iblockData['CATALOG_TYPE']);
170
171 $fields = array_merge($defaultValues, array_diff_key($fields, $blackList));
172
173 $fields['TYPE'] = (int)$fields['TYPE'];
174 if (!isset($allowedTypes[$fields['TYPE']]))
175 {
176 $result->addError(new ORM\EntityError(
177 Loc::getMessage('BX_CATALOG_MODEL_PRODUCT_ERR_BAD_PRODUCT_TYPE')
178 ));
179 return;
180 }
181
182 $isSpecialType = (
183 $fields['TYPE'] === Catalog\ProductTable::TYPE_EMPTY_SKU
184 || $fields['TYPE'] === Catalog\ProductTable::TYPE_FREE_OFFER
185 );
186 if ($isSpecialType)
187 {
188 $fields['AVAILABLE'] = Catalog\ProductTable::STATUS_NO;
189 $fields['QUANTITY'] = 0;
190 $fields['QUANTITY_RESERVED'] = 0;
191 $fields['QUANTITY_TRACE'] = Catalog\ProductTable::STATUS_YES;
192 $fields['CAN_BUY_ZERO'] = Catalog\ProductTable::STATUS_NO;
193 }
194 $isService = $fields['TYPE'] === Catalog\ProductTable::TYPE_SERVICE;
195 if ($isService)
196 {
197 $fields['QUANTITY_RESERVED'] = 0;
198 $fields['QUANTITY_TRACE'] = Catalog\ProductTable::STATUS_NO;
199 $fields['CAN_BUY_ZERO'] = Catalog\ProductTable::STATUS_YES;
200 }
201
202 if (is_string($fields['QUANTITY']) && !is_numeric($fields['QUANTITY']))
203 {
204 $result->addError(new ORM\EntityError(
206 'BX_CATALOG_MODEL_PRODUCT_ERR_BAD_NUMERIC_FIELD',
207 ['#FIELD#' => 'QUANTITY']
208 )
209 ));
210 }
211
212 if ($isService)
213 {
214 $fields['QUANTITY'] = (int)$fields['QUANTITY'];
215 if ($fields['QUANTITY'] !== 1)
216 {
217 $fields['QUANTITY'] = 0;
218 }
219 }
220 else
221 {
222 $fields['QUANTITY'] = (float)$fields['QUANTITY'];
223 }
224
225 if (is_string($fields['QUANTITY_RESERVED']) && !is_numeric($fields['QUANTITY_RESERVED']))
226 {
227 $result->addError(new ORM\EntityError(
229 'BX_CATALOG_MODEL_PRODUCT_ERR_BAD_NUMERIC_FIELD',
230 ['#FIELD#' => 'QUANTITY_RESERVED']
231 )
232 ));
233 }
234 $fields['QUANTITY_RESERVED'] = (float)$fields['QUANTITY_RESERVED'];
235
236 if ($fields['QUANTITY_RESERVED'] < 0)
237 {
238 $result->addError(new ORM\EntityError(
239 Loc::getMessage('BX_CATALOG_MODEL_PRODUCT_ERR_QUANTITY_RESERVE_LESS_ZERO'),
240 'BX_CATALOG_MODEL_PRODUCT_ERR_QUANTITY_RESERVE_LESS_ZERO'
241 ));
242 }
243
244 foreach ($tripleFields as $fieldName)
245 {
246 if (
247 $fields[$fieldName] != Catalog\ProductTable::STATUS_NO
248 && $fields[$fieldName] != Catalog\ProductTable::STATUS_YES
249 )
250 $fields[$fieldName] = $defaultValues[$fieldName];
251 }
252 foreach ($booleanFields as $fieldName)
253 {
254 if ($fields[$fieldName] != Catalog\ProductTable::STATUS_YES)
255 $fields[$fieldName] = $defaultValues[$fieldName];
256 }
257 foreach ($nullFields as $fieldName)
258 {
259 if ($fields[$fieldName] !== null)
260 {
261 $fields[$fieldName] = (int)$fields[$fieldName];
262 if ($fields[$fieldName] <= 0)
263 $fields[$fieldName] = null;
264 }
265 }
266 foreach ($sizeFields as $fieldName)
267 {
268 if ($fields[$fieldName] !== null)
269 {
270 $fields[$fieldName] = (float)$fields[$fieldName];
271 if ($fields[$fieldName] <= 0)
272 $fields[$fieldName] = null;
273 }
274 }
275 unset($fieldName);
276
277 if (
278 $fields['PRICE_TYPE'] != Catalog\ProductTable::PAYMENT_TYPE_REGULAR
279 && $fields['PRICE_TYPE'] != Catalog\ProductTable::PAYMENT_TYPE_TRIAL
280 )
281 $fields['PRICE_TYPE'] = $defaultValues['PRICE_TYPE'];
282 if (!in_array($fields['RECUR_SCHEME_TYPE'], $paymentPeriods, true))
283 $fields['RECUR_SCHEME_TYPE'] = $defaultValues['RECUR_SCHEME_TYPE'];
284
285 if (is_string($fields['WEIGHT']) && !is_numeric($fields['WEIGHT']))
286 {
287 $result->addError(new ORM\EntityError(
289 'BX_CATALOG_MODEL_PRODUCT_ERR_BAD_NUMERIC_FIELD',
290 ['#FIELD#' => 'WEIGHT']
291 )
292 ));
293 }
294 $fields['WEIGHT'] = (float)$fields['WEIGHT'];
295 if ($fields['TMP_ID'] !== null)
296 $fields['TMP_ID'] = mb_substr($fields['TMP_ID'], 0, 40);
297
298 /* purchasing price */
299 $purchasingCurrency = null;
300 $purchasingPrice = static::checkPriceValue($fields['PURCHASING_PRICE']);
301 if ($purchasingPrice !== null)
302 {
303 $purchasingCurrency = static::checkPriceCurrency($fields['PURCHASING_CURRENCY']);
304 if ($purchasingCurrency === null)
305 {
306 $result->addError(new ORM\EntityError(
307 Loc::getMessage('BX_CATALOG_MODEL_PRODUCT_ERR_WRONG_PURCHASING_CURRENCY')
308 ));
309 $purchasingPrice = null;
310 }
311 }
312 $fields['PURCHASING_PRICE'] = $purchasingPrice;
313 $fields['PURCHASING_CURRENCY'] = $purchasingCurrency;
314 unset($purchasingCurrency, $purchasingPrice);
315 /* purchasing price end */
316
317 if (array_key_exists('AVAILABLE', $fields))
318 {
319 if (
320 $fields['AVAILABLE'] != Catalog\ProductTable::STATUS_YES
321 && $fields['AVAILABLE'] != Catalog\ProductTable::STATUS_NO
322 )
323 {
324 unset($fields['AVAILABLE']);
325 }
326 else
327 {
328 if (!$isSpecialType)
329 {
330 if ($isService)
331 {
332 //TODO: remove this hack after reservation resource
333 $fields['QUANTITY'] = $fields['AVAILABLE'] !== Catalog\ProductTable::STATUS_YES ? 0 : 1;
334 }
335 else
336 {
337 $data['actions'][self::ACTION_CHANGE_PARENT_AVAILABLE] = true;
338 }
339 }
340 }
341 }
342
343 if ($result->isSuccess())
344 {
345 $fields['ID'] = $id;
346 $fields['NEGATIVE_AMOUNT_TRACE'] = $fields['CAN_BUY_ZERO'];
347 $fields['TIMESTAMP_X'] = new Main\Type\DateTime();
348
349 if (!isset($fields['AVAILABLE']))
350 {
351 self::calculateAvailable($fields, $data['actions']);
352 if ($fields['AVAILABLE'] === null)
353 $fields['AVAILABLE'] = Catalog\ProductTable::STATUS_NO;
354 }
355 if ($fields['TYPE'] === Catalog\ProductTable::TYPE_OFFER)
356 {
357 $data['actions'][self::ACTION_CHANGE_PARENT_TYPE] = true;
358 }
359 }
360
361 if ($result->isSuccess())
362 $data['fields'] = $fields;
363 unset($fields);
364 }
365
374 protected static function prepareForUpdate(ORM\Data\UpdateResult $result, $id, array &$data): void
375 {
376 $id = (int)$id;
377 if ($id <= 0)
378 {
379 $result->addError(new ORM\EntityError(
380 Loc::getMessage('BX_CATALOG_MODEL_PRODUCT_ERR_WRONG_PRODUCT_ID')
381 ));
382 return;
383 }
384
385 $iblockId = 0;
386 if (isset($data['external_fields']['IBLOCK_ID']))
387 $iblockId = (int)$data['external_fields']['IBLOCK_ID'];
388 if ($iblockId <= 0)
389 $iblockId = \CIBlockElement::GetIBlockByID($id);
390 if (empty($iblockId))
391 {
392 $result->addError(new ORM\EntityError(
393 Loc::getMessage('BX_CATALOG_MODEL_PRODUCT_ERR_ELEMENT_NOT_EXISTS')
394 ));
395 return;
396 }
397 $iblockData = \CCatalogSku::GetInfoByIBlock($iblockId);
398 if (empty($iblockData))
399 {
400 $result->addError(new ORM\EntityError(
401 Loc::getMessage('BX_CATALOG_MODEL_PRODUCT_ERR_SIMPLE_IBLOCK')
402 ));
403 return;
404 }
405 $data['external_fields']['IBLOCK_ID'] = $iblockId;
406
407 $fields = $data['fields'];
408 parent::prepareForUpdate($result, $id, $fields);
409 if (!$result->isSuccess())
410 return;
411
412 if (self::$separateSkuMode === null)
413 {
414 self::$separateSkuMode = Main\Config\Option::get('catalog', 'show_catalog_tab_with_offers') === 'Y';
415 }
416
417 static $quantityFields = null,
418 $paymentPeriods = null,
419 $tripleFields = null,
420 $booleanFields = null,
421 $nullFields = null,
422 $sizeFields = null,
423 $blackList = null;
424
425 if ($quantityFields === null)
426 {
427 $quantityFields = ['QUANTITY', 'QUANTITY_RESERVED'];
428 $paymentPeriods = Catalog\ProductTable::getPaymentPeriods(false);
429 $tripleFields = ['QUANTITY_TRACE', 'CAN_BUY_ZERO', 'SUBSCRIBE'];
430 $booleanFields = ['WITHOUT_ORDER', 'SELECT_BEST_PRICE', 'VAT_INCLUDED', 'BARCODE_MULTI', 'BUNDLE', 'AVAILABLE'];
431 $nullFields = ['MEASURE', 'TRIAL_PRICE_ID', 'VAT_ID', 'RECUR_SCHEME_LENGTH'];
432 $sizeFields = ['WIDTH', 'LENGTH', 'HEIGHT'];
433
434 $blackList = [
435 'ID' => true,
436 'NEGATIVE_AMOUNT_TRACE' => true
437 ];
438 }
439 $fields = array_diff_key($fields, $blackList);
440
441 $allowedTypes = self::getProductTypes($iblockData['CATALOG_TYPE']);
442
443 if (array_key_exists('TYPE', $fields))
444 {
445 $fields['TYPE'] = (int)$fields['TYPE'];
446 if (!isset($allowedTypes[$fields['TYPE']]))
447 {
448 $result->addError(new ORM\EntityError(
449 Loc::getMessage('BX_CATALOG_MODEL_PRODUCT_ERR_BAD_PRODUCT_TYPE')
450 ));
451 return;
452 }
453 }
454
455 foreach ($quantityFields as $fieldName)
456 {
457 if (array_key_exists($fieldName, $fields))
458 {
459 if ($fields[$fieldName] === null)
460 unset($fields[$fieldName]);
461 else
462 $fields[$fieldName] = (float)$fields[$fieldName];
463 }
464 }
465 foreach ($tripleFields as $fieldName)
466 {
467 if (array_key_exists($fieldName, $fields))
468 {
469 if (
470 $fields[$fieldName] != Catalog\ProductTable::STATUS_NO
471 && $fields[$fieldName] != Catalog\ProductTable::STATUS_YES
472 && $fields[$fieldName] != Catalog\ProductTable::STATUS_DEFAULT
473 )
474 unset($fields[$fieldName]);
475 }
476 }
477 if (isset($fields['SUBSCRIBE']))
478 $data['actions'][self::ACTION_SEND_NOTIFICATIONS] = true;
479 foreach ($booleanFields as $fieldName)
480 {
481 if (array_key_exists($fieldName, $fields))
482 {
483 if (
484 $fields[$fieldName] != Catalog\ProductTable::STATUS_NO
485 && $fields[$fieldName] != Catalog\ProductTable::STATUS_YES
486 )
487 unset($fields[$fieldName]);
488 }
489 }
490 foreach ($nullFields as $fieldName)
491 {
492 if (isset($fields[$fieldName]))
493 {
494 $fields[$fieldName] = (int)$fields[$fieldName];
495 if ($fields[$fieldName] <= 0)
496 {
497 $fields[$fieldName] = null;
498 }
499 }
500 }
501 foreach ($sizeFields as $fieldName)
502 {
503 if (isset($fields[$fieldName]))
504 {
505 $fields[$fieldName] = (float)$fields[$fieldName];
506 if ($fields[$fieldName] <= 0)
507 {
508 $fields[$fieldName] = null;
509 }
510 }
511 }
512 unset($fieldName);
513
514 if (array_key_exists('PRICE_TYPE', $fields))
515 {
516 if (
517 $fields['PRICE_TYPE'] != Catalog\ProductTable::PAYMENT_TYPE_REGULAR
518 && $fields['PRICE_TYPE'] != Catalog\ProductTable::PAYMENT_TYPE_TRIAL
519 && $fields['PRICE_TYPE'] != Catalog\ProductTable::PAYMENT_TYPE_SINGLE
520 )
521 unset($fields['PRICE_TYPE']);
522 }
523 if (array_key_exists('RECUR_SCHEME_TYPE', $fields))
524 {
525 if (!in_array($fields['RECUR_SCHEME_TYPE'], $paymentPeriods, true))
526 unset($fields['RECUR_SCHEME_TYPE']);
527 }
528
529 if (array_key_exists('WEIGHT', $fields))
530 {
531 if ($fields['WEIGHT'] === null)
532 {
533 unset($fields['WEIGHT']);
534 }
535 else
536 {
537 $fields['WEIGHT'] = (float)$fields['WEIGHT'];
538 $data['actions'][self::ACTION_RECALCULATE_SETS] = true;
539 }
540 }
541 if (isset($fields['TMP_ID']))
542 {
543 $fields['TMP_ID'] = mb_substr($fields['TMP_ID'], 0, 40);
544 }
545
546 if (array_key_exists('QUANTITY_RESERVED', $fields) && (float)$fields['QUANTITY_RESERVED'] < 0)
547 {
548 $result->addError(new ORM\EntityError(
549 Loc::getMessage('BX_CATALOG_MODEL_PRODUCT_ERR_QUANTITY_RESERVE_LESS_ZERO'),
550 'BX_CATALOG_MODEL_PRODUCT_ERR_QUANTITY_RESERVE_LESS_ZERO'
551 ));
552 }
553
554 /* purchasing price */
555 $existPurchasingPrice = array_key_exists('PURCHASING_PRICE', $fields);
556 $existPurchasingCurrency = array_key_exists('PURCHASING_CURRENCY', $fields);
557 if ($existPurchasingPrice)
558 {
559 $fields['PURCHASING_PRICE'] = static::checkPriceValue($fields['PURCHASING_PRICE']);
560 if ($fields['PURCHASING_PRICE'] === null)
561 {
562 $fields['PURCHASING_CURRENCY'] = null;
563 $existPurchasingCurrency = false;
564 }
565 }
566 if ($existPurchasingCurrency)
567 {
568 $fields['PURCHASING_CURRENCY'] = static::checkPriceCurrency($fields['PURCHASING_CURRENCY']);
569 if ($fields['PURCHASING_CURRENCY'] === null)
570 {
571 $result->addError(new ORM\EntityError(
572 Loc::getMessage('BX_CATALOG_MODEL_PRODUCT_ERR_WRONG_PURCHASING_CURRENCY')
573 ));
574 }
575 }
576 /* purchasing price end */
577
578 if ($result->isSuccess())
579 {
580 if (isset($fields['CAN_BUY_ZERO']))
581 $fields['NEGATIVE_AMOUNT_TRACE'] = $fields['CAN_BUY_ZERO'];
582 $fields['TIMESTAMP_X'] = new Main\Type\DateTime();
583
584 if (isset($fields['AVAILABLE']))
585 {
586 $copyFields =
587 isset($fields['TYPE'])
588 ? $fields
589 : array_merge(static::getCacheItem($id, true), $fields)
590 ;
591 $copyFields['TYPE'] = (int)$copyFields['TYPE'];
592 $isService = $copyFields['TYPE'] === Catalog\ProductTable::TYPE_SERVICE;
593 if ($isService)
594 {
595 //TODO: remove this hack after reservation resource
596 $fields['QUANTITY'] = $fields['AVAILABLE'] !== Catalog\ProductTable::STATUS_YES ? 0 : 1;
597 }
598 if (!$isService)
599 {
600 $data['actions'][self::ACTION_CHANGE_PARENT_AVAILABLE] = true;
601 }
602 $data['actions'][self::ACTION_RECALCULATE_SETS] = true;
603 $data['actions'][self::ACTION_SEND_NOTIFICATIONS] = true;
604 }
605 else
606 {
607 $needCalculateAvailable = (isset($fields['TYPE'])
608 || isset($fields['QUANTITY'])
609 || isset($fields['QUANTITY_TRACE'])
610 || isset($fields['CAN_BUY_ZERO'])
611 );
612 if ($needCalculateAvailable)
613 {
614 $needCache = (!isset($fields['TYPE'])
615 || !isset($fields['QUANTITY'])
616 || !isset($fields['QUANTITY_TRACE'])
617 || !isset($fields['CAN_BUY_ZERO'])
618 );
619 $copyFields = (
620 $needCache
621 ? array_merge(static::getCacheItem($id, true), $fields)
622 : $fields
623 );
624 $copyFields['TYPE'] = (int)$copyFields['TYPE'];
625 self::calculateAvailable($copyFields, $data['actions']);
626 if ($copyFields['AVAILABLE'] !== null)
627 $fields['AVAILABLE'] = $copyFields['AVAILABLE'];
628 unset($copyFields, $needCache);
629 }
630 unset($needCalculateAvailable);
631 }
632 $data['fields'] = $fields;
633 }
634
635 unset($fields);
636 }
637
645 protected static function runAddExternalActions($id, array $data): void
646 {
647 switch ($data['fields']['TYPE'])
648 {
649 case Catalog\ProductTable::TYPE_OFFER:
650 if (
651 isset($data['actions'][self::ACTION_CHANGE_PARENT_AVAILABLE])
652 || isset($data['actions'][self::ACTION_CHANGE_PARENT_TYPE])
653 )
654 {
655 Catalog\Product\Sku::calculateComplete(
656 $id,
657 $data['external_fields']['IBLOCK_ID'],
659 );
660 }
661 break;
662 case Catalog\ProductTable::TYPE_SKU:
663 if (isset($data['actions'][self::ACTION_CHANGE_PARENT_AVAILABLE]))
664 {
665 Catalog\Product\Sku::calculateComplete(
666 $id,
667 $data['external_fields']['IBLOCK_ID'],
669 );
670 }
671 break;
672 }
673 }
674
682 protected static function runUpdateExternalActions($id, array $data): void
683 {
684 $product = self::getCacheItem($id);
685 if (isset($data['actions'][self::ACTION_CHANGE_PARENT_AVAILABLE]))
686 {
687 switch ($product['TYPE'])
688 {
689 case Catalog\ProductTable::TYPE_OFFER:
690 if (isset($data['actions']['SKU_AVAILABLE']))
691 {
692 Catalog\Product\Sku::calculateComplete(
693 $id,
694 $data['external_fields']['IBLOCK_ID'],
696 );
697 }
698 self::updateElementModificationTime($id);
699 break;
700 case Catalog\ProductTable::TYPE_SKU:
701 if (isset($data['actions']['SKU_AVAILABLE']))
702 {
703 Catalog\Product\Sku::calculateComplete(
704 $id,
705 $data['external_fields']['IBLOCK_ID'],
707 );
708 }
709 break;
710 case Catalog\ProductTable::TYPE_PRODUCT:
711 case Catalog\ProductTable::TYPE_SET:
712 self::updateElementModificationTime($id);
713 break;
714 }
715 }
716 if (isset($data['actions'][self::ACTION_SEND_NOTIFICATIONS]))
717 {
718 self::checkSubscription($id, $product);
719 }
720 if (isset($data['actions'][self::ACTION_RECALCULATE_SETS]))
721 {
722 \CCatalogProductSet::recalculateSetsByProduct($id);
723 }
724
725 $changeAvailable = (
726 isset($product['AVAILABLE'])
727 && isset($product[self::PREFIX_OLD.'AVAILABLE'])
728 && $product[self::PREFIX_OLD.'AVAILABLE'] != $product['AVAILABLE']
729 );
730 if ($changeAvailable)
731 {
732 // clear public components cache
733 \CIBlock::clearIblockTagCache($data['external_fields']['IBLOCK_ID']);
734 // send old event
735 $eventId = 'OnProductQuantityTrace';
736 if (
737 Main\Config\Option::get('catalog', 'enable_processing_deprecated_events') === 'Y'
738 && Event::existEventHandlersById($eventId)
739 )
740 {
741 $description = [
742 'ID' => $product['ID'],
743 'ELEMENT_IBLOCK_ID' => $data['external_fields']['IBLOCK_ID'],
744 'IBLOCK_ID' => $data['external_fields']['IBLOCK_ID'],
745 'TYPE' => $product['TYPE'],
746 'AVAILABLE' => $product['AVAILABLE'],
747 'CAN_BUY_ZERO' => $product['CAN_BUY_ZERO'],
748 'NEGATIVE_AMOUNT_TRACE' => $product['CAN_BUY_ZERO'],
749 'QUANTITY_TRACE' => $product['QUANTITY_TRACE'],
750 'QUANTITY' => $product['QUANTITY'],
751 'OLD_QUANTITY' => $product[self::PREFIX_OLD.'QUANTITY'] ?? $product['QUANTITY'],
752 ];
753 $description['DELTA'] = $description['QUANTITY'] - $description['OLD_QUANTITY'];
754 $handlerData = [
755 $product['ID'],
756 $description,
757 ];
758 unset($description);
759
760 $eventManager = Main\EventManager::getInstance();
761 $handlerList = $eventManager->findEventHandlers('catalog', $eventId);
762 foreach ($handlerList as $handler)
763 {
764 $handler['FROM_MODULE_ID'] = 'catalog';
765 $handler['MESSAGE_ID'] = $eventId;
766 ExecuteModuleEventEx($handler, $handlerData);
767 }
768 unset($handler, $handlerList);
769 unset($handlerData);
770 }
771 }
772
773 unset($product);
774 }
775
782 protected static function runDeleteExternalActions($id): void
783 {
784 Catalog\PriceTable::deleteByProduct($id);
785 Catalog\MeasureRatioTable::deleteByProduct($id);
786 Catalog\ProductGroupAccessTable::deleteByProduct($id);
787 Catalog\StoreProductTable::deleteByProduct($id);
788 Catalog\SubscribeTable::onIblockElementDelete($id);
790 //TODO: replace this code
791 $conn = Main\Application::getConnection();
792 $helper = $conn->getSqlHelper();
793 $conn->queryExecute(
794 'delete from '.$helper->quote('b_catalog_product_sets').
795 ' where '.$helper->quote('ITEM_ID').' = '.$id.' or '.$helper->quote('OWNER_ID').' = '.$id
796 );
797 unset($helper, $conn);
798 }
799
808 private static function checkSubscription($id, array $product): void
809 {
810 if (
811 isset($product[self::PREFIX_OLD.'AVAILABLE'])
812 && Catalog\SubscribeTable::checkPermissionSubscribe($product['SUBSCRIBE'])
813 )
814 {
815 if (
816 $product[self::PREFIX_OLD.'AVAILABLE'] == Catalog\ProductTable::STATUS_NO
817 && $product['AVAILABLE'] == Catalog\ProductTable::STATUS_YES
818 )
819 {
820 Catalog\SubscribeTable::runAgentToSendNotice($id);
821 }
822 elseif (
823 $product[self::PREFIX_OLD.'AVAILABLE'] == Catalog\ProductTable::STATUS_YES
824 && $product['AVAILABLE'] == Catalog\ProductTable::STATUS_NO
825 )
826 {
827 Catalog\SubscribeTable::runAgentToSendRepeatedNotice($id);
828 }
829 if (
830 $product[self::PREFIX_OLD.'QUANTITY'] <= 0
831 && $product['QUANTITY'] > 0
832 )
833 {
834 if (self::$saleIncluded === null)
835 self::$saleIncluded = Loader::includeModule('sale');
836 if (self::$saleIncluded)
837 \CSaleBasket::ProductSubscribe($id, 'catalog');
838 }
839 }
840 }
841
842 private static function checkPriceValue($price): ?float
843 {
844 $result = null;
845
846 if ($price !== null)
847 {
848 if (is_string($price))
849 {
850 if ($price !== '' && is_numeric($price))
851 {
852 $price = (float)$price;
853 if (is_finite($price))
854 $result = $price;
855 }
856 }
857 elseif (
858 is_int($price)
859 || (is_float($price) && is_finite($price))
860 )
861 {
862 $result = $price;
863 }
864 }
865
866 return $result;
867 }
868
869 private static function checkPriceCurrency($currency): ?string
870 {
871 $result = null;
872 if (is_string($currency) && $currency !== '')
873 {
874 $result = $currency;
875 }
876
877 return $result;
878 }
879
880 private static function calculateAvailable(array &$fields, array &$actions)
881 {
882 $result = null;
883
884 switch ($fields['TYPE'])
885 {
886 case Catalog\ProductTable::TYPE_PRODUCT:
887 case Catalog\ProductTable::TYPE_FREE_OFFER:
888 $result = Catalog\ProductTable::calculateAvailable($fields);
889 $actions[self::ACTION_RECALCULATE_SETS] = true;
890 $actions[self::ACTION_SEND_NOTIFICATIONS] = true;
891 break;
892 case Catalog\ProductTable::TYPE_OFFER:
893 $result = Catalog\ProductTable::calculateAvailable($fields);
894 if (!self::$separateSkuMode)
895 $actions[self::ACTION_CHANGE_PARENT_AVAILABLE] = true;
896 $actions[self::ACTION_RECALCULATE_SETS] = true;
897 $actions[self::ACTION_SEND_NOTIFICATIONS] = true;
898 break;
899 case Catalog\ProductTable::TYPE_SKU:
900 if (self::$separateSkuMode)
901 $result = Catalog\ProductTable::calculateAvailable($fields);
902 else
903 $actions[self::ACTION_CHANGE_PARENT_AVAILABLE] = true;
904 break;
905 case Catalog\ProductTable::TYPE_SET:
906 $result = Catalog\ProductTable::calculateAvailable($fields);
907 break;
908 case Catalog\ProductTable::TYPE_EMPTY_SKU:
909 $result = Catalog\ProductTable::STATUS_NO;
910 break;
911 case Catalog\ProductTable::TYPE_SERVICE:
912 if (isset($fields['QUANTITY']))
913 {
914 $result = ($fields['QUANTITY'] > 0 ? Catalog\ProductTable::STATUS_YES : Catalog\ProductTable::STATUS_NO);
915 $actions[self::ACTION_SEND_NOTIFICATIONS] = true;
916 }
917 }
918
919 $fields['AVAILABLE'] = $result;
920 }
921
922 //TODO: remove after create \Bitrix\Catalog\Model\CatalogIblock
923 private static function getProductTypes($catalogType): array
924 {
925 $result = [];
926
927 switch ($catalogType)
928 {
929 case \CCatalogSku::TYPE_CATALOG:
930 $result = [
931 Catalog\ProductTable::TYPE_PRODUCT => true,
932 Catalog\ProductTable::TYPE_SET => true,
933 Catalog\ProductTable::TYPE_SERVICE => true,
934 ];
935 break;
936 case \CCatalogSku::TYPE_OFFERS:
937 $result = [
938 Catalog\ProductTable::TYPE_OFFER => true,
939 Catalog\ProductTable::TYPE_FREE_OFFER => true
940 ];
941 break;
942 case \CCatalogSku::TYPE_FULL:
943 $result = [
944 Catalog\ProductTable::TYPE_PRODUCT => true,
945 Catalog\ProductTable::TYPE_SET => true,
946 Catalog\ProductTable::TYPE_SKU => true,
947 Catalog\ProductTable::TYPE_EMPTY_SKU => true,
948 Catalog\ProductTable::TYPE_SERVICE => true,
949 ];
950 break;
951 case \CCatalogSku::TYPE_PRODUCT:
952 $result = [
953 Catalog\ProductTable::TYPE_SKU => true,
954 Catalog\ProductTable::TYPE_EMPTY_SKU => true
955 ];
956 break;
957 }
958
959 return $result;
960 }
961
962 //TODO: remove after create \Bitrix\Catalog\Model\CatalogIblock
963 private static function getDefaultProductType($catalogType): ?int
964 {
965 $result = null;
966
967 switch ($catalogType)
968 {
969 case \CCatalogSku::TYPE_CATALOG:
970 case \CCatalogSku::TYPE_FULL:
971 $result = Catalog\ProductTable::TYPE_PRODUCT;
972 break;
973 case \CCatalogSku::TYPE_OFFERS:
974 $result = Catalog\ProductTable::TYPE_OFFER;
975 break;
976 case \CCatalogSku::TYPE_PRODUCT:
977 $result = Catalog\ProductTable::TYPE_SKU;
978 break;
979 }
980
981 return $result;
982 }
983
984 private static function updateElementModificationTime(int $elementId): void
985 {
986 $conn = Main\Application::getConnection();
987 if (self::$queryElementDate === null)
988 {
989 $helper = $conn->getSqlHelper();
990 self::$queryElementDate = 'update ' . $helper->quote(Iblock\ElementTable::getTableName())
991 . ' set ' . $helper->quote('TIMESTAMP_X') . ' = ' . $helper->getCurrentDateTimeFunction()
992 . ' where ' . $helper->quote('ID') . '=';
993 }
994 $conn->queryExecute(self::$queryElementDate . $elementId);
995 }
996}
static prepareForUpdate(ORM\Data\UpdateResult $result, $id, array &$data)
Definition product.php:374
static prepareForAdd(ORM\Data\AddResult $result, $id, array &$data)
Definition product.php:77
static runDeleteExternalActions($id)
Definition product.php:782
static runUpdateExternalActions($id, array $data)
Definition product.php:682
static runAddExternalActions($id, array $data)
Definition product.php:645
static checkPermissionSubscribe($subscribe)
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29