13Localization\Loc::loadMessages(__FILE__);
23 private const PARTNER_CODE_BITRIX =
'3010144';
34 private $pathToSslCertificate =
'';
35 private $pathToSslCertificateKey =
'';
43 private const MAX_TEXT_LENGTH = 128;
50 return Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_TITLE');
56 private function getCheckTypeMap()
82 private function getCalculatedSignMap()
98 ($check->
getType() ===
'sellreturn')
118 $calculatedSignMap = $this->getCalculatedSignMap();
121 'id' => static::buildUuid(static::UUID_TYPE_CHECK, $checkData[
'unique_id']),
123 'group' => $this->
getField(
'NUMBER_KKM') ?:
null,
126 'type' => $calculatedSignMap[$checkData[
'calculated_sign']],
132 'customerContact' => $this->getCustomerContact($checkData),
134 'meta' => self::PARTNER_CODE_BITRIX
137 foreach ($checkData[
'items'] as $item)
139 $result[
'content'][
'positions'][] = $this->
buildPosition($checkData, $item, $isSellReturn);
142 $paymentTypeMap = $this->getPaymentTypeMap();
143 foreach ($checkData[
'payments'] as $payment)
145 $result[
'content'][
'checkClose'][
'payments'][] = [
146 'type' => $paymentTypeMap[$payment[
'type']],
147 'amount' => $payment[
'sum'],
160 protected function buildPosition(array $checkData, array $item,
bool $isSellReturn): array
171 if (isset($item[
'nomenclature_code']))
173 $result[
'nomenclatureCode'] = $this->buildPositionNomenclatureCode($item);
176 if (isset($item[
'supplier_info']))
191 return mb_substr($item[
'name'], 0, self::MAX_TEXT_LENGTH);
200 return $item[
'quantity'];
209 return $item[
'price'];
218 $checkType = $this->getCheckTypeMap();
220 return $checkType[$checkData[
'type']];
231 return $paymentObjectMap[$item[
'payment_object']];
247 return $this->mapVatValue($checkData[
'type'], $vat);
254 private function buildPositionNomenclatureCode(array $item)
256 return base64_encode($item[
'nomenclature_code']);
272 'supplierInfo' =>
null,
273 'supplierINN' =>
null,
280 if (!empty($supplier[
'phones']))
282 $phoneParser = PhoneNumber\Parser::getInstance();
284 foreach ($supplier[
'phones'] as $phone)
286 $phoneNumber = $phoneParser->parse($phone);
290 $phoneLength += mb_strlen($formattedPhone) + 4;
291 if ($phoneLength > $maxTagLength)
296 $result[
'supplierInfo'][
'phoneNumbers'][] = $formattedPhone;
301 if (!empty($supplier[
'name']) && $phoneLength < $maxTagLength)
303 $result[
'supplierInfo'][
'name'] = mb_substr($supplier[
'name'], 0, $maxTagLength - $phoneLength);
306 if (!empty($supplier[
'supplier_info'][
'inn']))
308 $result[
'supplierINN'] = $supplier[
'supplier_info'][
'inn'];
319 private function mapVatValue($checkType, $vat)
322 self::CODE_VAT_10 => [
330 self::CODE_VAT_20 => [
340 return $map[$vat][$checkType] ?? $vat;
347 private function getCustomerContact(array $data)
351 if ($customerContact ===
'EMAIL')
353 return $data[
'client_email'];
355 elseif ($customerContact ===
'PHONE')
357 $phone = \NormalizePhone($data[
'client_phone']);
358 if ($phone[0] !==
'7')
366 if ($data[
'client_phone'])
368 $phone = \NormalizePhone($data[
'client_phone']);
369 if ($phone[0] !==
'7')
377 return $data[
'client_email'];
421 private function getPaymentTypeMap()
436 private function getPostQueryHeaders($url, $data)
438 $sign = $this->
sign($data);
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";
475 $this->getUrl().
'/documents/',
491 $encodedData = $this->encode($data);
493 $headers = $this->getPostQueryHeaders($url, $encodedData);
494 if ($headers ===
false)
496 return $result->addError(
503 $queryResult = $this->send($url, $headers, $encodedData);
504 if (!$queryResult->isSuccess())
506 return $result->addErrors($queryResult->getErrors());
509 $result->setData([
'UUID' => $data[
'id']]);
517 private function getUrl()
521 return static::HANDLER_ACTIVE_URL;
524 return static::HANDLER_TEST_URL;
534 private function send($url, $headers, $data =
'')
536 $context = $this->createStreamContext();
540 $client = stream_socket_client($url, $errNumber, $errString, 5, STREAM_CLIENT_CONNECT, $context);
542 $result =
new Result();
543 if ($client !==
false)
547 fputs($client, $headers.$data);
548 $response = stream_get_contents($client);
553 [$responseHeaders, $content] = explode(
"\r\n\r\n", $response);
554 $httpCode = $this->extractResponseStatus($responseHeaders);
556 $result->addData([
'http_code' => $httpCode,
'content' => $content]);
559 $httpCode !== static::RESPONSE_HTTP_CODE_201
561 $httpCode !== static::RESPONSE_HTTP_CODE_200
564 $content = $this->decode($content);
565 if (isset($content[
'errors']))
567 $error = implode(
"\n", $content[
'errors']);
571 $error = Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_ERROR_RESPONSE_'.$httpCode);
574 $error = Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_ERROR_CHECK_PRINT');
578 return $result->addError(
new Errors\
Error($error));
585 Localization\
Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_ERROR_SEND_QUERY')
589 $error =
new Errors\Error($errNumber.
': '.$errString);
600 private function extractResponseStatus($headers)
602 $headers = explode(
"\n", $headers);
603 preg_match(
'#HTTP\S+ (\d+)#', $headers[0], $find);
605 return (
int)$find[1];
613 if ($this->pathToSslCertificate !==
''
614 && Main\
IO\File::isFileExists($this->pathToSslCertificate)
617 unlink($this->pathToSslCertificate);
620 if ($this->pathToSslCertificateKey !==
''
621 && Main\
IO\File::isFileExists($this->pathToSslCertificateKey)
624 unlink($this->pathToSslCertificateKey);
631 private function createStreamContext()
634 $this->pathToSslCertificate = $this->createTmpFile($sslCert);
637 $this->pathToSslCertificateKey = $this->createTmpFile($sslKey);
639 return stream_context_create([
641 'local_cert' => $this->pathToSslCertificate,
642 'local_pk' => $this->pathToSslCertificateKey,
655 $url = $this->getUrl();
656 $url .=
'/documents/'.$this->getValueFromSettings(
'SERVICE',
'INN').
'/status/'.$check->
getField(
'EXTERNAL_UUID');
670 $header = $this->getCheckQueryHeaders($url);
671 $queryResult = $this->send($url, $header);
673 if (!$queryResult->isSuccess())
675 return $result->addErrors($queryResult->getErrors());
678 $data = $queryResult->getData();
680 $response = $this->decode($data[
'content']);
681 if ($response ===
false)
683 return $result->addError(
new Errors\
Error(Localization\
Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_ERROR_CHECK_CHECK')));
686 return static::applyCheckResult($response);
696 $result = parent::validate();
697 if (!$result->isSuccess())
709 'group' => $this->
getField(
'NUMBER_KKM') ?:
null,
721 $url = $this->getUrl().
'/check/';
723 $encodedData = $this->encode($data);
725 $headers = $this->getPostQueryHeaders($url, $encodedData);
727 return $this->send($url, $headers, $encodedData);
734 private function getCheckQueryHeaders($url)
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";
764 if (empty($checkInfo))
769 $result[
'ID'] = $checkInfo[
'ID'];
770 $result[
'CHECK_TYPE'] = $checkInfo[
'TYPE'];
774 $result[
'LINK_PARAMS'] = [
795 if (!function_exists(
'openssl_get_privatekey') || !function_exists(
'openssl_private_encrypt'))
800 $data = pack(
'H*',
'3031300d060960864801650304020105000420') . hash(
'sha256', $data,
true);
807 openssl_private_encrypt($data, $res, $pk);
808 return base64_encode($res);
816 private function encode(array $data)
818 return Main\Web\Json::encode($data, JSON_UNESCAPED_UNICODE);
825 private function decode($data)
829 return Main\Web\Json::decode($data);
831 catch (Main\ArgumentException $exception)
841 private function createTmpFile($data)
843 $filePath = tempnam(sys_get_temp_dir(),
'orange_data');
844 if ($filePath ===
false)
851 file_put_contents($filePath, $data);
869 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY'),
872 'TYPE' =>
'DATABASE_FILE',
873 'CLASS' =>
'adm-designed-file',
876 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_PKEY'),
879 'TYPE' =>
'DATABASE_FILE',
880 'CLASS' =>
'adm-designed-file',
883 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_SSL_CERT'),
886 'TYPE' =>
'DATABASE_FILE',
887 'CLASS' =>
'adm-designed-file',
890 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_SSL_KEY'),
894 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_SSL_KEY_PASS'),
898 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_KEY_SIGN'),
904 $settings[
'SERVICE'] = [
905 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_SERVICE'),
910 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_SERVICE_INN_LABEL')
915 $settings[
'CLIENT'] = [
916 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT'),
921 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_INFO'),
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'),
932 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_VAT'),
937 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_VAT_LABEL_NOT_VAT'),
943 if (Main\Loader::includeModule(
'catalog'))
945 $dbRes = Catalog\VatTable::getList([
'filter' => [
'ACTIVE' =>
'Y']]);
946 $vatList = $dbRes->fetchAll();
954 foreach ($vatList as $vat)
957 if (isset($defaultVatList[(
int)$vat[
'RATE']]))
958 $value = $defaultVatList[(int)$vat[
'RATE']];
960 $settings[
'VAT'][
'ITEMS'][(int)$vat[
'ID']] = [
962 'LABEL' => $vat[
'NAME'].
' ['.(
int)$vat[
'RATE'].
'%]',
970 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_SNO'),
975 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_SNO_LABEL'),
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')
989 if (static::hasMeasureSettings())
991 $settings[
'MEASURE'] = static::getMeasureSettings();
994 $settings[
'INTERACTION'] = [
995 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_INTERACTION'),
999 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_ORANGE_DATA_SETTINGS_MODE_HANDLER_LABEL'),
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'),
1027 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_MEASURE_SUPPORT_SETTINGS_DEFAULT_VALUE'),
1031 if (Main\Loader::includeModule(
'catalog'))
1033 $measuresList = \CCatalogMeasure::getList();
1034 while ($measure = $measuresList->fetch())
1036 $measureItems[$measure[
'CODE']] = [
1038 'LABEL' => $measure[
'MEASURE_TITLE'],
1045 'LABEL' => Localization\Loc::getMessage(
'SALE_CASHBOX_MEASURE_SUPPORT_SETTINGS'),
1046 'ITEMS' => $measureItems,
1056 global $APPLICATION;
1058 $settings = parent::extractSettingsFromRequest($request);
1059 $files = $request->getFile(
'SETTINGS');
1061 foreach (static::getSettings()[
'SECURITY'][
'ITEMS'] as $fieldId => $field)
1063 $error = $files[
'error'][
'SECURITY'][$fieldId] ??
null;
1064 $tmpName = $files[
'tmp_name'][
'SECURITY'][$fieldId] ??
null;
1066 if ($field[
'TYPE'] ===
'DATABASE_FILE'
1071 $content = $APPLICATION->GetFileContent($tmpName);
1072 $settings[
'SECURITY'][$fieldId] = $content ?:
'';
1106 return '/corrections/';
1118 $calculatedSignMap = $this->getCalculatedSignMap();
1121 'id' => static::buildUuid(static::UUID_TYPE_CHECK, $data[
'unique_id']),
1123 'group' => $this->
getField(
'NUMBER_KKM') ?:
null,
1126 'type' => $calculatedSignMap[$data[
'calculated_sign']],
1135 foreach ($data[
'payments'] as $payment)
1139 $result[
'content'][
'cashSum'] = (float)$payment[
'sum'];
1143 $result[
'content'][
'eCashSum'] = (float)$payment[
'sum'];
1148 if (is_array($vats))
1150 foreach ($vats as $vat)
1152 $result[
'content'][$vat[
'code']] = $vat[
'value'];
1167 return $documentDate->format(
'Y-m-d\TH:i:s');
1176 return $correctionInfo[
'document_number'];
1185 return $correctionInfo[
'total_sum'];
1194 if (!isset($data[
'vats']) || !is_array($data[
'vats']) || empty($data[
'vats']))
1200 foreach ($data[
'vats'] as $item)
1203 if (is_null($vat) || $vat ===
'')
1226 'value' => $item[
'sum']
1252 return $map[$type] ?? 0;
1267 . $check->
getField(
'EXTERNAL_UUID')
static getMessage($code, $replace=null, $language=null)
const CALCULATED_SIGN_INCOME
const PARAM_CALCULATION_ATTR
const PAYMENT_TYPE_ADVANCE
const PAYMENT_TYPE_CREDIT
const PARAM_FISCAL_DOC_ATTR
const PARAM_FISCAL_DOC_NUMBER
const CALCULATED_SIGN_CONSUMPTION
const PARAM_FISCAL_RECEIPT_NUMBER
const PARAM_REG_NUMBER_KKT
const PAYMENT_TYPE_CASHLESS
getValueFromSettings($name, $code)
buildPositionQuantity(array $item)
static extractSettingsFromRequest(Main\HttpRequest $request)
getCorrectionTypeMap($type)
buildPositionPrice(array $item)
const RESPONSE_HTTP_CODE_200
static getSettings($modelId=0)
buildPositionPaymentMethodType(array $checkData)
printCorrectionImmediately(CorrectionCheck $check)
buildCorrectionCheckQuery(CorrectionCheck $check)
static getMeasureSettings()
buildCheckQuery(Check $check)
getCheckData(AbstractCheck $check)
const RESPONSE_HTTP_CODE_201
buildCheckQueryByCheckData(array $checkData, bool $isSellReturn)
buildPositionPaymentSubjectType(array $item)
const HANDLER_MODE_ACTIVE
buildPositionText(array $item)
static extractCheckData(array $data)
buildPositionSupplier(array $supplier)
static hasMeasureSettings()
checkCorrection(CorrectionCheck $check)
printImmediately(Check $check)
getCorrectionTotalSum($correctionInfo)
getCorrectionCauseDocumentNumber($correctionInfo)
getVatsByCheckData(array $data)
getCorrectionCauseDocumentDate($correctionInfo)
registerCheck($url, $data)
buildPosition(array $checkData, array $item, bool $isSellReturn)
buildPositionTax(array $checkData, $item)
const PAYMENT_OBJECT_EXCISE
const PAYMENT_OBJECT_LOTTERY
const PAYMENT_OBJECT_SOCIAL_INSURANCE
const PAYMENT_OBJECT_COMPOSITE
const PAYMENT_OBJECT_LOTTERY_PRIZE
const PAYMENT_OBJECT_MEDICAL_INSURANCE_IP
const PAYMENT_OBJECT_COMMODITY_MARKING_EXCISE
const PAYMENT_OBJECT_NON_OPERATING_GAIN
const PAYMENT_OBJECT_COMMODITY_MARKING_NO_MARKING_EXCISE
const PAYMENT_OBJECT_RESORT_FEE
const PAYMENT_OBJECT_PENSION_INSURANCE_IP
const PAYMENT_OBJECT_PROPERTY_RIGHT
const PAYMENT_OBJECT_GAMBLING_PRIZE
const PAYMENT_OBJECT_COMMODITY_MARKING
const PAYMENT_OBJECT_COMMODITY
const PAYMENT_OBJECT_AGENT_COMMISSION
const PAYMENT_OBJECT_ANOTHER
const PAYMENT_OBJECT_DEPOSIT
const PAYMENT_OBJECT_MEDICAL_INSURANCE
const PAYMENT_OBJECT_SERVICE
const PAYMENT_OBJECT_INTELLECTUAL_ACTIVITY
const PAYMENT_OBJECT_PAYMENT
const PAYMENT_OBJECT_EXPENSE
const PAYMENT_OBJECT_GAMBLING_BET
const PAYMENT_OBJECT_CASINO_PAYMENT
const PAYMENT_OBJECT_PENSION_INSURANCE
const PAYMENT_OBJECT_COMMODITY_MARKING_NO_MARKING
const PAYMENT_OBJECT_SALES_TAX
static getCheckInfoByExternalUuid($uuid)
static getObjectById($id)
const CORRECTION_TYPE_INSTRUCTION
const CORRECTION_TYPE_SELF
static addDebugInfo(?string $message, $cashboxId=null)
static addError(?string $message, $cashboxId=null)
static getTag2108Value(?string $measureCode)