Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
cashboxorangedata.php
1<?php
2
3namespace Bitrix\Sale\Cashbox;
4
12
13Localization\Loc::loadMessages(__FILE__);
14
20 extends Cashbox
22{
23 private const PARTNER_CODE_BITRIX = '3010144';
24
27
28 const HANDLER_MODE_TEST = 'TEST';
29 const HANDLER_MODE_ACTIVE = 'ACTIVE';
30
31 const HANDLER_TEST_URL = 'ssl://apip.orangedata.ru:2443/api/v2';
32 const HANDLER_ACTIVE_URL = 'ssl://api.orangedata.ru:12003/api/v2';
33
34 private $pathToSslCertificate = '';
35 private $pathToSslCertificateKey = '';
36
37 const CODE_VAT_0 = 5;
38 const CODE_VAT_10 = 2;
39 const CODE_VAT_20 = 1;
42
43 private const MAX_TEXT_LENGTH = 128;
44
48 public static function getName()
49 {
50 return Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_TITLE');
51 }
52
56 private function getCheckTypeMap()
57 {
58 return [
59 SellCheck::getType() => 4,
76 ];
77 }
78
82 private function getCalculatedSignMap()
83 {
84 return [
87 ];
88 }
89
94 public function buildCheckQuery(Check $check)
95 {
96 return $this->buildCheckQueryByCheckData(
97 $this->getCheckData($check),
98 ($check->getType() === 'sellreturn')
99 );
100 }
101
106 protected function getCheckData(AbstractCheck $check): array
107 {
108 return $check->getDataForCheck();
109 }
110
116 protected function buildCheckQueryByCheckData(array $checkData, bool $isSellReturn): array
117 {
118 $calculatedSignMap = $this->getCalculatedSignMap();
119
120 $result = [
121 'id' => static::buildUuid(static::UUID_TYPE_CHECK, $checkData['unique_id']),
122 'inn' => $this->getValueFromSettings('SERVICE', 'INN'),
123 'group' => $this->getField('NUMBER_KKM') ?: null,
124 'key' => $this->getValueFromSettings('SECURITY', 'KEY_SIGN') ?: null,
125 'content' => [
126 'type' => $calculatedSignMap[$checkData['calculated_sign']],
127 'positions' => [],
128 'checkClose' => [
129 'payments' => [],
130 'taxationSystem' => $this->getValueFromSettings('TAX', 'SNO'),
131 ],
132 'customerContact' => $this->getCustomerContact($checkData),
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 {
178 $result += $this->buildPositionAgentInfo();
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 {
321 $map = [
322 self::CODE_VAT_10 => [
329 ],
330 self::CODE_VAT_20 => [
337 ],
338 ];
339
340 return $map[$vat][$checkType] ?? $vat;
341 }
342
347 private function getCustomerContact(array $data)
348 {
349 $customerContact = $this->getValueFromSettings('CLIENT', 'INFO');
350
351 if ($customerContact === 'EMAIL')
352 {
353 return $data['client_email'];
354 }
355 elseif ($customerContact === 'PHONE')
356 {
357 $phone = \NormalizePhone($data['client_phone']);
358 if ($phone[0] !== '7')
359 {
360 $phone = '7'.$phone;
361 }
362
363 return '+'.$phone;
364 }
365
366 if ($data['client_phone'])
367 {
368 $phone = \NormalizePhone($data['client_phone']);
369 if ($phone[0] !== '7')
370 {
371 $phone = '7'.$phone;
372 }
373
374 return '+'.$phone;
375 }
376
377 return $data['client_email'];
378 }
379
417
421 private function getPaymentTypeMap()
422 {
423 return [
428 ];
429 }
430
436 private function getPostQueryHeaders($url, $data)
437 {
438 $sign = $this->sign($data);
439 if ($sign === false)
440 {
441 return false;
442 }
443
444 $urlObj = new Main\Web\Uri($url);
445
446 $header = "POST ".$urlObj->getPath()." HTTP/1.0\r\n";
447 $header .= "Host: ".$urlObj->getHost()."\r\n";
448 $header .= "Accept: application/json\r\n";
449 $header .= "Content-Type: application/json\r\n";
450 $header .= "X-Signature: ".$sign."\r\n";
451 $header .= sprintf("Content-length: %s\r\n", Text\BinaryString::getLength($data));
452 $header .= "\r\n";
453
454 return $header;
455 }
456
461 public function buildZReportQuery($id)
462 {
463 return [];
464 }
465
472 public function printImmediately(Check $check)
473 {
474 return $this->registerCheck(
475 $this->getUrl().'/documents/',
476 $this->buildCheckQuery($check)
477 );
478 }
479
487 protected function registerCheck($url, $data)
488 {
489 $result = new Result();
490
491 $encodedData = $this->encode($data);
492
493 $headers = $this->getPostQueryHeaders($url, $encodedData);
494 if ($headers === false)
495 {
496 return $result->addError(
497 new Errors\Error(
498 Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_SIGN')
499 )
500 );
501 }
502
503 $queryResult = $this->send($url, $headers, $encodedData);
504 if (!$queryResult->isSuccess())
505 {
506 return $result->addErrors($queryResult->getErrors());
507 }
508
509 $result->setData(['UUID' => $data['id']]);
510
511 return $result;
512 }
513
517 private function getUrl()
518 {
519 if ($this->getValueFromSettings('INTERACTION', 'MODE_HANDLER') === static::HANDLER_MODE_ACTIVE)
520 {
521 return static::HANDLER_ACTIVE_URL;
522 }
523
524 return static::HANDLER_TEST_URL;
525 }
526
534 private function send($url, $headers, $data = '')
535 {
536 $context = $this->createStreamContext();
537
538 $errNumber = '';
539 $errString = '';
540 $client = stream_socket_client($url, $errNumber, $errString, 5, STREAM_CLIENT_CONNECT, $context);
541
542 $result = new Result();
543 if ($client !== false)
544 {
545 Logger::addDebugInfo($headers.$data);
546
547 fputs($client, $headers.$data);
548 $response = stream_get_contents($client);
549 fclose($client);
550
551 Logger::addDebugInfo($response);
552
553 [$responseHeaders, $content] = explode("\r\n\r\n", $response);
554 $httpCode = $this->extractResponseStatus($responseHeaders);
555
556 $result->addData(['http_code' => $httpCode, 'content' => $content]);
557
558 if (
559 $httpCode !== static::RESPONSE_HTTP_CODE_201
560 &&
561 $httpCode !== static::RESPONSE_HTTP_CODE_200
562 )
563 {
564 $content = $this->decode($content);
565 if (isset($content['errors']))
566 {
567 $error = implode("\n", $content['errors']);
568 }
569 else
570 {
571 $error = Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_RESPONSE_'.$httpCode);
572 if (!$error)
573 {
574 $error = Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_CHECK_PRINT');
575 }
576 }
577
578 return $result->addError(new Errors\Error($error));
579 }
580 }
581 else
582 {
583 $result->addError(
584 new Errors\Error(
585 Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_SEND_QUERY')
586 )
587 );
588
589 $error = new Errors\Error($errNumber.': '.$errString);
590 Logger::addError($error->getMessage(), $this->getField('ID'));
591 }
592
593 return $result;
594 }
595
600 private function extractResponseStatus($headers)
601 {
602 $headers = explode("\n", $headers);
603 preg_match('#HTTP\S+ (\d+)#', $headers[0], $find);
604
605 return (int)$find[1];
606 }
607
611 public function __destruct()
612 {
613 if ($this->pathToSslCertificate !== ''
614 && Main\IO\File::isFileExists($this->pathToSslCertificate)
615 )
616 {
617 unlink($this->pathToSslCertificate);
618 }
619
620 if ($this->pathToSslCertificateKey !== ''
621 && Main\IO\File::isFileExists($this->pathToSslCertificateKey)
622 )
623 {
624 unlink($this->pathToSslCertificateKey);
625 }
626 }
627
631 private function createStreamContext()
632 {
633 $sslCert = $this->getValueFromSettings('SECURITY', 'SSL_CERT');
634 $this->pathToSslCertificate = $this->createTmpFile($sslCert);
635
636 $sslKey = $this->getValueFromSettings('SECURITY', 'SSL_KEY');
637 $this->pathToSslCertificateKey = $this->createTmpFile($sslKey);
638
639 return stream_context_create([
640 'ssl' => [
641 'local_cert' => $this->pathToSslCertificate,
642 'local_pk' => $this->pathToSslCertificateKey,
643 'passphrase' => $this->getValueFromSettings('SECURITY', 'SSL_KEY_PASS'),
644 ]
645 ]);
646 }
647
653 public function check(Check $check)
654 {
655 $url = $this->getUrl();
656 $url .= '/documents/'.$this->getValueFromSettings('SERVICE', 'INN').'/status/'.$check->getField('EXTERNAL_UUID');
657
658 return $this->checkInternal($url);
659 }
660
666 protected function checkInternal($url)
667 {
668 $result = new Result();
669
670 $header = $this->getCheckQueryHeaders($url);
671 $queryResult = $this->send($url, $header);
672
673 if (!$queryResult->isSuccess())
674 {
675 return $result->addErrors($queryResult->getErrors());
676 }
677
678 $data = $queryResult->getData();
679
680 $response = $this->decode($data['content']);
681 if ($response === false)
682 {
683 return $result->addError(new Errors\Error(Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_CHECK_CHECK')));
684 }
685
686 return static::applyCheckResult($response);
687 }
688
694 public function validate() : Result
695 {
696 $result = parent::validate();
697 if (!$result->isSuccess())
698 {
699 return $result;
700 }
701
702 return $this->testConnection();
703 }
704
705 protected function buildValidateQuery()
706 {
707 return [
708 'inn' => $this->getValueFromSettings('SERVICE', 'INN'),
709 'group' => $this->getField('NUMBER_KKM') ?: null,
710 'key' => $this->getValueFromSettings('SECURITY', 'KEY_SIGN') ?: null
711 ];
712 }
713
719 public function testConnection()
720 {
721 $url = $this->getUrl().'/check/';
722 $data = $this->buildValidateQuery();
723 $encodedData = $this->encode($data);
724
725 $headers = $this->getPostQueryHeaders($url, $encodedData);
726
727 return $this->send($url, $headers, $encodedData);
728 }
729
734 private function getCheckQueryHeaders($url)
735 {
736 $urlObj = new Main\Web\Uri($url);
737
738 $header = "GET ".$urlObj->getPath()." HTTP/1.0\r\n";
739 $header .= "Host: ".$urlObj->getHost()."\r\n";
740 $header .= "Accept: application/json\r\n";
741 $header .= "Content-Type: application/json\r\n";
742 $header .= "\r\n";
743
744 return $header;
745 }
746
754 protected static function extractCheckData(array $data)
755 {
756 $result = [];
757
758 if (!$data['id'])
759 {
760 return $result;
761 }
762
763 $checkInfo = CheckManager::getCheckInfoByExternalUuid($data['id']);
764 if (empty($checkInfo))
765 {
766 return $result;
767 }
768
769 $result['ID'] = $checkInfo['ID'];
770 $result['CHECK_TYPE'] = $checkInfo['TYPE'];
771
772 $check = CheckManager::getObjectById($checkInfo['ID']);
773 $dateTime = new Main\Type\DateTime($data['processedAt'], 'Y-m-d\TH:i:s.u');
774 $result['LINK_PARAMS'] = [
775 Check::PARAM_REG_NUMBER_KKT => $data['deviceRN'],
776 Check::PARAM_FISCAL_DOC_ATTR => $data['fp'],
777 Check::PARAM_FISCAL_DOC_NUMBER => $data['documentNumber'],
778 Check::PARAM_FISCAL_RECEIPT_NUMBER => $data['documentIndex'],
779 Check::PARAM_FN_NUMBER => $data['fsNumber'],
780 Check::PARAM_SHIFT_NUMBER => $data['shiftNumber'],
781 Check::PARAM_DOC_SUM => (float)$checkInfo['SUM'],
782 Check::PARAM_DOC_TIME => $dateTime->getTimestamp(),
783 Check::PARAM_CALCULATION_ATTR => $check::getCalculatedSign()
784 ];
785
786 return $result;
787 }
788
793 public function sign($data)
794 {
795 if (!function_exists('openssl_get_privatekey') || !function_exists('openssl_private_encrypt'))
796 {
797 return false;
798 }
799
800 $data = pack('H*', '3031300d060960864801650304020105000420') . hash('sha256', $data, true);
801 $pk = openssl_get_privatekey($this->getValueFromSettings('SECURITY', 'PKEY'));
802 if ($pk === false)
803 {
804 return false;
805 }
806
807 openssl_private_encrypt($data, $res, $pk);
808 return base64_encode($res);
809 }
810
816 private function encode(array $data)
817 {
818 return Main\Web\Json::encode($data, JSON_UNESCAPED_UNICODE);
819 }
820
825 private function decode($data)
826 {
827 try
828 {
829 return Main\Web\Json::decode($data);
830 }
831 catch (Main\ArgumentException $exception)
832 {
833 return false;
834 }
835 }
836
841 private function createTmpFile($data)
842 {
843 $filePath = tempnam(sys_get_temp_dir(), 'orange_data');
844 if ($filePath === false)
845 {
846 return '';
847 }
848
849 if ($data !== null)
850 {
851 file_put_contents($filePath, $data);
852 }
853
854 return $filePath;
855 }
856
865 public static function getSettings($modelId = 0)
866 {
867 $settings = [
868 'SECURITY' => [
869 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY'),
870 'ITEMS' => [
871 'PKEY' => [
872 'TYPE' => 'DATABASE_FILE',
873 'CLASS' => 'adm-designed-file',
874 'REQUIRED' => 'Y',
875 'NO_DELETE' => 'Y',
876 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_PKEY'),
877 ],
878 'SSL_CERT' => [
879 'TYPE' => 'DATABASE_FILE',
880 'CLASS' => 'adm-designed-file',
881 'REQUIRED' => 'Y',
882 'NO_DELETE' => 'Y',
883 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_SSL_CERT'),
884 ],
885 'SSL_KEY' => [
886 'TYPE' => 'DATABASE_FILE',
887 'CLASS' => 'adm-designed-file',
888 'REQUIRED' => 'Y',
889 'NO_DELETE' => 'Y',
890 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_SSL_KEY'),
891 ],
892 'SSL_KEY_PASS' => [
893 'TYPE' => 'STRING',
894 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_SSL_KEY_PASS'),
895 ],
896 'KEY_SIGN' => [
897 'TYPE' => 'STRING',
898 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_KEY_SIGN'),
899 ],
900 ]
901 ]
902 ];
903
904 $settings['SERVICE'] = [
905 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SERVICE'),
906 'REQUIRED' => 'Y',
907 'ITEMS' => [
908 'INN' => [
909 'TYPE' => 'STRING',
910 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SERVICE_INN_LABEL')
911 ]
912 ]
913 ];
914
915 $settings['CLIENT'] = [
916 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT'),
917 'ITEMS' => [
918 'INFO' => [
919 'TYPE' => 'ENUM',
920 'VALUE' => 'NONE',
921 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_INFO'),
922 'OPTIONS' => [
923 'DEFAULT' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_DEFAULT'),
924 'PHONE' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_PHONE'),
925 'EMAIL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_EMAIL'),
926 ]
927 ],
928 ]
929 ];
930
931 $settings['VAT'] = [
932 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_VAT'),
933 'REQUIRED' => 'Y',
934 'ITEMS' => [
935 'NOT_VAT' => [
936 'TYPE' => 'STRING',
937 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_VAT_LABEL_NOT_VAT'),
938 'VALUE' => 6
939 ]
940 ]
941 ];
942
943 if (Main\Loader::includeModule('catalog'))
944 {
945 $dbRes = Catalog\VatTable::getList(['filter' => ['ACTIVE' => 'Y']]);
946 $vatList = $dbRes->fetchAll();
947 if ($vatList)
948 {
949 $defaultVatList = [
950 0 => self::CODE_VAT_0,
951 10 => self::CODE_VAT_10,
953 ];
954 foreach ($vatList as $vat)
955 {
956 $value = '';
957 if (isset($defaultVatList[(int)$vat['RATE']]))
958 $value = $defaultVatList[(int)$vat['RATE']];
959
960 $settings['VAT']['ITEMS'][(int)$vat['ID']] = [
961 'TYPE' => 'STRING',
962 'LABEL' => $vat['NAME'].' ['.(int)$vat['RATE'].'%]',
963 'VALUE' => $value
964 ];
965 }
966 }
967 }
968
969 $settings['TAX'] = [
970 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SNO'),
971 'REQUIRED' => 'Y',
972 'ITEMS' => [
973 'SNO' => [
974 'TYPE' => 'ENUM',
975 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SNO_LABEL'),
976 'VALUE' => 0,
977 'OPTIONS' => [
978 0 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_OSN'),
979 1 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_UI'),
980 2 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_UIO'),
981 3 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_ENVD'),
982 4 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_ESN'),
983 5 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_PATENT')
984 ]
985 ]
986 ]
987 ];
988
989 if (static::hasMeasureSettings())
990 {
991 $settings['MEASURE'] = static::getMeasureSettings();
992 }
993
994 $settings['INTERACTION'] = [
995 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_INTERACTION'),
996 'ITEMS' => [
997 'MODE_HANDLER' => [
998 'TYPE' => 'ENUM',
999 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_MODE_HANDLER_LABEL'),
1000 'OPTIONS' => [
1001 static::HANDLER_MODE_ACTIVE => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_MODE_ACTIVE'),
1002 static::HANDLER_MODE_TEST => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_MODE_TEST'),
1003 ]
1004 ]
1005 ]
1006 ];
1007
1008 return $settings;
1009 }
1010
1014 protected static function hasMeasureSettings(): bool
1015 {
1016 return false;
1017 }
1018
1022 protected static function getMeasureSettings(): array
1023 {
1024 $measureItems = [
1025 'DEFAULT' => [
1026 'TYPE' => 'STRING',
1027 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_MEASURE_SUPPORT_SETTINGS_DEFAULT_VALUE'),
1028 'VALUE' => 0,
1029 ]
1030 ];
1031 if (Main\Loader::includeModule('catalog'))
1032 {
1033 $measuresList = \CCatalogMeasure::getList();
1034 while ($measure = $measuresList->fetch())
1035 {
1036 $measureItems[$measure['CODE']] = [
1037 'TYPE' => 'STRING',
1038 'LABEL' => $measure['MEASURE_TITLE'],
1039 'VALUE' => MeasureCodeToTag2108Mapper::getTag2108Value($measure['CODE']),
1040 ];
1041 }
1042 }
1043
1044 return [
1045 'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_MEASURE_SUPPORT_SETTINGS'),
1046 'ITEMS' => $measureItems,
1047 ];
1048 }
1049
1054 public static function extractSettingsFromRequest(Main\HttpRequest $request)
1055 {
1056 global $APPLICATION;
1057
1058 $settings = parent::extractSettingsFromRequest($request);
1059 $files = $request->getFile('SETTINGS');
1060
1061 foreach (static::getSettings()['SECURITY']['ITEMS'] as $fieldId => $field)
1062 {
1063 $error = $files['error']['SECURITY'][$fieldId] ?? null;
1064 $tmpName = $files['tmp_name']['SECURITY'][$fieldId] ?? null;
1065
1066 if ($field['TYPE'] === 'DATABASE_FILE'
1067 && $error === 0
1068 && $tmpName
1069 )
1070 {
1071 $content = $APPLICATION->GetFileContent($tmpName);
1072 $settings['SECURITY'][$fieldId] = $content ?: '';
1073 }
1074 }
1075
1076 return $settings;
1077 }
1078
1082 public static function getFfdVersion(): ?float
1083 {
1084 return 1.05;
1085 }
1086
1094 {
1095 return $this->registerCheck(
1096 $this->getUrl() . $this->getCorrectionUrlPath(),
1097 $this->buildCorrectionCheckQuery($check)
1098 );
1099 }
1100
1104 protected function getCorrectionUrlPath(): string
1105 {
1106 return '/corrections/';
1107 }
1108
1115 {
1116 $data = $this->getCheckData($check);
1117
1118 $calculatedSignMap = $this->getCalculatedSignMap();
1119
1120 $result = [
1121 'id' => static::buildUuid(static::UUID_TYPE_CHECK, $data['unique_id']),
1122 'inn' => $this->getValueFromSettings('SERVICE', 'INN'),
1123 'group' => $this->getField('NUMBER_KKM') ?: null,
1124 'key' => $this->getValueFromSettings('SECURITY', 'KEY_SIGN') ?: null,
1125 'content' => [
1126 'type' => $calculatedSignMap[$data['calculated_sign']],
1127 'correctionType' => $this->getCorrectionTypeMap($data['correction_info']['type']),
1128 'causeDocumentDate' => $this->getCorrectionCauseDocumentDate($data['correction_info']),
1129 'causeDocumentNumber' => $this->getCorrectionCauseDocumentNumber($data['correction_info']),
1130 'totalSum' => $this->getCorrectionTotalSum($data['correction_info']),
1131 'taxationSystem' => $this->getValueFromSettings('TAX', 'SNO')
1132 ],
1133 ];
1134
1135 foreach ($data['payments'] as $payment)
1136 {
1137 if ($payment['type'] === Check::PAYMENT_TYPE_CASH)
1138 {
1139 $result['content']['cashSum'] = (float)$payment['sum'];
1140 }
1141 else
1142 {
1143 $result['content']['eCashSum'] = (float)$payment['sum'];
1144 }
1145 }
1146
1147 $vats = $this->getVatsByCheckData($data);
1148 if (is_array($vats))
1149 {
1150 foreach ($vats as $vat)
1151 {
1152 $result['content'][$vat['code']] = $vat['value'];
1153 }
1154 }
1155
1156 return $result;
1157 }
1158
1163 protected function getCorrectionCauseDocumentDate($correctionInfo)
1164 {
1165 $documentDate = new Main\Type\DateTime($correctionInfo['document_date']);
1166
1167 return $documentDate->format('Y-m-d\TH:i:s');
1168 }
1169
1174 protected function getCorrectionCauseDocumentNumber($correctionInfo)
1175 {
1176 return $correctionInfo['document_number'];
1177 }
1178
1183 protected function getCorrectionTotalSum($correctionInfo)
1184 {
1185 return $correctionInfo['total_sum'];
1186 }
1187
1192 protected function getVatsByCheckData(array $data): ?array
1193 {
1194 if (!isset($data['vats']) || !is_array($data['vats']) || empty($data['vats']))
1195 {
1196 return null;
1197 }
1198
1199 $result = [];
1200 foreach ($data['vats'] as $item)
1201 {
1202 $vat = $this->getValueFromSettings('VAT', $item['type']);
1203 if (is_null($vat) || $vat === '')
1204 {
1205 $vat = $this->getValueFromSettings('VAT', 'NOT_VAT');
1206 }
1207
1208 switch ($vat)
1209 {
1210 case self::CODE_VAT_0:
1211 $vatKey = '3Sum';
1212 break;
1213 case self::CODE_VAT_10:
1214 $vatKey = '2Sum';
1215 break;
1216 case self::CODE_VAT_20:
1217 $vatKey = '1Sum';
1218 break;
1219 default:
1220 $vatKey = '4Sum';
1221 break;
1222 }
1223
1224 $result[] = [
1225 'code' => $this->getVatKeyPrefix() . $vatKey,
1226 'value' => $item['sum']
1227 ];
1228 }
1229
1230 return $result;
1231 }
1232
1236 protected function getVatKeyPrefix(): string
1237 {
1238 return 'tax';
1239 }
1240
1245 protected function getCorrectionTypeMap($type)
1246 {
1247 $map = [
1250 ];
1251
1252 return $map[$type] ?? 0;
1253 }
1254
1260 public function checkCorrection(CorrectionCheck $check)
1261 {
1262 return $this->checkInternal(
1263 $this->getUrl()
1264 . $this->getCorrectionUrlPath()
1265 . $this->getValueFromSettings('SERVICE', 'INN')
1266 . '/status/'
1267 . $check->getField('EXTERNAL_UUID')
1268 );
1269 }
1270}
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
getValueFromSettings($name, $code)
Definition cashbox.php:201
static extractSettingsFromRequest(Main\HttpRequest $request)
printCorrectionImmediately(CorrectionCheck $check)
buildCorrectionCheckQuery(CorrectionCheck $check)
buildCheckQueryByCheckData(array $checkData, bool $isSellReturn)
buildPosition(array $checkData, array $item, bool $isSellReturn)
const PAYMENT_OBJECT_SOCIAL_INSURANCE
Definition check.php:47
const PAYMENT_OBJECT_LOTTERY_PRIZE
Definition check.php:32
const PAYMENT_OBJECT_MEDICAL_INSURANCE_IP
Definition check.php:45
const PAYMENT_OBJECT_COMMODITY_MARKING_EXCISE
Definition check.php:50
const PAYMENT_OBJECT_NON_OPERATING_GAIN
Definition check.php:38
const PAYMENT_OBJECT_COMMODITY_MARKING_NO_MARKING_EXCISE
Definition check.php:49
const PAYMENT_OBJECT_RESORT_FEE
Definition check.php:40
const PAYMENT_OBJECT_PENSION_INSURANCE_IP
Definition check.php:43
const PAYMENT_OBJECT_PROPERTY_RIGHT
Definition check.php:37
const PAYMENT_OBJECT_GAMBLING_PRIZE
Definition check.php:30
const PAYMENT_OBJECT_COMMODITY_MARKING
Definition check.php:52
const PAYMENT_OBJECT_AGENT_COMMISSION
Definition check.php:34
const PAYMENT_OBJECT_MEDICAL_INSURANCE
Definition check.php:46
const PAYMENT_OBJECT_INTELLECTUAL_ACTIVITY
Definition check.php:33
const PAYMENT_OBJECT_GAMBLING_BET
Definition check.php:29
const PAYMENT_OBJECT_CASINO_PAYMENT
Definition check.php:48
const PAYMENT_OBJECT_PENSION_INSURANCE
Definition check.php:44
const PAYMENT_OBJECT_COMMODITY_MARKING_NO_MARKING
Definition check.php:51
static addDebugInfo(?string $message, $cashboxId=null)
Definition logger.php:43
static addError(?string $message, $cashboxId=null)
Definition logger.php:25