1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
cashboxorangedata.php
См. документацию.
1<?php
2
3namespace Bitrix\Sale\Cashbox;
4
5use Bitrix\Main;
6use Bitrix\Main\Localization;
7use Bitrix\Main\PhoneNumber;
8use Bitrix\Sale\Result;
9use Bitrix\Catalog;
10
12
18 extends Cashbox
20{
21 private const PARTNER_CODE_BITRIX = '3010144';
22
25
26 const HANDLER_MODE_TEST = 'TEST';
27 const HANDLER_MODE_ACTIVE = 'ACTIVE';
28
29 const HANDLER_TEST_URL = 'ssl://apip.orangedata.ru:2443/api/v2';
30 const HANDLER_ACTIVE_URL = 'ssl://api.orangedata.ru:12003/api/v2';
31
32 private $pathToSslCertificate = '';
33 private $pathToSslCertificateKey = '';
34
35 const CODE_VAT_0 = 5;
36 const CODE_VAT_10 = 2;
37 const CODE_VAT_20 = 1;
40
41 private const MAX_TEXT_LENGTH = 128;
42 protected const MAX_UUID_LENGTH = 64;
43
47 public static function getName()
48 {
49 return Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_TITLE');
50 }
51
55 private function getCheckTypeMap()
56 {
57 return [
58 SellCheck::getType() => 4,
75 ];
76 }
77
81 private function getCalculatedSignMap()
82 {
83 return [
86 ];
87 }
88
93 public function buildCheckQuery(Check $check)
94 {
95 return $this->buildCheckQueryByCheckData(
96 $this->getCheckData($check),
97 ($check->getType() === 'sellreturn')
98 );
99 }
100
105 protected function getCheckData(AbstractCheck $check): array
106 {
107 return $check->getDataForCheck();
108 }
109
115 protected function buildCheckQueryByCheckData(array $checkData, bool $isSellReturn): array
116 {
117 $calculatedSignMap = $this->getCalculatedSignMap();
118
119 $result = [
120 'id' => static::buildUuid(static::UUID_TYPE_CHECK, $checkData['unique_id']),
121 'inn' => $this->getValueFromSettings('SERVICE', 'INN'),
122 'group' => $this->getField('NUMBER_KKM') ?: null,
123 'key' => $this->getValueFromSettings('SECURITY', 'KEY_SIGN') ?: null,
124 'content' => [
125 'type' => $calculatedSignMap[$checkData['calculated_sign']],
126 'positions' => [],
127 'checkClose' => [
128 'payments' => [],
129 'taxationSystem' => $this->getValueFromSettings('TAX', 'SNO'),
130 ],
131 'customerContact' => $this->getCustomerContact($checkData),
132 'isInternetStore' => $this->getField('USE_OFFLINE') === 'N',
133 ],
134 'meta' => self::PARTNER_CODE_BITRIX
135 ];
136
137 foreach ($checkData['items'] as $item)
138 {
139 $result['content']['positions'][] = $this->buildPosition($checkData, $item, $isSellReturn);
140 }
141
142 $paymentTypeMap = $this->getPaymentTypeMap();
143 foreach ($checkData['payments'] as $payment)
144 {
145 $result['content']['checkClose']['payments'][] = [
146 'type' => $paymentTypeMap[$payment['type']],
147 'amount' => $payment['sum'],
148 ];
149 }
150
151 return $result;
152 }
153
160 protected function buildPosition(array $checkData, array $item, bool $isSellReturn): array
161 {
162 $result = [
163 'text' => $this->buildPositionText($item),
164 'quantity' => $this->buildPositionQuantity($item),
165 'price' => $this->buildPositionPrice($item),
166 'tax' => $this->buildPositionTax($checkData, $item),
167 'paymentMethodType' => $this->buildPositionPaymentMethodType($checkData),
168 'paymentSubjectType' => $this->buildPositionPaymentSubjectType($item),
169 ];
170
171 if (isset($item['nomenclature_code']))
172 {
173 $result['nomenclatureCode'] = $this->buildPositionNomenclatureCode($item);
174 }
175
176 if (isset($item['supplier_info']))
177 {
179 $result += $this->buildPositionSupplier($item['supplier_info']);
180 }
181
182 return $result;
183 }
184
189 protected function buildPositionText(array $item)
190 {
191 return mb_substr($item['name'], 0, self::MAX_TEXT_LENGTH);
192 }
193
198 protected function buildPositionQuantity(array $item)
199 {
200 return $item['quantity'];
201 }
202
207 protected function buildPositionPrice(array $item)
208 {
209 return $item['price'];
210 }
211
216 protected function buildPositionPaymentMethodType(array $checkData)
217 {
218 $checkType = $this->getCheckTypeMap();
219
220 return $checkType[$checkData['type']];
221 }
222
227 protected function buildPositionPaymentSubjectType(array $item)
228 {
229 $paymentObjectMap = $this->getPaymentObjectMap();
230
231 return $paymentObjectMap[$item['payment_object']];
232 }
233
239 protected function buildPositionTax(array $checkData, $item)
240 {
241 $vat = $this->getValueFromSettings('VAT', $item['vat']);
242 if ($vat === null)
243 {
244 $vat = $this->getValueFromSettings('VAT', 'NOT_VAT');
245 }
246
247 return $this->mapVatValue($checkData['type'], $vat);
248 }
249
254 private function buildPositionNomenclatureCode(array $item)
255 {
256 return base64_encode($item['nomenclature_code']);
257 }
258
259 protected function buildPositionAgentInfo(): array
260 {
264 return [
265 'agentType' => 6,
266 ];
267 }
268
269 protected function buildPositionSupplier(array $supplier): array
270 {
271 $result = [
272 'supplierInfo' => null,
273 'supplierINN' => null,
274 ];
275
276 // 239 max length for supplierInfo's tag
277 $maxTagLength = 239;
278 $phoneLength = 0;
279
280 if (!empty($supplier['phones']))
281 {
282 $phoneParser = PhoneNumber\Parser::getInstance();
283
284 foreach ($supplier['phones'] as $phone)
285 {
286 $phoneNumber = $phoneParser->parse($phone);
287 $formattedPhone = $phoneNumber->format(PhoneNumber\Format::E164);
288 if ($formattedPhone)
289 {
290 $phoneLength += mb_strlen($formattedPhone) + 4;
291 if ($phoneLength > $maxTagLength)
292 {
293 break;
294 }
295
296 $result['supplierInfo']['phoneNumbers'][] = $formattedPhone;
297 }
298 }
299 }
300
301 if (!empty($supplier['name']) && $phoneLength < $maxTagLength)
302 {
303 $result['supplierInfo']['name'] = mb_substr($supplier['name'], 0, $maxTagLength - $phoneLength);
304 }
305
306 if (!empty($supplier['supplier_info']['inn']))
307 {
308 $result['supplierINN'] = $supplier['supplier_info']['inn'];
309 }
310
311 return $result;
312 }
313
319 private function mapVatValue($checkType, $vat)
320 {
322 $this->getVatToCalcVatMap()
323 );
324
325 $map = $mapper->getMap();
326
327 return $map[$vat][$checkType] ?? $vat;
328 }
329
330 protected function getVatToCalcVatMap() : array
331 {
332 return [
333 self::CODE_VAT_10 => self::CODE_CALC_VAT_10,
334 self::CODE_VAT_20 => self::CODE_CALC_VAT_20,
335 ];
336 }
337
342 private function getCustomerContact(array $data)
343 {
344 $customerContact = $this->getValueFromSettings('CLIENT', 'INFO');
345
346 if ($customerContact === 'EMAIL')
347 {
348 return $data['client_email'];
349 }
350 elseif ($customerContact === 'PHONE')
351 {
352 $phone = \NormalizePhone($data['client_phone']);
353 if ($phone[0] !== '7')
354 {
355 $phone = '7'.$phone;
356 }
357
358 return '+'.$phone;
359 }
360
361 if ($data['client_phone'])
362 {
363 $phone = \NormalizePhone($data['client_phone']);
364 if ($phone[0] !== '7')
365 {
366 $phone = '7'.$phone;
367 }
368
369 return '+'.$phone;
370 }
371
372 return $data['client_email'];
373 }
374
412
416 private function getPaymentTypeMap()
417 {
418 return [
423 ];
424 }
425
431 private function getPostQueryHeaders($url, $data)
432 {
433 $sign = $this->sign($data);
434 if ($sign === false)
435 {
436 return false;
437 }
438
439 $urlObj = new Main\Web\Uri($url);
440
441 $header = "POST ".$urlObj->getPath()." HTTP/1.0\r\n";
442 $header .= "Host: ".$urlObj->getHost()."\r\n";
443 $header .= "Accept: application/json\r\n";
444 $header .= "Content-Type: application/json\r\n";
445 $header .= "X-Signature: ".$sign."\r\n";
446 $header .= sprintf("Content-length: %s\r\n", strlen($data));
447 $header .= "\r\n";
448
449 return $header;
450 }
451
456 public function buildZReportQuery($id)
457 {
458 return [];
459 }
460
467 public function printImmediately(Check $check)
468 {
469 return $this->registerCheck(
470 $this->getUrl().'/documents/',
471 $this->buildCheckQuery($check)
472 );
473 }
474
482 protected function registerCheck($url, $data)
483 {
484 $result = new Result();
485
486 $encodedData = $this->encode($data);
487
488 $headers = $this->getPostQueryHeaders($url, $encodedData);
489 if ($headers === false)
490 {
491 return $result->addError(
492 new Errors\Error(
493 Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_SIGN')
494 )
495 );
496 }
497
498 $queryResult = $this->send($url, $headers, $encodedData);
499 if (!$queryResult->isSuccess())
500 {
501 return $result->addErrors($queryResult->getErrors());
502 }
503
504 $result->setData(['UUID' => $data['id']]);
505
506 return $result;
507 }
508
512 private function getUrl()
513 {
514 if ($this->getValueFromSettings('INTERACTION', 'MODE_HANDLER') === static::HANDLER_MODE_ACTIVE)
515 {
516 return static::HANDLER_ACTIVE_URL;
517 }
518
519 return static::HANDLER_TEST_URL;
520 }
521
529 private function send($url, $headers, $data = '')
530 {
531 $context = $this->createStreamContext();
532
533 $errNumber = '';
534 $errString = '';
535 $client = stream_socket_client($url, $errNumber, $errString, 5, STREAM_CLIENT_CONNECT, $context);
536
537 $result = new Result();
538 if ($client !== false)
539 {
540 Logger::addDebugInfo($headers.$data);
541
542 fputs($client, $headers.$data);
543 $response = stream_get_contents($client);
544 fclose($client);
545
547
548 [$responseHeaders, $content] = explode("\r\n\r\n", $response);
549 $httpCode = $this->extractResponseStatus($responseHeaders);
550
551 $result->addData(['http_code' => $httpCode, 'content' => $content]);
552
553 if (
554 $httpCode !== static::RESPONSE_HTTP_CODE_201
555 &&
556 $httpCode !== static::RESPONSE_HTTP_CODE_200
557 )
558 {
559 $content = $this->decode($content);
560 if (isset($content['errors']))
561 {
562 $error = implode("\n", $content['errors']);
563 }
564 else
565 {
566 $error = Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_RESPONSE_'.$httpCode);
567 if (!$error)
568 {
569 $error = Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_CHECK_PRINT');
570 }
571 }
572
573 return $result->addError(new Errors\Error($error));
574 }
575 }
576 else
577 {
578 $result->addError(
579 new Errors\Error(
580 Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_SEND_QUERY')
581 )
582 );
583
584 $error = new Errors\Error($errNumber.': '.$errString);
585 Logger::addError($error->getMessage(), $this->getField('ID'));
586 }
587
588 return $result;
589 }
590
595 private function extractResponseStatus($headers)
596 {
597 $headers = explode("\n", $headers);
598 preg_match('#HTTP\S+ (\d+)#', $headers[0], $find);
599
600 return (int)$find[1];
601 }
602
606 public function __destruct()
607 {
608 if ($this->pathToSslCertificate !== ''
609 && Main\IO\File::isFileExists($this->pathToSslCertificate)
610 )
611 {
612 unlink($this->pathToSslCertificate);
613 }
614
615 if ($this->pathToSslCertificateKey !== ''
616 && Main\IO\File::isFileExists($this->pathToSslCertificateKey)
617 )
618 {
619 unlink($this->pathToSslCertificateKey);
620 }
621 }
622
626 private function createStreamContext()
627 {
628 $sslCert = $this->getValueFromSettings('SECURITY', 'SSL_CERT');
629 $this->pathToSslCertificate = $this->createTmpFile($sslCert);
630
631 $sslKey = $this->getValueFromSettings('SECURITY', 'SSL_KEY');
632 $this->pathToSslCertificateKey = $this->createTmpFile($sslKey);
633
634 return stream_context_create([
635 'ssl' => [
636 'local_cert' => $this->pathToSslCertificate,
637 'local_pk' => $this->pathToSslCertificateKey,
638 'passphrase' => $this->getValueFromSettings('SECURITY', 'SSL_KEY_PASS'),
639 ]
640 ]);
641 }
642
648 public function check(Check $check)
649 {
650 $url = $this->getUrl();
651 $url .= '/documents/'.$this->getValueFromSettings('SERVICE', 'INN').'/status/'.$check->getField('EXTERNAL_UUID');
652
653 return $this->checkInternal($url);
654 }
655
661 protected function checkInternal($url)
662 {
663 $result = new Result();
664
665 $header = $this->getCheckQueryHeaders($url);
666 $queryResult = $this->send($url, $header);
667
668 if (!$queryResult->isSuccess())
669 {
670 return $result->addErrors($queryResult->getErrors());
671 }
672
673 $data = $queryResult->getData();
674
675 $response = $this->decode($data['content']);
676 if ($response === false)
677 {
678 return $result->addError(new Errors\Error(Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_CHECK_CHECK')));
679 }
680
681 return static::applyCheckResult($response);
682 }
683
689 public function validate() : Result
690 {
691 $result = parent::validate();
692 if (!$result->isSuccess())
693 {
694 return $result;
695 }
696
697 return $this->testConnection();
698 }
699
700 protected function buildValidateQuery()
701 {
702 return [
703 'inn' => $this->getValueFromSettings('SERVICE', 'INN'),
704 'group' => $this->getField('NUMBER_KKM') ?: null,
705 'key' => $this->getValueFromSettings('SECURITY', 'KEY_SIGN') ?: null
706 ];
707 }
708
714 public function testConnection()
715 {
716 $url = $this->getUrl().'/check/';
717 $data = $this->buildValidateQuery();
718 $encodedData = $this->encode($data);
719
720 $headers = $this->getPostQueryHeaders($url, $encodedData);
721
722 return $this->send($url, $headers, $encodedData);
723 }
724
729 private function getCheckQueryHeaders($url)
730 {
731 $urlObj = new Main\Web\Uri($url);
732
733 $header = "GET ".$urlObj->getPath()." HTTP/1.0\r\n";
734 $header .= "Host: ".$urlObj->getHost()."\r\n";
735 $header .= "Accept: application/json\r\n";
736 $header .= "Content-Type: application/json\r\n";
737 $header .= "\r\n";
738
739 return $header;
740 }
741
749 protected static function extractCheckData(array $data)
750 {
751 $result = [];
752
753 if (!$data['id'])
754 {
755 return $result;
756 }
757
759 if (empty($checkInfo))
760 {
761 return $result;
762 }
763
764 $result['ID'] = $checkInfo['ID'];
765 $result['CHECK_TYPE'] = $checkInfo['TYPE'];
766
767 $check = CheckManager::getObjectById($checkInfo['ID']);
768 $dateTime = new Main\Type\DateTime($data['processedAt'], 'Y-m-d\TH:i:s.u');
769 $result['LINK_PARAMS'] = [
770 Check::PARAM_REG_NUMBER_KKT => $data['deviceRN'],
772 Check::PARAM_FISCAL_DOC_NUMBER => $data['documentNumber'],
773 Check::PARAM_FISCAL_RECEIPT_NUMBER => $data['documentIndex'],
774 Check::PARAM_FN_NUMBER => $data['fsNumber'],
775 Check::PARAM_SHIFT_NUMBER => $data['shiftNumber'],
776 Check::PARAM_DOC_SUM => (float)$checkInfo['SUM'],
777 Check::PARAM_DOC_TIME => $dateTime->getTimestamp(),
778 Check::PARAM_CALCULATION_ATTR => $check::getCalculatedSign()
779 ];
780
781 return $result;
782 }
783
788 public function sign($data)
789 {
790 if (!function_exists('openssl_get_privatekey') || !function_exists('openssl_private_encrypt'))
791 {
792 return false;
793 }
794
795 $data = pack('H*', '3031300d060960864801650304020105000420') . hash('sha256', $data, true);
796 $pk = openssl_get_privatekey($this->getValueFromSettings('SECURITY', 'PKEY'));
797 if ($pk === false)
798 {
799 return false;
800 }
801
802 openssl_private_encrypt($data, $res, $pk);
803 return base64_encode($res);
804 }
805
811 private function encode(array $data)
812 {
813 return Main\Web\Json::encode($data, JSON_UNESCAPED_UNICODE);
814 }
815
820 private function decode($data)
821 {
822 try
823 {
825 }
826 catch (Main\ArgumentException $exception)
827 {
828 return false;
829 }
830 }
831
836 private function createTmpFile($data)
837 {
838 $filePath = tempnam(sys_get_temp_dir(), 'orange_data');
839 if ($filePath === false)
840 {
841 return '';
842 }
843
844 if ($data !== null)
845 {
846 file_put_contents($filePath, $data);
847 }
848
849 return $filePath;
850 }
851
860 public static function getSettings($modelId = 0)
861 {
862 $settings = [
863 'SECURITY' => [
864 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY'),
865 'ITEMS' => [
866 'PKEY' => [
867 'TYPE' => 'DATABASE_FILE',
868 'CLASS' => 'adm-designed-file',
869 'REQUIRED' => 'Y',
870 'NO_DELETE' => 'Y',
871 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_PKEY'),
872 ],
873 'SSL_CERT' => [
874 'TYPE' => 'DATABASE_FILE',
875 'CLASS' => 'adm-designed-file',
876 'REQUIRED' => 'Y',
877 'NO_DELETE' => 'Y',
878 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_SSL_CERT'),
879 ],
880 'SSL_KEY' => [
881 'TYPE' => 'DATABASE_FILE',
882 'CLASS' => 'adm-designed-file',
883 'REQUIRED' => 'Y',
884 'NO_DELETE' => 'Y',
885 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_SSL_KEY'),
886 ],
887 'SSL_KEY_PASS' => [
888 'TYPE' => 'STRING',
889 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_SSL_KEY_PASS'),
890 ],
891 'KEY_SIGN' => [
892 'TYPE' => 'STRING',
893 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_KEY_SIGN'),
894 ],
895 ]
896 ]
897 ];
898
899 $settings['SERVICE'] = [
900 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SERVICE'),
901 'REQUIRED' => 'Y',
902 'ITEMS' => [
903 'INN' => [
904 'TYPE' => 'STRING',
905 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SERVICE_INN_LABEL')
906 ]
907 ]
908 ];
909
910 $settings['CLIENT'] = [
911 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT'),
912 'ITEMS' => [
913 'INFO' => [
914 'TYPE' => 'ENUM',
915 'VALUE' => 'NONE',
916 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_INFO'),
917 'OPTIONS' => [
918 'DEFAULT' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_DEFAULT'),
919 'PHONE' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_PHONE'),
920 'EMAIL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_EMAIL'),
921 ]
922 ],
923 ]
924 ];
925
926 $settings['VAT'] = [
927 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_VAT'),
928 'REQUIRED' => 'Y',
929 'ITEMS' => [
930 'NOT_VAT' => [
931 'TYPE' => 'STRING',
932 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_VAT_LABEL_NOT_VAT'),
933 'VALUE' => 6
934 ]
935 ]
936 ];
937
938 if (Main\Loader::includeModule('catalog'))
939 {
940 $dbRes = Catalog\VatTable::getList(['filter' => ['ACTIVE' => 'Y']]);
941 $vatList = $dbRes->fetchAll();
942 if ($vatList)
943 {
944 $defaultVatList = static::getDefaultVatList();
945
946 foreach ($vatList as $vat)
947 {
948 $value = null;
949 if (isset($defaultVatList[(int)$vat['RATE']]))
950 $value = $defaultVatList[(int)$vat['RATE']];
951
952 $settings['VAT']['ITEMS'][(int)$vat['ID']] = [
953 'TYPE' => 'STRING',
954 'LABEL' => $vat['NAME'].' ['.(int)$vat['RATE'].'%]',
955 'VALUE' => $value
956 ];
957 }
958 }
959 }
960
961 $settings['TAX'] = [
962 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SNO'),
963 'REQUIRED' => 'Y',
964 'ITEMS' => [
965 'SNO' => [
966 'TYPE' => 'ENUM',
967 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SNO_LABEL'),
968 'VALUE' => 0,
969 'OPTIONS' => [
970 0 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_OSN'),
971 1 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_UI'),
972 2 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_UIO'),
973 3 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_ENVD'),
974 4 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_ESN'),
975 5 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_PATENT')
976 ]
977 ]
978 ]
979 ];
980
981 if (static::hasMeasureSettings())
982 {
983 $settings['MEASURE'] = static::getMeasureSettings();
984 }
985
986 $settings['INTERACTION'] = [
987 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_INTERACTION'),
988 'ITEMS' => [
989 'MODE_HANDLER' => [
990 'TYPE' => 'ENUM',
991 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_MODE_HANDLER_LABEL'),
992 'OPTIONS' => [
993 static::HANDLER_MODE_ACTIVE => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_MODE_ACTIVE'),
994 static::HANDLER_MODE_TEST => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_MODE_TEST'),
995 ]
996 ]
997 ]
998 ];
999
1000 return $settings;
1001 }
1002
1003 protected static function getDefaultVatList() : array
1004 {
1005 return [
1006 0 => self::CODE_VAT_0,
1007 10 => self::CODE_VAT_10,
1008 20 => self::CODE_VAT_20,
1009 ];
1010 }
1011
1015 protected static function hasMeasureSettings(): bool
1016 {
1017 return false;
1018 }
1019
1023 protected static function getMeasureSettings(): array
1024 {
1025 $measureItems = [
1026 'DEFAULT' => [
1027 'TYPE' => 'STRING',
1028 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_MEASURE_SUPPORT_SETTINGS_DEFAULT_VALUE'),
1029 'VALUE' => 0,
1030 ]
1031 ];
1032 if (Main\Loader::includeModule('catalog'))
1033 {
1034 $measuresList = \CCatalogMeasure::getList();
1035 while ($measure = $measuresList->fetch())
1036 {
1037 $measureItems[$measure['CODE']] = [
1038 'TYPE' => 'STRING',
1039 'LABEL' => $measure['MEASURE_TITLE'],
1040 'VALUE' => MeasureCodeToTag2108Mapper::getTag2108Value($measure['CODE']),
1041 ];
1042 }
1043 }
1044
1045 return [
1046 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_MEASURE_SUPPORT_SETTINGS'),
1047 'ITEMS' => $measureItems,
1048 ];
1049 }
1050
1056 {
1057 global $APPLICATION;
1058
1059 $settings = parent::extractSettingsFromRequest($request);
1060 $files = $request->getFile('SETTINGS');
1061
1062 foreach (static::getSettings()['SECURITY']['ITEMS'] as $fieldId => $field)
1063 {
1064 $error = $files['error']['SECURITY'][$fieldId] ?? null;
1065 $tmpName = $files['tmp_name']['SECURITY'][$fieldId] ?? null;
1066
1067 if ($field['TYPE'] === 'DATABASE_FILE'
1068 && $error === 0
1069 && $tmpName
1070 )
1071 {
1072 $content = $APPLICATION->GetFileContent($tmpName);
1073 $settings['SECURITY'][$fieldId] = $content ?: '';
1074 }
1075 }
1076
1077 return $settings;
1078 }
1079
1083 public static function getFfdVersion(): ?float
1084 {
1085 return 1.05;
1086 }
1087
1095 {
1096 return $this->registerCheck(
1097 $this->getUrl() . $this->getCorrectionUrlPath(),
1098 $this->buildCorrectionCheckQuery($check)
1099 );
1100 }
1101
1105 protected function getCorrectionUrlPath(): string
1106 {
1107 return '/corrections/';
1108 }
1109
1116 {
1117 $data = $this->getCheckData($check);
1118
1119 $calculatedSignMap = $this->getCalculatedSignMap();
1120
1121 $result = [
1122 'id' => static::buildUuid(static::UUID_TYPE_CHECK, $data['unique_id']),
1123 'inn' => $this->getValueFromSettings('SERVICE', 'INN'),
1124 'group' => $this->getField('NUMBER_KKM') ?: null,
1125 'key' => $this->getValueFromSettings('SECURITY', 'KEY_SIGN') ?: null,
1126 'content' => [
1127 'type' => $calculatedSignMap[$data['calculated_sign']],
1128 'correctionType' => $this->getCorrectionTypeMap($data['correction_info']['type']),
1129 'causeDocumentDate' => $this->getCorrectionCauseDocumentDate($data['correction_info']),
1130 'causeDocumentNumber' => $this->getCorrectionCauseDocumentNumber($data['correction_info']),
1131 'totalSum' => $this->getCorrectionTotalSum($data['correction_info']),
1132 'taxationSystem' => $this->getValueFromSettings('TAX', 'SNO')
1133 ],
1134 ];
1135
1136 foreach ($data['payments'] as $payment)
1137 {
1138 if ($payment['type'] === Check::PAYMENT_TYPE_CASH)
1139 {
1140 $result['content']['cashSum'] = (float)$payment['sum'];
1141 }
1142 else
1143 {
1144 $result['content']['eCashSum'] = (float)$payment['sum'];
1145 }
1146 }
1147
1148 $vats = $this->getVatsByCheckData($data);
1149 if (is_array($vats))
1150 {
1151 foreach ($vats as $vat)
1152 {
1153 $result['content'][$vat['code']] = $vat['value'];
1154 }
1155 }
1156
1157 return $result;
1158 }
1159
1164 protected function getCorrectionCauseDocumentDate($correctionInfo)
1165 {
1166 $documentDate = new Main\Type\DateTime($correctionInfo['document_date']);
1167
1168 return $documentDate->format('Y-m-d\TH:i:s');
1169 }
1170
1175 protected function getCorrectionCauseDocumentNumber($correctionInfo)
1176 {
1177 return $correctionInfo['document_number'];
1178 }
1179
1184 protected function getCorrectionTotalSum($correctionInfo)
1185 {
1186 return $correctionInfo['total_sum'];
1187 }
1188
1193 protected function getVatsByCheckData(array $data): ?array
1194 {
1195 if (!isset($data['vats']) || !is_array($data['vats']) || empty($data['vats']))
1196 {
1197 return null;
1198 }
1199
1200 $result = [];
1201 foreach ($data['vats'] as $item)
1202 {
1203 $vat = $this->getValueFromSettings('VAT', $item['type']);
1204 if (is_null($vat) || $vat === '')
1205 {
1206 $vat = $this->getValueFromSettings('VAT', 'NOT_VAT');
1207 }
1208
1209 switch ($vat)
1210 {
1211 case self::CODE_VAT_0:
1212 $vatKey = '3Sum';
1213 break;
1214 case self::CODE_VAT_10:
1215 $vatKey = '2Sum';
1216 break;
1217 case self::CODE_VAT_20:
1218 $vatKey = '1Sum';
1219 break;
1220 default:
1221 $vatKey = '4Sum';
1222 break;
1223 }
1224
1225 $result[] = [
1226 'code' => $this->getVatKeyPrefix() . $vatKey,
1227 'value' => $item['sum']
1228 ];
1229 }
1230
1231 return $result;
1232 }
1233
1237 protected function getVatKeyPrefix(): string
1238 {
1239 return 'tax';
1240 }
1241
1246 protected function getCorrectionTypeMap($type)
1247 {
1248 $map = [
1251 ];
1252
1253 return $map[$type] ?? 0;
1254 }
1255
1261 public function checkCorrection(CorrectionCheck $check)
1262 {
1263 return $this->checkInternal(
1264 $this->getUrl()
1265 . $this->getCorrectionUrlPath()
1266 . $this->getValueFromSettings('SERVICE', 'INN')
1267 . '/status/'
1268 . $check->getField('EXTERNAL_UUID')
1269 );
1270 }
1271}
$type
Определения options.php:106
global $APPLICATION
Определения include.php:80
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
Определения error.php:15
static loadMessages($file)
Определения loc.php:65
static getMessage($code, $replace=null, $language=null)
Определения loc.php:30
static getList(array $parameters=array())
Определения datamanager.php:431
const E164
Определения format.php:7
static getInstance()
Определения parser.php:78
static decode($data)
Определения json.php:50
static encode($data, $options=null)
Определения json.php:22
Определения uri.php:17
const CALCULATED_SIGN_INCOME
Определения abstractcheck.php:34
const PARAM_CALCULATION_ATTR
Определения abstractcheck.php:31
const PARAM_FISCAL_DOC_ATTR
Определения abstractcheck.php:24
const PARAM_FISCAL_DOC_NUMBER
Определения abstractcheck.php:23
const CALCULATED_SIGN_CONSUMPTION
Определения abstractcheck.php:35
const PARAM_FISCAL_RECEIPT_NUMBER
Определения abstractcheck.php:25
const PAYMENT_TYPE_CASHLESS
Определения abstractcheck.php:40
getField($name)
Определения cashbox.php:159
getValueFromSettings($name, $code)
Определения cashbox.php:203
buildPositionQuantity(array $item)
Определения cashboxorangedata.php:198
static extractSettingsFromRequest(Main\HttpRequest $request)
Определения cashboxorangedata.php:1055
buildPositionPrice(array $item)
Определения cashboxorangedata.php:207
static getSettings($modelId=0)
Определения cashboxorangedata.php:860
buildPositionPaymentMethodType(array $checkData)
Определения cashboxorangedata.php:216
printCorrectionImmediately(CorrectionCheck $check)
Определения cashboxorangedata.php:1094
buildCorrectionCheckQuery(CorrectionCheck $check)
Определения cashboxorangedata.php:1115
buildCheckQuery(Check $check)
Определения cashboxorangedata.php:93
getCheckData(AbstractCheck $check)
Определения cashboxorangedata.php:105
buildCheckQueryByCheckData(array $checkData, bool $isSellReturn)
Определения cashboxorangedata.php:115
buildPositionPaymentSubjectType(array $item)
Определения cashboxorangedata.php:227
static extractCheckData(array $data)
Определения cashboxorangedata.php:749
buildPositionSupplier(array $supplier)
Определения cashboxorangedata.php:269
checkCorrection(CorrectionCheck $check)
Определения cashboxorangedata.php:1261
printImmediately(Check $check)
Определения cashboxorangedata.php:467
getCorrectionTotalSum($correctionInfo)
Определения cashboxorangedata.php:1184
getCorrectionCauseDocumentNumber($correctionInfo)
Определения cashboxorangedata.php:1175
getCorrectionCauseDocumentDate($correctionInfo)
Определения cashboxorangedata.php:1164
buildPosition(array $checkData, array $item, bool $isSellReturn)
Определения cashboxorangedata.php:160
buildPositionTax(array $checkData, $item)
Определения cashboxorangedata.php:239
const PAYMENT_OBJECT_EXCISE
Определения check.php:25
const PAYMENT_OBJECT_LOTTERY
Определения check.php:31
const PAYMENT_OBJECT_SOCIAL_INSURANCE
Определения check.php:47
const PAYMENT_OBJECT_COMPOSITE
Определения check.php:35
const PAYMENT_OBJECT_LOTTERY_PRIZE
Определения check.php:32
const PAYMENT_OBJECT_MEDICAL_INSURANCE_IP
Определения check.php:45
const PAYMENT_OBJECT_COMMODITY_MARKING_EXCISE
Определения check.php:50
const PAYMENT_OBJECT_NON_OPERATING_GAIN
Определения check.php:38
const PAYMENT_OBJECT_COMMODITY_MARKING_NO_MARKING_EXCISE
Определения check.php:49
const PAYMENT_OBJECT_RESORT_FEE
Определения check.php:40
const PAYMENT_OBJECT_PENSION_INSURANCE_IP
Определения check.php:43
const PAYMENT_OBJECT_PROPERTY_RIGHT
Определения check.php:37
const PAYMENT_OBJECT_GAMBLING_PRIZE
Определения check.php:30
const PAYMENT_OBJECT_COMMODITY_MARKING
Определения check.php:52
const PAYMENT_OBJECT_COMMODITY
Определения check.php:24
const PAYMENT_OBJECT_AGENT_COMMISSION
Определения check.php:34
const PAYMENT_OBJECT_ANOTHER
Определения check.php:36
const PAYMENT_OBJECT_DEPOSIT
Определения check.php:41
const PAYMENT_OBJECT_MEDICAL_INSURANCE
Определения check.php:46
const PAYMENT_OBJECT_SERVICE
Определения check.php:27
const PAYMENT_OBJECT_INTELLECTUAL_ACTIVITY
Определения check.php:33
const PAYMENT_OBJECT_PAYMENT
Определения check.php:28
const PAYMENT_OBJECT_EXPENSE
Определения check.php:42
const PAYMENT_OBJECT_GAMBLING_BET
Определения check.php:29
const PAYMENT_OBJECT_CASINO_PAYMENT
Определения check.php:48
const PAYMENT_OBJECT_PENSION_INSURANCE
Определения check.php:44
const PAYMENT_OBJECT_JOB
Определения check.php:26
const PAYMENT_OBJECT_COMMODITY_MARKING_NO_MARKING
Определения check.php:51
const PAYMENT_OBJECT_SALES_TAX
Определения check.php:39
static getCheckInfoByExternalUuid($uuid)
Определения checkmanager.php:1685
static getObjectById($id)
Определения checkmanager.php:1698
static getType()
Определения creditcheck.php:20
static addDebugInfo(?string $message, $cashboxId=null)
Определения logger.php:43
static addError(?string $message, $cashboxId=null)
Определения logger.php:25
static getTag2108Value(?string $measureCode)
Определения measurecodetotag2108mapper.php:51
static getType()
Определения sellcheck.php:19
$content
Определения commerceml.php:144
$data['IS_AVAILABLE']
Определения .description.php:13
</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
$context
Определения csv_new_setup.php:223
NormalizePhone($number, $minLength=10)
Определения tools.php:4959
$map
Определения config.php:5
Определения directory.php:3
$files
Определения mysql_to_pgsql.php:30
trait Error
Определения error.php:11
$payment
Определения payment.php:14
$sign
Определения payment.php:69
$settings
Определения product_settings.php:43
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$vat
Определения template.php:273
$response
Определения result.php:21
$error
Определения subscription_card_product.php:20
$url
Определения iframe.php:7
$dbRes
Определения yandex_detail.php:168
$vatList
Определения yandex_run.php:916