1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
cumulative.php
См. документацию.
1<?php
2
3namespace Sale\Handlers\DiscountPreset;
4
5use Bitrix\Main;
6use Bitrix\Main\Error;
7use Bitrix\Main\Localization\Loc;
8use Bitrix\Sale;
9use Bitrix\Sale\Discount\Actions;
10use Bitrix\Sale\Discount\CumulativeCalculator;
11use Bitrix\Sale\Discount\Preset\ArrayHelper;
12use Bitrix\Sale\Discount\Preset\BasePreset;
13use Bitrix\Sale\Discount\Preset\HtmlHelper;
14use Bitrix\Sale\Discount\Preset\Manager;
15use Bitrix\Sale\Discount\Preset\State;
16
17final class Cumulative extends BasePreset
18{
19 const TYPE_FIXED = Actions::VALUE_TYPE_FIX;
20 const TYPE_PERCENT = Actions::VALUE_TYPE_PERCENT;
21
22 const TYPE_COUNT_PERIOD_ALL_TIME = CumulativeCalculator::TYPE_COUNT_PERIOD_ALL_TIME;
23 const TYPE_COUNT_PERIOD_INTERVAL = CumulativeCalculator::TYPE_COUNT_PERIOD_INTERVAL;
24 const TYPE_COUNT_PERIOD_RELATIVE = CumulativeCalculator::TYPE_COUNT_PERIOD_RELATIVE;
25
26 const MARK_DEFAULT_EMPTY_ROW = PHP_INT_MIN;
27
28 public function getTitle()
29 {
30 return Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_NAME');
31 }
32
33 public function getDescription()
34 {
35 return '';
36 }
37
38 public function getAvailableState(): int
39 {
40 if (Sale\Config\Feature::isCumulativeDiscountsEnabled())
41 {
42 return BasePreset::AVAILABLE_STATE_ALLOW;
43 }
44 if ($this->bitrix24Included)
45 {
46 return BasePreset::AVAILABLE_STATE_TARIFF;
47 }
48
49 return BasePreset::AVAILABLE_STATE_DISALLOW;
50 }
51
52 public function getAvailableHelpLink(): ?array
53 {
54 if ($this->getAvailableState() === BasePreset::AVAILABLE_STATE_TARIFF)
55 {
56 return Sale\Config\Feature::getCumulativeDiscountsHelpLink();
57 }
58
59 return null;
60 }
61
62
66 public function getCategory()
67 {
68 return Manager::CATEGORY_PRODUCTS;
69 }
70
71 public function getFirstStepName()
72 {
73 return 'InputName';
74 }
75
76 public function processShowInputName(State $state)
77 {
78 return $this->processShowInputNameInternal($state);
79 }
80
81 public function processSaveInputName(State $state)
82 {
83 return $this->processSaveInputNameInternal($state, 'InputRanges');
84 }
85
86 public function processShowInputRanges(State $state)
87 {
88 $lid = $state->get('discount_lid');
89 $currency = \Bitrix\Sale\Internals\SiteCurrencyTable::getSiteCurrency($lid);
90
91 $rows = $this->generateRows($state);
92 $templateRow = $this->generateRow(-1, array(), 'display: none');
93
94 return $this->generateJavascript() . '
95 <table width="100%" border="0" cellspacing="7" cellpadding="0">
96 <tbody>
97 <tr>
98 <td class="adm-detail-content-cell-l" style="width:25%;"><strong>' . Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_LABEL_RANGES') . ':</strong></td>
99 <td class="adm-detail-content-cell-r" style="width:75%;">
100 <table id="range_table" style="width: auto;" class="internal" border="0" cellspacing="7" cellpadding="0">
101 <tbody>
102 <tr class="heading">
103 <td align="center">' . Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_H_SUM', array('#CURRENCY#' => $currency,)) . '</td>
104 <td align="center">' . Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_H_DISCOUNT_VALUE') . '</td>
105 </tr>
106 ' . $rows . '
107 ' . $templateRow . '
108 </table>
109 <div style="width: 100%; text-align: left; margin-top: 10px;">
110 <input id="clone_range" class="adm-btn" type="button" value="' . Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_DISCOUNT_ADD_RANGE') . '">
111 </div>
112 </td>
113 </tr>
114
115 <tr>
116 <td class="adm-detail-content-cell-l" style="width:25%;"><strong>' . Loc::getMessage('SALE_BASE_PRESET_ACTIVE_PERIOD') . ':</strong></td>
117 <td class="adm-detail-content-cell-r">' .
118 HtmlHelper::generateSelect(
119 'discount_type_sum_period',
120 array(
121 self::TYPE_COUNT_PERIOD_ALL_TIME => Loc::getMessage("SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_TYPE_COUNT_PERIOD_ALL_TIME"),
122 self::TYPE_COUNT_PERIOD_INTERVAL => Loc::getMessage("SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_TYPE_COUNT_PERIOD_INTERVAL"),
123 self::TYPE_COUNT_PERIOD_RELATIVE => Loc::getMessage("SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_TYPE_COUNT_PERIOD_RELATIVE"),
124 ),
125 $state['discount_type_sum_period']
126 ) . '
127 </td>
128 </tr>
129
130 <tr id="tr_interval_start" class="js-date-interval" style="display: none;">
131 <td class="adm-detail-content-cell-l" width="25%">' . Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_PERIOD_START') . ':</td>
132 <td class="adm-detail-content-cell-r" width="75%">' . \CAdminCalendar::CalendarDate('discount_sum_order_start', $state['discount_sum_order_start'], 19, true) . '</td>
133 </tr>
134 <tr id="tr_interval_end" class="js-date-interval" style="display: none;">
135 <td class="adm-detail-content-cell-l" width="25%">' . Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_PERIOD_END') . ':</td>
136 <td class="adm-detail-content-cell-r" width="75%">' . \CAdminCalendar::CalendarDate('discount_sum_order_end', $state['discount_sum_order_end'], 19, true) . '</td>
137 </tr>
138 <tr id="tr_relative" class="js-date-relative" style="display: none;">
139 <td class="adm-detail-content-cell-l" width="25%">' . Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_PERIOD_RELATIVE') . ':</td>
140 <td class="adm-detail-content-cell-r" width="75%"><input type="text" name="discount_sum_period_value" id="discount_sum_period_value" value="' . $state['discount_sum_period_value'] . '" size="7" maxlength="10">&nbsp;' .
141 HtmlHelper::generateSelect('discount_sum_period_type',
142 array(
143 'Y' => Loc::getMessage("SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_PERIOD_RELATIVE_Y"),
144 'M' => Loc::getMessage("SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_PERIOD_RELATIVE_M"),
145 'D' => Loc::getMessage("SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_PERIOD_RELATIVE_D"),
146 ),
147 $state['discount_sum_period_type']
148 ) . '
149 </td>
150 </tr>
151 <tr>
152 <td class="adm-detail-content-cell-l" style="width:25%;"><strong>' . Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_DONT_APPLY_IF_THERE_IS_DISCOUNTS') . ':</strong></td>
153 <td class="adm-detail-content-cell-r">
154 <input type="checkbox" name="discount_skip_if_there_were_discounts" value="Y" ' . ($state->get('discount_skip_if_there_were_discounts', 'N') == 'Y'? 'checked' : '') . '>
155 </td>
156 </tr>
157 <tr>
158 <td class="adm-detail-content-cell-l" style="width:25%;"><strong>' . Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_APPLY_IF_MORE_PROFITABLE_DISCOUNT') . ':</strong></td>
159 <td class="adm-detail-content-cell-r">
160 <input type="checkbox" name="discount_apply_if_more_profitable" value="Y" ' . ($state->get('discount_apply_if_more_profitable', 'N') == 'Y'? 'checked' : '') . '>
161 </td>
162 </tr>
163
164 </tbody>
165 </table>
166 <script>
167 BX.ready(function(){
168 BX.bind(BX("discount_type_sum_period"), "change", function(){
169 var table = BX("count_period_table");
170 var trIntervalStart = BX("tr_interval_start");
171 var trIntervalEnd = BX("tr_interval_end");
172 var trRelative = BX("tr_relative");
173
174 switch(this.value)
175 {
176 case "' . self::TYPE_COUNT_PERIOD_ALL_TIME . '":
177 BX.hide(trIntervalStart, "table-row");
178 BX.hide(trIntervalEnd, "table-row");
179 BX.hide(trRelative, "table-row");
180
181 break;
182 case "' . self::TYPE_COUNT_PERIOD_INTERVAL . '":
183 BX.hide(trRelative, "table-row");
184 BX.show(trIntervalStart, "table-row");
185 BX.show(trIntervalEnd, "table-row");
186
187 break;
188 case "' . self::TYPE_COUNT_PERIOD_RELATIVE . '":
189 BX.show(trRelative, "table-row");
190 BX.hide(trIntervalStart, "table-row");
191 BX.hide(trIntervalEnd, "table-row");
192
193 break;
194 }
195 });
196 setTimeout(function(){
197 BX.fireEvent(BX("discount_type_sum_period"), "change");
198 }, 300);
199 })
200 </script>
201 ';
202 }
203
204 protected function generateJavascript()
205 {
206 return '
207 <script>
208 BX.ready(function(){
209 BX.bind(BX("clone_range"), "click", function(){
210 var row = BX("range_-1").cloneNode(true);
211 row.id = "";
212 BX.insertAfter(row, BX.lastChild(BX("range_table")));
213 row.style.display = "";
214 });
215 });
216 </script>
217 ';
218 }
219
220 protected function generateRows(State $state)
221 {
222 $html = '';
223 foreach ($state->get('discount_ranges', $this->getDefaultRowValues()) as $i => $range)
224 {
225 if (
226 ($range['sum'] === '' || $range['sum'] === null) ||
227 empty($range['value']) ||
228 empty($range['type'])
229 )
230 {
231 continue;
232 }
233
234 $html .= $this->generateRow($i, $range);
235 }
236
237 return $html;
238 }
239
240 private function fillValueInsteadMarkedEmpty(&$value)
241 {
242 if ($value === self::MARK_DEFAULT_EMPTY_ROW)
243 {
244 $value = '';
245 }
246 }
247
248 protected function generateRow($index, array $range, $style = '')
249 {
250 $sum = $range['sum'] ?? '';
251 $value = $range['value'] ?? '';
252 $type = $range['type'] ?? '';
253
254 $this->fillValueInsteadMarkedEmpty($sum);
255 $this->fillValueInsteadMarkedEmpty($value);
256 $this->fillValueInsteadMarkedEmpty($type);
257
258 return '
259 <tr id="range_' . $index . '" style="' . $style . '">
260 <td>
261 ' . Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_DISCOUNT_SUM_GREATER_THAN') . '
262 <input type="text" name="range_sum[]" size="13" value="' . $sum . '">
263 </td>
264 <td>
265 <input type="text" name="range_value[]" size="13" value="' . $value . '">
266 ' . $this->generateSelectWithDiscountType("range_type[]", $type) . '
267 </td>
268 </tr>
269 ';
270 }
271
272 protected function generateSelectWithDiscountType($name, $selectedValue)
273 {
274 return HtmlHelper::generateSelect(
275 $name,
276 array(
277 self::TYPE_PERCENT => Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_DISCOUNT_TYPE_PERCENT'),
278 self::TYPE_FIXED => Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_DISCOUNT_TYPE_VALUE'),
279 ),
280 $selectedValue
281 );
282 }
283
284 protected function buildRangesFromState(State $state)
285 {
287 $rangeSum = $state->get('range_sum', array());
289 $rangeValue = $state->get('range_value', array());
291 $rangeType = $state->get('range_type', array());
292
293 $matrix = array();
294 foreach ($rangeSum as $i => $item)
295 {
296 if (empty($rangeValue[$i]) && empty($rangeSum[$i]))
297 {
298 continue;
299 }
300
301 if (!isset($rangeValue[$i]))
302 {
303 $this->errorCollection[] = new Error(Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_ERROR_BAD_RANGE'));
304
305 return null;
306 }
307
308 $rangeValue[$i] = str_replace(",", ".", $rangeValue[$i]);
309 $rangeValue[$i] = doubleval($rangeValue[$i]);
310
311 if ($rangeValue[$i] <= 0)
312 {
313 $this->errorCollection[] = new Error(Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_ERROR_RANGE_VALUE'));
314
315 return null;
316 }
317
318 if ($rangeType[$i] === self::TYPE_PERCENT && $rangeValue[$i] > 100)
319 {
320 $this->errorCollection[] = new Error(Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_ERROR_RANGE_VALUE'));
321
322 return null;
323 }
324
325 $matrix[] = array(
326 'sum' => $rangeSum[$i],
327 'value' => $rangeValue[$i],
328 'type' => $rangeType[$i]?: self::TYPE_PERCENT,
329 );
330 }
331
332 if (!$matrix)
333 {
334 return null;
335 }
336
337 Main\Type\Collection::sortByColumn($matrix, 'sum');
338
339 $prevSum = null;
340 foreach ($matrix as $row)
341 {
342 if ($row['sum'] === $prevSum)
343 {
344 $this->errorCollection[] = new Error(Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_CUMULATIVE_ERROR_RANGE_FROM_DUPLICATE'));
345
346 return null;
347 }
348 }
349
350 return $matrix;
351 }
352
353 public function processSaveInputRanges(State $state)
354 {
355 $ranges = $this->buildRangesFromState($state);
356 $state['discount_ranges'] = $ranges;
357
358 if ($ranges)
359 {
360 unset($state['range_sum']);
361 unset($state['range_value']);
362 unset($state['range_type']);
363 }
364
365 switch ($state['discount_type_sum_period'])
366 {
367 case self::TYPE_COUNT_PERIOD_ALL_TIME:
368 unset($state['discount_sum_order_start']);
369 unset($state['discount_sum_order_end']);
370 unset($state['discount_sum_period_value']);
371 unset($state['discount_sum_period_type']);
372
373 break;
374 case self::TYPE_COUNT_PERIOD_INTERVAL:
375 unset($state['discount_sum_period_value']);
376 unset($state['discount_sum_period_type']);
377
378 break;
379 case self::TYPE_COUNT_PERIOD_RELATIVE:
380 unset($state['discount_sum_order_start']);
381 unset($state['discount_sum_order_end']);
382
383 break;
384 }
385
386 if($state['discount_skip_if_there_were_discounts'] !== 'Y' || !$this->request->getPost('discount_skip_if_there_were_discounts'))
387 {
388 $state['discount_skip_if_there_were_discounts'] = 'N';
389 }
390
391 if($state['discount_apply_if_more_profitable'] !== 'Y' || !$this->request->getPost('discount_apply_if_more_profitable'))
392 {
393 $state['discount_apply_if_more_profitable'] = 'N';
394 }
395
396 if(!$ranges)
397 {
398 $this->errorCollection[] = new Error(Loc::getMessage('SALE_HANDLERS_DISCOUNTPRESET_ERROR_EMPTY_VALUE'));
399 }
400
401 if(!$this->errorCollection->isEmpty())
402 {
403 return array($state, 'InputRanges');
404 }
405
406 return array($state, 'CommonSettings');
407 }
408
409 protected function getAllowableUserGroups()
410 {
411 $allowableUserGroups = parent::getAllowableUserGroups();
412
413 unset($allowableUserGroups[2]); //There is nonsense to use cumulative discounts for guests.
414
415 return $allowableUserGroups;
416 }
417
418 public function processShowCommonSettings(State $state)
419 {
420 return $this->processShowCommonSettingsInternal($state);
421 }
422
423 public function processSaveCommonSettings(State $state)
424 {
425 return $this->processSaveCommonSettingsInternal($state);
426 }
427
428 public function generateState(array $discountFields)
429 {
430 $discountFields = $this->normalizeDiscountFields($discountFields);
431
432 $firstChildrenControlId = ArrayHelper::getByPath($discountFields, 'ACTIONS.CHILDREN.0.CHILDREN.0.CLASS_ID') === \CSaleActionCondCtrlBasketFields::CONTROL_ID_APPLIED_DISCOUNT;
433 $stateFields = array(
434 'discount_lid' => $discountFields['LID'],
435 'discount_name' => $discountFields['NAME'],
436 'discount_groups' => $this->getUserGroupsByDiscount($discountFields['ID']),
437 'discount_ranges' => ArrayHelper::getByPath($discountFields, 'ACTIONS.CHILDREN.0.DATA.ranges'),
438 'discount_type_sum_period' => ArrayHelper::getByPath($discountFields, 'ACTIONS.CHILDREN.0.DATA.type_sum_period'),
439 'discount_skip_if_there_were_discounts' => $firstChildrenControlId? 'Y' : '',
440 'discount_apply_if_more_profitable' => ArrayHelper::getByPath($discountFields, 'ACTIONS.CHILDREN.0.DATA.apply_if_more_profitable'),
441 'discount_sum_order_start' => '',
442 'discount_sum_order_end' => '',
443 'discount_sum_period_value' => '',
444 'discount_sum_period_type' => '',
445 );
446
447 $periodData = ArrayHelper::getByPath($discountFields, 'ACTIONS.CHILDREN.0.DATA.sum_period_data', array());
448 $stateFields = array_merge($stateFields, array_filter(array_intersect_key($periodData, array(
449 'discount_sum_order_start' => true,
450 'discount_sum_order_end' => true,
451 'discount_sum_period_value' => true,
452 'discount_sum_period_type' => true,
453 ))));
454
455 return parent::generateState($discountFields)->append($stateFields);
456 }
457
458 public function generateDiscount(State $state)
459 {
460 $periodData = array(
461 'discount_sum_order_start' => $state['discount_sum_order_start'],
462 'discount_sum_order_end' => $state['discount_sum_order_end'],
463 'discount_sum_period_value' => $state['discount_sum_period_value'],
464 'discount_sum_period_type' => $state['discount_sum_period_type'],
465 );
466
467 $filterChildren = array();
468 if ($state['discount_skip_if_there_were_discounts'] === 'Y')
469 {
470 $filterChildren = array(
471 array(
473 'DATA' => array(
474 'logic' => 'Equal',
475 'value' => 'N',
476 ),
477 )
478 );
479 }
480
481 return array_merge(parent::generateDiscount($state), array(
482 'CONDITIONS' => array(
483 'CLASS_ID' => 'CondGroup',
484 'DATA' => array(
485 'All' => 'AND',
486 'True' => 'True',
487 ),
488 'CHILDREN' => array(),
489 ),
490 'ACTIONS' => array(
491 'CLASS_ID' => 'CondGroup',
492 'DATA' => array(
493 'All' => 'AND',
494 ),
495 'CHILDREN' => array(
496 array(
497 'CLASS_ID' => \CSaleCumulativeAction::getControlID(),
498 'DATA' => array(
499 'ranges' => $state['discount_ranges'],
500 'type_sum_period' => $state['discount_type_sum_period'],
501 'sum_period_data' => $periodData,
502 'apply_if_more_profitable' => $state['discount_apply_if_more_profitable'],
503 'All' => 'AND',
504 'True' => 'True',
505 ),
506 'CHILDREN' => $filterChildren,
507 ),
508 ),
509 ),
510 ));
511 }
512
516 protected function getDefaultRowValues()
517 {
518 return array(
519 array(
520 'sum' => self::MARK_DEFAULT_EMPTY_ROW,
521 'value' => self::MARK_DEFAULT_EMPTY_ROW,
522 'type' => self::TYPE_PERCENT,
523 ),
524 array(
525 'sum' => self::MARK_DEFAULT_EMPTY_ROW,
526 'value' => self::MARK_DEFAULT_EMPTY_ROW,
527 'type' => self::TYPE_PERCENT,
528 ),
529 array(
530 'sum' => self::MARK_DEFAULT_EMPTY_ROW,
531 'value' => self::MARK_DEFAULT_EMPTY_ROW,
532 'type' => self::TYPE_PERCENT,
533 ),
534 array(
535 'sum' => self::MARK_DEFAULT_EMPTY_ROW,
536 'value' => self::MARK_DEFAULT_EMPTY_ROW,
537 'type' => self::TYPE_PERCENT,
538 ),
539 );
540 }
541}
$sum
Определения checkout.php:6
$type
Определения options.php:106
normalizeDiscountFields(array $discountFields)
Определения basepreset.php:608
processSaveCommonSettingsInternal(State $state, $nextStep=self::FINAL_STEP)
Определения basepreset.php:923
processSaveInputNameInternal(State $state, $nextStep)
Определения basepreset.php:811
processShowInputNameInternal(State $state)
Определения basepreset.php:789
processShowCommonSettingsInternal(State $state)
Определения basepreset.php:836
getUserGroupsByDiscount($discountId)
Определения basepreset.php:703
const CONTROL_ID_APPLIED_DISCOUNT
Определения sale_act.php:1521
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$name
Определения menu_edit.php:35
$value
Определения Param.php:39
trait Error
Определения error.php:11
$i
Определения factura.php:643
$currency
Определения template.php:266
$rows
Определения options.php:264