Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
cashboxatolfarm.php
1<?php
2
3namespace Bitrix\Sale\Cashbox;
4
10
11Localization\Loc::loadMessages(__FILE__);
12
18{
19 const OPERATION_CHECK_REGISTRY = 'registry';
20 const OPERATION_CHECK_CHECK = 'check';
21 const OPERATION_GET_TOKEN = 'get_token';
22
23 const REQUEST_TYPE_GET = 'get';
24 const REQUEST_TYPE_POST = 'post';
25
26 const TOKEN_OPTION_NAME = 'atol_access_token';
27
28 const SERVICE_URL = 'https://online.atol.ru/possystem/v3';
29
32
33 const CODE_VAT_0 = 'vat0';
34 const CODE_VAT_10 = 'vat10';
35 const CODE_VAT_20 = 'vat20';
36
37 protected const MAX_NAME_LENGTH = 128;
38
43 public function buildCheckQuery(Check $check)
44 {
45 $data = $check->getDataForCheck();
46
48 $dateTime = $data['date_create'];
49
50 $result = array(
51 'timestamp' => $dateTime->format('d.m.Y H:i:s'),
52 'external_id' => static::buildUuid(static::UUID_TYPE_CHECK, $data['unique_id']),
53 'service' => array(
54 'inn' => $this->getValueFromSettings('SERVICE', 'INN'),
55 'callback_url' => $this->getCallbackUrl(),
56 'payment_address' => $this->getValueFromSettings('SERVICE', 'P_ADDRESS'),
57 ),
58 'receipt' => array(
59 'attributes' => array(
60 'sno' => $this->getValueFromSettings('TAX', 'SNO')
61 ),
62 'payments' => array(),
63 'items' => array(),
64 'total' => (float)$data['total_sum']
65 )
66 );
67
68 $phone = \NormalizePhone($data['client_phone']);
69 if (is_string($phone))
70 {
71 if ($phone[0] === '7')
72 $phone = mb_substr($phone, 1);
73 }
74 else
75 {
76 $phone = '';
77 }
78
79 $email = $data['client_email'] ?: '';
80
81 $clientInfo = $this->getValueFromSettings('CLIENT', 'INFO');
82 if ($clientInfo === 'PHONE')
83 {
84 $result['receipt']['attributes'] = ['phone' => $phone];
85 }
86 elseif ($clientInfo === 'EMAIL')
87 {
88 $result['receipt']['attributes'] = ['email' => $email];
89 }
90 else
91 {
92 $result['receipt']['attributes'] = [
93 'email' => $email,
94 'phone' => $phone,
95 ];
96 }
97
98 foreach ($data['payments'] as $payment)
99 {
100 $result['receipt']['payments'][] = array(
101 'type' => (int)$this->getValueFromSettings('PAYMENT_TYPE', $payment['type']),
102 'sum' => (float)$payment['sum']
103 );
104 }
105
106 foreach ($data['items'] as $i => $item)
107 {
108 $vat = $this->getValueFromSettings('VAT', $item['vat']);
109
110 $result['receipt']['items'][] = array(
111 'name' => mb_substr($item['name'], 0, self::MAX_NAME_LENGTH),
112 'price' => (float)$item['price'],
113 'sum' => (float)$item['sum'],
114 'quantity' => $item['quantity'],
115 'tax' => ($vat !== null) ? $vat : $this->getValueFromSettings('VAT', 'NOT_VAT')
116 );
117 }
118
119 return $result;
120 }
121
125 protected function getCallbackUrl()
126 {
127 $context = Main\Application::getInstance()->getContext();
128 $scheme = $context->getRequest()->isHttps() ? 'https' : 'http';
129 $server = $context->getServer();
130 $domain = $server->getServerName();
131
132 if (preg_match('/^(?<domain>.+):(?<port>\d+)$/', $domain, $matches))
133 {
134 $domain = $matches['domain'];
135 $port = $matches['port'];
136 }
137 else
138 {
139 $port = $server->getServerPort();
140 }
141 $port = in_array($port, array(80, 443)) ? '' : ':'.$port;
142
143 return sprintf('%s://%s%s/bitrix/tools/sale_farm_check_print.php', $scheme, $domain, $port);
144 }
145
149 public static function getName()
150 {
151 return Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_TITLE');
152 }
153
158 protected static function extractCheckData(array $data)
159 {
160 $result = array();
161
162 if (!$data['uuid'])
163 {
164 return $result;
165 }
166
167 $checkInfo = CheckManager::getCheckInfoByExternalUuid($data['uuid']);
168 if (empty($checkInfo))
169 {
170 return $result;
171 }
172
173 if ($data['error'])
174 {
175 $errorType = static::getErrorType($data['error']['code']);
176
177 $result['ERROR'] = array(
178 'CODE' => $data['error']['code'],
179 'MESSAGE' => $data['error']['text'],
180 'TYPE' => ($errorType === Errors\Error::TYPE) ? Errors\Error::TYPE : Errors\Warning::TYPE
181 );
182 }
183
184 $result['ID'] = $checkInfo['ID'];
185 $result['CHECK_TYPE'] = $checkInfo['TYPE'];
186
187 $check = CheckManager::getObjectById($checkInfo['ID']);
188 $dateTime = new Main\Type\DateTime($data['payload']['receipt_datetime'], 'd.m.Y H:i:s');
189 $result['LINK_PARAMS'] = array(
190 Check::PARAM_REG_NUMBER_KKT => $data['payload']['ecr_registration_number'],
191 Check::PARAM_FISCAL_DOC_ATTR => $data['payload']['fiscal_document_attribute'],
192 Check::PARAM_FISCAL_DOC_NUMBER => $data['payload']['fiscal_document_number'],
193 Check::PARAM_FISCAL_RECEIPT_NUMBER => $data['payload']['fiscal_receipt_number'],
194 Check::PARAM_FN_NUMBER => $data['payload']['fn_number'],
195 Check::PARAM_SHIFT_NUMBER => $data['payload']['shift_number'],
196 Check::PARAM_DOC_SUM => $data['payload']['total'],
197 Check::PARAM_DOC_TIME => $dateTime->getTimestamp(),
198 Check::PARAM_CALCULATION_ATTR => $check::getCalculatedSign()
199 );
200
201 return $result;
202 }
203
208 public function buildZReportQuery($id)
209 {
210 return array();
211 }
212
217 protected static function extractZReportData(array $data)
218 {
219 return array();
220 }
221
225 protected function getCheckTypeMap()
226 {
227 return array(
228 SellCheck::getType() => 'sell',
229 SellReturnCashCheck::getType() => 'sell_refund',
230 SellReturnCheck::getType() => 'sell_refund'
231 );
232 }
233
241 protected function getRequestUrl($operation, $token, array $queryData = array())
242 {
243 $groupCode = $this->getField('NUMBER_KKM');
244
245 if ($operation === static::OPERATION_CHECK_REGISTRY)
246 {
247 return static::SERVICE_URL.'/'.$groupCode.'/'.$queryData['CHECK_TYPE'].'?tokenid='.$token;
248 }
249 elseif ($operation === static::OPERATION_CHECK_CHECK)
250 {
251 return static::SERVICE_URL.'/'.$groupCode.'/report/'.$queryData['EXTERNAL_UUID'].'?tokenid='.$token;
252 }
253 elseif ($operation === static::OPERATION_GET_TOKEN)
254 {
255 return static::SERVICE_URL.'/getToken';
256 }
257
258 throw new Main\SystemException();
259 }
260
266 public function printImmediately(Check $check)
267 {
268 $checkQuery = $this->buildCheckQuery($check);
269 $validateResult = $this->validateCheckQuery($checkQuery);
270 if (!$validateResult->isSuccess())
271 {
272 return $validateResult;
273 }
274
275 $operation = 'sell';
276 if ($check::getCalculatedSign() === Check::CALCULATED_SIGN_CONSUMPTION)
277 {
278 $operation = 'sell_refund';
279 }
280
281 return $this->registerCheck($operation, $checkQuery);
282 }
283
290 protected function registerCheck($operation, array $check)
291 {
292 $printResult = new Result();
293
294 $token = $this->getAccessToken();
295 if ($token === '')
296 {
297 $token = $this->requestAccessToken();
298 if ($token === '')
299 {
300 $printResult->addError(new Main\Error(Localization\Loc::getMessage('SALE_CASHBOX_ATOL_REQUEST_TOKEN_ERROR')));
301 return $printResult;
302 }
303 }
304
305 $url = $this->getRequestUrl(static::OPERATION_CHECK_REGISTRY, $token, ['CHECK_TYPE' => $operation]);
306
307 $result = $this->send(static::REQUEST_TYPE_POST, $url, $check);
308 if (!$result->isSuccess())
309 {
310 return $result;
311 }
312
313 $response = $result->getData();
314 if ($response['http_code'] === static::RESPONSE_HTTP_CODE_401)
315 {
316 $token = $this->requestAccessToken();
317 if ($token === '')
318 {
319 $printResult->addError(new Main\Error(Localization\Loc::getMessage('SALE_CASHBOX_ATOL_REQUEST_TOKEN_ERROR')));
320 return $printResult;
321 }
322
323 $url = $this->getRequestUrl(static::OPERATION_CHECK_REGISTRY, $token, array('CHECK_TYPE' => $operation));
324 $result = $this->send(static::REQUEST_TYPE_POST, $url, $check);
325 if (!$result->isSuccess())
326 {
327 return $result;
328 }
329
330 $response = $result->getData();
331 }
332
333 if ($response['http_code'] === static::RESPONSE_HTTP_CODE_200)
334 {
335 if ($response['uuid'])
336 {
337 $printResult->setData(array('UUID' => $response['uuid']));
338 }
339 else
340 {
341 $printResult->addError(new Main\Error(Localization\Loc::getMessage('SALE_CASHBOX_ATOL_CHECK_REG_ERROR')));
342 }
343 }
344 else
345 {
346 if (isset($response['error']['text']))
347 {
348 $printResult->addError(new Main\Error($response['error']['text']));
349 }
350 else
351 {
352 $printResult->addError(new Main\Error(Localization\Loc::getMessage('SALE_CASHBOX_ATOL_CHECK_REG_ERROR')));
353 }
354 }
355
356 return $printResult;
357 }
358
363 public function check(Check $check)
364 {
365 return $this->checkByUuid(
366 $check->getField('EXTERNAL_UUID')
367 );
368 }
369
370 protected function checkByUuid($uuid)
371 {
372 $url = $this->getRequestUrl(
373 static::OPERATION_CHECK_CHECK,
374 $this->getAccessToken(),
375 ['EXTERNAL_UUID' => $uuid]
376 );
377
378 $result = $this->send(static::REQUEST_TYPE_GET, $url);
379 if (!$result->isSuccess())
380 {
381 return $result;
382 }
383
384 $response = $result->getData();
385 if ($response['http_code'] === static::RESPONSE_HTTP_CODE_401)
386 {
387 $token = $this->requestAccessToken();
388 if ($token === '')
389 {
390 $result->addError(new Main\Error(Localization\Loc::getMessage('SALE_CASHBOX_ATOL_REQUEST_TOKEN_ERROR')));
391 return $result;
392 }
393
394 $url = $this->getRequestUrl(
395 static::OPERATION_CHECK_CHECK,
396 $this->getAccessToken(),
397 ['EXTERNAL_UUID' => $uuid]
398 );
399
400 $result = $this->send(static::REQUEST_TYPE_GET, $url);
401 if (!$result->isSuccess())
402 {
403 return $result;
404 }
405
406 $response = $result->getData();
407 }
408
409 $response['uuid'] = $uuid;
410
411 if ($response['status'] === 'wait')
412 {
413 $result->addError(new Main\Error(Localization\Loc::getMessage('SALE_CASHBOX_ATOL_REQUEST_STATUS_WAIT')));
414 return $result;
415 }
416
417 return static::applyCheckResult($response);
418 }
419
424 protected function validateCheckQuery(array $checkData)
425 {
426 $result = new Result();
427
428 if ($checkData['receipt']['attributes']['email'] === '' && $checkData['receipt']['attributes']['phone'] === '')
429 {
430 $result->addError(new Main\Error(Localization\Loc::getMessage('SALE_CASHBOX_ATOL_ERR_EMPTY_PHONE_EMAIL')));
431 }
432
433 foreach ($checkData['receipt']['items'] as $item)
434 {
435 if ($item['tax'] === null)
436 {
437 $result->addError(new Main\Error(Localization\Loc::getMessage('SALE_CASHBOX_ATOL_ERR_EMPTY_TAX')));
438 break;
439 }
440 }
441
442 return $result;
443 }
444
451 private function send($method, $url, array $data = array())
452 {
453 $result = new Result();
454
455 $http = new Main\Web\HttpClient();
456 $http->setHeader('Content-Type', 'application/json; charset=utf-8');
457
458 if ($method === static::REQUEST_TYPE_POST)
459 {
460 $http->disableSslVerification();
461 $data = $this->encode($data);
462
464
465 $response = $http->post($url, $data);
466 }
467 else
468 {
469 $response = $http->get($url);
470 }
471
472 if ($response !== false)
473 {
474 Logger::addDebugInfo($response);
475
476 try
477 {
478 $response = $this->decode($response);
479 if (!is_array($response))
480 {
481 $response = [];
482 }
483
484 $response['http_code'] = $http->getStatus();
485 $result->addData($response);
486 }
487 catch (Main\ArgumentException $e)
488 {
489 $result->addError(new Main\Error($e->getMessage()));
490 }
491 }
492 else
493 {
494 $error = $http->getError();
495 foreach ($error as $code =>$message)
496 {
497 $result->addError(new Main\Error($message, $code));
498 }
499 }
500
501 return $result;
502 }
503
508 public static function getSettings($modelId = 0)
509 {
510 $settings = array(
511 'AUTH' => array(
512 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_AUTH'),
513 'REQUIRED' => 'Y',
514 'ITEMS' => array(
515 'LOGIN' => array(
516 'TYPE' => 'STRING',
517 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_AUTH_LOGIN_LABEL')
518 ),
519 'PASS' => array(
520 'TYPE' => 'STRING',
521 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_AUTH_PASS_LABEL')
522 ),
523 )
524 ),
525 'SERVICE' => array(
526 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_SERVICE'),
527 'REQUIRED' => 'Y',
528 'ITEMS' => array(
529 'INN' => array(
530 'TYPE' => 'STRING',
531 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_SERVICE_INN_LABEL')
532 ),
533 'P_ADDRESS' => array(
534 'TYPE' => 'STRING',
535 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_SERVICE_URL_LABEL')
536 ),
537 )
538 ),
539 'CLIENT' => [
540 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_CLIENT'),
541 'ITEMS' => array(
542 'INFO' => array(
543 'TYPE' => 'ENUM',
544 'VALUE' => 'NONE',
545 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_CLIENT_INFO'),
546 'OPTIONS' => array(
547 'NONE' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_CLIENT_NONE'),
548 'PHONE' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_CLIENT_PHONE'),
549 'EMAIL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_CLIENT_EMAIL'),
550 )
551 ),
552 )
553 ]
554 );
555
556 $settings['PAYMENT_TYPE'] = array(
557 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_P_TYPE'),
558 'REQUIRED' => 'Y',
559 'ITEMS' => array()
560 );
561
562 $systemPaymentType = array(
565 );
566 foreach ($systemPaymentType as $type => $value)
567 {
568 $settings['PAYMENT_TYPE']['ITEMS'][$type] = array(
569 'TYPE' => 'STRING',
570 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_P_TYPE_LABEL_'.ToUpper($type)),
571 'VALUE' => $value
572 );
573 }
574
575 $settings['VAT'] = array(
576 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_BITRIX_SETTINGS_VAT'),
577 'REQUIRED' => 'Y',
578 'ITEMS' => array(
579 'NOT_VAT' => array(
580 'TYPE' => 'STRING',
581 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_BITRIX_SETTINGS_VAT_LABEL_NOT_VAT'),
582 'VALUE' => 'none'
583 )
584 )
585 );
586
587 if (Main\Loader::includeModule('catalog'))
588 {
589 $dbRes = Catalog\VatTable::getList(['filter' => ['ACTIVE' => 'Y']]);
590 $vatList = $dbRes->fetchAll();
591 if ($vatList)
592 {
593 $defaultVatList = [
594 0 => self::CODE_VAT_0,
595 10 => self::CODE_VAT_10,
597 ];
598
599 foreach ($vatList as $vat)
600 {
601 $value = '';
602 if (isset($defaultVatList[(int)$vat['RATE']]))
603 $value = $defaultVatList[(int)$vat['RATE']];
604
605 $settings['VAT']['ITEMS'][(int)$vat['ID']] = array(
606 'TYPE' => 'STRING',
607 'LABEL' => $vat['NAME'].' ['.(int)$vat['RATE'].'%]',
608 'VALUE' => $value
609 );
610 }
611 }
612 }
613
614 $settings['TAX'] = array(
615 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_SNO'),
616 'REQUIRED' => 'Y',
617 'ITEMS' => array(
618 'SNO' => array(
619 'TYPE' => 'ENUM',
620 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SETTINGS_SNO_LABEL'),
621 'VALUE' => 'osn',
622 'OPTIONS' => array(
623 'osn' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SNO_OSN'),
624 'usn_income' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SNO_UI'),
625 'usn_income_outcome' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SNO_UIO'),
626 'envd' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SNO_ENVD'),
627 'esn' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SNO_ESN'),
628 'patent' => Localization\Loc::getMessage('SALE_CASHBOX_ATOL_FARM_SNO_PATENT')
629 )
630 )
631 )
632 );
633
634 if (static::hasMeasureSettings())
635 {
636 $settings['MEASURE'] = static::getMeasureSettings();
637 }
638
639 return $settings;
640 }
641
645 protected static function hasMeasureSettings(): bool
646 {
647 return false;
648 }
649
653 protected static function getMeasureSettings(): array
654 {
655 $measureItems = [
656 'DEFAULT' => [
657 'TYPE' => 'STRING',
658 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_MEASURE_SUPPORT_SETTINGS_DEFAULT_VALUE'),
659 'VALUE' => 0,
660 ]
661 ];
662 if (Main\Loader::includeModule('catalog'))
663 {
664 $measuresList = \CCatalogMeasure::getList();
665 while ($measure = $measuresList->fetch())
666 {
667 $measureItems[$measure['CODE']] = [
668 'TYPE' => 'STRING',
669 'LABEL' => $measure['MEASURE_TITLE'],
670 'VALUE' => MeasureCodeToTag2108Mapper::getTag2108Value($measure['CODE']),
671 ];
672 }
673 }
674
675 return [
676 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_MEASURE_SUPPORT_SETTINGS'),
677 'ITEMS' => $measureItems,
678 ];
679 }
680
684 public static function getGeneralRequiredFields()
685 {
686 $generalRequiredFields = parent::getGeneralRequiredFields();
687
688 $map = CashboxTable::getMap();
689 $generalRequiredFields['NUMBER_KKM'] = $map['NUMBER_KKM']['title'];
690 return $generalRequiredFields;
691 }
692
696 private function getAccessToken()
697 {
698 return Main\Config\Option::get('sale', $this->getOptionName(), '');
699 }
700
704 private function setAccessToken($token)
705 {
706 Main\Config\Option::set('sale', $this->getOptionName(), $token);
707 }
708
712 private function getOptionName()
713 {
714 return static::getOptionPrefix() . '_' .ToLower($this->getField('NUMBER_KKM'));
715 }
716
720 protected function getOptionPrefix(): string
721 {
722 return static::TOKEN_OPTION_NAME;
723 }
724
729 private function encode(array $data)
730 {
731 return Main\Web\Json::encode($data);
732 }
733
739 private function decode($data)
740 {
741 return Main\Web\Json::decode($data);
742 }
743
748 private function requestAccessToken()
749 {
750 $url = $this->getRequestUrl(static::OPERATION_GET_TOKEN, '');
751
752 $data = array(
753 'login' => $this->getValueFromSettings('AUTH', 'LOGIN'),
754 'pass' => $this->getValueFromSettings('AUTH', 'PASS')
755 );
756
757 $result = $this->send(static::REQUEST_TYPE_POST, $url, $data);
758 if ($result->isSuccess())
759 {
760 $response = $result->getData();
761 if (isset($response['token']))
762 {
763 $this->setAccessToken($response['token']);
764
765 return $response['token'];
766 }
767 }
768
769 return '';
770 }
771
777 protected static function getErrorType($errorCode)
778 {
779 return Errors\Error::TYPE;
780 }
781
782}
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
registerCheck($operation, array $check)
getRequestUrl($operation, $token, array $queryData=array())
buildCheckQuery(Check $check)
getValueFromSettings($name, $code)
Definition cashbox.php:201
static addDebugInfo(?string $message, $cashboxId=null)
Definition logger.php:43