Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
sequentnumbergenerator.php
1<?
3
10
11Loc::loadMessages(__FILE__);
12
18{
19 const DAY = 'day';
20 const MONTH = 'month';
21 const YEAR = 'year';
22 const TEMPLATE_WORD_NUMBER = 'NUMBER';
23
24 const ERROR_SEQUENCE_NOT_SET = 'ERROR_SEQUENCE_NOT_SET';
25
26 protected $start;
27 protected $step;
28 protected $length = 0;
29 protected $padString = '0';
30 protected $periodicBy;
31 protected $timezone;
33
34 protected $nowTime;
35
37 protected $nextNumber;
38
40 protected $currentNumber;
41
43 protected $numeratorId;
44 protected $numberHash;
45
47 public function setConfig($config)
48 {
49 $this->setFromArrayOrDefault('timezone', $config);
50 $this->setFromArrayOrDefault('start', $config, 1, 'int');
51 $this->setFromArrayOrDefault('step', $config, 1, 'int');
52 $this->setFromArrayOrDefault('length', $config, 0, 'int');
53 $this->setFromArrayOrDefault('padString', $config, '0', 'string');
54 $this->setFromArrayOrDefault('isDirectNumeration', $config, false, 'bool');
55 $this->setFromArrayOrDefault('periodicBy', $config);
56 $this->setFromArrayOrDefault('nowTime', $config, time());
57 if (is_array($config) && isset($config['numeratorId']))
58 {
59 $this->lastInvocationTime = $config['lastInvocationTime'] ?? null;
60 $this->numeratorId = $config['numeratorId'];
61 if ($this->isDirectNumeration)
62 {
63 $this->numberHash = $this->numeratorId;
64 }
65 }
66 else
67 {
68 $this->nextNumber = $this->start;
69 }
70 }
71
73 public static function getSettingsFields()
74 {
75 $timezonesSettings = static::getTimezoneSettings();
76 foreach ($timezonesSettings as $index => $timezonesSetting)
77 {
78 $timezonesSettings[$index]['settingName'] = $timezonesSetting['name'];
79 unset($timezonesSettings[$index]['name']);
80 }
81 return [
82 [
83 'settingName' => 'length', 'type' => 'int', 'default' => 0,
84 'title' => Loc::getMessage('TITLE_BITRIX_MAIN_NUMERATOR_GENERATOR_SEQUENTNUMBERGENERATOR_LENGTH'),
85 ],
86 [
87 'settingName' => 'padString', 'type' => 'string', 'default' => '0',
88 'title' => Loc::getMessage('TITLE_BITRIX_MAIN_NUMERATOR_GENERATOR_SEQUENTNUMBERGENERATOR_PAD_STRING'),
89 ],
90 [
91 'settingName' => 'start', 'type' => 'int', 'default' => 1,
92 'title' => Loc::getMessage('TITLE_BITRIX_MAIN_NUMERATOR_GENERATOR_SEQUENTNUMBERGENERATOR_START'),
93 ],
94 [
95 'settingName' => 'step', 'type' => 'int', 'default' => 1,
96 'title' => Loc::getMessage('TITLE_BITRIX_MAIN_NUMERATOR_GENERATOR_SEQUENTNUMBERGENERATOR_STEP'),
97 ],
98 [
99 'settingName' => 'periodicBy', 'type' => 'array',
100 'title' => Loc::getMessage('TITLE_BITRIX_MAIN_NUMERATOR_GENERATOR_SEQUENTNUMBERGENERATOR_PERIODICBY'),
101 'values' => [
102 [
103 'settingName' => 'default', 'value' => '',
104 'title' => Loc::getMessage('TITLE_BITRIX_MAIN_NUMERATOR_GENERATOR_SEQUENTNUMBERGENERATOR_PERIODICBY_DEFAULT'),
105 ],
106 [
107 'settingName' => self::DAY, 'value' => self::DAY,
108 'title' => Loc::getMessage('TITLE_BITRIX_MAIN_NUMERATOR_GENERATOR_SEQUENTNUMBERGENERATOR_PERIODICBY_DAY'),
109 ],
110 [
111 'settingName' => self::MONTH, 'value' => self::MONTH,
112 'title' => Loc::getMessage('TITLE_BITRIX_MAIN_NUMERATOR_GENERATOR_SEQUENTNUMBERGENERATOR_PERIODICBY_MONTH'),
113 ],
114 [
115 'settingName' => self::YEAR, 'value' => self::YEAR,
116 'title' => Loc::getMessage('TITLE_BITRIX_MAIN_NUMERATOR_GENERATOR_SEQUENTNUMBERGENERATOR_PERIODICBY_YEAR'),
117 ],
118 ],
119 ],
120 [
121 'settingName' => 'timezone', 'type' => 'array', 'values' => $timezonesSettings,
122 'title' => Loc::getMessage('TITLE_BITRIX_MAIN_NUMERATOR_GENERATOR_SEQUENTNUMBERGENERATOR_TIMEZONE'),
123 ],
124 [
125 'settingName' => 'isDirectNumeration', 'type' => 'boolean',
126 'title' => Loc::getMessage('TITLE_BITRIX_MAIN_NUMERATOR_GENERATOR_SEQUENTNUMBERGENERATOR_ISDIRECTNUMERATION'),
127 ],
128 ];
129 }
130
132 public static function getTemplateWordsSettings()
133 {
134 return [
135 static::getPatternFor(static::TEMPLATE_WORD_NUMBER) =>
136 Loc::getMessage('BITRIX_MAIN_NUMERATOR_GENERATOR_SEQUENTNUMBERGENERATOR_WORD_NUMBER'),
137 ];
138 }
139
141 public function getConfig()
142 {
143 return [
144 'start' => $this->start,
145 'step' => $this->step,
146 'length' => $this->length,
147 'padString' => $this->padString,
148 'periodicBy' => $this->periodicBy,
149 'timezone' => $this->timezone,
150 'isDirectNumeration' => (bool)$this->isDirectNumeration,
151 ];
152 }
153
163 protected function getSettings($numeratorId = null, $createIfEmpty = true)
164 {
165 if ($numeratorId === null)
166 {
168 }
169 $nextNumberSettings = NumeratorSequenceTable::getSettings($numeratorId, $this->getNumberHash());
170 if (!$nextNumberSettings && $createIfEmpty)
171 {
172 $nextNumberSettings = NumeratorSequenceTable::setSettings($numeratorId, $this->getNumberHash(), $this->start, $this->nowTime);
173 }
174 return $nextNumberSettings;
175 }
176
180 private function getNumberHash()
181 {
182 if ($this->numberHash === null)
183 {
184 $this->setNumberHash($this->numeratorId);
185 }
186 return $this->numberHash;
187 }
188
190 public function parseTemplate($template)
191 {
192 for ($tryouts = 0; $tryouts < 50; $tryouts++)
193 {
194 $this->nextNumber = null;
195 $this->currentNumber = null;
196 $nextNumberSettings = $this->getSettings();
197 if (!$nextNumberSettings)
198 {
199 continue;
200 }
201 $this->lastInvocationTime = $nextNumberSettings['LAST_INVOCATION_TIME'];
202 $this->calculateNextAndCurrentNumber($nextNumberSettings['NEXT_NUMBER']);
203 $this->lastInvocationTime = $this->nowTime;
204 $affectedRows = $this->saveNumeratorSequenceSettings(
205 $this->numeratorId,
206 $this->getNumberHash(),
207 [
208 'NEXT_NUMBER' => $this->nextNumber,
209 'LAST_INVOCATION_TIME' => $this->lastInvocationTime,
210 ],
211 $nextNumberSettings['NEXT_NUMBER']
212 );
213 if ($affectedRows == 1)
214 {
215 break;
216 }
217 }
218
219 return $this->replaceNumberInPattern($template);
220 }
221
222 protected function saveNumeratorSequenceSettings($numeratorId, $numberHash, $fields, $whereNextNumber = null)
223 {
224 return NumeratorSequenceTable::updateSettings($numeratorId, $numberHash, $fields, $whereNextNumber);
225 }
226
228 public static function getTemplateWordsForParse()
229 {
230 return [static::getPatternFor(static::TEMPLATE_WORD_NUMBER)];
231 }
232
234 public function parseTemplateForPreview($template)
235 {
236 $nextNumberSettings = $this->getSettings($this->numeratorId, false);
237 $this->lastInvocationTime = $nextNumberSettings['LAST_INVOCATION_TIME'] ?? $this->nowTime;
238 $this->calculateNextAndCurrentNumber($nextNumberSettings['NEXT_NUMBER'] ?? $this->start);
239 return $this->replaceNumberInPattern($template);
240 }
241
242 private function replaceNumberInPattern($template)
243 {
244 $resultNumber = $this->currentNumber;
245 if ($this->length > 0)
246 {
247 $resultNumber = \Bitrix\Main\Text\UtfSafeString::pad($resultNumber, $this->length, $this->padString, STR_PAD_LEFT);
248 }
249 return str_replace(static::getPatternFor(static::TEMPLATE_WORD_NUMBER), $resultNumber, $template);
250 }
251
254 {
255 if (!$numeratorId)
256 {
257 return null;
258 }
259 $this->numeratorId = $numeratorId;
260 $nextNumberSettings = $this->getSettings($numeratorId, false);
261 if ($nextNumberSettings)
262 {
263 return $nextNumberSettings['NEXT_NUMBER'];
264 }
265 else
266 {
267 return $this->start;
268 }
269 }
270
274 public static function getAvailableForType()
275 {
276 return 'DEFAULT';
277 }
278
279 /*** @inheritdoc */
280 public function setNextNumber($numeratorId, $newNumber, $whereNumber)
281 {
282 $this->nextNumber = $newNumber;
283 $sequence = $this->getSettings($numeratorId, false);
284 if (!$sequence)
285 {
286 return (new Result())->addError(new Error(Loc::getMessage('NUMERATOR_UPDATE_SEQUENT_IS_NOT_SET_YET')));
287 }
288 $affectedRows = $this->saveNumeratorSequenceSettings(
290 $this->getNumberHash(),
291 [
292 'NEXT_NUMBER' => $this->nextNumber,
293 ],
294 $whereNumber
295 );
296 if ($affectedRows == 1)
297 {
298 return new Result();
299 }
300 return (new Result())->addError(new Error(Loc::getMessage('NUMERATOR_SEQUENT_DEFAULT_INTERNAL_ERROR')));
301 }
302
306 private function resetCurrentNumberIfNeeded()
307 {
308 if ($this->periodicBy)
309 {
310 if ($this->periodicBy == static::YEAR && $this->isHasChanged(static::YEAR))
311 {
312 $this->currentNumber = $this->start;
313 }
314 if ($this->periodicBy == static::MONTH)
315 {
316 if ($this->isHasChanged(static::MONTH) || $this->isSameMonthButDifferentYear())
317 {
318 $this->currentNumber = $this->start;
319 }
320 }
321 if ($this->periodicBy == static::DAY)
322 {
323 if ($this->isHasChanged(static::DAY)
324 || $this->isSameDayButDifferent(static::MONTH)
325 || $this->isSameDayButDifferent(static::YEAR))
326 {
327 $this->currentNumber = $this->start;
328 }
329 }
330 }
331 }
332
336 private function isSameMonthButDifferentYear()
337 {
338 return $this->getLastInvocationUserTime()->format('m') === $this->getNowUserTime()->format('m') && $this->isHasChanged(static::YEAR);
339 }
340
345 private function isSameDayButDifferent($interval)
346 {
347 $isSameDay = $this->getLastInvocationUserTime()->format('d') === $this->getNowUserTime()->format('d');
348 if ($interval == static::MONTH)
349 {
350 return $isSameDay && $this->isHasChanged(static::MONTH);
351 }
352 if ($interval == static::YEAR)
353 {
354 return $isSameDay && $this->isHasChanged(static::YEAR);
355 }
356 return false;
357 }
358
363 private function isHasChanged($interval)
364 {
365 if ($interval == static::MONTH)
366 {
367 return $this->getLastInvocationUserTime()->format('m') !== $this->getNowUserTime()->format('m');
368 }
369 if ($interval == static::DAY)
370 {
371 return $this->getLastInvocationUserTime()->format('d') !== $this->getNowUserTime()->format('d');
372 }
373 if ($interval == static::YEAR)
374 {
375 return $this->getLastInvocationUserTime()->format('Y') !== $this->getNowUserTime()->format('Y');
376 }
377 return false;
378 }
379
383 private static function getTimezoneSettings()
384 {
385 $timezones = \CTimeZone::GetZones();
386 $settings = [];
387 foreach ($timezones as $timezoneValue => $timezoneName)
388 {
389 $settings[] = ['name' => $timezoneName, 'value' => $timezoneValue,];
390 }
391 return $settings;
392 }
393
394 private function getNowUserTime()
395 {
396 return $this->createDateTimeInCurrentTimezone($this->nowTime);
397 }
398
399 private function getLastInvocationUserTime()
400 {
401 return $this->createDateTimeInCurrentTimezone($this->lastInvocationTime);
402 }
403
404 private function createDateTimeInCurrentTimezone($timestamp)
405 {
406 $dateTime = \DateTime::createFromFormat('U', $timestamp);
407 if ($this->timezone)
408 {
409 $result = $dateTime->setTimezone(new \DateTimeZone($this->timezone));
410 if ($result === false)
411 {
412 $dateTime = \DateTime::createFromFormat('U', $timestamp);
413 }
414 }
415 return $dateTime;
416 }
417
419 public function validateConfig($config)
420 {
421 $result = new Result();
422 return $result;
423 }
424
426 public function setNumberHash($numberHash)
427 {
428 if (!is_string($numberHash) && !is_int($numberHash))
429 {
430 return;
431 }
432 if ($this->numberHash === null)
433 {
434 $this->numberHash = (string)$numberHash;
435 }
436 }
437
438 private function calculateNextAndCurrentNumber($initNumber)
439 {
440 $this->currentNumber = $initNumber;
441 $this->resetCurrentNumberIfNeeded();
442 $this->nextNumber = $this->currentNumber + $this->step;
443 }
444}
static loadMessages($file)
Definition loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
setFromArrayOrDefault($value, $config, $default=null, $type=null)
saveNumeratorSequenceSettings($numeratorId, $numberHash, $fields, $whereNextNumber=null)
static setSettings($numeratorId, $numberHash, $defaultNumber, $lastInvocationTime)
static updateSettings($numeratorId, $numberHash, $fields, $whereNextNumber=null)