<?php
namespace Models;

class php_shtrihm_json_Fr_php {
	public $f3, $sets, $web, $config, $sp, $core;

	public $new_smde=true;

	public $pay_cash=0;

	public $class;

	public $admin_pass=30;

	public $mpCapCashCore=23;

	public $tlv_types=array(
		1=>array(
			'type'=>3,
			'param'=>'TagValueInt'
		),
 		3=>array(
			'type'=>6,
			'param'=>'TagValueDateTime'
		),
 		4=>array(
 			'type'=>5,
 			'param'=>'TagValueBin'
 		),
 		5=>array(
 			'type'=>7,
 			'param'=>'TagValueStr'
 		)
	);

	public $kkm_state_data=array(
		'GetECRStatus'=>array(
			'Get_ECRSoftVersion'=>'Версия внутреннего программного обеспечения ККМ',
			'Get_ECRBuild'=>'Номер сборки ПО ККМ',
			'Get_ECRSoftDate'=>'Дата внутреннего программного обеспечения ККМ',
			'Get_LogicalNumber'=>'Логический номер ККМ в торговом зале',
			'Get_OpenDocumentNumber'=>'Сквозной номер последнего документа ККМ',
			'Get_ReceiptRibbonIsPresent'=>array(
				'title'=>'Признак наличия в ККМ рулона чековой ленты',
				'data'=>array(
					0=>'рулона нет',
					1=>'рулон есть'
				)
			),
			'Get_JournalRibbonIsPresent'=>array(
				'title'=>'Признак наличия в ККМ рулона операционного журнала',
				'data'=>array(
					0=>'рулона нет',
					1=>'рулон есть'
				)
			),
			'Get_SlipDocumentIsPresent'=>array(
				'title'=>'Признак наличия в ККМ подкладного документа',
				'data'=>array(
					0=>'подкладного документа нет',
					1=>'подкладной документ есть'
				)
			),
			'Get_SlipDocumentIsMoving'=>array(
				'title'=>'Признак прохождения подкладного документа под датчиком контроля подкладного документа',
				'data'=>array(
					0=>'подкладной документ отсутствует',
					1=>'подкладной документ проходит под датчиком'
				)
			),
			'Get_PointPosition'=>array(
				'title'=>'Признак положения десятичной точки',
				'data'=>array(
					0=>'десятичная точка отделяет 0 разрядов',
					1=>'десятичная точка отделяет 2 разряда'
				)
			),
			'Get_EKLZIsPresent'=>array(
				'title'=>'Признак наличия в ККМ ЭКЛЗ',
				'data'=>array(
					0=>'ЭКЛЗ нет',
					1=>'ЭКЛЗ есть'
				)
			),
			'Get_JournalRibbonOpticalSensor'=>array(
				'title'=>'Признак прохождения ленты операционного журнала под оптическим датчиком операционного журнала',
				'data'=>array(
					0=>'ленты операционного журнала нет',
					1=>'лента операционного журнала проходит под оптическим датчиком'
				)
			),
			'Get_ReceiptRibbonOpticalSensor'=>array(
				'title'=>'Признак прохождения чековой ленты под оптическим датчиком чековой ленты',
				'data'=>array(
					0=>'чековой ленты нет',
					1=>'чековая лента проходит под оптическим датчиком'
				)
			),
			'Get_JournalRibbonLever'=>array(
				'title'=>'Признак положения рычага термоголовки ленты операционного журнала',
				'data'=>array(
					0=>'рычаг термоголовки ленты опущен',
					1=>'рычаг термоголовки ленты операционного журнала поднят'
				)
			),
			'Get_ReceiptRibbonLever'=>array(
				'title'=>'Признак положения рычага термоголовки чековой ленты',
				'data'=>array(
					0=>'рычаг термоголовки чековой ленты опущен',
					1=>'рычаг термоголовки чековой ленты поднят'
				)
			),
			'Get_LidPositionSensor'=>array(
				'title'=>'Признак положения крышки корпуса',
				'data'=>array(
					0=>'крышка корпуса установлена',
					1=>'крышка корпуса не установлена'
				)
			),
			'Get_IsPrinterLeftSensorFailure'=>array(
				'title'=>'Признак отказа левого датчика печатающего механизма',
				'data'=>array(
					0=>'отказа датчика нет',
					1=>'имеет место отказ датчика'
				)
			),
			'Get_IsPrinterRightSensorFailure'=>array(
				'title'=>'Признак отказа правого датчика печатающего механизма',
				'data'=>array(
					0=>'отказа датчика нет',
					1=>'имеет место отказ датчика'
				)
			),
			'Get_IsDrawerOpen'=>array(
				'title'=>'Признак состояния денежного ящика',
				'data'=>array(
					0=>'денежный ящик закрыт',
					1=>'денежный ящик открыт'
				)
			),
			'Get_IsEKLZOverflow'=>array(
				'title'=>'Признак состояния ЭКЛЗ',
				'data'=>array(
					0=>'ЭКЛЗ еще не близка к переполнению',
					1=>'ЭКЛЗ близка к переполнению'
				)
			),
			'Get_QuantityPointPosition'=>array(
				'title'=>'Признак положения десятичной точки в количестве товара',
				'data'=>array(
					0=>'6 знаков после запятой',
					1=>'3 знака после запятой'
				)
			),
			'Get_ECRMode'=>array(
				'title'=>'Режим ККМ',
				'data'=>array(
					0=>'Принтер в рабочем режиме',
					1=>'Выдача данных',
					2=>'Открытая смена, 24 часа не кончились',
					3=>'Открытая смена, 24 часа кончились',
					4=>'Закрытая смена',
					5=>'Блокировка по неправильному паролю налогового инспектора',
					6=>'Ожидание подтверждения ввода даты',
					7=>'Разрешение изменения положения десятичной точки',
					8=>'Открытый документ',
					9=>'Режим разрешения технологического обнуления',
					10=>'Тестовый прогон',
					11=>'Печать полного фискального отчета',
					12=>'Печать длинного отчета ЭКЛЗ',
					13=>'Работа с фискальным подкладным документом',
					14=>'Печать подкладного документа',
					15=>'Фискальный подкладной документ сформирован'
				)
			),
			'Get_ECRMode8Status'=>array(
				'title'=>'Подрежим режима "Открытый документ"',
				'data'=>array(
					0=>'Открыт чек продажи',
					1=>'Открыт чек покупки',
					2=>'Открыт чек возврата продажи',
					3=>'Открыт чек возврата покупки'
				)
			),
			'Get_ECRAdvancedMode'=>array(
				'title'=>'Подрежим ККМ',
				'data'=>array(
					0=>'Бумага есть',
					1=>'Пассивное отсутствие бумаги',
					2=>'Активное отсутствие бумаги',
					3=>'После активного отсутствия бумаги',
					4=>'Фаза печати операции длинного отчета',
					5=>'Фаза печати операции',
				)
			),
			'Get_PortNumber'=>'Порт ККМ',
			'Get_FMSoftVersion'=>'Версия внутреннего программного обеспечения ФП ККМ',
			'Get_FMBuild'=>'Номер сборки ПО ФП ККМ',
			'Get_FMSoftDate'=>'Дата внутреннего программного обеспечения ККМ',
			'Get_Date'=>'Внутренняя дата ККМ',
			'Get_Time'=>'Внутренне время ККМ',
			'Get_FM1IsPresent'=>array(
				'title'=>'Признак наличия в ККМ ФП1',
				'data'=>array(
					0=>'ФП1 нет',
					1=>'ФП1 есть'
				)
			),
			'Get_FM2IsPresent'=>array(
				'title'=>'Признак наличия в ККМ ФП2',
				'data'=>array(
					0=>'ФП2 нет',
					1=>'ФП2 есть'
				)
			),
			'Get_LicenseIsPresent'=>array(
				'title'=>'Признак наличия в ККМ лицензии',
				'data'=>array(
					0=>'лицензия не введена',
					1=>'лицензия введена'
				)
			),
			'Get_FMOverflow'=>array(
				'title'=>'Признак переполнения ФП',
				'data'=>array(
					0=>'переполнения ФП нет',
					1=>'переполнение ФП'
				)
			),
			'Get_IsBatteryLow'=>array(
				'title'=>'Признак напряжения на батарее',
				'data'=>array(
					0=>'напряжение нормальное',
					1=>'напряжение пониженное'
				)
			),
			'Get_IsLastFMRecordCorrupted'=>array(
				'title'=>'Признак испорченности последней записи в ФП',
				'data'=>array(
					0=>'не испорчена',
					1=>'испорчена'
				)
			),
			'Get_IsFMSessionOpen'=>array(
				'title'=>'Признак открытой смены в ФП',
				'data'=>array(
					0=>'закрыта',
					1=>'открыта'
				)
			),
			'Get_IsFM24HoursOver'=>array(
				'title'=>'Признак истечения 24 часов в ФП',
				'data'=>array(
					0=>'24 часа не истекли',
					1=>'24 часа истекли'
				)
			),
			'Get_SerialNumber'=>'Серийный номер ККМ',
			'Get_SessionNumber'=>'Номер последней закрытой на ККМ смены',
			'Get_FreeRecordInFM'=>'Количество свободных записей в ФП',
			'Get_RegistrationNumber'=>'Количество перерегистраций',
			'Get_FreeRegistration'=>'Количество оставшихся перерегистраций',
			'Get_INN'=>'Идентификационный номер налогоплательщика',
		),
		'FNGetExpirationTime'=>array(
			'Get_Date'=>'Срок действия ФН',
		),
		'FNGetInfoExchangeStatus'=>array(
			'Get_MessageCount'=>'Количество сообщений для ОФД'
		),
		'Get_FNGetSerial'=>array(
			'FNGetSerial'=>'Номер ФН'
		)
	);

	public $tableParams=array(
		'classic'=>array(
			'inn'=>array(
				'table'=>18,
				'row'=>1,
				'cell'=>2,
			),
			'org_name'=>array(
				'table'=>18,
				'row'=>1,
				'cell'=>7,
			),
			'org_address'=>array(
				'table'=>18,
				'row'=>1,
				'cell'=>9,
			),
			'no_print'=>array(
				'table'=>17,
				'row'=>1,
				'cell'=>7,
			),
			'tax'=>array(
				'table'=>6,
				'row'=>1,
				'cell'=>1
			),
			'users'=>array(
				'table'=>2,
				'row'=>30,
				'cell'=>2
			)
		),
		'core'=>array(
			'inn'=>array(
				'table'=>13,
				'row'=>1,
				'cell'=>2,
			),
			'org_name'=>array(
				'table'=>13,
				'row'=>1,
				'cell'=>7,
			),
			'org_address'=>array(
				'table'=>13,
				'row'=>1,
				'cell'=>9,
			),
			'no_print'=>array(
				'table'=>1,
				'row'=>1,
				'cell'=>48,
			),
			'tax'=>array(
				'table'=>6,
				'row'=>1,
				'cell'=>1
			),
			'users'=>array(
				'table'=>2,
				'row'=>30,
				'cell'=>2
			),
			'users_inn'=>array(
				'table'=>17,
				'row'=>1,
				'cell'=>23
			)
		)
	);

	public $receipt_types=array(
		0=>0,
		1=>2,
		128=>128,
		130=>130
	);

	public $operation_type=array(
		0=>1,
		2=>2,
		128=>1,
		130=>2
	);

	public $after_check_mark='no';

	public $CheckItemLocalError=array(
		2=>'ФН не содержит ключ проверки кода проверки этого КМ',
		3=>'Проверка невозможна, так как отсутствуют идентификаторы применения GS1 91 и / или 92 или их формат неверный',
		4=>'Внутренняя ошибка в ФН при проверке этого КМ'
	);

	public $KMServerErrorCode=array(
		1=>'КИЗ отсутствует в базе Серверы СКЗКМ или КИЗ отсутствует в базе ИСМ',
		2=>'Не корректен формат КИЗ',
		3=>'Криптографическая проверка КПКИЗ дала отрицательный результат',
		4=>'КИЗ имеет в базе Серверы СКЗКМ статус не совместимый с запрашиваемым изменением.',
		5=>'В списке вложения обнаружены ошибки'
	);

	public $KMServerCheckingStatus=array(
		0=>array(0=>'Код маркировки не был проверен ФН и (или) ОИСМ'),
		1=>array(0=>'Результат проверки КП КМ отрицательный'),
		2=>array(0=>'Сведения о статусе товара от ОИСМ не получены'),
		3=>array(0=>'От ОИСМ получены сведения, что планируемый статус товара некорректен'),
	);

	public $vat=array(
		'vatNo'=>4,
		'vat0'=>3,
		'vat105'=>9,
		'vat5'=>7,
		'vat107'=>10,
		'vat7'=>8,
		'vat110'=>6,
		'vat10'=>2,
		'vat120'=>5,
		'vat20'=>1
	);

	public $connect=false;

	public $fd=0; // Номер ФД
	public $fp=0; // Фискальный признак
	public $kkm_time=0; // Время чека
	
	function __construct($f3, $sets, $config) {
		$this->f3=$f3;
		$u=explode('//', $sets['url']);
		$u=explode(':', $u[1]);
		$sets['host']=$u[0];
		$sets['port']=$u[1];
		$this->sets=$sets;

		$this->web=\Web::instance();
        $this->config=$config;
        $this->class='agorta'.$this->rm_name();
        if(!isset($this->config['ConnectionURI'])) {
            $this->config['ConnectionURI']='tcp://192.168.137.111:7778?timeout=15000&protocol=v1&enq_mode=1&ack_timeout=1000';
        }
        if(!isset($this->config['FontSize']) || $this->config['FontSize']>9 || $this->config['FontSize']<1) {
            $this->config['FontSize']=1;
        }
        if(!isset($this->config['FontBold'])) {
            $this->config['FontBold']=2;
        }
        if(!isset($this->config['PaperWidth'])) {
            $this->config['PaperWidth']=32;
        }

        if(!isset($this->config['QRSize'])) {
            $this->config['QRSize']=5;
        }

        $url_components = parse_url($this->config['ConnectionURI']);
		parse_str($url_components['query'], $params);
		if($params['timeout']) {
			$this->timeout=$params['timeout']/1000+1;
		}
		else {
			$this->timeout=16;
		}

		foreach($this->vat as $type=>$vat){
			if(!isset($this->config['vat_type'][$type])) {
            	$this->config['vat_type'][$type]=$vat;
        	}
		}
		$this->url=$this->sets['url'].'/classic/'.$this->class;
		require_once($this->f3->get('ROOT').'/app/websockets/ClientInterface.php');
		require_once($this->f3->get('ROOT').'/app/websockets/ConnectionException.php');
		require_once($this->f3->get('ROOT').'/app/websockets/Client.php');
	}

	function __destruct() {
		$this->Disconnect();
	}

	function Disconnect() {
		if($this->connect && $this->sp) {
			$data=array(
				array(
					'method'=>'Disconnect'
				)
			);
			$this->send_task($data);
			$this->connect=false;
			$this->sp=false;
			// Удаляем экземпляр классического интерфейса
			$options=array(
				'method'=>'DELETE'
			);
			$close_res=$this->web->request($this->url, $options);
			if(mb_strpos($close_res['headers'][0], '200')===false) {
				$error.='Disconnect. Не удается удалить экземпляр классического интерфейса';
				$this->f3->get('logs_model')->save_log($error.' (Касса)');
			}
		}
	}

	function kkm_init() {
		//$this->f3->set('SESSION.'.$this->sets['printer'].'_drivers_init', 1);
	}

	function rm_name() {
		$russian = array('А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я', 'а', 'б', 'в', 'г', 'д', 'е', 'ё', 'ж', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п', 'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч', 'ш', 'щ', 'ъ', 'ы', 'ь', 'э', 'ю', 'я');

		$translit = array('A', 'B', 'V', 'G', 'D', 'E', 'E', 'Gh', 'Z', 'I', 'Y', 'K', 'L', 'M', 'N', 'O', 'P', 'R', 'S', 'T', 'U', 'F', 'H', 'C', 'Ch', 'Sh', 'Sch', 'Y', 'Y', 'Y', 'E', 'Yu', 'Ya', 'a', 'b', 'v', 'g', 'd', 'e', 'e', 'gh', 'z', 'i', 'y', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 't', 'u', 'f', 'h', 'c', 'ch', 'sh', 'sch', 'y', 'y', 'y', 'e', 'yu', 'ya');

		$rm=str_replace($russian, $translit, $this->sets['rm']);
		$rm=preg_replace('/[^a-zA-Z\d]/ui', '', $rm);

		return $rm;
	}

	// Отправка задания
	function send_task($data) {
		$n=0;
		$methods=implode(', ', array_column($data, 'method'));
		if(!$this->connect) {
			// Создаем класс экземпляр классического интерфейса
			$options=array(
				'method'=>'POST'
			);
			$res=$this->web->request($this->url, $options);
			$json=json_decode($res['body'], true);
			if((mb_strpos($res['headers'][0], '201')===false && mb_strpos($res['headers'][0], '409')===false) || !$res['headers'][0]) {
				return array(
				    'success'=>false,
					'txt'=>$methods.'. Не удается создасть экземпляр классического интерфейса'
				);
			}
			$vars=array(
				0=>array(
					'jsonrpc'=>'2.0',
					'method'=>'Connect',
					'params'=>array(
						'Password'=>$this->admin_pass,
						'ConnectionURI'=>$this->config['ConnectionURI'],
					),
					'id'=>0
				)
			);
			$n=1;
			$this->connect=true;
		}

		foreach($data as $d){
			if(!$d['params']) $d['params']=array();
			if(!isset($d['params']['Password']) && mb_substr($d['method'], 0, 4)!='Set_' && mb_substr($d['method'], 0, 4)!='Get_') $d['params']['Password']=$this->admin_pass;

			$vars[$n]=array(
				'jsonrpc'=>'2.0',
				'method'=>$d['method'],
				'params'=>$d['params'],
				'id'=>$n
			);
			$n++;
		}

		$ok=true;
		$mark=false;

		//Отправляем команды
		if(!$this->sp){
			$this->sp=new \Paragi\PhpWebsocket\Client((string)$this->sets['host'], (int)$this->sets['port'], '', '', $this->timeout, false, false, '/classic/'.$this->class);
		}
		$this->sp->write(json_encode($vars, JSON_PRESERVE_ZERO_FRACTION), true, false); //добавил, чтобы стринги были
		$this->request='';
		try {
			$this->request=$this->sp->read();
		} catch (\Paragi\PhpWebsocket\ConnectionException $e) {
			$ok=false;
    		return array(
			    'success'=>false,
				'txt'=>$methods.'. Не удается получить ответ'
			);
		}

    	$res=json_decode($this->request, true);
    	$result=array();
    	if(isset($res['error'])) {
    		// Не типичный ответ при ошибках
    		$res=array(
    			$res
    		);
    	}
    	if($res) {
    		foreach($res as $r){
    			if(is_array($r) && !array_key_exists('result', $r)) {
    				if(isset($r['error'])) {
    					$r['result']=array(
    						'ResultCode'=>$r['error']['code'],
    						'ResultCodeDescription'=>$r['error']['message'],
    					);
    				}
    				else {
    					$r['result']=array(
    						'ResultCode'=>-9999999,
    						'ResultCodeDescription'=>'Что-то подозрительное',
    					);
    				}
    			}
    			elseif(!is_array($r['result'])) {
    				$r['result']=array(
    					'ResultCode'=>0,
    					'value'=>$r['result']
    				);
    			}
    			if($r['result']['ResultCode']==0) {
    				$r['result']['ResultCodeDescription']='Выполнено успешно';
    			}
    			elseif($r['result']['ResultCode']!=55) {
    				$ok=false;
    				$error_txt.='<p class="false">Метод: '.$vars[$r['id']]['method'].'. Код ответа: '.$r['result']['ResultCode'].'. '.$r['result']['ResultCodeDescription'].' Парметры: '.json_encode($vars[$r['id']]['params']).'</p>';
    			}

    			if($vars[$r['id']]['method']=='Get_DocumentNumber') {
    				$this->fd=$r['result']['value'];
    				$this->kkm_time=time();
    			}
    			if($vars[$r['id']]['method']=='Get_FiscalSign') {
    				$this->fp=$r['result']['value'];
    			}
    			if($vars[$r['id']]['method']=='FNCloseCheckEx') {
    				$this->kkm_time=time();
    				$this->fd=$r['result']['DocumentNumber'];
    				$this->fp=$r['result']['FiscalSign'];
    			}
    			if($vars[$r['id']]['method']=='FNGetStatus') {
    				$this->kkm_time=$r['result']['Date'];
    			}

    			if($vars[$r['id']]['method']=='FNGetDocumentAsString') {
    				$this->send_task($this->text_format($r['result']['StringForPrinting']));
    			}

    			/*if($vars[$r['id']]['method']=='FNCheckItemBarcode') {
    				if(isset($this->CheckItemLocalError[$r['result']['CheckItemLocalError']])) {
    					$r['result']['ResultCode']=-9999998;
    					$ok=false;
    					$error_txt.='<p class="false">'.$this->CheckItemLocalError[$r['result']['CheckItemLocalError']].'</p>';
    				}
    				if(isset($this->KMServerErrorCode[$r['result']['KMServerErrorCode']])) {
    					$r['result']['ResultCode']=-9999997;
    					$ok=false;
    					$error_txt.='<p class="false">'.$this->KMServerErrorCode[$r['result']['KMServerErrorCode']].'</p>';
    				}
    				if(isset($r['result']['KMServerCheckingStatus'])) {
    					$KMServerCheckingStatus=str_split(decbin($r['result']['KMServerCheckingStatus']), 1);
    					foreach($KMServerCheckingStatus as $n=>$v){
    						if(isset($this->KMServerCheckingStatus[$n][$v])) {
    							$r['result']['ResultCode']=-9999996;
    							$ok=false;
    							$error_txt.='<p class="false">'.$this->KMServerCheckingStatus[$n][$v].'</p>';
    						}
    					}
    				}
    				if($ok) $this->after_check_mark='FNAcceptMarkingCode';
    				else $this->after_check_mark='FNDeclineMarkingCode';
    			}*/

    			if($vars[$r['id']]['method']=='Get_CheckItemLocalError') {
    				$mark=true;
	    			if(isset($this->CheckItemLocalError[$r['result']['value']])) {
						$r['result']['ResultCode']=-9999998;
						$ok=false;
						$error_txt.='<p class="false">'.$this->CheckItemLocalError[$r['result']['value']].'</p>';
					}
				}
				if($vars[$r['id']]['method']=='Get_KMServerErrorCode') {
					$mark=true;
					if(isset($this->KMServerErrorCode[$r['result']['value']])) {
						$r['result']['ResultCode']=-9999997;
						$ok=false;
						$error_txt.='<p class="false">'.$this->KMServerErrorCode[$r['result']['value']].'</p>';
					}
				}
				if($vars[$r['id']]['method']=='Get_KMServerCheckingStatus') {
					$mark=true;
					if(isset($r['result']['value'])) {
						if($r['result']['value']<0) {
							$r['result']['value']='0000000';
						}
						$KMServerCheckingStatus=str_split(decbin($r['result']['value']), 1);
						foreach($KMServerCheckingStatus as $n=>$v){
							if(isset($this->KMServerCheckingStatus[$n][$v])) {
								$r['result']['ResultCode']=-9999996;
								$ok=false;
								$error_txt.='<p class="false">'.$this->KMServerCheckingStatus[$n][$v].'</p>';
							}
						}
					}
				}
				if($mark) {
					if($ok) $this->after_check_mark='FNAcceptMarkingCode';
					else $this->after_check_mark='FNDeclineMarkingCode';
				}

    			$result[$r['id']]=array(
    				'success'=>$r['result']['ResultCode']==0?true:false,
    				'method'=>$vars[$r['id']]['method'],
    				'result'=>$r['result'],
    				'params'=>$vars[$r['id']]['params']
    			);
	    	}
    	}
    	else {
    		$ok=false;
    		return array(
			    'success'=>false,
				'txt'=>$methods.'. Не удается получить ответ'
			);
    	}
		return array(
			'success'=>$ok,
			'txt'=>$ok?'Выполнено успешно':$error_txt,
			'result'=>$result
		);
	}

	function fr_submode() {
		$data=array(
			array(
				'method'=>'Get_ECRMode',
			),
			array(
				'method'=>'Get_ECRAdvancedMode'
			)
		);
		$res=$this->send_task($data);
		if(!$res['success']) {
			return $res;
		}
		$fr_mode=false;
		$fr_submode=false;
		foreach($res['result'] as $r) {
			if($r['method']=='Get_ECRMode') {
				$fr_mode=$r['result']['value'];
			}
			elseif($r['method']=='Get_ECRAdvancedMode') {
				$fr_submode=$r['result']['value'];
			}
		}
		if($fr_mode!==false && $fr_submode!==false) {
			return array(
				'success'=>true,
				'fr_mode'=>$fr_mode,
				'fr_submode'=>$fr_submode
			);
		}
		return array(
			'success'=>false
		);
	}

	// Отправка запроса
	function send_ajax($strs, $timeout=3, $view=false) {
		/*if(!$this->f3->get('SESSION.'.$this->sets['printer'].'_drivers_init')) {
			$this->kkm_init();
		}*/

		if(mb_strpos($strs, 'continue_print')>0 || mb_strpos($strs, 'continue_print')===false) {
			$fr_submode=$this->fr_submode();
			if($fr_submode['success'] && $fr_submode['fr_submode']==3) {
				$data=$this->wtf("continue_print;\n");
			}
			elseif($fr_submode['success'] && $fr_submode['fr_mode']==8) {
				$data=$this->wtf("g;\n");
			}
			if($data) {
				$fr_error_ignore=$this->f3->get('fr_error_ignore');
				$this->f3->set('fr_error_ignore', 1);
				$res=$this->send_task($data);
				$this->f3->set('fr_error_ignore', $fr_error_ignore);
			}
		}
		$print_reciept=false;
		$data=$this->wtf($strs);
		if(in_array('WaitForPrinting', array_column($data, 'method'))) {
			$print_reciept=true;
		}
		if(!$data) {
			$error_txt=$strs.': Команда не поддерживается';
			$this->f3->get('logs_model')->save_log($error_txt.' (Касса)');
			if($this->f3->get('fr_error_ignore')) {
                return array(
                    'success'=>false,
					'error'=>$error_txt
                );
            }
			echo '<p class="false">'.$error_txt.'</p>';
			exit();
		}
		// Отправляем запрос
		$res=$this->send_task($data);
		if($this->fd || $this->fp) {
			$res['success']=true;
			$res['fd']=$this->fd;
			$res['fp']=$this->fp;
			$res['kkm_time']=$this->kkm_time;
		}

		if($this->after_check_mark!='no') {
			$this->f3->set('fr_error_ignore', 1);
			$data=array(
				array(
					'method'=>$this->after_check_mark
				)
			);
			$this->after_check_mark='no';
			$after_check_mark=$this->send_task($data);
			if(!$after_check_mark['success']) {
				$this->f3->get('logs_model')->save_log($this->after_check_mark.'. '.$after_check_mark['txt'].' (Касса)');
			}
		}

		if(!$res['success'] && !$view) {
			$this->f3->get('logs_model')->save_log($res['txt'].' (Касса)');
			if($this->f3->get('fr_error_ignore')) {
				return array(
					'success'=>false,
					'txt'=>$res['txt']
				);
			}
			echo '<p class="false">'.$res['txt'].'</p>';
			if($print_reciept==true) {
				$fr_submode=$this->fr_submode();
				if($fr_submode['success'] && $fr_submode['fr_submode']==2) {
					echo $continue_html= \Template::instance()->render('fr_error_continue_print.htm');
				}
				else {
					echo $continue_html= \Template::instance()->render('fr_error.htm');
				}
			}
			else {
				echo $continue_html= \Template::instance()->render('fr_error.htm');
			}
			exit();
		}

		if($view) {
			$txt='';
			if($res['result']) {
				foreach($res['result'] as $n=>$r){
					if(!$r['success']) {
						$this->f3->get('logs_model')->save_log('Метод: '.$r['method'].'. '.$r['result']['ResultCodeDescription'].' (Касса)');
					}

					if($r['method']=='GetECRStatus') {
						$txt=$this->kkm_state($res['result']);
						break;
					}
					$txt.="<p class='".($r['result']['ResultCode']==0 || $r['result']['ResultCode']==55?'true':'false')."'>Метод: ".$r['method'].'. Код ответа: '.$r['result']['ResultCode'].". Результат: ".$r['result']['ResultCodeDescription']."</p>";
				}
			}
			else {
				$method=array_column($data, 'method');
				$this->f3->get('logs_model')->save_log('Методы: '.implode(', ', $method).'. '.$res['txt'].' (Касса)');
				$txt.="<p class='false'>".$res['txt']."</p>";
			}
			echo $txt;
		}

		return $res;
	}

	function kkm_state($result) {
		$txt='';
		$keys=$this->kkm_state_data;
		foreach($result as $r) {
			$key=$r['method'];

			if(isset($keys[$key])) {
				$method=$key;
				continue;
			}

			if(isset($keys[$method][$key])) {
				if(!$r['success']) {
					$txt.="<p class='".($r['result']['ResultCode']==0 || $r['result']['ResultCode']==55?'true':'false')."'>Метод: ".$r['method'].'. Код ответа: '.$r['result']['ResultCode'].". Результат: ".$r['result']['ResultCodeDescription']."</p>";
					continue;
				}
				if($key=='FNGetSerial') {
					$r['result']['value']=$r['result']['SerialNumber'];
				}

				$value=$r['result']['value'];
				if(is_array($keys[$method][$key])) {
					if(($key=='Get_ECRMode8Status' && $data['Get_ECRMode']==8) || $key!='Get_ECRMode8Status') {
						$txt.='<p><b>'.$keys[$method][$key]['title'].'</b> - '.$keys[$method][$key]['data'][$value].'</p>';
					}
				}
				else {
					if($key=='Get_ECRSoftDate' || $key=='Get_FMSoftDate' || $key=='Get_Date') {
						if(!is_numeric($value)) $value=strtotime($value);
						$value=date('d.m.Y', $value);
					}
					if($key=='Get_Time') {
						if(!is_numeric($value)) $value=strtotime($value);
						$value=date('H:i:s', $value);
					}
					$txt.='<p><b>'.$keys[$method][$key].'</b> - '.$value.'</p>';
				}
			}
		}
		return $txt;
	}

	function is_core() {
		if(isset($this->core)) return $this->core;
		$data=array(
    		array(
    			'method'=>'Set_ModelParamNumber',
    			'params'=>array(
    				0=>$this->mpCapCashCore
    			)
    		),
    		array(
    			'method'=>'ReadModelParamValue',
    		),
    		array(
    			'method'=>'Get_ModelParamValue',
    		),
    	);
    	$res=$this->send_task($data);
    	$is_core=false;
    	if($res['success']) {
    		foreach($res['result'] as $r){
    			if($r['method']=='Get_ModelParamValue') {
    				$is_core=(int)$r['result']['value']==1?true:false;
    			}
    		}
    	}
    	$this->core=$is_core;
    	return $is_core;
	}

    // Узнаем состояние смены
    function change_status() {
        $data=array(
    		array(
    			'method'=>'Get_ECRMode'
    		),
    		array(
    			'method'=>'FNGetSerial'
    		)
    	);
    	$res=$this->send_task($data);
    	$this->Disconnect();
    	if(!$res['success']) {
    		$this->f3->get('logs_model')->save_log('Состояние смены. '. $res['txt'].' (Касса)');
			if($this->f3->get('fr_error_ignore')) {
				return array(
					'success'=>false,
					'txt'=>$res['txt']
				);
			}
			echo '<p class="false">'.$res['txt'].'</p>';
			exit();
    	}
    	$status=false;

    	foreach($res['result'] as $r){
    		if($r['method']=='Get_ECRMode') {
    			if((int)$r['result']['value']==3) {
					$status='expired';
				}
				elseif((int)$r['result']['value']==2) {
					$status='opened';
				}
				elseif((int)$r['result']['value']==4) {
					$status='closed';
				}
    		}
    		elseif($r['method']=='FNGetSerial' && $r['result']['ResultCode']==0) {
    			$this->f3->set('fn', $r['result']['SerialNumber']);
    		}
    	}
    	return $status;
    }

    function check_fnstatus() {
    	$core=$this->is_core();
    	$tableParams=$this->tableParams[$core?'core':'classic'];
    	$data=array(
    		array(
            	'method'=>'FNGetExpirationTime'
            ),
            array(
            	'method'=>'Get_Date'
            ),
            array(
            	'method'=>'FNGetInfoExchangeStatus'
            ),
            array(
            	'method'=>'Get_MessageCount'
            ),
            array(
    			'method'=>'ReadTable',
    			'params'=>array(
    				'TableNumber'=>$tableParams['inn']['table'],
					'RowNumber'=>$tableParams['inn']['row'],
					'FieldNumber'=>$tableParams['inn']['cell']
    			)
    		),
    		array(
    			'method'=>'Get_ValueOfFieldString'
    		),
    		array(
    			'method'=>'ReadTable',
    			'params'=>array(
    				'TableNumber'=>$tableParams['org_name']['table'],
					'RowNumber'=>$tableParams['org_name']['row'],
					'FieldNumber'=>$tableParams['org_name']['cell']
    			)
    		),
    		array(
    			'method'=>'Get_ValueOfFieldString'
    		),
    		array(
    			'method'=>'ReadTable',
    			'params'=>array(
    				'TableNumber'=>$tableParams['org_address']['table'],
					'RowNumber'=>$tableParams['org_address']['row'],
					'FieldNumber'=>$tableParams['org_address']['cell']
    			)
    		),
    		array(
    			'method'=>'Get_ValueOfFieldString'
    		),
    	);

    	$res=$this->send_task($data);
    	$this->Disconnect();
    	if(!$res['success']) {
    		$this->f3->get('logs_model')->save_log('Disconnect. '.$res['txt'].' (Касса)');
			return array(
				'success'=>false,
				'txt'=>$res['txt']
			);
    	}

    	$ofd_n=0;
        $fn_end=0;
        $org_data=array();
        $org_key='';

        foreach($res['result'] as $r){
        	if($r['method']=='Get_Date') {
        		if(!is_numeric($r['result']['value'])) $r['result']['value']=strtotime($r['result']['value']);
        		$fn_end=$r['result']['value'];
        	}
        	elseif($r['method']=='Get_MessageCount') {
        		$ofd_n=$r['result']['value'];
        	}
        	elseif($r['method']=='ReadTable') {
        		if($r['params']['FieldNumber']==$tableParams['inn']['cell']) {
        			$org_key='inn';
        		}
        		elseif($r['params']['FieldNumber']==$tableParams['org_name']['cell']) {
        			$org_key='org_name';
        		}
        		elseif($r['params']['FieldNumber']==$tableParams['org_address']['cell']) {
        			$org_key='org_address';
        		}
        	}
        	elseif($r['method']=='Get_ValueOfFieldString'){
        		$org_data[$org_key]=$r['result']['value'];
        	}
        }

        return array(
            'success'=>true,
            'kkm'=>'Штрих-М',
            'ffd'=>$this->sets['version'],
            'inn'=>$org_data['inn'],
            'kpp'=>'',
            'org_name'=>$org_data['org_name'],
            'org_address'=>$org_data['org_address'],
            'ofd_n'=>$ofd_n,
            'fn_end'=>$fn_end
        );
    }

    function GetFontMetrics($font=null) {
    	if(!$font) $font=$this->config['FontSize'];
    	$data=array(
    		array(
    			'method'=>'GetFontMetrics',
    			'params'=>array(
    				'FontType'=>(int)$font
    			)
    		),
    		array(
    			'method'=>'Get_PrintWidth',
    		),
    		array(
    			'method'=>'Get_CharWidth',
    		)
    	);
    	$res=$this->send_task($data);
    	if(!$res['success'] && $font!=$this->config['FontSize']) {
    		$font=$this->config['FontSize'];
    		return $this->GetFontMetrics($this->config['FontSize']);
    	}
    	if(!$res['success']) {
    		return $res;
    	}

    	$result=array(
    		'success'=>true,
    		'PrintWidth'=>0,
    		'CharWidth'=>0,
    		'font'=>$font
    	);

    	foreach($res['result'] as $r){
    		if($r['method']=='Get_PrintWidth') {
    			$result['PrintWidth']=$r['result']['value'];
    		}
    		elseif($r['method']=='Get_CharWidth') {
    			$result['CharWidth']=$r['result']['value'];
    		}
    	}

    	return $result;
    }

    function get_paper_width() {
    	if($this->config['PaperWidth']>0) return $this->config['PaperWidth'];

    	$font_data=$this->GetFontMetrics($font);
        if($font_data['success']===true) {
        	return $font_data['PrintWidth'];
        }
        else return 32;
    }

	// Форматирование текста
    function text_format($str, $w=null, $font=-1, $pm=false) {
    	$str=str_replace("\n", '#kkm_br#', $str);
    	if($font<0) $font=$this->config['FontSize'];

    	if($this->config['PaperWidth']==0) {
	    	$font_data=$this->GetFontMetrics($font);
	    	if($font_data['success']) {
	    		$w=floor($font_data['PrintWidth']/$font_data['CharWidth']);
	    		$font=$font_data['font'];
	    	}
	    	else {
	    		$w=32;
	    	}
	    }

    	if(!$w)$w=$this->config['PaperWidth'];
    	$lines=array();
    	$str=str_replace('#kkm_br##kkm_right#', '#kkm_right#', $str);
    	$str=str_replace('#kkm_br##kkm_center#', '#kkm_center#', $str);
    	$str=str_replace('#kkm_right#', '#kkm_br##kkm_right#', $str);
    	$str=str_replace('#kkm_center#', '#kkm_br##kkm_center#', $str);
    	$strs=explode('#kkm_br#', $str);
    	$delay=(int)$this->config['delayPrint']?1:0;

    	foreach($strs as $s){
    		$s=str_replace('#kkm_bold#', '', $s);
	        // Заменяем #kkm_right# на пробелы
	        $kkm_right="#kkm_right#"; # тэг сдвига вправо
	        $space_n=$w-(mb_strlen($s)-mb_strlen($kkm_right)); // кол-во пробелов
	        $s=str_replace($kkm_right, str_repeat(" ", max(0, $space_n)), $s); // заменяем тэг сдвига на пробелы

	        // Заменяем #kkm_center# на пробелы
	        $kkm_center="#kkm_center#"; // тэг сдвига в центр
	        $space_n=floor(($w-(mb_strlen($s)-mb_strlen($kkm_center)))/2); // кол-во пробелов
	        $s=str_replace($kkm_center, str_repeat(" ", max(0, $space_n)), $s); // заменяем тэг сдвига на пробелы
	        if(!$s) continue;
	        if($pm===true) {
	        	$str=mb_str_split($s, $w);
	        }
	        else {
	        	$str=array($s);
	        }
	        foreach($str as $s) {
	        	if(!$s) $s=' ';
	        	$lines[]=array(
	    			'method'=>'PrintStringWithFont',
	    			'params'=>array(
	    				'UseReceiptRibbon'=>1,
	    				'UseJournalRibbon'=>1,
	    				'StringForPrinting'=>$s,
	    				'FontType'=>(int)$font,
	    				'DelayedPrint'=>$delay
	    			)
	    		);
	        }
    	}
    	if((int)$this->config['delayPrint']==1) {
    		$lines[]=array(
    			'method'=>'PrintStringWithFont',
    			'params'=>array(
    				'UseReceiptRibbon'=>1,
    				'UseJournalRibbon'=>1,
    				'StringForPrinting'=>'',
    				'FontType'=>$this->config['FontSize'],
    				'DelayedPrint'=>0
    			)
    		);
    	}
    	$lines[]=array(
			'method'=>'WaitForPrinting'
		);
    	return $lines;
    }

    function strToHex($string) {
    	$n=mb_strlen($string);
    	$string=iconv('utf-8', 'windows-1251', $string);
		$hex='';
		for ($i=0; $i < $n; $i++) {
			$hex .= dechex(ord($string[$i]));
		}
		return $hex;
	}

	function set_cashier($cmd, $cashier_data) {
		$data=array();
		$core=$this->is_core();
		$tableParams=$this->tableParams[$core?'core':'classic'];
		if($core) {
			$data[]=array(
				'method'=>'WriteTable',
				'params'=>array(
					'TableNumber'=>$tableParams['users']['table'],
					'RowNumber'=>$tableParams['users']['row'],
					'FieldNumber'=>$tableParams['users']['cell'],
					'ValueOfFieldString'=>$cashier_data[0]
				)
			);
			$data[]=$cmd;
		}
		else {
			$data[]=$cmd;
			$data[]=array(
				'method'=>'FNSendTag',
				'params'=>array(
					'TagNumber'=>1021,
					'TagType'=>7,
					'TagValueStr'=>$cashier_data[0],
					'TagValueLength'=>mb_strlen($cashier_data[0])
				)
			);
		}
		if($cashier_data[1] && mb_strlen($cashier_data[1])==12) {
			$data[]=array(
    			'method'=>'FNSendTag',
    			'params'=>array(
    				'TagNumber'=>1203,
					'TagType'=>7,
					'TagValueStr'=>(string)$cashier_data[1],
					'TagValueLength'=>12
    			)
    		);
		}
		return $data;
	}

	function get_marking($mark) {
        $part21=explode('-', $mark);
        $len=mb_strlen($mark);
        $type=0;
        $gtin=false;
        $serial=false;

        if($len==8 && ctype_digit($mark)) {
        	$type=17672;
        }
        elseif ($len==13 && ctype_digit($mark)) {
        	$type=17677;
        }
        elseif ($len==14 && ctype_digit($mark)) {
        	$type=18702;
        }
        elseif($len!=8
            && $len!=13
            && $len!=14
            && preg_match("/[A-Za-z0-9\/\\!”%&’()*+-.,:;=<>?]/", $mark)
            && mb_substr($mark, 0, 2)=='01'
            && mb_substr($mark, 16, 2)=='21') {
            if(mb_strpos($mark, '')===false && mb_strpos($mark, '\\u001d')===false) {
                $mark=''.mb_substr($mark, 0, 31).''.mb_substr($mark, 31, 6).''.mb_substr($mark, 37);
            }
            if(mb_substr($mark, 0, 1)=='') {
                $mark=mb_substr($mark, 1);
            }
            elseif(mb_substr($mark, 0, 6)=='\\u001d') {
                $mark=mb_substr($mark, 6);
            }
            $part=explode('', $mark);
            if(count($part)==1) {
                $part=explode('\u001d', $mark);
            }
            $gtin=mb_substr($part[0], 2, 14);
            $serial=mb_substr($part[0], 18, 13);
            $type=17485;
        }
        elseif($len==29
            && preg_match('/^[0-9a-zA-Z!”%&’()*+-.,:;=<>?]+$/' , $mark)
            && ctype_digit(mb_substr($mark, 0, 14))
            && mb_substr($mark, 0, 2)!='01') {
        	$type=17485;
            $gtin=mb_substr($mark, 0, 14);
            $serial=mb_substr($mark, 14, 11);
        }
        elseif($len==21
            && count($part21)==3
            && ctype_alnum($part21[0])
            && ctype_digit($part21[1])
            && ctype_alnum($part21[2])) {
            $type=21062;
        }
        elseif($len==68 && preg_match('/^[0-9A-Z]+$/' , $mark) && mb_substr($mark, 0, 2)!='01') {
            $type=50452;
        }
        elseif($len==150 && preg_match('/^[0-9A-Z]+$/' , $mark) && mb_substr($mark, 0, 2)!='01') {
            $type=50462;
        }

        return array(
        	'type'=>$type,
        	'gtin'=>$gtin,
        	'serial'=>$serial
        );
    }

    function prepareArg($num, $bytes) {
        return implode('', array_reverse(str_split(sprintf('%0' . ($bytes * 2) . 'x', $num), 2)));
    }
    function prepareText($text, $bytes = 40) {
        if (preg_match('//u', $text)) { // is UTF
            $text = iconv('UTF-8', 'cp1251//IGNORE', $text);
        }
        $text_code = '';
        for ($i = 0, $n = strlen($text); $i < $n; $i++) {
            $byte = sprintf('%x', ord($text{$i}));
            $text_code .= $byte;
        }
        $maxlen = $bytes * 2;
        return str_pad(substr($text_code, 0, $maxlen), $maxlen, '0', STR_PAD_RIGHT); // 40 bytes max!
    }

    // Формирование tlv
    function make_tlv($tag, $value, $s=5) {
        if($s==5) {
            $l=strlen($value);
            $value=$this->prepareText($value, $l);
        }
        elseif($s==1) {
            $l=1;
            $value=$this->prepareArg($value, 1);
        }
        elseif($s==2) {
            $l=2;
            $value=$this->prepareArg($value, 2);
        }
        elseif($s==3) {
            $l=4;
            $value=$this->prepareArg($value, 4);
        }
        else {
            $l=strlen($value)/2;
        }
        $tlv=$this->prepareArg($tag, 2).$this->prepareArg($l, 2).$value;
        return $tlv;
    }

    function check_mark($mark, $count=1, $unit=0) {
    	$core=$this->is_core();
    	$count=(float)str_replace(',', '.', $count);
        $status=1;
        if($unit!=0) {
            $status=2;
        }
        else {
            $count=1;
        }

        $tlv=$this->make_tlv(2108, (int)$unit, 1);
        $count_l=strlen($count);
        $floor_l=strlen((int)floor($count));
        $k=max($count_l-$floor_l-1, 0);
        if($k>0) {
            $count*=pow(10, $k);
        }

        $tlv.=$this->prepareArg(1023, 2).$this->prepareArg(2, 2).$this->prepareArg($k, 1).$this->prepareArg($count, 1);

    	$data=array(
    		array(
	    		'method'=>'FNCheckItemBarcode',
	    		'params'=>array(
	    			'BarCode'=>$mark,
	    			'ItemStatus'=>$status,
	    			'CheckItemMode'=>0,
	    			'TLVDataHex'=>$tlv
	    		)
	    	),
    		array(
    			'method'=>'Get_CheckItemLocalError'
    		),
    		array(
    			'method'=>'Get_KMServerErrorCode'
    		),
    		array(
    			'method'=>'Get_KMServerCheckingStatus'
    		),
    	);
    	if($unit==0 && $core) {
    		unset($data[0]['params']['TLVDataHex']);
    	}
    	return $data;
    }

	// Что надо выполнить
	function wtf($strs) {
		$core=$this->is_core();
		$tableParams=$this->tableParams[$core?'core':'classic'];
		$str=explode("\n", $strs);
		$data=array(); // Массив заданий
		$print=1;
		$correct=false;
		foreach ($str as $s) {
			$s=trim($s);
        	$p=explode(';', $s);
        	$n=count($p);
        	if($p[$n-1]=='') {
            	unset($p[$n-1]);
            	$n-=1;
        	}
        	if($s=='') continue;
        	if($p[0]=='beep') { // Гудок
        		$data[]=array(
        			'method'=>'Beep'
        		);
        	}
        	elseif($p[0]=='feed') { // Протяжка
                $data[]=array(
                    'method'=>'FeedDocument',
                    'params'=>array(
                    	'StringQuantity'=>(int)$p[1],
                    	'UseSlipDocument'=>1,
                    	'UseReceiptRibbon'=>1,
    					'UseJournalRibbon'=>1,
                    )
                );
                $data[]=array(
					'method'=>'WaitForPrinting'
				);
        	}
        	elseif($p[0]=='g') {
        		$data[]=array(
        			'method'=>'CancelCheck'
        		);
        	}
        	elseif($p[0]=='open_session') { // Открытие смены
        		if($p[1]==0) {
        			$print=0;
        			$data[]=array(
        				'method'=>'WriteTable',
        				'params'=>array(
        					'TableNumber'=>$tableParams['no_print']['table'],
							'RowNumber'=>$tableParams['no_print']['row'],
							'FieldNumber'=>$tableParams['no_print']['cell'],
							'ValueOfFieldInteger'=>1
        				)
        			);
        		}

        		$d=array(
        			'method'=>'FNBeginOpenSession'
        		);

        		if($p[2]) {
        			$cashier_data=explode('{{user_inn}}', $p[2]);
            		$d=$this->set_cashier($d, $cashier_data);
        		}
        		$data=array_merge($data, $d);

        		$data[]=array(
        			'method'=>'FNOpenSession'
        		);

        		$data[]=array(
					'method'=>'WaitForPrinting'
				);
				$data[]=array(
					'method'=>'Get_DocumentNumber'
				);
				$data[]=array(
					'method'=>'Get_FiscalSign'
				);
        	}
        	elseif($p[0]=='z') { // Закрытие смены
        		if($p[1]==0) {
        			$print=0;
        			$data[]=array(
        				'method'=>'WriteTable',
        				'params'=>array(
        					'TableNumber'=>$tableParams['no_print']['table'],
							'RowNumber'=>$tableParams['no_print']['row'],
							'FieldNumber'=>$tableParams['no_print']['cell'],
							'ValueOfFieldInteger'=>1
        				)
        			);
        		}
        		$d=array(
        			'method'=>'FNBeginCloseSession'
        		);

        		if($p[2]) {
        			$cashier_data=explode('{{user_inn}}', $p[2]);
            		$d=$this->set_cashier($d, $cashier_data);
        		}
        		$data=array_merge($data, $d);

        		$data[]=array(
        			'method'=>'FNCloseSession'
        		);

        		$data[]=array(
					'method'=>'WaitForPrinting'
				);
				$data[]=array(
					'method'=>'Get_DocumentNumber'
				);
				$data[]=array(
					'method'=>'Get_FiscalSign'
				);
        	}
        	elseif ($p[0]=='p') { // Печать текста
        		$data=array_merge($data, $this->text_format($p[1]));
        	}
        	elseif ($p[0]=='pm') { // Печать текста
        		$data=array_merge($data, $this->text_format($p[2], $p[1], null, true));
        	}
            elseif ($p[0]=='qr' || ($core && $p[0]=='barcode')) {
            	$qr=$this->strToHex($p[1]);
            	if(!$p[2]) $p[2]=$this->config['QRSize'];
            	elseif($p[2]<3)$p[2]=3;
            	elseif($p[2]>8)$p[2]=8;
            	$data[]=array(
            		'method'=>'LoadBlockData',
            		'params'=>array(
            			'BlockType'=>0,
            			'BlockNumber'=>1,
            			'BlockDataHex'=>(int)$qr
            		)
            	);
            	$data[]=array(
            		'method'=>'Print2DBarcode',
            		'params'=>array(
            			'BarcodeType'=>3,
						'BarcodeDataLength'=>mb_strlen($p[1]),
						'BarcodeStartBlockNumber'=>1,
						'BarcodeParameter1'=>0,
						'BarcodeParameter3'=>(int)$p[2],
						'BarcodeParameter5'=>3,
						'BarcodeAlignment'=>0
            		)
            	);
            	$data[]=array(
					'method'=>'WaitForPrinting'
				);
            }
            elseif ($p[0]=='barcode' && !$core) { // Печать текста
		        
		        $data[]=array(
		        	'method'=>'PrintBarCode',
		        	'params'=>array(
		        		'BarCode'=>$p[1]
		        	)
		        );

        		$data[]=array(
					'method'=>'WaitForPrinting'
				);
            }
        	elseif($p[0]=='print_font') { // Печать строки заданным размером шрифта
        		$data=array_merge($data, $this->text_format($p[2], null, $p[1]));
        	}
        	elseif($p[0]=='print_bold') { // Печать жирной строки
        		$data=array_merge($data, $this->text_format($p[1], null, $this->config['FontBold']));
        	}
        	elseif($p[0]=='x') { // X отчет
        		$data[]=array(
        			'method'=>'PrintReportWithoutCleaning'
        		);
        		$data[]=array(
					'method'=>'WaitForPrinting'
				);
        	}
        	elseif($p[0]=='continue_print') { // Продолжить печать чека
        		$data[]=array(
        			'method'=>'ContinuePrint'
        		);
        		$data[]=array(
					'method'=>'WaitForPrinting'
				);
        	}
        	elseif($p[0]=='c') { // Печать копии последнего чека
        		$data[]=array(
        			'method'=>'RepeatDocument'
        		);
        		$data[]=array(
					'method'=>'WaitForPrinting'
				);
        	}
        	elseif($p[0]=='printdoc') { // Печать копии последнего чека
        		$data[]=array(
        			'method'=>'FNGetDocumentAsString',
        			'params'=>array(
        				'DocumentNumber'=>(int)$p[1]
        			)
        		);
        	}
        	elseif($p[0]=='d') { // Статус ККМ
        		foreach($this->kkm_state_data as $method=>$value){
            		$data[]=array(
            			'method'=>$method
            		);
            		foreach($value as $key=>$v) {
            			$data[]=array(
	            			'method'=>$key
	            		);
            		}
            	}
        	}
        	elseif($p[0]=='preitem' || $p[0]=='preitem_end' || $p[0]=='postitem' || $p[0]=='postitem_end') {
            	// ничего не надо делать
        	}
        	elseif($p[0]=='imde') { // Внесение или изъятие из кассы
        		$this->pay_cash=1;
        		$method='CashIncome';
        		if($p[1]<0){
        			$p[1]=abs($p[1]);
        			$method='CashOutcome';
        		}

        		$data[]=array(
        			'method'=>$method,
        			'params'=>array(
        				'Summ1'=>(int)$p[1]
        			)
        		);

        		$data[]=array(
					'method'=>'WaitForPrinting'
				);
        	}
        	elseif($p[0]=='b') { // Открытие чека
        		if($p[3]==0) {
        			$print=0;
	        		$data[]=array(
	        			'method'=>'WriteTable',
	        			'params'=>array(
	        				'TableNumber'=>$tableParams['no_print']['table'],
							'RowNumber'=>$tableParams['no_print']['row'],
							'FieldNumber'=>$tableParams['no_print']['cell'],
							'ValueOfFieldInteger'=>1
	        			)
	        		);
	        	}
	        	$this->ticket_type=$this->receipt_types[$p[1]];
	        	if($this->ticket_type==128 || $this->ticket_type==130) $correct=true;
        		$d=array(
        			'method'=>'OpenCheck',
        			'params'=>array(
        				'CheckType'=>$this->ticket_type
        			)
        		);
        		if($p[2]) {
        			$cashier_data=explode('{{user_inn}}', $p[2]);
            		$d=$this->set_cashier($d, $cashier_data);
        		}
        		$data=array_merge($data, $d);
        		$this->ticket_total=$p[4];
            	$this->ticket_sum=$p[5];
        	}
        	elseif($p[0]=='partner') {
        		$partner=json_decode($p[1], true);
        		if($partner['client']) {
        			if(is_numeric($partner['client']) && mb_strlen($partner['client'])==11) $partner['client']='+'.$partner['client'];
        			$data[]=array(
						'method'=>'FNSendCustomerEmail',
						'params'=>array(
							'CustomerEmail'=>$partner['client']
						)
					);
				}
				if($partner['ur']==1) {
					$data[]=array(
						'method'=>'FNBeginSTLVTag',
						'params'=>array(
							'TagNumber'=>1256,
						)
					);
					$data[]=array(
                		'method'=>'FNAddTag',
						'params'=>array(
							'TagID'=>0,
							'TagNumber'=>1227,
							'TagType'=>7,
							'TagValueStr'=>$partner['name'],
							'TagValueLength'=>mb_strlen($partner['name'])
						)
                	);
                	$data[]=array(
                		'method'=>'FNAddTag',
						'params'=>array(
							'TagID'=>0,
							'TagNumber'=>1228,
							'TagType'=>7,
							'TagValueStr'=>$partner['inn'],
							'TagValueLength'=>mb_strlen($partner['inn'])
						)
                	);
                	$data[]=array(
                		'method'=>'FNSendSTLVTag'
                	);
            	}
        	}
        	elseif($p[0]=='set_tlv') {
        		$p[1]=(int)$p[1];
        		if($p[1]==1008) {
        			if(is_numeric($p[3]) && mb_strlen($p[3])==11) $p[3]='+'.$p[3];
        			$data[]=array(
						'method'=>'FNSendCustomerEmail',
						'params'=>array(
							'CustomerEmail'=>$p[3]
						)
					);
            	}
            	else {
	            	$tlv_type=$this->tlv_types[$p[2]];
	            	if(!$tlv_type)$tlv_type=$this->tlv_types[5];
	            	if($tlv_type['param']=='TagValueBin' || $tlv_type['param']=='TagValueInt' || $tlv_type['param']=='TagValueDateTime') {
	            		$p[3]=(int)$p[3];
	            	}
	        		$data[]=array(
						'method'=>'FNSendTag',
						'params'=>array(
							'TagNumber'=>$p[1],
							'TagType'=>$tlv_type['type'],
							$tlv_type['param']=>$p[3],
							'TagValueLength'=>mb_strlen($p[3])
						)
					);
				}
        	}
        	elseif($p[0]=='smde') { // Добавление товара в чек
        		$line=json_decode($p[1], true);
        		if(!$line['psr']) $line['psr']=4;
        		if(!$line['ppr']) $line['ppr']=1;
        		if(!isset($line['units'])) $line['units']=255;
        		$line['quantity']=round($line['quantity']/1000, 3);

        		$operation=array(
        			'method'=>'FNOperation',
        			'params'=>array(
        				'CheckType'=>(int)$this->operation_type[$this->ticket_type],
						'Quantity'=>(float)$line['quantity'],
						'Price'=>(int)$line['price'],
						'Summ1'=>0,
						'Summ1Enabled'=>$core?false:0,
						'TaxValue'=>0,
						'TaxValueEnabled'=>0,
						'Tax1'=>(int)$line['vat'],
						'MeasureUnit'=>(int)$line['units'],
						'Department'=>(int)$line['section'],
						'PaymentTypeSign'=>(int)$line['psr'],
						'PaymentItemSign'=>(int)$line['ppr'],
						'StringForPrinting'=>preg_replace('/[^ a-zа-яё\d]/ui', ' ', $line['title'])
        			)
        		);
			//если версия ффд 1.05, то не передаем единицы измерения т.к. это может вызывать ошибку.
			if($this->sets['version']=='1.05') { unset($operation['params']['MeasureUnit']); }
				       
        		$tag_operation=array();
        		if($this->sets['version']=='1.05' && $line['mark']!='') {
        			$mark_data=$this->get_marking($line['mark']);
        			$m=array(
                    	'method'=>'FNSendItemCodeData',
                    	'params'=>array(
                    		'MarkingType'=>$mark_data['type']
                    	)
                    );
                    if($mark_data['type']==17485) {
                    	$m['GTIN']=$mark_data['gtin'];
                    	$m['SerialNumber']=$mark_data['serial'];
                    }
                    else {
                    	$m['BarCode']=$mark;
                    }
                    $tag_operation[]=$m;
        		}

        		$operation=array($operation);
        		if($line['mark'] && (int)$line['egais']!=1) {
        			$this->send_ajax('check_mark;'.$line['mark'].';'.$line['quantity'].';'.$line['units'].";\n", 3, false);
        			//$check_mark=$this->check_mark($line['mark'], $line['quantity'], $line['units']);
        			//$operation=array_merge($operation, $check_mark);
        		}

        		if($this->sets['version']=='1.2') {
        			if($line['reqId']!='' && $line['reqTimestamp']>0) {
        				if($core) {
	        				$tag_operation[]=array(
	        					'method'=>'FNBeginSTLVTag',
								'params'=>array(
									'TagNumber'=>1260,
								)
	        				);

	        				$tag_operation[]=array(
		                		'method'=>'FNAddTag',
								'params'=>array(
									'TagID'=>0,
									'TagNumber'=>1262,
									'TagType'=>7,
									'TagValueStr'=>'030',
									'TagValueLength'=>3
								)
		                	);
		                	$tag_operation[]=array(
		                		'method'=>'FNAddTag',
								'params'=>array(
									'TagID'=>0,
									'TagNumber'=>1263,
									'TagType'=>7,
									'TagValueStr'=>'21.11.2023',
									'TagValueLength'=>12
								)
		                	);
		                	$tag_operation[]=array(
		                		'method'=>'FNAddTag',
								'params'=>array(
									'TagID'=>0,
									'TagNumber'=>1264,
									'TagType'=>7,
									'TagValueStr'=>'1944',
									'TagValueLength'=>4
								)
		                	);
		                	$str1265='UUID='.$line['reqId'].'&Time='.$line['reqTimestamp'];
		                	if($line['inst']!='') {
		                        $str1265.='&Inst='.$line['inst'];
		                    }
		                    if($line['version']!='') {
		                        $str1265.='&Ver='.$line['version'];
		                    }
		                	$tag_operation[]=array(
		                		'method'=>'FNAddTag',
								'params'=>array(
									'TagID'=>0,
									'TagNumber'=>1265,
									'TagType'=>7,
									'TagValueStr'=>$str1265,
									'TagValueLength'=>mb_strlen($str1265)
								)
		                	);
		                	$tag_operation[]=array(
		                		'method'=>'FNSendSTLVTagOperation'
		                	);
		                }
		                else {
		                	$tag_operation[]=array(
		                		'method'=>'FNSendTagOperation',
								'params'=>array(
									'TagNumber'=>1262,
									'TagType'=>7,
									'TagValueStr'=>'030',
									'TagValueLength'=>3
								)
		                	);
		                	$tag_operation[]=array(
		                		'method'=>'FNSendTagOperation',
								'params'=>array(
									'TagNumber'=>1263,
									'TagType'=>7,
									'TagValueStr'=>'21.11.2023',
									'TagValueLength'=>12
								)
		                	);
		                	$tag_operation[]=array(
		                		'method'=>'FNSendTagOperation',
								'params'=>array(
									'TagNumber'=>1264,
									'TagType'=>7,
									'TagValueStr'=>'1944',
									'TagValueLength'=>4
								)
		                	);
		                	$str1265='UUID='.$line['reqId'].'&Time='.$line['reqTimestamp'];
		                	$tag_operation[]=array(
		                		'method'=>'FNSendTagOperation',
								'params'=>array(
									'TagNumber'=>1265,
									'TagType'=>7,
									'TagValueStr'=>$str1265,
									'TagValueLength'=>mb_strlen($str1265)
								)
		                	);
		                }
        			}

        			if(isset($line['country_id']) && $line['country_id']!='000') {
        				$tag_operation[]=array(
	                		'method'=>'FNSendTagOperation',
							'params'=>array(
								'TagNumber'=>1230,
								'TagType'=>7,
								'TagValueStr'=>$line['country_id'],
								'TagValueLength'=>3
							)
	                	);

	                	$tag_operation[]=array(
	                		'method'=>'FNSendTagOperation',
							'params'=>array(
								'TagNumber'=>1231,
								'TagType'=>7,
								'TagValueStr'=>$line['gtd'],
								'TagValueLength'=>mb_strlen($line['gtd'])
							)
	                	);
        			}

	                if($line['partner']) {
	                	if(!isset($line['partner']['type'])) $line['partner']['type']=5;

	                	$tag_operation[]=array(
	                		'method'=>'FNSendTagOperation',
							'params'=>array(
								'TagNumber'=>1222,
								'TagType'=>3,
								'TagValueInt'=>pow(2, $line['partner']['type']),
								'TagValueLength'=>2
							)
	                	);
	                	if(mb_strlen($line['partner']['inn'])>=10) {
		                	$tag_operation[]=array(
		                		'method'=>'FNSendTagOperation',
								'params'=>array(
									'TagNumber'=>1226,
									'TagType'=>7,
									'TagValueStr'=>$line['partner']['inn'],
									'TagValueLength'=>mb_strlen($line['partner']['inn'])
								)
		                	);
				        }

				        if(mb_strlen($line['partner']['phone'])>=10 || $line['partner']['name']!='') {
				        	$tag_operation[]=array(
		    					'method'=>'FNBeginSTLVTag',
								'params'=>array(
									'TagNumber'=>1224,
								)
		    				);

				        	if(mb_strlen($line['partner']['phone'])>=10) {
				        		$line['partner']['phone']='+'.$line['partner']['phone'];
		                		$tag_operation[]=array(
			                		'method'=>'FNAddTag',
									'params'=>array(
										'TagID'=>0,
										'TagNumber'=>1171,
										'TagType'=>7,
										'TagValueStr'=>$line['partner']['phone'],
										'TagValueLength'=>mb_strlen($line['partner']['phone'])
									)
			                	);
			                }

			                if($line['partner']['name']!='') {
		                		$tag_operation[]=array(
			                		'method'=>'FNAddTag',
									'params'=>array(
										'TagID'=>0,
										'TagNumber'=>1225,
										'TagType'=>7,
										'TagValueStr'=>$line['partner']['name'],
										'TagValueLength'=>mb_strlen($line['partner']['name'])
									)
			                	);
			                }

		                	$tag_operation[]=array(
		                		'method'=>'FNSendSTLVTagOperation'
		                	);
				        }
	            	}

	            	if($line['mark']!='' && !$correct && (int)$line['egais']!=1) {
	                	$mark=str_replace('{{U+003B}}', ';', $line['mark']);
	                    $tag_operation[]=array(
	                    	'method'=>'FNSendItemBarcode',
	                    	'params'=>array(
	                    		'BarCode'=>$mark,
	                    		'MCOSUSign'=>0
	                    	)
	                    );
	                }
	            }
	            if($core) {
	            	$data=array_merge($data, $tag_operation, $operation);
	            }
	            else {
	            	$data=array_merge($data, $operation, $tag_operation);
	            }
        	}
        	elseif($p[0]=='tmde') { // Оплата чека
        		$c=count($p);

        		$d=array(
        			'method'=>'FNCloseCheckEx',
        			'params'=>array(
        				'RoundingSumm'=>$this->ticket_sum-$this->ticket_total>0?1:0,
        				'StringForPrinting'=>''
        			)
        		);

        		$pays=array_flip($this->sets['pays_type']);
        		if($c==3) { // передан 1 способ оплаты ФФД 1.05
        			if($pays[$p[2]]==1) $this->pay_cash=1;
        			$d['params']['Summ'.$p[2]]=(int)$p[1];
        		}
        		elseif($c==9) { // Несколько способов оплаты ФФД 1.05
        			for($i=1; $i<=4; $i++) {
        				if(isset($pays[$i-1]) && $pays[$i-1]==1) $this->pay_cash=1;
        				$d['params']['Summ'.$i]=(int)$p[$i];
        				$d['params']['TaxValue'.$i]=(int)$p[$i+4];
        			}
        		}
        		elseif($c==7) { // Несколько способов оплаты ФФД 1.2
        			for($i=1; $i<=5; $i++) {
        				if(isset($pays[$i-1]) && $pays[$i-1]==1) $this->pay_cash=1;
        				if($pays[$i]==1) $this->pay_cash==1;
        				$d['params']['Summ'.$i]=(int)$p[$i];
        			}
        			$d['params']['TaxType']=hexdec($p[6]);
        		}
       			for($i=1; $i<=16; $i++) {
       				if(!isset($d['params']['Summ'.$i]))	$d['params']['Summ'.$i]=0;
        		}
        		for($i=1; $i<=6; $i++) {
       				if(!isset($d['params']['TaxValue'.$i]))	$d['params']['TaxValue'.$i]=0;
        		}
        		$data[]=$d;
        		$data[]=array(
					'method'=>'WaitForPrinting'
				);
				/*$data[]=array(
					'method'=>'Get_DocumentNumber'
				);*/
				$data[]=array(
					'method'=>'FNGetStatus'
				);
				/*$data[]=array(
					'method'=>'Get_FiscalSign'
				);*/
        	}
        	elseif($p[0]=='check_mark') {
        		if(!$p[2]) $p[2]=1;
            	if(!isset($p[3])) $p[3]=0;
            	$p[1]=str_replace('{{U+003B}}', ';', $p[1]);
            	$data=array_merge($data, $this->check_mark($p[1], $p[2], $p[3]));
        	}
        	elseif($p[0]=='set_table'){
        		$d=array(
        			'method'=>'WriteTable',
	    			'params'=>array(
	    				'TableNumber'=>$p[1],
						'RowNumber'=>$p[2],
						'FieldNumber'=>$p[3],
	    			)
        		);
        		if(is_numeric($p[4])) {
        			$d['params']['ValueOfFieldInteger']=$p[4];
        		}
        		else {
        			$d['params']['ValueOfFieldString']=$p[4];
        		}
        		$data[]=$d;
        	}
        	elseif($p[0]=='cut_check'){
        		$data[]=array(
        			'method'=>'CutCheck',
				'params'=>array(
					'CutType'=>($p[1]?1:0)
				)
        		);
        	}
        	elseif($p[0]=='open_cash_box'){
        		if(!isset($p[2]) || $p[2]==0 || $this->pay_cash==1) {
        			$data[]=array(
        				'method'=>'OpenDrawer'
        			);
        		}
        	}
        }

        if($print==0) {
        	$data[]=array(
    			'method'=>'WriteTable',
    			'params'=>array(
    				'TableNumber'=>$tableParams['no_print']['table'],
					'RowNumber'=>$tableParams['no_print']['row'],
					'FieldNumber'=>$tableParams['no_print']['cell'],
					'ValueOfFieldInteger'=>0
    			)
    		);
        }
        return $data;
	}
}
