Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
price.php
1<?php
2
3
5
6
17
18final class Price extends Controller implements EventBindInterface
19{
20 //region Actions
24 public function getFieldsAction(): array
25 {
26 return ['PRICE' => $this->getViewFields()];
27 }
28
36 public function listAction(PageNavigation $pageNavigation, array $select = [], array $filter = [], array $order = []): Page
37 {
38 return new Page(
39 'PRICES',
40 $this->getList($select, $filter, $order, $pageNavigation),
41 $this->count($filter)
42 );
43 }
44
49 public function getAction(int $id): ?array
50 {
51 $r = $this->exists($id);
52 if (!$r->isSuccess())
53 {
54 $this->addErrors($r->getErrors());
55
56 return null;
57 }
58
59 return ['PRICE' => $this->get($id)];
60 }
61
68 public function modifyAction(array $fields): ?array
69 {
70 if (!is_array($fields['PRODUCT']['PRICES']))
71 {
72 $this->addError(new Error('Product prices are empty'));
73
74 return null;
75 }
76
77 $r = $this->modifyValidate($fields['PRODUCT']['PRICES']);
78
79 if ($r->isSuccess())
80 {
81 $r = $this->modifyBefore($fields);
82 if($r->isSuccess())
83 {
84 $r = $this->modify($fields);
85 }
86 }
87
88 if (!$r->isSuccess())
89 {
90 $this->addErrors($r->getErrors());
91
92 return null;
93 }
94
95 $ids = $r->getData()[0];
96 $entityTable = $this->getEntityTable();
97
98 return [
99 'PRICES' =>
100 $entityTable::getList(
101 ['filter' => ['=ID' => $ids]]
102 )
103 ->fetchAll()
104 ];
105 }
106
111 public function deleteAction(int $id): ?bool
112 {
113 $r = $this->exists($id);
114 if ($r->isSuccess())
115 {
116 $r = $this->deleteValidate($id);
117 if ($r->isSuccess())
118 {
119 $r = \Bitrix\Catalog\Model\Price::delete($id);
120 }
121 }
122
123 if (!$r->isSuccess())
124 {
125 $this->addErrors($r->getErrors());
126
127 return null;
128 }
129
130 return true;
131 }
132
138 public function updateAction(int $id, array $fields): ?array
139 {
140 $price = $this->get($id);
141 if (!$price)
142 {
143 $this->addError(new Error('Price is not exists'));
144
145 return null;
146 }
147
148 $fields = array_merge($price, $fields);
149 return $this->modifySingleProductPrice($fields);
150 }
151
156 public function addAction(array $fields): ?array
157 {
158 unset($fields['ID']);
159
160 return $this->modifySingleProductPrice($fields);
161 }
162 //endregion
163
164 private function modifySingleProductPrice(array $fields): ?array
165 {
166 $resultGroupType = GroupTable::getRowById((int)$fields['CATALOG_GROUP_ID']);
167 if (!$resultGroupType)
168 {
169 $this->addError(new Error('Validate price error. Catalog price group is wrong'));
170
171 return null;
172 }
173
174 $entityTable = $this->getEntityTable();
175 $prices =
176 $entityTable::getList(
177 [
178 'filter' => [
179 '=PRODUCT_ID' => $fields['PRODUCT_ID'],
180 '=CATALOG_GROUP_ID' => $fields['CATALOG_GROUP_ID'],
181 ]
182 ]
183 )
184 ->fetchAll()
185 ;
186
187 $prices = array_combine(array_column($prices, 'ID'), $prices);
188 $prices[$fields['ID']] = $fields;
189
190 $r = $this->modifyValidate($prices);
191 if ($r->isSuccess())
192 {
193 $r = $this->modify([
194 'PRODUCT' => [
195 'ID' => $fields['PRODUCT_ID'],
196 'PRICES' => [$fields]
197 ]
198 ]);
199 }
200
201 if (!$r->isSuccess())
202 {
203 $this->addErrors($r->getErrors());
204 return null;
205 }
206
207 $ids = $r->getData()[0];
208
209 return [
210 'PRICE'=>$this->get($ids[0])
211 ];
212 }
213
214 protected function modify($fields)
215 {
216 $ids = [];
217 $productId = $fields['PRODUCT']['ID'];
218 $prices = $fields['PRODUCT']['PRICES'];
219
220 $r = $this->checkPermissionIBlockElementPriceModify($productId);
221 if($r->isSuccess())
222 {
223 foreach ($prices as $price)
224 {
225 if(isset($price['ID']))
226 {
227 self::normalizeFields($price);
228
229 $result = \Bitrix\Catalog\Model\Price::update($price['ID'], $price);
230 if($result->isSuccess())
231 {
232 $ids[] = $price['ID'];
233 }
234 }
235 else
236 {
237 $result = \Bitrix\Catalog\Model\Price::add([
238 'PRODUCT_ID' => $productId,
239 'CATALOG_GROUP_ID' => $price['CATALOG_GROUP_ID'],
240 'CURRENCY' => $price['CURRENCY'],
241 'PRICE' => $price['PRICE'],
242 'QUANTITY_FROM' => isset($price['QUANTITY_FROM']) ? $price['QUANTITY_FROM']:null,
243 'QUANTITY_TO' => isset($price['QUANTITY_TO']) ? $price['QUANTITY_TO']:null,
244 'EXTRA_ID' => isset($price['EXTRA_ID']) ? $price['EXTRA_ID']:null,
245 ]);
246
247 if($result->isSuccess())
248 {
249 $ids[] = $result->getId();
250 }
251 }
252
253 if($result->isSuccess() == false)
254 {
255 $r->addErrors($result->getErrors());
256 }
257 }
258
259 if($r->isSuccess())
260 $r->setData([$ids]);
261 }
262
263 return $r;
264 }
265
266 protected function modifyBefore($fields)
267 {
268 $productId = $fields['PRODUCT']['ID'];
269
270 $ids = [];
271 $prices = $fields['PRODUCT']['PRICES'];
272 foreach ($prices as $price)
273 {
274 $ids[]=$price['ID'];
275 }
276
277 $entityTable = $this->getEntityTable();
278
279 $res = $entityTable::getList(['filter'=>['PRODUCT_ID'=>$productId]]);
280 while ($item = $res->fetch())
281 {
282 if(in_array($item['ID'], $ids) == false)
283 {
284 $entityTable::delete($item['ID']);
285 }
286 }
287
288 return new Result();
289 }
290
291 private static function normalizeFields(array &$fields)
292 {
293 if (isset($fields['QUANTITY_FROM']))
294 {
295 if (is_string($fields['QUANTITY_FROM']) && $fields['QUANTITY_FROM'] === '')
296 $fields['QUANTITY_FROM'] = null;
297 elseif ($fields['QUANTITY_FROM'] === false || $fields['QUANTITY_FROM'] === 0)
298 $fields['QUANTITY_FROM'] = null;
299 }
300 else
301 {
302 $fields['QUANTITY_FROM'] = null;
303 }
304
305 if (isset($fields['QUANTITY_TO']))
306 {
307 if (is_string($fields['QUANTITY_TO']) && $fields['QUANTITY_TO'] === '')
308 $fields['QUANTITY_TO'] = null;
309 elseif ($fields['QUANTITY_TO'] === false || $fields['QUANTITY_TO'] === 0)
310 $fields['QUANTITY_TO'] = null;
311 }
312 else
313 {
314 $fields['QUANTITY_TO'] = null;
315 }
316
317 if (isset($fields['EXTRA_ID']))
318 {
319 if (is_string($fields['EXTRA_ID']) && $fields['EXTRA_ID'] === '')
320 $fields['EXTRA_ID'] = null;
321 elseif ($fields['EXTRA_ID'] === false)
322 $fields['EXTRA_ID'] = null;
323 }
324 else
325 {
326 $fields['EXTRA_ID'] = null;
327 }
328 }
329
330 private function modifyValidate(array $items): Result
331 {
332 $r = new Result();
333 $items = array_values($items);
334 $basePriceType = GroupTable::getBasePriceType();
335 $basePriceTypeId = $basePriceType['ID'];
336 $groupTypes = GroupTable::getTypeList();
337 $sortedByType = [];
338 $extendPrices = false;
339 foreach ($items as $fields)
340 {
341 $groupId = (int)$fields['CATALOG_GROUP_ID'];
342 if (!isset($groupTypes[$groupId]))
343 {
344 $r->addError(new Error('Validate price error. Catalog price group is wrong'));
345
346 return $r;
347 }
348
349 if (!$extendPrices)
350 {
351 $extendPrices = (isset($fields['QUANTITY_FROM']) || isset($fields['QUANTITY_TO']));
352 }
353 $sortedByType[$groupId][] = $fields;
354 }
355
356 $allowEmptyRange = Option::get('catalog', 'save_product_with_empty_price_range') === 'Y';
357 $enableQuantityRanges = Feature::isPriceQuantityRangesEnabled();
358
359 if (!$extendPrices)
360 {
361 if (count($items) > count($sortedByType))
362 {
363 $r->addError(new Error('Validate price error. Catalog product is allowed has only single price without ranges in price group.'));
364 }
365
366 return $r;
367 }
368
369 if ($enableQuantityRanges === false)
370 {
371 $r->addError(new Error('Validate price error. Price quantity ranges disabled'));
372
373 return $r;
374 }
375
376 $basePrices = $sortedByType[$basePriceTypeId];
377 if (!$basePrices)
378 {
379 $r->addError(new Error('Validate price error. Ranges of base price are not equal to another price group range'));
380
381 return $r;
382 }
383
384 $basePrices = $this->sortPriceRanges($basePrices);
385
386 foreach ($sortedByType as $typeId => $prices)
387 {
388 $count = count($prices);
389 $prices = $this->sortPriceRanges($prices);
390
391 foreach ($prices as $i => $item)
392 {
393 $quantityFrom = (float)$item['QUANTITY_FROM'];
394 $quantityTo = (float)$item['QUANTITY_TO'];
395
396 if (
397 $typeId !== $basePriceTypeId
398 && (
399 !isset($basePrices[$i])
400 || $quantityFrom !== (float)$basePrices[$i]['QUANTITY_FROM']
401 || $quantityTo !== (float)$basePrices[$i]['QUANTITY_TO']
402 )
403 )
404 {
405 $r->addError(
406 new Error(
407 'Validate price error. Ranges of base price are not equal to another price group range'
408 )
409 );
410
411 return $r;
412 }
413
414 if (
415 ($i !== 0 && $quantityFrom <= 0)
416 || ($i === 0 && $quantityFrom < 0)
417 )
418 {
419 $r->addError(
420 new Error(
421 "Quantity bounds error: lower bound {$quantityFrom} must be above zero (for the first range)"
422 )
423 );
424 }
425
426 if (
427 ($i !== $count-1 && $quantityTo <= 0)
428 || ($i === $count-1 && $quantityTo < 0)
429 )
430 {
431 $r->addError(
432 new Error(
433 "Quantity bounds error: higher bound {$quantityTo} must be above zero (for the last range)"
434 )
435 );
436
437 }
438
439 if ($quantityFrom > $quantityTo && ($i !== $count-1 || $quantityTo > 0))
440 {
441 $r->addError(
442 new Error(
443 "Quantity bounds error: range {$quantityFrom}-{$quantityTo} is incorrect"
444 )
445 );
446 }
447
448 $nextQuantityFrom = (float)$prices[$i + 1]["QUANTITY_FROM"];
449 $nextQuantityTo = (float)$prices[$i + 1]["QUANTITY_TO"];
450 if ($i < $count-1 && $quantityTo >= $nextQuantityFrom)
451 {
452 $r->addError(
453 new Error(
454 "Quantity bounds error: ranges {$quantityFrom}-{$quantityTo} and {$nextQuantityFrom}-{$nextQuantityTo} overlap"
455 )
456 );
457 }
458
459 if (
460 $i < $count-1
461 && $nextQuantityFrom - $quantityTo > 1
462 && !$allowEmptyRange
463 )
464 {
465 $validRangeFrom = $quantityTo + 1;
466 $validRangeTo = $nextQuantityFrom - 1;
467
468 $r->addError(
469 new Error(
470 "Invalid quantity range entry: no price is specified for range {$validRangeFrom}-{$validRangeTo})"
471 )
472 );
473 }
474
475 if ($i >= $count-1 && $quantityTo > 0)
476 {
477 $r->addError(
478 new Error(
479 "Invalid quantity range entry: no price is specified for quantity over {$quantityTo}"
480 )
481 );
482 }
483 }
484 }
485
486 return $r;
487 }
488
489 private function sortPriceRanges(array $prices): array
490 {
491 $count = count($prices);
492
493 for ($i = 0; $i < $count - 1; $i++)
494 {
495 for ($j = $i + 1; $j < $count; $j++)
496 {
497 if ($prices[$i]["QUANTITY_FROM"] > $prices[$j]["QUANTITY_FROM"])
498 {
499 $tmp = $prices[$i];
500 $prices[$i] = $prices[$j];
501 $prices[$j] = $tmp;
502 }
503 }
504 }
505
506 return $prices;
507 }
508
509 protected function getEntityTable()
510 {
511 return new PriceTable();
512 }
513
514 protected function exists($id)
515 {
516 $r = new Result();
517 if(isset($this->get($id)['ID']) == false)
518 $r->addError(new Error('Price is not exists'));
519
520 return $r;
521 }
522
523 protected function deleteValidate($id)
524 {
525 return new Result();
526 }
527
528 //region checkPermissionController
529 protected function checkPermissionEntity($name, $arguments=[])
530 {
531 $name = mb_strtolower($name); //for ajax mode
532
533 if($name == 'modify')
534 {
535 $r = $this->checkModifyPermissionEntity();
536 }
537 else
538 {
539 $r = parent::checkPermissionEntity($name);
540 }
541 return $r;
542 }
543
544 protected function checkModifyPermissionEntity()
545 {
546 $r = $this->checkReadPermissionEntity();
547 if ($r->isSuccess())
548 {
549 if (!$this->accessController->check(ActionDictionary::ACTION_PRICE_EDIT))
550 {
551 $r->addError(new Error('Access Denied', 200040300020));
552 }
553 }
554
555 return $r;
556 }
557
558 protected function checkReadPermissionEntity()
559 {
560 $r = new Result();
561
562 if (
563 !$this->accessController->check(ActionDictionary::ACTION_CATALOG_READ)
564 && !$this->accessController->check(ActionDictionary::ACTION_PRICE_EDIT)
565 )
566 {
567 $r->addError(new Error('Access Denied', 200040300010));
568 }
569 return $r;
570 }
571 //endregion
572
573 //region checkPermissionIBlock
574 private function checkPermissionIBlockElementPriceModify($productId)
575 {
576 $r = new Result();
577
578 $iblockId = \CIBlockElement::GetIBlockByID($productId);
579 if(!\CIBlockElementRights::UserHasRightTo($iblockId, $productId, self::IBLOCK_ELEMENT_EDIT_PRICE))
580 {
581 $r->addError(new Error('Access Denied', 200040300030));
582 }
583 return $r;
584 }
585 //endregion
586}
modifyAction(array $fields)
Definition price.php:68
updateAction(int $id, array $fields)
Definition price.php:138
listAction(PageNavigation $pageNavigation, array $select=[], array $filter=[], array $order=[])
Definition price.php:36
checkPermissionEntity($name, $arguments=[])
Definition price.php:529