<?php
namespace Models;

class php_mercury_Fr_php {
    public $f3, $sets, $config, $port;

    public $new_smde=true;

    public $fd=0; // Номер ФД
    public $fp=0; // Фискальный признак
    public $kkm_time=0; // Время чека
    public $ticket=false;
    public $items=array();
    public $buyer=false;
    public $ticket_types=array(
        0=>0,
        1=>1,
        128=>4,
        130=>6,
    );
    public $pay_cash=0;
    public $paymethods=array(
        0=>'cash',
        1=>'ecash',
        2=>'prepayment',
        3=>'credit',
        4=>'consideration'
    );
    public $sno=array(
        '01'=>0, // Общая
        '02'=>1, // Упрощённая (Доход)
        '04'=>2, // Упрощённая (Доход минус Расход)
        '08'=>3, // Единый налог на вменённый доход
        '10'=>4, // Единый сельскохозяйственный налог
        '20'=>5 // Патентная система налогообложения
    );
    public $kkm_status_keys=array(
        'paperPresence'=>array(
            'title'=>'Наличие бумаги'
        ),
        'shiftInfo'=>array(
            'title'=>'Состояние смены',
            'values'=>array(
                'isOpen'=>array('title'=>'Смена открыта'),
                'is24Expired'=>array('title'=>'Смена открыта более 24 часов'),
                'num'=>array('title'=>'Номер последней смены'),
                'lastOpen'=>array('title'=>'Дата и время последней смены'),
                'cash'=>array('title'=>'Сумма наличных в денежном ящике')
            )
        ),
        'checkInfo'=>array(
            'title'=>'Состояние чека',
            'values'=>array(
                'isOpen'=>array('title'=>'Чек открыт'),
                'num'=>array('title'=>'Номер последнего закрытого чека в смене'),
                'goodsQty'=>array('title'=>'Кол-во позиций в чековом буфере')
            )
        ),
        'fnInfo'=>array(
            'title'=>'Состояние ФН',
            'values'=>array(
                'status'=>array('title'=>'Состояние'),
                'fnNum'=>array('title'=>'Номер ФН'),
                'lastDoc'=>array(
                    'title'=>'Последний документ в ФН',
                    'values'=>array(
                        'num'=>array('title'=>'Номер'),
                        'dateTime'=>array('title'=>'Дата и время регистрации')
                    )
                ),
                'unsignedDocs'=>array(
                    'title'=>'Документы не переданные в ОФД',
                    'values'=>array(
                        'qty'=>array('title'=>'Кол-во'),
                        'firstNum'=>array('title'=>'Номер первого непереданного документа'),
                        'firstDateTime'=>array('title'=>'Дата и время первого непереданного документа'),
                    )
                )
            )
        )
    );
    public $fn_status=array(
        0=>'Готов к фискализации',
        1=>'Фискальный режим',
        2=>'Постфискальный режим (идет передача ФД в ОФД)',
        3=>'Закрыт (доступен только в режиме чтения данных из архива)'
    );
    
    function __destruct()
       {
           //print "Уничтожается " . __CLASS__  . "\n";
           if($this->key) $this->closeKkmSession($this->key);
       }
    
    function __construct($f3, $sets, $config) {
        $this->f3=$f3;
        $this->sets=$sets;
        //$this->config=parse_ini_file($sets['driver'].'/server.config'); // Настройки драйвера
        $this->config=$config;
        require_once $sets['driver'].'/KKM.ports.php';
        $this->config['PortType']=strtolower($this->config['PortType']);
        if($this->config['PortType']=='remote') {
            $sets['url']=mb_substr($sets['url'], mb_strrpos($sets['url'], '/')+1);
            $url=explode(':', $sets['url']);
            $host = $url[0];
            $port = $url[1];
            $this->port = new \RemotePort($host, $port);
        }
        elseif($this->config['PortType']=='socket') {
            $sets['url']=mb_substr($sets['url'], mb_strrpos($sets['url'], '/')+1);
            $url=explode(':', $sets['url']);
            $host = $url[0];
            $port = $url[1];
            $this->port = new \RemoteSocket($host, $port);
        }
        elseif($this->config['PortType']=='ssh') {
            $u=mb_substr($sets['url'], mb_strrpos($sets['url'], '/'));
            $url=explode(':', $u);
            $host = $url[0];
            $port = $url[1];
            $this->port = new \SshComPort($host, $port);
        }
        else {
            $this->port = new \LocalComPort($sets['url']);
        }
        if(!$this->port->open()) {
            $error_txt='Не удалось соедениться со службой Inecrman';
            $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(!$this->config['WaitTime']) $this->config['WaitTime']=10;
        if(!$this->config['Attempts']) $this->config['Attempts']=1100;
        $vat_type=array(
            'vat20'=>1,
            'vat10'=>2,
            'vat120'=>3,
            'vat110'=>4,
            'vat0'=>5,
            'vatNo'=>6,
            'vat5'=>7,
            'vat7'=>8,
            'vat105'=>9,
            'vat107'=>10,
            'vat22'=>11,
            'vat122'=>12,
        );
        foreach($vat_type as $type=>$vat){
            if(!isset($this->config['vat_type'][$type])) {
                $this->config['vat_type'][$type]=$vat;
            }
        }
    }

    function prepareArg($num, $bytes) {
        return implode('', str_split(sprintf('%0' . ($bytes * 2) . 'x', $num), 2));
    }

    function send_command($array) {
        $json=json_encode($array);
        $len=mb_strlen($json);
        $data=pack('H*', $this->prepareArg($len, 4)).$json;
        $res=$this->port->write($data);
        if($res) {
            $n=0;
            while ($n<$this->config['Attempts']) {
                $res=$this->port->read(4);
                if(!$res) {
                    usleep($this->config['WaitTime']);
                    $n++;
                }
                else {
                    break;
                }
            }
            if(!$res) {
                $error_txt='Не удалось прочитать ответ: '.$json;
                $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;
                $this->closeKkmSession($this->key);
                exit();
            }
            $len=hexdec(implode('', unpack('H*', $res)));
            $res=$this->port->read($len);
            if(!$res) {
                $error_txt='Не удалось прочитать ответ: '.$json;
                $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;
                $this->closeKkmSession($this->key);
                exit();
            }
            return json_decode($res);
        }
        else {
            $error_txt='Не удалось отправить команду: '.$json;
            $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;
            $this->closeKkmSession($this->key);
            exit();
        }
    }

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

    // Открыть сессию 
    function openKkmSession() {
        $data=array(
            'sessionKey'=>null,
            'command'=>'OpenSession'
        );
        if($this->config['portName']) {
            $data['portName']=$this->config['portName'];
        }
        if($this->config['baudRate']) {
            $data['baudRate']=$this->config['baudRate'];
        }
        if($this->config['model']) {
            $data['model']=$this->config['model'];
        }
        if($this->config['serialNumber']) {
            $data['serialNumber']=$this->config['serialNumber'];
        }
        if($this->config['debug']) {
            $data['debug']=$this->config['debug'];
        }
        if($this->config['logPath']) {
            $data['logPath']=$this->config['logPath'];
        }
        $res=$this->send_command($data);
        return $res;
    }

    // Закрыть сессию
    function closeKkmSession($key) {
        $data=array(
            'sessionKey'=>$key,
            'command'=>'CloseSession'
        );
        return $this->send_command($data);
    }

    // Гудок
    function beep($data) {
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'PrintText',
            'text'=>'Бип-бип-бип, твой сигналит джип'
        );
        return $this->send_command($pars);
    }

    // Печать по номеру ФД
    function printdoc($data) {
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'PrintDocFromFN',
            'fiscalDocNum'=>$data[1]
        );
        return $this->send_command($pars);
    }

    // Протяжка
    function feed($data) {
        $txt=str_repeat("\n", $data[1]);
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'PrintText',
            'text'=>$txt
        );
        return $this->send_command($pars);
    }

    // Открыть смену
    function open_session($data) {
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'OpenShift',
        );
        if($data[1]==1) {
            $pars['printDoc']=true;
        }
        else {
            $pars['printDoc']=false;
        }
        if($data[2]) {
            $cashier=explode('{{user_inn}}', $data[2]);
            $pars['cashierInfo']=array(
                'cashierName'=>$cashier[0]
            );
            if($cashier[1] && mb_strlen($cashier[1])==12) {
                $pars['cashierInfo']['cashierINN']=$cashier[1];
            }
        }
        return $this->send_command($pars);
    }

    // Закрыть смену
    function z($data) {
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'CloseShift'
        );
        if($data[1]==1) {
            $pars['printDoc']=true;
        }
        else {
            $pars['printDoc']=false;
        }
        if($data[2]) {
            $cashier=explode('{{user_inn}}', $data[2]);
            $pars['cashierInfo']=array(
                'cashierName'=>$cashier[0]
            );
            if($cashier[1] && mb_strlen($cashier[1])==12) {
                $pars['cashierInfo']['cashierINN']=$cashier[1];
            }
        }
        return $this->send_command($pars);
    }

    // Печать QR
    function qr($data) {
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'PrintBarCode',
            'bcType'=>4,
            'value'=>$data[1]
        );
        return $this->send_command($pars);
    }

    // Печать штрих-кода
    function barcode($data) {
        if(mb_strlen($data[1])==8) {
            $type=1;
        }
        else {
            $type=2;
        }
        // Пока типы 1 и 2 не работают, печатаем qr
        $type=4;
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'PrintBarCode',
            'bcType'=>$type,
            'value'=>(string)$data[1]
        );
        $res=$this->send_command($pars);
        if($res->result==0) {
            $res=$this->p(array(1=>'#kkm_center#'.$data[1]));
        }
        return $res;
    }

    function get_paper_width() {
        if($this->config['PaperWidth']>0) return $this->config['PaperWidth'];
        $close=false;
        if(!$this->key) {
            $close=true;
            $res=$this->openKkmSession();
            if($res->result!=0) {
                $this->f3->get('logs_model')->save_log($res->description.' (Касса)');
                return 0;
            }
            $this->key=$res->sessionKey;
        }
         $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'GetCommonInfo'
        );
        $res=$this->send_command($pars);
        if($close) $this->closeKkmSession($this->key);
        if($res->result==0) {
            $this->config['PaperWidth']=isset($res->cpl[1])?$res->cpl[1]:32;
            return $this->config['PaperWidth'];
        }
        $this->f3->get('logs_model')->save_log($res->description.' (Касса)');
        return 0;
    }

    // Узнать ширину бумаги
    function get_width() {
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'GetCommonInfo'
        );
        $res=$this->send_command($pars);
        if($res->result==0) {
            $this->config['PaperWidth']=isset($res->cpl[1])?$res->cpl[1]:32;
        }
        else {
            $this->f3->get('logs_model')->save_log($res->description.' (Касса)');
            echo '<p class="false">'.$res->description.'</p>';
        }
    }

    // Форматирование текста
    function text_format($s, $w=null) {
        if(!$w) {
            $this->get_paper_width();
        }
        $w=$this->config['PaperWidth'];
        $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 p($data) {
        if(!isset($data[1])) $data[1]=' ';
        $txts=explode('#kkm_br#',$data[1]);
        $txt='';
        foreach ($txts as $n=>$t) {
            if(!$t)$t=' ';
            $txt.=$this->text_format($t)."\n";
        }
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'PrintText',
            'text'=>$txt
        );
        $res=$this->send_command($pars);
        return $res;
    }

    function pm($data) {
        if(!isset($data[2])) $data[2]=' ';
        if($data[1]>0) {
            $w=$data[1];
        }
        else {
            if(!$this->config['PaperWidth']) {
                $this->get_paper_width();
            }
            $w=$this->config['PaperWidth'];
        }
        $txts=explode('#kkm_br#',$data[2]);
        $txt='';
        $strs=array();
        foreach ($txts as $n=>$t) {
            if(!$t)$t=' ';
            $t=$this->text_format($t);
            if(!$t) continue;
            $str=mb_str_split($t, $w);
            foreach($str as $s) {
                $txt.=$s."\n";
            }
        }
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'PrintText',
            'text'=>$txt
        );
        $res=$this->send_command($pars);
        return $res;
    }

    // Меркурий так не умеет
    function print_font($data) {
        return $this->p(array('p', $data[2]));
    }

    // Меркурий так тоже не умеет
    function print_bold($data) {
        return $this->p($data);
    }

    // X отчет
    function x($data) {
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'PrintReport',
            'reportCode'=>1
        );
        return $this->send_command($pars);
    }

    // Статус ККМ
    function d($data) {
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'GetStatus'
        );
        $res=$this->send_command($pars);
        if($res->result!=0) {
            return $res;
        }
        $keys=$this->kkm_status_keys;

        $txt='';
        foreach ($res as $key => $value) {
            if(isset($keys[$key])) {
                $txt.='<div>';
                $txt.='<b>'.$keys[$key]['title'].'</b>: ';
                if(is_array($keys[$key]['values'])) {
                    $txt.='<ol>';
                    foreach ($value as $v_key => $val) {
                        $txt.='<li>';
                        $txt.='<b>'.$keys[$key]['values'][$v_key]['title'].'</b>: ';
                        if(is_array($keys[$key]['values'][$v_key]['values'])) {
                            $txt.='<ol>';
                            foreach ($val as $k => $v) {
                                $txt.='<li>';
                                $txt.='<b>'.$keys[$key]['values'][$v_key]['values'][$k]['title'].'</b>: ';
                                if($v===true) {
                                    $txt.='да';
                                }
                                elseif($v===false) {
                                    $txt.='нет';
                                }
                                else {
                                    $txt.=$v;
                                }
                                $txt.='</li>';
                            }
                            $txt.='</ol>';
                        }
                        else {
                            if($val===true) {
                                $txt.='да';
                            }
                            elseif($val===false) {
                                $txt.='нет';
                            }
                            else {
                                if($key=='fnInfo' && $v_key=='status') {
                                    $val=$this->fn_status[$val];
                                }
                                $txt.=$val;
                            }
                        }
                        $txt.='</li>';
                    }
                    $txt.='</ol>';
                }
                else {
                    if($value===true) {
                        $txt.='да';
                    }
                    elseif($value===false) {
                        $txt.='нет';
                    }
                    else {
                        $txt.=$value;
                    }
                }
                $txt.='</div>';
            }
        }
        echo $txt;
        $this->closeKkmSession($this->key);
        exit();
    }

    // Внесение или изъятие из кассы
    function imde($data) {
        $command='BringMoney';
        if($data[1]<0) $command='WithdrawMoney';
        $cashier=explode('{{user_inn}}', $data[2]);
        $this->pay_cash=1;
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>$command,
            'cash'=>abs($data[1]),
            'cashierInfo'=>array(
                'cashierName'=>$cashier[0]
            )
        );
        if($cashier[1] && mb_strlen($cashier[1])==12) {
            $pars['cashierInfo']['cashierINN']=$cashier[1];
        }
        return $this->send_command($pars);
    }

    // Открытие чека
    function b($data) {
        if($data[3]==0 || $data[3]=='0') $printDoc=false;
        else $printDoc=true;
        $cashier=explode('{{user_inn}}', $data[2]);
        //if($data[1]==128) $data[1]=0;
        //elseif($data[1]==130) $data[1]=1;
        $this->ticket=array(
            'sessionKey'=>$this->key,
            'command'=>'OpenCheck',
            'checkType'=>$this->ticket_types[$data[1]],
            'taxSystem'=>$this->sno[$this->sets['sno']],
            'printDoc'=>$printDoc,
            'cashierInfo'=>array(
                'cashierName'=>$cashier[0]
            ),
        );
        if($cashier[1] && mb_strlen($cashier[1])==12) {
            $this->ticket['cashierInfo']['cashierINN']=$cashier[1];
        }
        $res['result']=0;
        return (object) $res;
    }

    // Добавление в чек покупателя
    function set_tlv($data) {
        if($data[1]==1008) {
            if(is_array($this->ticket)) {
                if(is_numeric($data[3]) && mb_strlen($data[3])==11) {
                    $data[3]='+'.$data[3];
                }
                $this->buyer=$data[3];
                $res['result']=0;
            }
            else {
                $res['result']=1;
                $res['description']='Чек не открыт';
                $this->f3->get('logs_model')->save_log($res['description'].' (Касса)');
            }
        }
        elseif($data[1]==1173) {
            if(is_array($this->ticket)) {
                $this->ticket['correctionInfo']['correctionType']=(int)$data[3];
                $res['result']=0;
            }
            else {
                $res['result']=1;
                $res['description']='Чек не открыт';
                $this->f3->get('logs_model')->save_log($res['description'].' (Касса)');
            }
        }
        elseif($data[1]==1178) {
            if(is_array($this->ticket)) {
                $this->ticket['correctionInfo']['causeDocDate']=date('Y-m-d', $data[3]);
                $res['result']=0;
            }
            else {
                $res['result']=1;
                $res['description']='Чек не открыт';
                $this->f3->get('logs_model')->save_log($res['description'].' (Касса)');
            }
        }
        elseif($data[1]==1179) {
            if(is_array($this->ticket)) {
                $this->ticket['correctionInfo']['causeDocNum']=$data[3];
                $res['result']=0;
            }
            else {
                $res['result']=1;
                $res['description']='Чек не открыт';
                $this->f3->get('logs_model')->save_log($res['description'].' (Касса)');
            }
        }
        else {
            $res['result']=1;
            $res['description']='Команда неизвестна';
            $this->f3->get('logs_model')->save_log($res['description'].' (Касса)');
        }
        return (object) $res;
    }

    // Обработка кода маркировки
    function mark($mark) {
        $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);
            $mark=str_replace('', '', $mark);
            $mark=mb_substr($mark, 0, 37);
        }
        return $mark;
    }

    // Добавление товара в чек
    function smde($data) {
        $line=json_decode($data[1], true);
        if(!$line['psr']) $line['psr']=4;
        if(!$line['ppr']) $line['ppr']=1;
        if(!isset($line['units'])) $line['units']=255;


        if(is_array($this->ticket)) {
            //$p_name=explode('{{marking}}', $data[1]);
            $item=array(
                'sessionKey'=>$this->key,
                'command'=>'AddGoods',
                'productName'=>$line['title'],
                'qty'=>(int)$line['quantity']*10,
                'section'=>(int)$line['section'],
                'taxCode'=>(int)$line['vat'],
                'price'=>(int)$line['price'],
                'paymentFormCode'=>(int)$line['psr'],
                'productTypeCode'=>(int)$line['ppr']
            );

            if($this->sets['version']=='1.2') {
                $item['measureUnit']=(int)$line['units'];
                if($line['partner']) {
                    $item['agent']=array(
                        'code'=>$line['partner']['type']?$line['partner']['type']:5,
                        'supplierPhone'=>array($line['partner']['phone']),
                        'supplierINN'=>$line['partner']['inn'],
                        'supplierName'=>$line['partner']['name']
                    );
                }
            }

            if(isset($line['country_id']) && $line['country_id']!='000') {
                $item['countryOfOrigin']=$line['country_id'];
                $item['customsDeclaration']=$line['gtd'];
            }

            if($line['mark'] || $line['mark']!='') {
                if((int)$line['egais']!=1) {
                    $line['mark']=str_replace('{{U+003B}}', ';', $line['mark']);
                    if($this->sets['version']=='1.2') {
                        $item['markingCode']=base64_encode($this->mark($line['mark']));
                        if($line['gp']!='furs') {
                            $mark_res=$this->check_mark(array(1=>$line['mark'], 2=>$line['quantity']/1000, 3=>$line['units']), 255);
                            if($mark_res->result!=0) {
                                return $mark_res;
                            }
                            $mark_res=$this->accept_mark();
                            if($mark_res->result!=0) {
                                return $mark_res;
                            }
                        }
                        $item['mcInfo']=array(
                            'mc'=>base64_encode($line['mark']),
                            'plannedStatus'=>$line['units']==0?1:2,
                        );
                    }
                    else {
                        $item['nomenclatureCode']=$this->mark($line['mark']);
                    }
                }

                if($line['reqId']!='' && $line['reqTimestamp']>0) {
                    $item['industryAttribute']=array(
                        array(
                            'docDate'=>'2023-11-23',
                            'idFOIV'=>'030',
                            'docNum'=>'1944',
                            'value'=>'UUID='.$line['reqId'].'&Time='.$line['reqTimestamp']
                        )
                    );
                    if($line['inst']!='') {
                        $item['industryAttribute'][0]['value'].='&Inst='.$line['inst'];
                    }
                    if($line['version']!='') {
                        $item['industryAttribute'][0]['value'].='&Ver='.$line['version'];
                    }
                }
            }
            $this->items[]=$item;
            $res['result']=0;
        }
        else {
            $res['result']=1;
            $res['description']='Чек не открыт';
            $this->f3->get('logs_model')->save_log($res['description'].' (Касса)');
        }
        return (object) $res;
    }

    // Оплата чека
    function tmde($data) {
        if(is_array($this->ticket)) {
            if(count($data)>3) {
                $this->ticket['taxSystem']=$this->sno[$data[count($data)-1]];
            }
            $res=$this->send_command($this->ticket); // Открываем чек
            if($res->result!=0) {
                $error_txt.='<p class="false">'.$res->description.'</p>';
                $this->f3->get('logs_model')->save_log($res->description.' (Касса)');
                //print_r($strs);
                //print_r($res);
                //die;
                //$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(is_array($this->items)) { // Добавляем товары
                foreach ($this->items as $item) {
                    $res=$this->send_command($item);
                    if($res->result!=0) {
                        $error_txt.='<p class="false">'.$res->description.'</p>';
                        $this->f3->get('logs_model')->save_log($res->description.' (Касса)');
                        if($this->f3->get('fr_error_ignore')) {
                            return array(
                                'success'=>false,
                                'error'=>$error_txt
                            );
                        }
                        echo $error_txt;
                        exit();
                    }
                }
            }
        }
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'CloseCheck',
            'sendCheckTo'=>$this->buyer,
        );

        if(count($data)==3) {
            if($this->paymethods[$data[2]]=='cash') $this->pay_cash=1;
            $pars['payment'][$this->paymethods[$data[2]]]=(int)$data[1];
        }
        else {
            if(count($data)==7) $end=4;
            else $end=5;
            for($j=0; $j<$end; $j++) {
                if($this->paymethods[$j]=='cash') $this->pay_cash=1;
                $pars['payment'][$this->paymethods[$j]]=(int)$data[$j+1];
            }
        }
        $this->ticket=false;
        $this->items=array();
        $this->buyer=false;
        $res=$this->send_command($pars);
        $this->kkm_time=time();
        if($res->result==0) {
            $pars=array(
                'sessionKey'=>$this->key,
                'command'=>'GetStatus'
            );
            $status=$this->send_command($pars);
            if($status->result==0 && isset($status->fnInfo->lastDoc->dateTime)) {
                $this->kkm_time=strtotime($status->fnInfo->lastDoc->dateTime);
            }
        }
        return $res;
    }

    // Аннулирование открытого чека
    function g($data) {
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'ResetCheck',
        );
        return $this->send_command($pars);
    }

    // открыть денежный ящик
    function open_cash_box($data) {
        if(!isset($data[2]) || $data[2]==0 || $this->pay_cash==1) {
            $pars=array(
                'sessionKey'=>$this->key,
                'command'=>'OpenBox',
            );
            return $this->send_command($pars);
        }
    }

    // Продолжить печать чека
    function continue_print($data) {
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'BreakOperation',
        );
        return $this->send_command($pars);
    }

    function check_mark($data, $checkFlags=0) {
        if(!$data[2]) $data[2]=1;
        $data[1]=str_replace('{{U+003B}}', ';', $data[1]);
        if(!isset($data[3])) $data[3]=255;
        $status=1;
        if($data[3]!=0) {
            $status=2;
        }
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'CheckMarkingCode',
            'mc'=>base64_encode($data[1]),
            'plannedStatus'=>$status,
            'qty'=>(int)($data[2]*10000),
            'measureUnit'=>(int)$data[3],
            'processingMode'=>0,
            'timeout'=>5
        );
        $res=$this->send_command($pars);
        if(!$res->result==0) return $res;
        return $this->check_mark_result($checkFlags);
    }

    function check_mark_result($checkFlags=0) {
        $n=5;
        while($n>0) {
            sleep(1);
            $pars=array(
                'sessionKey'=>$this->key,
                'command'=>'GetMarkingCodeCheckResult'
            );
            $res=$this->send_command($pars);
            if($res->result==0 && $res->isCompleted===true) {
                $n=0;
                if($res->onlineCheck->result!=0) {
                    if($checkFlags==0)$res->result=1;
                    $res->marking_error=true;
                    $res->description=$res->onlineCheck->description;
                }
                return $res;
            }
            $n-=1;
        }
        if($checkFlags==0) {
            $res->result=1;
        }
        $res->description=$res->onlineCheck->description;
        return $res;
    }

    function accept_mark() {
        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'AcceptMarkingCode'
        );
        return $this->send_command($pars);
    }

    function preitem($data) {
        return (object)array('result'=>0);
    }

    function preitem_end($data) {
        return (object)array('result'=>0);
    }

    function postitem($data) {
        return (object)array('result'=>0);
    }

    function postitem_end($data) {
        return (object)array('result'=>0);
    }

    function partner($data) {
        if(is_array($this->ticket)) {
            $partner=json_decode($data[1], true);
            if($partner['ur']==1) {
                $this->ticket['buyerInfo']=array(
                    'buyerName'=>$partner['name'],
                    'buyerINN'=>$partner['inn']
                );
            }
            if($partner['client']) {
                if(is_numeric($partner['client']) && mb_strlen($partner['client'])==11) {
                    $partner['client']='+'.$partner['client'];
                }
                $this->buyer=$partner['client'];
            }
            $res['result']=0;
        }
        else {
            $res['result']=1;
            $res['description']='Чек не открыт';
            $this->f3->get('logs_model')->save_log($res['description'].' (Касса)');
        }
    }

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

        $res=$this->openKkmSession();
        if($res->result!=0) {
            echo '<p class="false">'.$res->description.'</p>';
            $this->f3->get('logs_model')->save_log($res->description.' (Касса)');
            exit();
        }
        $this->key=$res->sessionKey;
        $false=0;
        $str=explode("\n", $strs);

        foreach ($str as $s) {
            if($s=='') continue;
            $ss=explode(';', $s);
            $method=$ss[0];
            $n=count($ss);
            if($ss[$n-1]=='') {
                unset($ss[$n-1]);
            }
            if(method_exists($this, $method)) {
                $res=$this->$method($ss);
            }
            else {
                $error_txt='Неизвестная команда: '.$ss[0];
                $error_txt.='<p class="false">'.$error_txt.'</p>';
                $this->f3->get('logs_model')->save_log($error_txt.' (Касса)');
                $false++;
                continue;
            }
            if($res->result!=0) {
                $error_txt.='<p class="false">'.$res->description.'</p>';
                $this->f3->get('logs_model')->save_log($res->description.' (Касса)');
                $false++;
            }
            elseif($view) {
                echo '<p class="true">Команда выполнена успешно: '.$ss[0].'</p>';
            }
            if($res->fiscalDocNum && $res->fiscalSign) {
                $this->fd=$res->fiscalDocNum;
                $this->fp=$res->fiscalSign;
            }
        }

        $this->closeKkmSession($this->key);
        if($false>0 && !$view && !$res->marking_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();
        }
        $res=(array) $res;
        if((int)$res['result']==0) {
            $success=true;
        }
        else {
            $success=false;
        }
        $res['success']=$success;
        if($this->fd!=0 && $this->fp!=0) {
            $res['fd']=$this->fd;
            $res['fp']=$this->fp;
            $res['kkm_time']=$this->kkm_time;
        }
        return $res;
    }

    // Проверить смену
    function change_status() {
        $res=$this->openKkmSession();
        if($res->result!=0) {
            echo '<p class="false">'.$res->description.'</p>';
            $this->f3->get('logs_model')->save_log($res->description.' (Касса)');
            exit();
        }

        $this->key=$res->sessionKey;

        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'GetStatus'
        );
        $res=$this->send_command($pars);
        if($res->result!=0) {
            echo '<p class="false">'.$res->description.'</p>';
            $this->f3->get('logs_model')->save_log($res->description.' (Касса)');
            $this->closeKkmSession($this->key);
            exit();
        }

        if($res->checkInfo->isOpen===true) {
            // Если есть октрытый чек, то его нужно аннулировать
            $this->g(null);
        }

        // closed - закрыта
        // opened - открыта
        // expired - истекла
        if($res->shiftInfo->isOpen) {
            $status='opened';
            if($res->shiftInfo->is24Expired) {
                $status='expired';
            }
        }
        else {
            $status='closed';
        }
        $this->closeKkmSession($this->key);

        $this->f3->set('fn', $res->fnInfo->fnNum);

        return $status;
    }

    function check_fnstatus() {
        $res=$this->openKkmSession();
        if($res->result!=0) {
            $this->f3->get('logs_model')->save_log($res->description.' (Касса)');
            return array(
                'success'=>false,
                'error'=>$res->description
            );
        }

        $this->key=$res->sessionKey;

        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'GetStatus'
        );
        $res=$this->send_command($pars);
        if($res->result!=0) {
            $this->closeKkmSession($this->key);
            $this->f3->get('logs_model')->save_log($res->description.' (Касса)');
            return array(
                'success'=>false,
                'error'=>$res->description
            );
        }

        $pars=array(
            'sessionKey'=>$this->key,
            'command'=>'GetRegistrationInfo'
        );
        $reg_data=$this->send_command($pars);
        if($reg_data->result!=0) {
            $this->closeKkmSession($this->key);
            $this->f3->get('logs_model')->save_log($reg_data->description.' (Касса)');
            return array(
                'success'=>false,
                'error'=>$reg_data->description
            );
        }

        $this->closeKkmSession($this->key);

        $this->f3->set('fn', $res->fnInfo->fnNum);

        return array(
            'success'=>true,
            'kkm'=>'Меркурий',
            'ffd'=>$this->sets['version'],
            'inn'=>$reg_data->registrationInfo->owner->inn,
            'kpp'=>'',
            'org_name'=>$reg_data->registrationInfo->owner->name,
            'org_address'=>$reg_data->registrationInfo->kkt->address,
            'ofd_n'=>$res->fnInfo->unsignedDocs->qty,
            'fn_end'=>0
        );
    }
}
