1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
price.php
См. документацию.
1<?php
2
3namespace Bitrix\Catalog\Controller;
4
5use Bitrix\Catalog\Access\ActionDictionary;
6use Bitrix\Catalog\PriceTable;
7use Bitrix\Main\Error;
8use Bitrix\Main\Config\Option;
9use Bitrix\Main\Result;
10use Bitrix\Rest\Event\EventBindInterface;
11use Bitrix\Catalog\GroupTable;
12use Bitrix\Catalog\Config\Feature;
13
14final class Price extends Controller implements EventBindInterface
15{
16 use ListAction; // default listAction realization
17 use GetAction; // default getAction realization
18 use CheckExists; // default implementation of existence check
19
20 //region Actions
24 public function getFieldsAction(): array
25 {
26 return [$this->getServiceItemName() => $this->getViewFields()];
27 }
28
33
38
45 public function modifyAction(array $fields): ?array
46 {
47 if (!is_array($fields['PRODUCT']['PRICES']))
48 {
49 $this->addError(new Error('Product prices are empty'));
50
51 return null;
52 }
53
54 $r = $this->modifyValidate($fields['PRODUCT']['PRICES']);
55
56 if ($r->isSuccess())
57 {
58 $r = $this->modifyBefore($fields);
59 if($r->isSuccess())
60 {
61 $r = $this->modify($fields);
62 }
63 }
64
65 if (!$r->isSuccess())
66 {
67 $this->addErrors($r->getErrors());
68
69 return null;
70 }
71
72 $ids = $r->getData()[0];
73 $entityTable = $this->getEntityTable();
74
75 return [
76 'PRICES' =>
77 $entityTable::getList(
78 ['filter' => ['=ID' => $ids]]
79 )
80 ->fetchAll()
81 ];
82 }
83
88 public function deleteAction(int $id): ?bool
89 {
90 $r = $this->exists($id);
91 if ($r->isSuccess())
92 {
93 $r = $this->deleteValidate($id);
94 if ($r->isSuccess())
95 {
97 }
98 }
99
100 if (!$r->isSuccess())
101 {
102 $this->addErrors($r->getErrors());
103
104 return null;
105 }
106
107 return true;
108 }
109
115 public function updateAction(int $id, array $fields): ?array
116 {
117 $price = $this->get($id);
118 if (!$price)
119 {
120 $this->addError(new Error('Price is not exists'));
121
122 return null;
123 }
124
125 $fields = array_merge($price, $fields);
126 return $this->modifySingleProductPrice($fields);
127 }
128
133 public function addAction(array $fields): ?array
134 {
135 unset($fields['ID']);
136
137 return $this->modifySingleProductPrice($fields);
138 }
139 //endregion
140
141 private function modifySingleProductPrice(array $fields): ?array
142 {
143 $resultGroupType = GroupTable::getRowById((int)$fields['CATALOG_GROUP_ID']);
144 if (!$resultGroupType)
145 {
146 $this->addError(new Error('Validate price error. Catalog price group is wrong'));
147
148 return null;
149 }
150
151 $entityTable = $this->getEntityTable();
152 $prices =
153 $entityTable::getList(
154 [
155 'filter' => [
156 '=PRODUCT_ID' => $fields['PRODUCT_ID'],
157 '=CATALOG_GROUP_ID' => $fields['CATALOG_GROUP_ID'],
158 ]
159 ]
160 )
161 ->fetchAll()
162 ;
163
164 $prices = array_combine(array_column($prices, 'ID'), $prices);
165 $prices[$fields['ID']] = $fields;
166
167 $r = $this->modifyValidate($prices);
168 if ($r->isSuccess())
169 {
170 $r = $this->modify([
171 'PRODUCT' => [
172 'ID' => $fields['PRODUCT_ID'],
173 'PRICES' => [$fields]
174 ]
175 ]);
176 }
177
178 if (!$r->isSuccess())
179 {
180 $this->addErrors($r->getErrors());
181 return null;
182 }
183
184 $ids = $r->getData()[0];
185
186 return [
187 $this->getServiceItemName() => $this->get($ids[0])
188 ];
189 }
190
191 protected function modify($fields)
192 {
193 $ids = [];
194 $productId = $fields['PRODUCT']['ID'];
195 $prices = $fields['PRODUCT']['PRICES'];
196
197 $r = $this->checkPermissionIBlockElementPriceModify($productId);
198 if($r->isSuccess())
199 {
200 foreach ($prices as $price)
201 {
202 if(isset($price['ID']))
203 {
204 self::normalizeFields($price);
205
206 $result = \Bitrix\Catalog\Model\Price::update($price['ID'], $price);
207 if($result->isSuccess())
208 {
209 $ids[] = $price['ID'];
210 }
211 }
212 else
213 {
215 'PRODUCT_ID' => $productId,
216 'CATALOG_GROUP_ID' => $price['CATALOG_GROUP_ID'],
217 'CURRENCY' => $price['CURRENCY'],
218 'PRICE' => $price['PRICE'],
219 'QUANTITY_FROM' => isset($price['QUANTITY_FROM']) ? $price['QUANTITY_FROM']:null,
220 'QUANTITY_TO' => isset($price['QUANTITY_TO']) ? $price['QUANTITY_TO']:null,
221 'EXTRA_ID' => isset($price['EXTRA_ID']) ? $price['EXTRA_ID']:null,
222 ]);
223
224 if($result->isSuccess())
225 {
226 $ids[] = $result->getId();
227 }
228 }
229
230 if($result->isSuccess() == false)
231 {
232 $r->addErrors($result->getErrors());
233 }
234 }
235
236 if($r->isSuccess())
237 $r->setData([$ids]);
238 }
239
240 return $r;
241 }
242
243 protected function modifyBefore($fields)
244 {
245 $productId = $fields['PRODUCT']['ID'];
246
247 $ids = [];
248 $prices = $fields['PRODUCT']['PRICES'];
249 foreach ($prices as $price)
250 {
251 $ids[]=$price['ID'];
252 }
253
254 $entityTable = $this->getEntityTable();
255
256 $res = $entityTable::getList(['filter'=>['PRODUCT_ID'=>$productId]]);
257 while ($item = $res->fetch())
258 {
259 if(in_array($item['ID'], $ids) == false)
260 {
261 $entityTable::delete($item['ID']);
262 }
263 }
264
265 return new Result();
266 }
267
268 private static function normalizeFields(array &$fields)
269 {
270 if (isset($fields['QUANTITY_FROM']))
271 {
272 if (is_string($fields['QUANTITY_FROM']) && $fields['QUANTITY_FROM'] === '')
273 $fields['QUANTITY_FROM'] = null;
274 elseif ($fields['QUANTITY_FROM'] === false || $fields['QUANTITY_FROM'] === 0)
275 $fields['QUANTITY_FROM'] = null;
276 }
277 else
278 {
279 $fields['QUANTITY_FROM'] = null;
280 }
281
282 if (isset($fields['QUANTITY_TO']))
283 {
284 if (is_string($fields['QUANTITY_TO']) && $fields['QUANTITY_TO'] === '')
285 $fields['QUANTITY_TO'] = null;
286 elseif ($fields['QUANTITY_TO'] === false || $fields['QUANTITY_TO'] === 0)
287 $fields['QUANTITY_TO'] = null;
288 }
289 else
290 {
291 $fields['QUANTITY_TO'] = null;
292 }
293
294 if (isset($fields['EXTRA_ID']))
295 {
296 if (is_string($fields['EXTRA_ID']) && $fields['EXTRA_ID'] === '')
297 $fields['EXTRA_ID'] = null;
298 elseif ($fields['EXTRA_ID'] === false)
299 $fields['EXTRA_ID'] = null;
300 }
301 else
302 {
303 $fields['EXTRA_ID'] = null;
304 }
305 }
306
307 private function modifyValidate(array $items): Result
308 {
309 $r = new Result();
310 $items = array_values($items);
312 $basePriceTypeId = $basePriceType['ID'];
313 $groupTypes = GroupTable::getTypeList();
314 $sortedByType = [];
315 $extendPrices = false;
316 foreach ($items as $fields)
317 {
318 $groupId = (int)$fields['CATALOG_GROUP_ID'];
319 if (!isset($groupTypes[$groupId]))
320 {
321 $r->addError(new Error('Validate price error. Catalog price group is wrong'));
322
323 return $r;
324 }
325
326 if (!$extendPrices)
327 {
328 $extendPrices = (isset($fields['QUANTITY_FROM']) || isset($fields['QUANTITY_TO']));
329 }
330 $sortedByType[$groupId][] = $fields;
331 }
332
333 $allowEmptyRange = Option::get('catalog', 'save_product_with_empty_price_range') === 'Y';
334 $enableQuantityRanges = Feature::isPriceQuantityRangesEnabled();
335
336 if (!$extendPrices)
337 {
338 if (count($items) > count($sortedByType))
339 {
340 $r->addError(new Error('Validate price error. Catalog product is allowed has only single price without ranges in price group.'));
341 }
342
343 return $r;
344 }
345
346 if ($enableQuantityRanges === false)
347 {
348 $r->addError(new Error('Validate price error. Price quantity ranges disabled'));
349
350 return $r;
351 }
352
353 $basePrices = $sortedByType[$basePriceTypeId];
354 if (!$basePrices)
355 {
356 $r->addError(new Error('Validate price error. Ranges of base price are not equal to another price group range'));
357
358 return $r;
359 }
360
361 $basePrices = $this->sortPriceRanges($basePrices);
362
363 foreach ($sortedByType as $typeId => $prices)
364 {
365 $count = count($prices);
366 $prices = $this->sortPriceRanges($prices);
367
368 foreach ($prices as $i => $item)
369 {
370 $quantityFrom = (float)$item['QUANTITY_FROM'];
371 $quantityTo = (float)$item['QUANTITY_TO'];
372
373 if (
374 $typeId !== $basePriceTypeId
375 && (
376 !isset($basePrices[$i])
377 || $quantityFrom !== (float)$basePrices[$i]['QUANTITY_FROM']
378 || $quantityTo !== (float)$basePrices[$i]['QUANTITY_TO']
379 )
380 )
381 {
382 $r->addError(
383 new Error(
384 'Validate price error. Ranges of base price are not equal to another price group range'
385 )
386 );
387
388 return $r;
389 }
390
391 if (
392 ($i !== 0 && $quantityFrom <= 0)
393 || ($i === 0 && $quantityFrom < 0)
394 )
395 {
396 $r->addError(
397 new Error(
398 "Quantity bounds error: lower bound {$quantityFrom} must be above zero (for the first range)"
399 )
400 );
401 }
402
403 if (
404 ($i !== $count-1 && $quantityTo <= 0)
405 || ($i === $count-1 && $quantityTo < 0)
406 )
407 {
408 $r->addError(
409 new Error(
410 "Quantity bounds error: higher bound {$quantityTo} must be above zero (for the last range)"
411 )
412 );
413
414 }
415
416 if ($quantityFrom > $quantityTo && ($i !== $count-1 || $quantityTo > 0))
417 {
418 $r->addError(
419 new Error(
420 "Quantity bounds error: range {$quantityFrom}-{$quantityTo} is incorrect"
421 )
422 );
423 }
424
425 $nextQuantityFrom = (float)$prices[$i + 1]["QUANTITY_FROM"];
426 $nextQuantityTo = (float)$prices[$i + 1]["QUANTITY_TO"];
427 if ($i < $count-1 && $quantityTo >= $nextQuantityFrom)
428 {
429 $r->addError(
430 new Error(
431 "Quantity bounds error: ranges {$quantityFrom}-{$quantityTo} and {$nextQuantityFrom}-{$nextQuantityTo} overlap"
432 )
433 );
434 }
435
436 if (
437 $i < $count-1
438 && $nextQuantityFrom - $quantityTo > 1
439 && !$allowEmptyRange
440 )
441 {
442 $validRangeFrom = $quantityTo + 1;
443 $validRangeTo = $nextQuantityFrom - 1;
444
445 $r->addError(
446 new Error(
447 "Invalid quantity range entry: no price is specified for range {$validRangeFrom}-{$validRangeTo})"
448 )
449 );
450 }
451
452 if ($i >= $count-1 && $quantityTo > 0)
453 {
454 $r->addError(
455 new Error(
456 "Invalid quantity range entry: no price is specified for quantity over {$quantityTo}"
457 )
458 );
459 }
460 }
461 }
462
463 return $r;
464 }
465
466 private function sortPriceRanges(array $prices): array
467 {
468 $count = count($prices);
469
470 for ($i = 0; $i < $count - 1; $i++)
471 {
472 for ($j = $i + 1; $j < $count; $j++)
473 {
474 if ($prices[$i]["QUANTITY_FROM"] > $prices[$j]["QUANTITY_FROM"])
475 {
476 $tmp = $prices[$i];
477 $prices[$i] = $prices[$j];
478 $prices[$j] = $tmp;
479 }
480 }
481 }
482
483 return $prices;
484 }
485
486 protected function getEntityTable()
487 {
488 return new PriceTable();
489 }
490
491 protected function deleteValidate($id)
492 {
493 return new Result();
494 }
495
496 //region checkPermissionController
497 protected function checkPermissionEntity($name, $arguments=[])
498 {
499 $name = mb_strtolower($name); //for ajax mode
500
501 if($name == 'modify')
502 {
503 $r = $this->checkModifyPermissionEntity();
504 }
505 else
506 {
507 $r = parent::checkPermissionEntity($name);
508 }
509 return $r;
510 }
511
512 protected function checkModifyPermissionEntity()
513 {
514 $r = $this->checkReadPermissionEntity();
515 if ($r->isSuccess())
516 {
517 if (!$this->accessController->check(ActionDictionary::ACTION_PRICE_EDIT))
518 {
519 $r->addError($this->getErrorModifyAccessDenied());
520 }
521 }
522
523 return $r;
524 }
525
526 protected function checkReadPermissionEntity()
527 {
528 $r = new Result();
529
530 if (
531 !$this->accessController->check(ActionDictionary::ACTION_CATALOG_READ)
532 && !$this->accessController->check(ActionDictionary::ACTION_PRICE_EDIT)
533 )
534 {
535 $r->addError($this->getErrorReadAccessDenied());
536 }
537 return $r;
538 }
539 //endregion
540
541 //region checkPermissionIBlock
542 private function checkPermissionIBlockElementPriceModify($productId)
543 {
544 $r = new Result();
545
546 $iblockId = \CIBlockElement::GetIBlockByID($productId);
547 if(!\CIBlockElementRights::UserHasRightTo($iblockId, $productId, self::IBLOCK_ELEMENT_EDIT_PRICE))
548 {
549 $r->addError(new Error('Access Denied', 200040300030));
550 }
551 return $r;
552 }
553 //endregion
554}
$count
Определения admin_tab.php:4
checkModifyPermissionEntity()
Определения price.php:512
deleteValidate($id)
Определения price.php:491
checkReadPermissionEntity()
Определения price.php:526
modifyAction(array $fields)
Определения price.php:45
addAction(array $fields)
Определения price.php:133
getFieldsAction()
Определения price.php:24
deleteAction(int $id)
Определения price.php:88
updateAction(int $id, array $fields)
Определения price.php:115
modifyBefore($fields)
Определения price.php:243
checkPermissionEntity($name, $arguments=[])
Определения price.php:497
modify($fields)
Определения price.php:191
static getBasePriceType()
Определения group.php:209
static getTypeList()
Определения group.php:273
static update($id, array $data)
Определения entity.php:229
static add(array $data)
Определения entity.php:150
static delete($id)
Определения entity.php:317
Определения error.php:15
static getRowById($id, array $parameters=[])
Определения datamanager.php:380
if(!is_array($prop["VALUES"])) $tmp
Определения component_props.php:203
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$result
Определения get_property_values.php:14
$basePriceType
Определения iblock_catalog_change_price.php:22
$iblockId
Определения iblock_catalog_edit.php:30
if(preg_match('/^ else[a-z0-9_]{2}$/i', $siteID)===1)
Определения cron_frame.php:23
$name
Определения menu_edit.php:35
exists($id)
Определения checkexists.php:30
trait CheckExists
Определения checkexists.php:8
trait GetAction
Определения getaction.php:8
trait ListAction
Определения listaction.php:9
trait Error
Определения error.php:11
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$items
Определения template.php:224
$fields
Определения yandex_run.php:501