<?php
namespace Models;

class php_atol_Fr_php {
	public $f3, $sets, $web, $config;

	public $new_smde=true;

    protected $electronically=array(true, false);

	protected $tax=array( // Налог
		1=>'vat0', // НДС 0
        2=>'vat10', // НДС 10 выделить
        3=>'vat20', // НДС 20 выделить
        4=>'none', // Без НДС
        5=>'vat110', // НДС 10 начислить
        6=>'vat120' // НДС 20 начислить
	);

	protected $sno=array(
		'01'=>'osn',
		'02'=>'usnIncome',
		'04'=>'usnIncomeOutcome',
		'08'=>'osn',
		'10'=>'esn',
		'20'=>'patent'
	);

	protected $units=array(
		0=>'piece',
		10=>'gram',
		11=>'kilogram',
		12=>'ton',
		20=>'centimeter',
		21=>'decimeter',
		22=>'meter',
		30=>'squareCentimeter',
		31=>'squareDecimeter',
		32=>'squareMeter',
		40=>'milliliter',
		41=>'liter',
		42=>'cubicMeter',
		50=>'kilowattHour',
		51=>'gkal',
		70=>'day',
		71=>'hour',
		72=>'minute',
		73=>'second',
		80=>'kilobyte',
		81=>'megabyte',
		82=>'gigabyte',
		83=>'terabyte',
		255=>'otherUnits'
	);

	protected $paymentObject=array( // Признак предмета расчета
		1=>'commodity', // товар
        2=>'excise', // подакцизный товар
        3=>'job', // работа
        4=>'service', // услуга
        5=>'gamblingBet',  // ставка азартной игры
        6=>'gamblingPrize', // выигрыш азартной игры
        7=>'lottery', // лотерейный билет
        8=>'lotteryPrize', // выигрыш лотереи
        9=>'intellectualActivity', // предоставление результатов интерелектуальной деятельности
        10=>'payment', // платеж
        11=>'agentCommission', // агентское вознаграждение
        12=>'composite', // составной предмет расчета
        13=>'another', // иной предмет расчета
        14=>'proprietaryLaw', // имущественное право
        15=>'nonOperatingIncome', // внереализационный доход
        16=>'otherContributions', // иные платежи и взносы
        17=>'merchantTax', // торговый сбор
        18=>'resortFee', // курортный сбор
        19=>'deposit', // залог
        20=>'consumption', // расход
        21=>'soleProprietorCPIContributions', // взносы на ОПС ИП
        22=>'cpiContributions', // взносы на ОПС
        23=>'soleProprietorCMIContributions', // взносы на ОМС ИП
        24=>'cmiContributions', // взносы на ОМС
        25=>'csiContributions', // взносы на ОСС
        26=>'casinoPayment', // платеж казино
        27=>'fundsIssuance', // выдача денежных средств
		30=>'exciseWithoutMarking', // подакцизный товар, не имеющий код маркировки
		31=>'exciseWithMarking', // подакцизный товар, имеющий код маркировки
		32=>'commodityWithoutMarking', // товар, не имеющий код маркировки
		33=>'commodityWithMarking' // товар, имеющий код маркировки
	);

	protected $paymentMethod=array( // Признак способа расчета
		1=>'fullPrepayment', // предоплата 100%
        2=>'prepayment', // предоплата
        3=>'advance', // аванс
        4=>'fullPayment', // полный расчет
        5=>'partialPayment', // частичный расчет и кредит
        6=>'credit', // передача в кредит
        7=>'creditPayment' // оплата кредита
	);

	public $fd=0; // Номер ФД
	public $fp=0; // Фискальный признак
	public $kkm_time=0; // Время чека
	
	function __construct($f3, $sets, $config) {
		$this->f3=$f3;
		$this->sets=$sets;
		$this->web=\Web::instance();
		//$this->config=parse_ini_file($sets['driver'].'/server.config'); // Настройки драйвера
        $this->config=$config;
        if(!isset($this->config['WaitTime'])) {
            $this->config['WaitTime']=30000;
        }
        if(!isset($this->config['AuthUser'])) {
            $this->config['AuthUser']=99;
        }
        if(!isset($this->config['AuthPass'])) {
            $this->config['AuthPass']='Agorta_4ever';
        }
        $vat_type=array(
			'vatNo'=>4,
			'vat0'=>1,
			'vat110'=>5,
			'vat10'=>2,
			'vat120'=>6,
			'vat20'=>3
		);
		foreach($vat_type as $type=>$vat){
			if(!isset($this->config['vat_type'][$type])) {
            	$this->config['vat_type'][$type]=$vat;
        	}
		}
	}

    function get_mark_type($mark) {
        $strlen=mb_strlen($mark);
        if($strlen==68 && ctype_alnum($mark)) {
            return "egais20";
        }
        if($strlen==150 && ctype_alnum($mark)) {
            return "egais30";
        }

        return "other";
    }

	function kkm_init() {
		/*$data=array(
			'type'=>'setDeviceParameters',
			'deviceParameters'=>array(
				array(
					'key'=>19,
					'value'=>1
				),
				array(
					'key'=>62,
					'value'=>1
				)
			)
		);
		$uuid=uniqid(); // id задания
		$options = $this->options($uuid, $data);
		// Оотправляем запрос
		$res=$this->send_task($options);
		// Проверка выполнения задания
		$res=$this->check_task($uuid);
		if(!$res['success']) {
			echo $res['error'];
			echo $continue_html= \Template::instance()->render('fr_error_continue.htm');
			exit();
		}*/
		//$this->f3->set('SESSION.'.$this->sets['printer'].'_drivers_init', 1);
	}

	// Формирование данных для отправки
	function options($uuid, $data, $timeout=3) {
		$options = array( // Данные для отправки на сервер
			'timeout'=>$timeout,
			'header'=>array(
				'authorization: Basic '.base64_encode($this->config['AuthUser'].':'.$this->config['AuthPass']),
				'Content-Type: application/json',
        		'Expect:'
			),
    		'method'  => 'POST',
    		'content'=>json_encode(
    			array(
    				'uuid'=>$uuid,
    				'request'=>$data
    			)
    		)
		);

		return $options;
	}

	// Отправка задания
	function send_task($options) {
		$url=$this->sets['url'].'/requests';
        if($this->config['deviceID']) {
            $url.='?deviceID='.$this->config['deviceID'];
        }
		$res=$this->web->request($url, $options);
		if(mb_strpos($res['headers'][0], '200')===false && mb_strpos($res['headers'][0], '201')===false) {

			if(mb_strpos($res['headers'][0], '401')>0) {
				$error_txt="<p class='false'>Авторизация на web-сервере АТОЛ не пройдена</p>";
			}
			elseif(mb_strpos($res['headers'][0], '403')>0) {
				$error_txt="<p class='false'>ККТ не активирована</p>";
			}
			elseif(mb_strpos($res['headers'][0], '404')>0) {
				$error_txt="<p class='false'>ККТ по заданному идентификатору не найдена или ККТ по умолчанию не выбрана</p>";
			}
			elseif(mb_strpos($res['headers'][0], '408')>0) {
				$error_txt="<p class='false'>За 30 секунд не удалось захватить управление драйвером (занят фоновыми непрерываемыми задачами). Повторите запрос позже.</p>";
			}
			elseif(mb_strpos($res['headers'][0], '409')>0) {
				$error_txt="<p class='false'>Задание с таким же uuid уже существует</p>";
			}
			elseif(mb_strpos($res['headers'][0], '420')>0) {
				$error_txt="<p class='false'>Произошла ошибка во время проверки формата задания</p>";
			}
			else {
				$error_txt="<p class='false'>Ошибка добавления JSON задания</p>";
			}
			$this->f3->get('logs_model')->save_log($error_txt.' (Касса)');
			if($this->f3->get('fr_error_ignore')) {
                return array(
                    'success'=>false,
					'error'=>$error_txt
                );
            }
            echo $error_txt;
			exit();
		}
		return $res;
	}

	function check_task($uuid) {
		$url=$this->sets['url'].'/requests/'.$uuid;
		$options=array(
			'timeout'=>3,
			'method'=>'GET',
			'header'=>array(
				'authorization: Basic '.base64_encode($this->config['AuthUser'].':'.$this->config['AuthPass']),
				'Content-Type: application/json',
        		'Expect:'
			),
		);
		// Ждем выполнения задания
		$ok=false;
		$error='';
		$n=200;
		for($i=0; $i<$n; $i++) {
			usleep($this->config['WaitTime']);
			$res=$this->web->request($url, $options);
			if(mb_strpos($res['headers'][0], '200')===false && mb_strpos($res['headers'][0], '201')===false) {
				if(mb_strpos($res['headers'][0], '401')>0) {
					$error_txt="<p class='false'>Авторизация на web-сервере АТОЛ не пройдена</p>";
				}
				elseif(mb_strpos($res['headers'][0], '403')>0) {
					$error_txt="<p class='false'>ККТ не активирована</p>";
				}
				elseif(mb_strpos($res['headers'][0], '404')>0) {
					$error_txt="<p class='false'>ККТ по заданному идентификатору не найдена или ККТ по умолчанию не выбрана</p>";
				}
				else {
					$error_txt="<p class='false'>Ошибка чтения JSON задания</p>";
				}
				$this->f3->get('logs_model')->save_log($error_txt.' (Касса)');
				if($this->f3->get('fr_error_ignore')) {
                	return array(
                    	'success'=>false,
						'error'=>$error_txt
                	);
            	}
			}
			$json=json_decode($res['body'], true);
			// Проверяем результаты заданий
			$ready=0;
			$wait=1;
			if(!is_array($json['results'])) {
				return array(
                   	'success'=>false,
					'error'=>$res['body']
                );
			}
			foreach ($json['results'] as $r) {
				if($r['status']=='ready') { // Задание выполнено
					$ready=1;
					$wait=0;
					if(isset($r['offlineValidation']['fmCheckErrorReason'])) {
						$errors=array(
							'typeIncorrect'=>'КМ данного типа не подлежит проверке в ФН',
							'noKeys'=>'ФН не содержит ключи проверки кода проверки этого КМ',
							'noGS1'=>'проверка невозможна, так как остутствуют идентификаторы применения GS1 91 и/или 92 или их формат неверный',
							'other'=>'проверка невозможна по иной причине'
						);
						if(isset($errors[$r['offlineValidation']['fmCheckErrorReason']])) {
							$this->f3->get('logs_model')->save_log($errors[$r['offlineValidation']['fmCheckErrorReason']].' (Касса)');
							return array(
								'success'=>true,
								'check_mark'=>false,
								'error'=>$errors[$r['offlineValidation']['fmCheckErrorReason']],
								'results'=>$json['results']
							);
						}
					}
					if(isset($r['onlineValidation']['markOperatorItemStatus'])) {
						$errors=array(
							'itemEstimatedStatusIncorrect'=>'планируемый статус товара некорректен',
							'itemSaleStopped'=>'оборот товара приостановлен',
						);
						if(isset($errors[$r['onlineValidation']['markOperatorItemStatus']])) {
							$this->f3->get('logs_model')->save_log($errors[$r['onlineValidation']['markOperatorItemStatus']].' (Касса)');
							return array(
								'success'=>true,
								'check_mark'=>false,
								'error'=>$errors[$r['onlineValidation']['markOperatorItemStatus']],
								'results'=>$json['results']
							);
						}
					}
					if(isset($r['result']['onlineValidation']['itemInfoCheckResult'])) {
						$this->check_mark_result=$r['result']['onlineValidation']['itemInfoCheckResult'];
					}
					if(isset($r['result']['fiscalParams'])) {
						$this->fd=$r['result']['fiscalParams']['fiscalDocumentNumber'];
						$this->fp=$r['result']['fiscalParams']['fiscalDocumentSign'];
						$this->kkm_time=strtotime($r['result']['fiscalParams']['fiscalDocumentDateTime']);
						$this->f3->set('fn', $r['result']['fiscalParams']['fnNumber']);
					}
				}
				if($r['status']=='wait' || $r['status']=='inProgress') { // Задание ждет очереди или еще выполняется
					$ready=0;
				}
				if($r['status']=='error') { // Ошибка выполнения задания, дальше ничего не выполнится
                   $error.='<p class="false">'.$r['error']['description'].'</p>';
                    $this->f3->get('logs_model')->save_log($r['error']['description'].' (Касса)');
					$i=$n;
					$ready=0;
					$wait=0;
					break;
				}
			}
			if($ready==1) {
				$i=$n;
				$ok=true;
			}
		}
		if($wait==1) {
			$ok=false;
			$json['results'][0]['errorDescription']='Не удалось дождаться выполнения задания ';
		}
		if($ok===false) { // Вывод ошибок
			foreach ($json['results'] as $a=>$r) {
				$error.='<p class="false">'.$r['errorDescription'].'</p>';
			}
			$this->f3->get('logs_model')->save_log($error.' (Касса)');
		}
		return array(
			'success'=>$ok,
			'error'=>$error,
			'results'=>$json['results']
		);
	}

	// Выводим состояние ФН
	function fnInfo($data) {
		$p=array(
			'serial'=>array('title'=>'Заводской номер ФН'),
			'livePhase'=>array(
				'title'=>'Фаза жизни ФН',
				'values'=>array(
					'init'=>'настройка ФН',
					'configured'=>'настроен, готов в активации',
					'fiscalMode'=>'фискальный режим',
					'postFiscalMode'=>'постфискальный режим',
					'accessArchive'=>'доступ к архиву ФН',
					'unknown'=>'неизвестная фаза жизни',
				)
			),
			'version'=>array('title'=>'Версия ФН'),
			'execution'=>array('title'=>'Исполнение ФН'),
			'numberOfRegistrations'=>array('title'=>'Количество проведенных регистраций'),
			'registrationsRemaining'=>array('title'=>'Количество оставшихся регистраций'),
			'validityDate'=>array('title'=>'Срок действия ФН'),
			'ffdVersion'=>array('title'=>'Версия ФФД ККТ'),
			'fnFfdVersion'=>array('title'=>'Версия ФФД ФН'),
			'warnings'=>array(
				'title'=>'Предупреждения ФН',
				'array'=>array(
					'memoryOverflow'=>array('title'=>'Память ФН переполнена'),
					'needReplacement'=>array('title'=>'Требуется срочная замена ФН'),
					'ofdTimeout'=>array('title'=>'Превышено время ожидания ответа от ОФД'),
					'resourceExhausted'=>array('title'=>'Исчерпан ресурс ФН'),
					'criticalError'=>array('title'=>'Критическая ошибка ФН'),
					'fnContainsKeysUpdaterServerUri'=>array('title'=>'ФН содержит URI сервера ОКП'),
				)
			),
		);
		echo"<br><p><b>Информации о ФН</b></p>";
		foreach ($data['fnInfo'] as $key => $value) {
			if(isset($p[$key])) {
				if($value===false) $v='Нет';
				elseif($value===true) $v='Да';
				elseif(isset($p[$key]['array'])) {
					echo '<p><b>'.$p[$key]['title'].'</b></p>';
					foreach($value as $k=>$val) {
						echo '<p><b>'.$p[$key]['array'][$k]['title'].'</b> - '.($val?'Да':'Нет').'</p>';
					}
					continue;
				}
				elseif(isset($p[$key]['values'][$value])) $v=$p[$key]['values'][$value];
				else $v=$value;
				echo '<p><b>'.$p[$key]['title'].'</b> - '.$v.'</p>';
			}
			else {
				if(is_array($value)) {
					echo '<p><b>'.$key.'</b> - '.implode('; ', $value).'</p>';
				}
				else {
					echo '<p><b>'.$key.'</b> - '.$value.'</p>';
				}
			}
		}
	}

	// Выводим состояние ОФД
	function ofdStatus($data) {
		if(array_key_first($data)!='status') {
			$data=array_reverse($data);
		}
		$p=array(
			'status'=>array(
				'title'=>'Состояния обмена с ОФД',
				'array'=>array(
					'notSentCount'=>array('title'=>'Количество неотправленных ФД'),
					'notSentFirstDocNumber'=>array('title'=>'Номер первого неотправленного ФД'),
					'notSentFirstDocDateTime'=>array('title'=>'Дата и время первого неотправленного ФД'),
					'numberOfRegistrations'=>array('title'=>'Количество проведенных регистраций'),
					'lastSuccessKeysUpdate'=>array('title'=>'Дата и время последнего успешного ОКП'),
				)
			),
			'errors'=>array(
				'title'=>'Ошибки обмена',
				'array'=>array(
					'network'=>array(
						'title'=>'Ошибки сети',
						'array'=>array(
							'code'=>'Код ошибки',
							'description'=>'Текст ошибки'
						)
					),
					'ofd'=>array(
						'title'=>'Ошибки ОФД',
						'array'=>array(
							'code'=>'Код ошибки',
							'description'=>'Текст ошибки'
						)
					),
					'fn'=>array(
						'title'=>'Ошибки ФН',
						'array'=>array(
							'code'=>'Код ошибки',
							'description'=>'Текст ошибки'
						)
					),
					'documentNumber'=>array('title'=>'Номер ФД, на котором произошла ошибка'),
					'fnCommandCode'=>array('title'=>'Команда ФН, на которой произошла ошибка'),
					'lastSuccessConnectionDateTime'=>array('title'=>'Дата и время последнего успешного соединения с ОФД'),
				)
			),
		);
		echo"<br>";
		foreach ($data as $key => $value) {
			if(isset($p[$key])) {
				if($value===false) $v='Нет';
				elseif($value===true) $v='Да';
				elseif(isset($p[$key]['array'])) {
					echo '<p><b>'.$p[$key]['title'].'</b></p>';
					foreach($value as $k=>$val) {
						if(is_array($val)) {
							echo '<p><b>'.$p[$key]['array'][$k]['title'].'</b> - ';
							foreach($val as $vkey=>$vv){
								echo $p[$key]['array'][$k]['array'][$vkey].': '.$vv.'; ';
							}
							echo"</p>";
						}
						else echo '<p><b>'.$p[$key]['array'][$k]['title'].'</b> - '.$val.'</p>';
					}
					continue;
				}
				elseif(isset($p[$key]['values'][$value])) $v=$p[$key]['values'][$value];
				else $v=$value;
				echo '<p><b>'.$p[$key]['title'].'</b> - '.$v.'</p>';
			}
			else {
				echo '<p><b>'.$key.'</b> - '.$value.'</p>';
			}
		}
	}

	// Выводим статус ККМ в понятном виде
	function deviceStatus($data) {
		$p=array(
			'currentDateTime'=>array('title'=>'Текущие дата и время'),
			'shift'=>array(
				'title'=>'Состояние смены',
				'values'=>array(
					'closed'=>'закрыта',
					'opened'=>'открыта',
					'expired'=>'истекла'
				)
			),
			'blocked'=>array('title'=>'ККТ заблокирована'),
			'coverOpened'=>array('title'=>'Крышка открыта'),
			'paperPresent'=>array('title'=>'Наличие чековой ленты'),
			'fiscal'=>array('title'=>'ККТ зарегистрирована'),
			'fnFiscal'=>array('title'=>'ФН фискализирован'),
			'fnPresent'=>array('title'=>'ФН обнаружен'),
			'cashDrawerOpened'=>array('title'=>'Денежный ящик открыт')
		);
		foreach ($data as $key => $value) {
			if(isset($p[$key])) {
				if($value===false) $v='Нет';
				elseif($value===true) $v='Да';
				elseif(isset($p[$key]['values'][$value])) $v=$p[$key]['values'][$value];
				else $v=$value;
				echo '<p><b>'.$p[$key]['title'].'</b> - '.$v.'</p>';
			}
			else {
				echo '<p><b>'.$key.'</b> - '.$value.'</p>';
			}
		}
	}

	// Отправка запроса
	function send_ajax($strs, $timeout=3, $view=false) {
		/*if(!$this->f3->get('SESSION.'.$this->sets['printer'].'_drivers_init')) {
			$this->kkm_init();
		}*/
		$data=$this->wtf($strs);
		if(!$data) {
			$error_txt=$strs.': Команда не поддерживается';
			$error_txt.='<p class="false">'.$error_txt.'</p>';
			$this->f3->get('logs_model')->save_log($error_txt.' (Касса)');
			if($this->f3->get('fr_error_ignore')) {
                return array(
                    'success'=>false,
					'error'=>$error_txt
                );
            }
            echo $error_txt;
			exit();
		}

		if($data[0]['type']=='beginMarkingCodeValidation') {
			$uuid=uniqid(); // id задания
			$cancel[0]=array(
				'type'=>'cancelMarkingCodeValidation'
			);
			$options = $this->options($uuid, $cancel);
			$res=$this->send_task($options);
			// Проверка выполнения задания
			$res=$this->check_task($uuid);
			if(!$res['success']) {
				$error_txt=$strs.'<br>';
				$error_txt.=$res['error'];
				$this->f3->get('logs_model')->save_log($strs.': '.$res['error'].' (Касса)');
				if($this->f3->get('fr_error_ignore')) {
	                return array(
	                    'success'=>false,
						'error'=>$error_txt
	                );
	            }
	            echo $error_txt;
				exit();
			}
			usleep($this->config['WaitTime']);
		}

		$uuid=uniqid(); // id задания
		$options = $this->options($uuid, $data);
		// Оотправляем запрос
		$res=$this->send_task($options);
		// Проверка выполнения задания
		$res=$this->check_task($uuid);
		if(!$res['success']) {
			$error_txt=$strs.'<br>';
			$error_txt.=$res['error'];
			$this->f3->get('logs_model')->save_log($strs.': '.$res['error'].' (Касса)');
			if($this->f3->get('fr_error_ignore')) {
                return array(
                    'success'=>false,
					'error'=>$error_txt
                );
            }
            echo $error_txt;
			exit();
		}
		if($data[0]['type']=='beginMarkingCodeValidation') {
			for($i=0; $i<10; $i++) {
				usleep($this->config['WaitTime']);
				$uuid=uniqid(); // id задания
				$check[0]=array(
					'type'=>'getMarkingCodeValidationStatus'
				);
				$options = $this->options($uuid, $check);
				// Оотправляем запрос
				$res=$this->send_task($options);
				// Проверка выполнения задания
				$res=$this->check_task($uuid);
				if($res['results'][0]['result']['driverError']['code']>0) {
					$i=100;
					if($this->add_item){
						break;
					}
					else {
						$this->f3->get('logs_model')->save_log($res['results'][0]['result']['driverError']['description'].' (Касса)');
						return array(
							'success'=>false,
							'txt'=>strip_tags($res['results'][0]['result']['driverError']['description'])
						);
					}
				}
				if($res['results'][0]['result']['onlineValidation']['markOperatorItemStatus']) {
					$i=100;
					break;
				}
				if(!$res['success']) {
					$error_txt=$strs.'<br>';
					$error_txt.=$res['error'];
					$this->f3->get('logs_model')->save_log($strs.': '.$res['error'].' (Касса)');
					if($this->f3->get('fr_error_ignore')) {
		                return array(
		                    'success'=>false,
							'error'=>$error_txt
		                );
		            }
		            echo $error_txt;
					exit();
				}
				usleep($this->config['WaitTime']);
			}
			$uuid=uniqid(); // id задания
			$cancel[0]=array(
				'type'=>'acceptMarkingCode'
			);
			$options = $this->options($uuid, $cancel);
			$res=$this->send_task($options);
			// Проверка выполнения задания
			$res=$this->check_task($uuid);
			if(!$res['success']) {
				//echo $strs.'<br>';
				//echo $res['error'];
				$this->f3->get('logs_model')->save_log($strs.': '.$res['error'].' (Касса)');
				$res['check_mark']=false;
				//exit();
			}
			usleep($this->config['WaitTime']);
		}


		if(isset($res['check_mark']) && $res['check_mark']===false) {
			$this->f3->get('logs_model')->save_log($res['error'].' (Касса)');
			return array(
				'success'=>$res['check_mark'],
				'txt'=>strip_tags($res['error'])
			);
		}
		if($view) {
			foreach ($res['results'] as $key=>$r) {
				if(isset($r['result']['deviceStatus'])) {
					$this->deviceStatus($r['result']['deviceStatus']);
				}
				elseif(isset($r['result']['fnInfo'])) {
					$this->fnInfo($r['result']);
				}
				elseif(isset($r['result']['status']['notSentCount'])
					 || isset($r['result']['status']['notSentFirstDocNumber'])
					 || isset($r['result']['status']['notSentFirstDocDateTime'])
					 || isset($r['result']['status']['numberOfRegistrations'])
					 || isset($r['result']['status']['lastSuccessKeysUpdate'])
					) {
					$this->ofdStatus($r['result']);
				}
				else {
					if($r['result']) {
						var_dump($r['result']);
					}
					if($r['error']['code']==0) {
						echo '<p class="true">'.$r['error']['description'].'</p>';
					}
					else {
						echo '<p class="true">'.$r['errorDescription'].'</p>';
						$this->f3->get('logs_model')->save_log($r['errorDescription'].' (Касса)');
					}
				}
			}
		}
		if(!$res['success'] && !$view) {
			$this->f3->get('logs_model')->save_log($strs.': '.$res['error'].' (Касса)');
			if($this->f3->get('fr_error_ignore')) {
                return array(
                    'success'=>false,
					'error'=>$error_txt
                );
            }
            echo $error_txt;
			echo $continue_html= \Template::instance()->render('fr_error.htm');
			exit();
		}

		if($this->fd!=0 && $this->fp!=0) {
			$res['fd']=$this->fd;
			$res['fp']=$this->fp;
			$res['kkm_time']=$this->kkm_time;
		}

		return $res;
	}

	// Информация о ККМ
	function getDeviceInfo() {
		$data[0]=array(
			'type'=>'getDeviceInfo'
		);
		$uuid=uniqid(); // id задания
		$options = $this->options($uuid, $data);
		// Отправляем запрос
		$res=$this->send_task($options);
		// Проверка выполнения задания
		$res=$this->check_task($uuid);
		if(!$res['success']) {
			echo $res['error'];
			$this->f3->get('logs_model')->save_log($res['error'].' (Касса)');
			exit();
		}
		return $res['results'][0]['result']['deviceInfo'];
	}

	function check_fnstatus() {
		$data=array(
			array(
        		'type'=>'getFnInfo'
        	),
        	array(
        		'type'=>'ofdExchangeStatus'
        	),
        	array(
        		'type'=>'getRegistrationInfo'
        	)
        );
        $uuid=uniqid(); // id задания
        $options = $this->options($uuid, $data);
        // Отправляем запрос
        $res=$this->send_task($options);
        // Проверка выполнения задания
        $res=$this->check_task($uuid);
        if(!$res['success']) {
            $this->f3->get('logs_model')->save_log($res['error'].' (Касса)');
            return array(
	    		'success'=>false,
	    		'txt'=>$res['error']
	    	);
        }
        $ofd_n=false;
        $fn_end=false;
        foreach($res['results'] as $r){
        	if($r['result']['fnInfo']) {
        		$fn_end=strtotime($r['result']['fnInfo']['validityDate']);
        		continue;
        	}
        	if($r['result']['status']) {
        		$ofd_n=$r['result']['status']['notSentCount'];
        	}
        	if($r['result']['device']) {
        		$inn=$r['result']['organization']['vatin'];
        		$org_name=$r['result']['organization']['name'];
        		$org_address=$r['result']['organization']['address'];
        	}
        }
        if($ofd_n!==false && $fn_end!==false) {
	        return array(
	        	'success'=>true,
	        	'kkm'=>'Атол',
	        	'ffd'=>$this->sets['version'],
	        	'inn'=>$inn,
            	'kpp'=>'',
            	'org_name'=>$org_name,
            	'org_address'=>$org_address,
	        	'ofd_n'=>$ofd_n,
	        	'fn_end'=>$fn_end
	        );
	    }
	    return array(
	    	'success'=>false,
	    	'txt'=>'error'
	    );
	}

    // Узнаем состояние смены
    function change_status() {
        $data[0]=array(
            'type'=>'getShiftStatus'
        );
        $uuid=uniqid(); // id задания
        $options = $this->options($uuid, $data);
        // Отправляем запрос
        $res=$this->send_task($options);
        // Проверка выполнения задания
        $res=$this->check_task($uuid);
        if(!$res['success']) {
            echo $res['error'];
            $this->f3->get('logs_model')->save_log($res['error'].' (Касса)');
            exit();
        }
        $status=$res['results'][0]['result']['shiftStatus']['state'];
        // closed - закрыта
        // opened - открыта
        // expired - истекла
        return $status;
    }

	// Узнать ширину бумаги
	function get_width() {
		if($this->config['PaperWidth']>0) {
			return $this->config['PaperWidth'];
		}
		$info=$this->getDeviceInfo();
		$this->config['PaperWidth']=$info['receiptLineLength'];
		return $info['receiptLineLength'];
	}

	// Форматирование текста
    function text_format($s, $w=null) {
        if(!$w)$w=$this->get_width();
        $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); // заменяем тэг сдвига на пробелы
        return $s;
    }

    function get_1162($pg, $mark) {
        if($pg=='pharma') $pg='medicines';
        $data[0]=array(
            "type"=>$pg
        );
        if($pg=='furs') {
            $data['serial']=$mark;
        }
        elseif($pg=='tobacco') {
            $data['gtin']=mb_substr($mark, 0, 14);
            $data['serial']=mb_substr($mark, 14, 7);
        }
        else {
            $data['gtin']=mb_substr($mark, 2, 14);
            $data['serial']=mb_substr($mark, 18, 13);
        }
        return $data;
    }

	// Что надо выполнить
	function wtf($strs) {
		$str=explode("\n", $strs);
		$ticket=false;
		$i=0;
        $ticket_i=false;
        $preitem=false;
        $preitems=array();
        $postitem=false;
        $postitems=array();
        $clearmark=false;
		foreach ($str as $s) {
			$items=array();
			$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]=='preitem') {
                $preitem=true;
                $ticket_i=$i;
                continue;
            }
            if($p[0]=='postitem') {
                if($ticket_i===false) $ticket_i=$i;
                $postitem=true;
                continue;
            }
        	if($p[0]=='beep') { // Гудок
        		$data[$i]=array(
        			'type'=>'nonFiscal',
        			'items'=>array(
        				array(
        					'type'=>'text',
        					'text'=>'Бип-бип-бип! Твой сигналит джип'
        				)
        			),
        			'printFooter'=>false
        		);
        	}
        	elseif($p[0]=='feed') { // Протяжка
                $this_item=array();
                for($j=0; $j<$p[1]; $j++) {
    	        	$this_item[]=array(
        				'type'=>'text',
        				'text'=>' '
            		);
	            }
                if($preitem===true) {
                    $preitems=array_merge($preitems, $this_item);
                }
                elseif($postitem===true) {
                    $postitems=array_merge($postitems, $this_item);
                }
                else {
                    $items=array_merge($items, $this_item);
                    $data[$i]=array(
                        'type'=>'nonFiscal',
                        'items'=>$items,
                        'printFooter'=>false
                    );
                }
        	}
        	elseif($p[0]=='open_session') { // Открытие смены
                if(!isset($p[1]))$p[1]=1;
            	$data[$i]=array(
	            	'type'=>'openShift',
                    'electronically'=>$this->electronically[$p[1]]
    	        );
    	        if(isset($p[2])) {
                    $cashier=explode('{{user_inn}}', $p[2]);
    	        	$data[$i]['operator']=array(
        				'name'=>$cashier[0]
        			);
                    if($cashier[1] && mb_strlen($cashier[1])==12) {
                        $data[$i]['operator']['vatin']=$cashier[1];
                    }
    	        }
        	}
        	elseif($p[0]=='z') { // Закрытие смены
                if(!isset($p[1]))$p[1]=1;
            	$data[$i]=array(
            		'type'=>'closeShift',
                    'electronically'=>$this->electronically[$p[1]]
            	);
            	if(isset($p[2])) {
                    $cashier=explode('{{user_inn}}', $p[2]);
    	        	$data[$i]['operator']=array(
        				'name'=>$cashier[0]
        			);
                    if($cashier[1] && mb_strlen($cashier[1])==12) {
                        $data[$i]['operator']['vatin']=$cashier[1];
                    }
    	        }
        	}
        	elseif ($p[0]=='p' || $p[0]=='pm') { // Печать текста
        		$txt=explode('#kkm_br#',$p[1]);
                $this_item=array();
        		foreach ($txt as $t) {
        			$this_item[]=array(
        				'type'=>'text',
        				'text'=>$this->text_format($t),
        				'wrap'=>'words'
            		);
        		}
                if($preitem===true) {
                    $preitems=array_merge($preitems, $this_item);
                }
                elseif($postitem===true) {
                    $postitems=array_merge($postitems, $this_item);
                }
                else {
                    $items=array_merge($items, $this_item);
                    $data[$i]=array(
                        'type'=>'nonFiscal',
                        'items'=>$items,
                        'printFooter'=>false
                    );
                }
        	}
            elseif ($p[0]=='qr') { // Печать текста
                $this_item=array();
                $scale=1;
                if(isset($p[2]) && is_numeric($p[2])) {
                	$scale=(int)$p[2];
                }
                else {
                	$scale=1;
                }
                $this_item[]=array(
                    'type'=>'barcode',
                    'barcode'=>$p[1],
                    'barcodeType'=>'QR',
                    'scale'=>$scale
                );
                if($preitem===true) {
                    $preitems=array_merge($preitems, $this_item);
                }
                elseif($postitem===true) {
                    $postitems=array_merge($postitems, $this_item);
                }
                else {
                    $items=array_merge($items, $this_item);
                    $data[$i]=array(
                        'type'=>'nonFiscal',
                        'items'=>$items,
                        'printFooter'=>false
                    );
                }
            }
            elseif ($p[0]=='barcode') { // Печать текста
                $this_item=array();
                $scale=1;
                if(isset($p[2]) && is_numeric($p[2])) {
                	$scale=(int)$p[2];
                }
                else {
                	$scale=1;
                }
                $this_item[]=array(
                    'type'=>'barcode',
                    'barcode'=>$p[1],
                    'barcodeType'=>'EAN'.mb_strlen($p[1]),
                    'scale'=>$scale,
                    'printText'=>true
                );
                if($preitem===true) {
                    $preitems=array_merge($preitems, $this_item);
                }
                elseif($postitem===true) {
                    $postitems=array_merge($postitems, $this_item);
                }
                else {
                    $items=array_merge($items, $this_item);
                    $data[$i]=array(
                        'type'=>'nonFiscal',
                        'items'=>$items,
                        'printFooter'=>false
                    );
                }
            }
        	elseif($p[0]=='print_font') { // Печать строки заданным размером шрифта
        		$txt=explode('#kkm_br#',$p[2]);
        		$w=$this->get_width();
        		$double_width=false;
        		$double_height=false;
        		if($p[1]>4)$p[1]=4;
				if($p[1]==2) {
        			$double_width=true;
        			$w/=2;
        		}
        		elseif($p[1]==3) {
        			$double_height=true;
        		}
        		elseif($p[1]==4) {
        			$double_width=true;
        			$double_height=true;
        			$w/=2;
        		}
                $this_item=array();
        		foreach ($txt as $t) {
        			$this_item[]=array(
        				'type'=>'text',
        				'text'=>$this->text_format($t, $w),
        				'wrap'=>'words',
        				'doubleWidth'=>$double_width,
        				'doubleHeight'=>$double_height,
            		);
        		}
        		if($preitem===true) {
                    $preitems=array_merge($preitems, $this_item);
                }
                elseif($postitem===true) {
                    $postitems=array_merge($postitems, $this_item);
                }
                else {
                    $items=array_merge($items, $this_item);
                    $data[$i]=array(
                        'type'=>'nonFiscal',
                        'items'=>$items,
                        'printFooter'=>false
                    );
                }
        	}
        	elseif($p[0]=='print_bold') { // Печать жирной строки
        		$txt=explode('#kkm_br#',$p[1]);
        		$w=$this->get_width()/2;
                $this_item=array();
        		foreach ($txt as $t) {
        			$this_item[]=array(
        				'type'=>'text',
        				'text'=>$this->text_format($t, $w),
        				'wrap'=>'words',
        				'doubleWidth'=>true,
        				'doubleHeight'=>true,
            		);
        		}
        		if($preitem===true) {
                    $preitems=array_merge($preitems, $this_item);
                }
                elseif($postitem===true) {
                    $postitems=array_merge($postitems, $this_item);
                }
                else {
                    $items=array_merge($items, $this_item);
                    $data[$i]=array(
                        'type'=>'nonFiscal',
                        'items'=>$items,
                        'printFooter'=>false
                    );
                }
        	}
        	elseif($p[0]=='x') { // X отчет
        		$data[$i]=array(
        			'type'=>'reportX'
        		);
        	}
        	elseif($p[0]=='continue_print') { // Продолжить печать чека
        		$data[$i]=array(
        			'type'=>'continuePrint'
        		);
        	}
        	elseif($p[0]=='c') { // Печать копии последнего чека
        		$data[$i]=array(
        			'type'=>'printLastReceiptCopy'
        		);
        	}
        	elseif($p[0]=='d') { // Статус ККМ
            	$data[$i]=array(
        			'type'=>'getDeviceStatus'
        		);
        		$i++;
        		$data[$i]=array(
        			'type'=>'getFnInfo'
        		);
        		$i++;
        		$data[$i]=array(
        			'type'=>'ofdExchangeStatus'
        		);
        	}
        	elseif($p[0]=='imde') { // Внесение или изъятие из кассы
        		$type='cashIn';
        		if($p[1]<0)$type='cashOut';
        		$data[$i]=array(
        			'type'=>$type,
        			'cashSum'=>abs($p[1]/100)
        		);
        	}
        	elseif($p[0]=='b') { // Открытие чека
        		$ticket=true;
        		$types=array(0=>'sell', 1=>'sellReturn', 128=>'sellCorrection', 130=>'sellReturnCorrection');
                $cashier=explode('{{user_inn}}', $p[2]);
        		$data[$i]=array(
        			'type'=>$types[$p[1]],
        			'ignoreNonFiscalPrintErrors'=>true,
        			'electronically'=>$this->electronically[$p[3]],
        			'validateMarkingCodes'=>false,
        			'operator'=>array(
        				'name'=>$cashier[0]
        			)
        		);
                if($cashier[1] && mb_strlen($cashier[1])==12) {
                    $data[$i]['operator']['vatin']=$cashier[1];
                }
                if($p[4]>0) {
                    $data[$i]['total']=$p[4]/100;
                }
        	}
        	elseif($p[0]=='set_tlv' && $p[1]==1173 && $ticket===true) { // Тип коррекции
        		$data[$i]['correctionType']=$p[3]==0?'self':'instruction';
        	}
        	elseif($p[0]=='set_tlv' && $p[1]==1178 && $ticket===true) { // Тип коррекции
        		$data[$i]['correctionBaseDate']=date('Y.m.d', $p[3]);
        	}
        	elseif($p[0]=='set_tlv' && $p[1]==1179 && $ticket===true) { // Тип коррекции
        		$data[$i]['correctionBaseNumber']=(string)$p[3];
        	}
        	elseif($p[0]=='set_tlv' && $p[1]==1008 && $ticket===true) { // Добавление в чек покупателя
        		if(mb_strpos($p[3], '@')===false) {
        			$p[3]='+'.$p[3];
        		}
        		$data[$i]['clientInfo']=array(
        			'emailOrPhone'=>$p[3]
        		);
        	}
        	elseif($p[0]=='set_tlv' && $p[1]==1192 && $ticket===true) { // ФПД ошибочного чека
        		$data[$i]['items'][]=array(
        			'type'=>'additionalAttribute',
        			'value'=>$p[3],
        			'print'=>true
        		);
        	}
        	elseif($p[0]=='smde' && $ticket===true) { // Добавление товара в чек
        		$this->add_item=true;
        		$line=json_decode($p[1], true);
        		if(!$line['psr']) $line['psr']=1;
        		if(!$line['ppr']) $line['ppr']=1;
        		if(!isset($line['units'])) $line['units']=255;
        		$line['price']/=100;
        		$line['quantity']/=1000;

        		$this_item=array(
        			'type'=>'position',
        			'name'=>$line['title'],
        			'price'=>$line['price'],
        			'quantity'=>$line['quantity'],
        			'amount'=>round($line['price']*$line['quantity'], 2),
        			'department'=>(int)$line['section'],
        			'paymentMethod'=>$this->paymentMethod[$line['psr']],
        			'paymentObject'=>$this->paymentObject[$line['ppr']],
        			'tax'=>array(
        				'type'=>$this->tax[$line['vat']]
        			)
        		);

        		if($line['partner']) {
        			$this_item['supplierInfo']=array(
        				'phones'=>array((string)'+'.$line['partner']['phone']),
        				'name'=>$line['partner']['name'],
        				'vatin'=>(string)$line['partner']['inn']
        			);
        			$this_item['agentInfo']['agents']=array('commissionAgent');
        		}

        		if($this->sets['version']=='1.2') {
        			$this_item['measurementUnit']=$line['units'];

        			/*if($line['barcode'] && !$line['mark']) {
        				$this_item['productCodes']=array(
        					'codes'=>array($line['barcode'])
        				);
        			}*/

        		}
                if($line['mark']) {
                    if($this->sets['version']=='1.2') {
                    	$clearmark=true;
                    	unset($this->check_mark_result);
                    	$this->send_ajax('check_mark;'.$line['mark'].';'.$line['quantity'].';'.$line['units'].";\n", 3, false);
                    	if(!$this->check_mark_result) {
                    		$this->check_mark_result=array(
                    			"imcCheckFlag"=>true,
                    			"imcCheckResult"=>false,
							    "imcStatusInfo"=>false,
							    "imcEstimatedStatusCorrect"=>false,
							    "ecrStandAloneFlag"=>true
                    		);
                    	}
                    	$mark=str_replace('{{U+003B}}', ';', $line['mark']);
                    	$status='itemPieceSold';
                    	if($line['units']!=0) {
                    		$status='itemDryForSale';
                    	}
                    	$this_item['imcParams']=array(
							"imcType"=>'auto',
							"imc"=>base64_encode($mark),
							"itemEstimatedStatus"=>$status,
            				"imcModeProcessing"=>0,
            				'itemInfoCheckResult'=>$this->check_mark_result
						);
                    }
                    else {
                    	if(isset($p_name[2]) && ($p_name[2]=='furs' || $p_name[2]=='pharma' || $p_name[2]=='tobacco' || $p_name[2]=='shoes')) {
                        	$mark=str_replace('', '', $mark);
                        	$mark=str_replace('\\u001d', '', $mark);
	                        $this_item['nomenclatureCode']=$this->get_1162($line['gp'], $mark);
	                    }
	                    else {
	                        $mlen=mb_strlen($mark);
	                        if($mlen!=8
	                            && $mlen!=13
	                            && $mlen!=14
	                            && preg_match("/[A-Za-z0-9\/\\!”%&’()*+-.,:;=<>?]/", $mark)
	                            && mb_substr($mark, 0, 2)=='01'
	                            && mb_substr($mark, 16, 2)=='21') {
	                            $mark=str_replace('', '\\u001d', $mark);
	                            if(mb_strpos($mark, '\\u001d')===false) {  
	                                $mark='\\u001d'.mb_substr($mark, 0, 31).'\\u001d'.mb_substr($mark, 31, 6).'\\u001d'.mb_substr($mark, 37);
	                            }
	                        }
	                        $this_item['markingCode']=array(
	                            "type"=>$this->get_mark_type($line['gp']),
	                            "mark"=>base64_encode($mark)
	                        );
	                    }
	                }
                }
                $data[$i]['items'][]=$this_item;
        	}
        	elseif($p[0]=='tmde' && $ticket===true) { // Оплата чека
        		if(count($p)==3) { // передан 1 способ оплаты ФФД 1.05
        			$data[$i]['payments'][]=array(
        				'type'=>(string)$p[2],
        				'sum'=>$p[1]/100
        			);
        		}
        		else { // Несколько способов оплаты
        			$data[$i]['taxationType']=$this->sno[$p[count($p)-1]];
        			if(count($p)==7) $end=4;
        			else $end=5;
        			for($j=0; $j<$end; $j++) {
        				$data[$i]['payments'][]=array(
        					'type'=>(string)$j,
        					'sum'=>$p[$j+1]/100
        				);
        			}
        		}
        		$ticket=false;
        		if($this->sets['version']=='1.2' && $clearmark===true) {
        			$i++;
        			$data[$i]=array(
        				'type'=>'clearMarkingCodeValidationResult'
        			);
        		}
        	}
        	elseif($p[0]=='open_cash_box') { // открыть денежный ящик
        		$data[$i]=array(
        			'type'=>'openCashDrawer'
        		);
        	}
        	elseif($p[0]=='check_mark') {
        		/*$data[$i]=array(
        			'type'=>'cancelMarkingCodeValidation'
        		);
        		$i++;*/
        		if(!$p[2])$p[2]=1;
        		if(!isset($p[3]))$p[3]=0;
        		$status='itemPieceSold';
        		if($p[3]!=0) {
        			$status='itemDryForSale';
        			$p[2]=(float)str_replace(',', '.', $p[2]);
        		}
        		else {
        			$p[2]=(float)1;
        		}
        		$data[$i]=array(
        			'type'=>'beginMarkingCodeValidation',
        			"params"=>array(
        				"imcType"=>"auto",
        				"imc"=>base64_encode(str_replace('{{U+003B}}', ';', $p[1])),
        				"itemEstimatedStatus"=>$status,
        				"imcModeProcessing"=>0
    				)
        		);

        		if($p[3]!=0) {
        			$data[$i]['params']['itemQuantity']=$p[2];
        			$data[$i]['params']['itemUnits']=$this->units[$p[3]];
        		}
        		
        		/*$i++;
        		$data[$i]=array(
        			'type'=>'getMarkingCodeValidationStatus'
        		);*/
        	}

        	if($ticket===false && $preitem===false && $postitem===false) {
                $i++;
        	}
            if($p[0]=='preitem_end') {
                $preitem=false;
            }
            if($p[0]=='postitem_end') {
                $postitem=false;
            }
        }
        if($preitems!==false) {
        	if($data[0]['type']=='nonFiscal') {
        		$data[0]['items']=array_merge($preitems, $data[0]['items']);
        	}
        	else {
            	$data[$ticket_i]['preItems']=$preitems;
            }
        }
        if($postitems!==false) {
        	if($data[0]['type']=='nonFiscal') {
        		$data[0]['items']=array_merge($data[0]['items'], $postitems);
        	}
        	else {
            	$data[$ticket_i]['postItems']=$postitems;
            }
        }
        return $data;
	}
}
