%PDF- %PDF-
| Direktori : /proc/self/root/home/bitrix/www/bitrix/modules/sale/lib/cashbox/ |
| Current File : //proc/self/root/home/bitrix/www/bitrix/modules/sale/lib/cashbox/cashboxorangedata.php |
<?php
namespace Bitrix\Sale\Cashbox;
use Bitrix\Main;
use Bitrix\Main\Text;
use Bitrix\Main\Localization;
use Bitrix\Sale\Cashbox\Errors;
use Bitrix\Sale\Result;
use Bitrix\Catalog;
Localization\Loc::loadMessages(__FILE__);
/**
* Class CashboxOrangeData
* @package Bitrix\Sale\Cashbox
*/
class CashboxOrangeData extends Cashbox implements IPrintImmediately, ICheckable
{
const RESPONSE_HTTP_CODE_200 = 200;
const RESPONSE_HTTP_CODE_201 = 201;
const HANDLER_MODE_TEST = 'TEST';
const HANDLER_MODE_ACTIVE = 'ACTIVE';
const HANDLER_TEST_URL = 'ssl://apip.orangedata.ru:2443/api/v2';
const HANDLER_ACTIVE_URL = 'ssl://api.orangedata.ru:12003/api/v2';
private $pathToSslCertificate = '';
private $pathToSslCertificateKey = '';
/**
* @return string
*/
public static function getName()
{
return Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_TITLE');
}
/**
* @return array
*/
private function getCheckTypeMap()
{
return array(
SellCheck::getType() => 4,
SellReturnCashCheck::getType() => 4,
SellReturnCheck::getType() => 4,
AdvancePaymentCheck::getType() => 3,
AdvanceReturnCashCheck::getType() => 3,
AdvanceReturnCheck::getType() => 3,
PrepaymentCheck::getType() => 2,
PrepaymentReturnCheck::getType() => 2,
PrepaymentReturnCashCheck::getType() => 2,
FullPrepaymentCheck::getType() => 1,
FullPrepaymentReturnCheck::getType() => 1,
FullPrepaymentReturnCashCheck::getType() => 1,
CreditCheck::getType() => 6,
CreditReturnCheck::getType() => 6,
CreditPaymentCheck::getType() => 7,
);
}
/**
* @return array
*/
private function getCalculatedSignMap()
{
return array(
Check::CALCULATED_SIGN_INCOME => 1,
Check::CALCULATED_SIGN_CONSUMPTION => 2
);
}
/**
* @param Check $check
* @return array
* @throws Main\NotImplementedException
*/
public function buildCheckQuery(Check $check)
{
$checkInfo = $check->getDataForCheck();
$calculatedSignMap = $this->getCalculatedSignMap();
$result = array(
'id' => static::buildUuid(static::UUID_TYPE_CHECK, $checkInfo['unique_id']),
'inn' => $this->getValueFromSettings('SERVICE', 'INN'),
'group' => $this->getField('NUMBER_KKM') ?: null,
'key' => $this->getValueFromSettings('SECURITY', 'KEY_SIGN') ?: null,
'content' => array(
'type' => $calculatedSignMap[$check::getCalculatedSign()],
'positions' => array(),
'checkClose' => array(
'payments' => array(),
'taxationSystem' => $this->getValueFromSettings('TAX', 'SNO'),
),
'customerContact' => $this->getCustomerContact($checkInfo),
)
);
$checkType = $this->getCheckTypeMap();
foreach ($checkInfo['items'] as $item)
{
$vat = $this->getValueFromSettings('VAT', $item['vat']);
if ($vat === null)
{
$vat = $this->getValueFromSettings('VAT', 'NOT_VAT');
}
$result['content']['positions'][] = array(
'text' => $item['name'],
'quantity' => $item['quantity'],
'price' => $item['price'],
'tax' => $vat,
'paymentMethodType' => $checkType[$check::getType()],
'paymentSubjectType' => null
);
}
$paymentTypeMap = $this->getPaymentTypeMap();
foreach ($checkInfo['payments'] as $payment)
{
$result['content']['checkClose']['payments'][] = array(
'type' => $paymentTypeMap[$payment['type']],
'amount' => $payment['sum'],
);
}
return $result;
}
/**
* @param array $data
* @return mixed|string
*/
private function getCustomerContact(array $data)
{
$customerContact = $this->getValueFromSettings('CLIENT', 'INFO');
if ($customerContact === 'EMAIL')
{
return $data['client_email'];
}
elseif ($customerContact === 'PHONE')
{
$phone = \NormalizePhone($data['client_phone']);
if ($phone[0] !== '7')
{
$phone = '7'.$phone;
}
return '+'.$phone;
}
if ($data['client_phone'])
{
$phone = \NormalizePhone($data['client_phone']);
if ($phone[0] !== '7')
{
$phone = '7'.$phone;
}
return '+'.$phone;
}
return $data['client_email'];
}
/**
* @return array
*/
private function getPaymentTypeMap()
{
return array(
Check::PAYMENT_TYPE_CASH => 1,
Check::PAYMENT_TYPE_CASHLESS => 2,
Check::PAYMENT_TYPE_ADVANCE => 14,
Check::PAYMENT_TYPE_CREDIT => 15,
);
}
/**
* @param $url
* @param $data
* @return string
*/
private function getPrintQueryHeaders($url, $data)
{
$sign = $this->sign($data);
if ($sign === false)
{
return false;
}
$urlObj = new Main\Web\Uri($url);
$header = "POST /api/v2/documents/ HTTP/1.0\r\n";
$header .= "Host: ".$urlObj->getHost()."\r\n";
$header .= "Accept: application/json\r\n";
$header .= "Content-Type: application/json\r\n";
$header .= "X-Signature: ".$sign."\r\n";
$header .= sprintf("Content-length: %s\r\n", Text\BinaryString::getLength($data));
$header .= "\r\n";
return $header;
}
/**
* @param $id
* @return array
*/
public function buildZReportQuery($id)
{
return array();
}
/**
* @param Check $check
* @return Result
* @throws Main\ArgumentException
* @throws Main\NotImplementedException
*/
public function printImmediately(Check $check)
{
$result = new Result();
$url = $this->getUrl();
$url .= '/documents/';
$data = $this->buildCheckQuery($check);
$encodedData = $this->encode($data);
$headers = $this->getPrintQueryHeaders($url, $encodedData);
if ($headers === false)
{
$result->addError(new Errors\Error(Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_SIGN')));
return $result;
}
$queryResult = $this->sendQuery($url, $headers, $encodedData);
if (!$queryResult->isSuccess())
{
$result->addErrors($queryResult->getErrors());
return $result;
}
$response = $queryResult->getData();
$httpCode = $response['http_code'];
if ($httpCode === static::RESPONSE_HTTP_CODE_201)
{
$result->setData(array('UUID' => $data['id']));
}
else
{
$error = '';
if (isset($response['content']))
{
$content = $this->decode($response['content']);
if (isset($content['errors']))
{
$error = implode("\n", $content['errors']);
}
else
{
$error = Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_RESPONSE_'.$httpCode);
}
}
if (!$error)
{
$error = Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_CHECK_PRINT');
}
$result->addError(new Errors\Error($error));
}
return $result;
}
/**
* @return string
*/
private function getUrl()
{
if ($this->getValueFromSettings('INTERACTION', 'MODE_HANDLER') === static::HANDLER_MODE_ACTIVE)
{
return static::HANDLER_ACTIVE_URL;
}
return static::HANDLER_TEST_URL;
}
/**
* @param $url
* @param $headers
* @param string $data
* @return Result
*/
private function sendQuery($url, $headers, $data = '')
{
$context = $this->createStreamContext();
$errNumber = '';
$errString = '';
$client = stream_socket_client($url, $errNumber, $errString, 5, STREAM_CLIENT_CONNECT, $context);
$result = new Result();
if ($client !== false)
{
fputs($client, $headers.$data);
$response = stream_get_contents($client);
fclose($client);
list($responseHeaders, $content) = explode("\r\n\r\n", $response);
$httpCode = $this->extractResponseStatus($responseHeaders);
$result->addData(array('http_code' => $httpCode, 'content' => $content));
}
else
{
$result->addError(
new Errors\Error(
Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_SEND_QUERY')
)
);
$error = new Errors\Error($errNumber.': '.$errString);
Manager::writeToLog($this->getField('ID'), $error);
}
return $result;
}
/**
* @param $headers
* @return int
*/
private function extractResponseStatus($headers)
{
$headers = explode("\n", $headers);
preg_match('#HTTP\S+ (\d+)#', $headers[0], $find);
return (int)$find[1];
}
/**
* @return void
*/
public function __destruct()
{
if ($this->pathToSslCertificate !== ''
&& Main\IO\File::isFileExists($this->pathToSslCertificate)
)
{
unlink($this->pathToSslCertificate);
}
if ($this->pathToSslCertificateKey !== ''
&& Main\IO\File::isFileExists($this->pathToSslCertificateKey)
)
{
unlink($this->pathToSslCertificateKey);
}
}
/**
* @return resource
*/
private function createStreamContext()
{
$sslCert = $this->getValueFromSettings('SECURITY', 'SSL_CERT');
$this->pathToSslCertificate = $this->createTmpFile($sslCert);
$sslKey = $this->getValueFromSettings('SECURITY', 'SSL_KEY');
$this->pathToSslCertificateKey = $this->createTmpFile($sslKey);
return stream_context_create(array(
'ssl' => array(
'local_cert' => $this->pathToSslCertificate,
'local_pk' => $this->pathToSslCertificateKey,
'passphrase' => $this->getValueFromSettings('SECURITY', 'SSL_KEY_PASS'),
)
));
}
/**
* @param Check $check
* @return Result
*/
public function check(Check $check)
{
$result = new Result();
$url = $this->getUrl();
$url .= '/documents/'.$this->getValueFromSettings('SERVICE', 'INN').'/status/'.$check->getField('EXTERNAL_UUID');
$header = $this->getCheckQueryHeaders($url);
$queryResult = $this->sendQuery($url, $header);
$data = $queryResult->getData();
if ($data['http_code'] !== static::RESPONSE_HTTP_CODE_200)
{
$error = Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_RESPONSE_'.$data['http_code']);
if (!$error)
{
$error = implode("\n", $queryResult->getErrorMessages());
}
$result->addError(new Errors\Error($error));
return $result;
}
$response = $this->decode($data['content']);
if ($response === false)
{
$result->addError(new Errors\Error(Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_ERROR_CHECK_CHECK')));
return $result;
}
return static::applyCheckResult($response);}
/**
* @param $url
* @return string
*/
private function getCheckQueryHeaders($url)
{
$urlObj = new Main\Web\Uri($url);
$header = "GET ".$urlObj->getPath()." HTTP/1.0\r\n";
$header .= "Host: ".$urlObj->getHost()."\r\n";
$header .= "Accept: application/json\r\n";
$header .= "Content-Type: application/json\r\n";
$header .= "\r\n";
return $header;
}
/**
* @param array $data
* @return array
* @throws Main\ArgumentException
* @throws Main\NotImplementedException
* @throws Main\ObjectException
*/
protected static function extractCheckData(array $data)
{
$result = array();
if (!$data['id'])
{
return $result;
}
$checkInfo = CheckManager::getCheckInfoByExternalUuid($data['id']);
$result['ID'] = $checkInfo['ID'];
$result['CHECK_TYPE'] = $checkInfo['TYPE'];
$check = CheckManager::getObjectById($checkInfo['ID']);
$dateTime = new Main\Type\DateTime($data['processedAt'], 'Y-m-d\TH:i:s.u');
$result['LINK_PARAMS'] = array(
Check::PARAM_REG_NUMBER_KKT => $data['deviceRN'],
Check::PARAM_FISCAL_DOC_ATTR => $data['fp'],
Check::PARAM_FISCAL_DOC_NUMBER => $data['documentNumber'],
Check::PARAM_FISCAL_RECEIPT_NUMBER => $data['documentIndex'],
Check::PARAM_FN_NUMBER => $data['fsNumber'],
Check::PARAM_SHIFT_NUMBER => $data['shiftNumber'],
Check::PARAM_DOC_SUM => $data['total'],
Check::PARAM_DOC_TIME => $dateTime->getTimestamp(),
Check::PARAM_CALCULATION_ATTR => $check::getCalculatedSign()
);
return $result;
}
/**
* @param $data
* @return string
*/
public function sign($data)
{
if (!function_exists('openssl_get_privatekey') || !function_exists('openssl_private_encrypt'))
{
return false;
}
$data = pack('H*', '3031300d060960864801650304020105000420') . hash('sha256', $data, true);
$pk = openssl_get_privatekey($this->getValueFromSettings('SECURITY', 'PKEY'));
openssl_private_encrypt($data, $res, $pk);
return base64_encode($res);
}
/**
* @param array $data
* @return mixed
* @throws Main\ArgumentException
*/
private function encode(array $data)
{
return Main\Web\Json::encode($data, JSON_UNESCAPED_UNICODE);
}
/**
* @param string $data
* @return mixed
*/
private function decode($data)
{
try
{
return Main\Web\Json::decode($data);
}
catch (Main\ArgumentException $exception)
{
return false;
}
}
/**
* @param $data
* @return mixed
*/
private function createTmpFile($data)
{
$filePath = tempnam(sys_get_temp_dir(), 'orange_data');
if ($data !== null)
{
file_put_contents($filePath, $data);
}
return $filePath;
}
/**
* @param int $modelId
* @return array
* @throws Main\ArgumentException
* @throws Main\LoaderException
* @throws Main\ObjectPropertyException
* @throws Main\SystemException
*/
public static function getSettings($modelId = 0)
{
$settings = array(
'SECURITY' => array(
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY'),
'ITEMS' => array(
'PKEY' => array(
'TYPE' => 'SECURITY_FILE_CONTROL',
'CLASS' => 'adm-designed-file',
'REQUIRED' => 'Y',
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_PKEY'),
),
'SSL_CERT' => array(
'TYPE' => 'SECURITY_FILE_CONTROL',
'CLASS' => 'adm-designed-file',
'REQUIRED' => 'Y',
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_SSL_CERT'),
),
'SSL_KEY' => array(
'TYPE' => 'SECURITY_FILE_CONTROL',
'CLASS' => 'adm-designed-file',
'REQUIRED' => 'Y',
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_SSL_KEY'),
),
'SSL_KEY_PASS' => array(
'TYPE' => 'STRING',
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_SSL_KEY_PASS'),
),
'KEY_SIGN' => array(
'TYPE' => 'STRING',
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SECURITY_KEY_SIGN'),
),
)
)
);
$settings['SERVICE'] = array(
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SERVICE'),
'REQUIRED' => 'Y',
'ITEMS' => array(
'INN' => array(
'TYPE' => 'STRING',
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SERVICE_INN_LABEL')
)
)
);
$settings['CLIENT'] = [
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT'),
'ITEMS' => array(
'INFO' => array(
'TYPE' => 'ENUM',
'VALUE' => 'NONE',
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_INFO'),
'OPTIONS' => array(
'DEFAULT' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_DEFAULT'),
'PHONE' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_PHONE'),
'EMAIL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_CLIENT_EMAIL'),
)
),
)
];
$settings['VAT'] = array(
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_VAT'),
'REQUIRED' => 'Y',
'ITEMS' => array(
'NOT_VAT' => array(
'TYPE' => 'STRING',
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_VAT_LABEL_NOT_VAT'),
'VALUE' => 6
)
)
);
if (Main\Loader::includeModule('catalog'))
{
$dbRes = Catalog\VatTable::getList(array('filter' => array('ACTIVE' => 'Y')));
$vatList = $dbRes->fetchAll();
if ($vatList)
{
$defaultVat = array(0 => 5, 10 => 2, 18 => 1, 20 => 1);
foreach ($vatList as $vat)
{
$value = '';
if (isset($defaultVat[(int)$vat['RATE']]))
$value = $defaultVat[(int)$vat['RATE']];
$settings['VAT']['ITEMS'][(int)$vat['ID']] = array(
'TYPE' => 'STRING',
'LABEL' => $vat['NAME'].' ['.(int)$vat['RATE'].'%]',
'VALUE' => $value
);
}
}
}
$settings['TAX'] = array(
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SNO'),
'REQUIRED' => 'Y',
'ITEMS' => array(
'SNO' => array(
'TYPE' => 'ENUM',
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_SNO_LABEL'),
'VALUE' => 0,
'OPTIONS' => array(
0 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_OSN'),
1 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_UI'),
2 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_UIO'),
3 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_ENVD'),
4 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_ESN'),
5 => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SNO_PATENT')
)
)
)
);
$settings['INTERACTION'] = array(
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_INTERACTION'),
'ITEMS' => array(
'MODE_HANDLER' => array(
'TYPE' => 'ENUM',
'LABEL' => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_SETTINGS_MODE_HANDLER_LABEL'),
'OPTIONS' => array(
static::HANDLER_MODE_ACTIVE => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_MODE_ACTIVE'),
static::HANDLER_MODE_TEST => Localization\Loc::getMessage('SALE_CASHBOX_ORANGE_DATA_MODE_TEST'),
)
)
)
);
return $settings;
}
/**
* @param Main\HttpRequest $request
* @return array
*/
public static function extractSettingsFromRequest(Main\HttpRequest $request)
{
global $APPLICATION;
$settings = parent::extractSettingsFromRequest($request);
$files = $request->getFile('SETTINGS');
foreach ($settings['SECURITY'] as $fieldId => $field)
{
if ($files['error']['SECURITY'][$fieldId.'_FILE'] === 0
&& $files['tmp_name']['SECURITY'][$fieldId.'_FILE']
)
{
$content = $APPLICATION->GetFileContent($files['tmp_name']['SECURITY'][$fieldId.'_FILE']);
$settings['SECURITY'][$fieldId] = $content ?: '';
}
}
return $settings;
}
/**
* @return bool
*/
public static function isSupportedFFD105()
{
return true;
}
}