<?php
namespace Models;

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

    public $new_smde=true;

    public $fd=0; // Номер ФД
    public $fp=0; // Фискальный признак
    public $kkm_time=0; // Время чека
    public $ticket=false;
    public $pre_item=false;
    public $header=false;
    public $post_item=false;
    public $footer=false;
    public $ticket_types=array(
        0=>array('type'=>1, 'url'=>'receipt.json'),
        1=>array('type'=>2, 'url'=>'receipt.json'),
        128=>array('type'=>1, 'url'=>'correction.json'),
        130=>array('type'=>2, 'url'=>'correction.json'),
    );
    public $paymethods=array(
        0=>'cash',
        1=>'card',
        2=>'prepay',
        3=>'postpay',
        4=>'barter'
    );
    public $sno=array(
        '01'=>1, // Общая
        '02'=>2, // Упрощённая (Доход)
        '04'=>4, // Упрощённая (Доход минус Расход)
        '08'=>8, // Единый налог на вменённый доход
        '10'=>16, // Единый сельскохозяйственный налог
        '20'=>32 // Патентная система налогообложения
    );

    public $size=array(
        1=>'small',
        2=>'normal',
        3=>'middle',
        4=>'large'
    );
    
    function __construct($f3, $sets, $config) {
        $this->f3=$f3;
        $this->sets=$sets;
        if(!$config['UserLogin']) $config['UserLogin']='su';
        if(!$config['UserPass']) $config['UserPass']='99';
        if(mb_substr($this->sets['url'], -1)=='/') {
            $this->sets['url']=mb_substr($this->sets['url'], 0, -1);
        }
        if(!$config['FontSize']) $config['FontSize']=2;
        if(!isset($config['brief'])) $config['brief']=0;
        if(!isset($config['silent'])) $config['silent']=0;
        
        $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($config['vat_type'][$type])) {
                $config['vat_type'][$type]=$vat;
            }
        }
        $this->config=$config;
        $this->web=\Web::instance();
    }

    function send_command($url, $options) {
        $options['header'][]='Authorization: Basic '.base64_encode($this->config['UserLogin'].':'.$this->config['UserPass']);
        $options['header'][]='Content-Type: application/json';
        $options['header'][]='Expect:';

        $url=$this->sets['url'].$url;

        $res=$this->web->request($url, $options);
        $json=json_decode($res['body'], true);

        if($this->config['debug']==1) {
            $this->f3->get('logs_model')->save_log("Запрос\n".$url."\n".json_encode($options)."\n\nОтвет\n".$res['body']."\n(Драйвер Нева)", 'info');
        }

        if(mb_strpos($res['headers'][0], '200')>0) {
            if(!$json) {
                $json=array(
                    'result'=>0,
                    'body'=>$res['body']
                );
            }
            return $json;
        }
        else {
            if($this->errors[$json['result']]) {
                $error_txt=$this->errors[$json['result']];
            }
            elseif($json['message']) {
                $error_txt=$json['message'];
            }
            else {
                $error_txt='Не удалось отправить команду: '.$url;
            }
            $this->f3->get('logs_model')->save_log($error_txt.' (Касса)', 'error');
            if($this->f3->get('fr_error_ignore')) {
                return array(
                    'success'=>false,
                    'error'=>$error_txt
                );
            }
            echo $error_txt;
            exit();
        }
    }

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

    // Протяжка
    function feed($data) {
        $str[1]=str_repeat("#kkm_br#", $data[1]);
        return $this->p($str);
    }

    // Открыть смену
    function open_session($data) {
        $url='/cycleopen.json';
        $vars=array();
        if($data[1]==1) {
            $url.='?silent=0';
        }
        else {
            $url.='?silent=1';
        }
        if($data[2]) {
            $cashier=explode('{{user_inn}}', $data[2]);
            $vars['document']['cashier']=$cashier[0];
            if($cashier[1] && mb_strlen($cashier[1])==12) {
                $vars['document']['cashierInn']=$cashier[1];
            }
        }
        $this->ticket=array();
        $this->ticket['document']=$vars;
        $this->ticket['url']=$url;
        $this->ticket['method']='POST';
        return array('result'=>0);
        /*$options=array(
            'method'=>'POST',
            'content'=>json_encode($vars)
        );
        return $this->send_command($url, $options);*/
    }

    // Закрыть смену
    function z($data) {
        $url='/cycleclose.json';
        $vars=array();
        if($this->config['silent']==0) {
            if($data[1]==1) {
                $url.='?silent=0';
            }
            else {
                $url.='?silent=1';
            }
            $url.='&brief='.$this->config['brief'];
        }
        else {
            $url.='?silent=1';
        }

        if($data[2]) {
            $cashier=explode('{{user_inn}}', $data[2]);
            $vars['document']['cashier']=$cashier[0];
            if($cashier[1] && mb_strlen($cashier[1])==12) {
                $vars['document']['cashierInn']=$cashier[1];
            }
        }
        $this->ticket=array();
        $this->ticket['document']=$vars;
        $this->ticket['url']=$url;
        $this->ticket['method']='POST';
        return array('result'=>0);
        /*$options=array(
            'method'=>'POST',
            'content'=>json_encode($vars)
        );
        return $this->send_command($url, $options);*/
    }

    // Печать QR
    function qr($data) {
        $url='/images/qrcode';
        $options=array(
            'method'=>'POST',
            'content'=>file_get_contents($this->f3->get('SCHEME').'://'.$this->f3->get('HOST').':'.$this->f3->get('PORT').'/qr/gen?url='.urlencode($data[1]))
        );
        $res=$this->send_command($url, $options);
        if($res['result']==0) {
            $data[1]='[img name=qrcode altname=qrcode]'.$data[1].'[/img]';
            $res=$this->p($data);
        }
        return $res;
    }

    // Печать штрих-кода
    function barcode($data) {
        $data[1]='[center][barcode]'.$data[1]."[/barcode][/center]\n[center]".$data[1]."[/center]";
        return $this->p($data);
    }

    // Форматирование текста
    function text_format($s, $font='normal') {
        $left=true;
        if(mb_strpos($s, '#kkm_right#')!==false) {
            $s=str_replace("#kkm_right#", '[right]', $s).'[/right]';
            $left=false;
        }
        if(mb_strpos($s, '#kkm_center#')!==false) {
            $s=str_replace("#kkm_center#", '[center]', $s).'[/center]';
            $left=false;
        }
        if(mb_strpos($s, '#kkm_bold#')!==false) {
            $s=str_replace('#kkm_bold#', '[b]', $s).'[/b]';
        }
        if($left===true) {
            $s='[left]'.$s.'[/left]';
        }
        $s='[size='.$font.']'.$s.'[/size]';
        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);
        }
        if($this->pre_item) {
            $this->header.="\n".$txt;
            $res=array('result'=>0);
        }
        elseif($this->post_item) {
            $this->footer.="\n".$txt;
            $res=array('result'=>0);
        }
        else {
            $url='/print/string.bb';
            $options=array(
                'method'=>'POST',
                'content'=>$txt
            );
            if($this->ticket===true) {
                $this->ticket=array();
                $this->ticket['url']=$url;
                $this->ticket['method']='POST';
                $this->ticket['content'].=$txt;
                return array('result'=>0);
            }
            $res=$this->send_command($url, $options);
            if((int)$res['result']!=0) {
                return $res;
            }
        }
        return $res;
    }

    function pm($data) {
        if(!isset($data[2])) $data[2]=' ';
        $txts=explode('#kkm_br#',$data[2]);
        $txt='';
        foreach ($txts as $n=>$t) {
            if(!$t)$t=' ';
            $t=$this->text_format($t);
            if(!$t) continue;
            if($data[1]>0) {
                $str=mb_str_split($t, $data[1]);
            }
            else {
                $str=array($t);
            }
            foreach($str as $s) {
                if(!$s) $s=' ';
                $txt.=$s."\n";
            }
        }
        if($this->pre_item) {
            $this->header.="\n".$txt;
            $res=array('result'=>0);
        }
        elseif($this->post_item) {
            $this->footer.="\n".$txt;
            $res=array('result'=>0);
        }
        else {
            $url='/print/string.bb';
            $options=array(
                'method'=>'POST',
                'content'=>$txt
            );
            if($this->ticket===true) {
                $this->ticket=array();
                $this->ticket['url']=$url;
                $this->ticket['method']='POST';
                $this->ticket['content'].=$txt;
                return array('result'=>0);
            }
            $res=$this->send_command($url, $options);
            if((int)$res['result']!=0) {
                return $res;
            }
        }
        return $res;
    }

    function print_font($data) {
        if(!isset($data[2])) $data[2]=' ';
        $txts=explode('#kkm_br#',$data[2]);
        $txt='';
        $font=$this->size[$data[1]]?$this->size[$data[1]]:'normal';
        foreach ($txts as $n=>$t) {
            if($t=='') continue;
            $txt.=$this->text_format($t, $font);
        }
        if($this->pre_item) {
            $this->header.="\n".$txt;
            $res=array('result'=>0);
        }
        elseif($this->post_item) {
            $this->footer.="\n".$txt;
            $res=array('result'=>0);
        }
        else {
            $url='/print/string.bb';
            $options=array(
                'method'=>'POST',
                'content'=>$txt
            );
            if($this->ticket===true) {
                $this->ticket=array();
                $this->ticket['url']=$url;
                $this->ticket['method']='POST';
                $this->ticket['content'].=$txt;
                return array('result'=>0);
            }
            $res=$this->send_command($url, $options);
            if((int)$res['result']!=0) {
                return $res;
            }
        }
        return $res;
    }

    function print_bold($data) {
        if(!isset($data[1])) $data[1]=' ';
        $txts=explode('#kkm_br#',$data[1]);
        $txt='';
        foreach ($txts as $n=>$t) {
            if($t=='')continue;
            $txt.=$this->text_format('#kkm_bold#'.$t);
        }
        if($this->pre_item) {
            $this->header.="\n".$txt;
            $res=array('result'=>0);
        }
        elseif($this->post_item) {
            $this->footer.="\n".$txt;
            $res=array('result'=>0);
        }
        else {
            $url='/print/string.bb';
            $options=array(
                'method'=>'POST',
                'content'=>$txt
            );
            if($this->ticket===true) {
                $this->ticket=array();
                $this->ticket['url']=$url;
                $this->ticket['method']='POST';
                $this->ticket['content'].=$txt;
                return array('result'=>0);
            }
            $res=$this->send_command($url, $options);
            if((int)$res['result']!=0) {
                return $res;
            }
        }
        return $res;
    }

    // X отчет
    function x($data) {
        $url='/calcreport.json';
        $options=array(
            'method'=>'GET'
        );
        return $this->send_command($url, $options);
    }

    // Статус ККМ
    function d($data) {
        $url='/cashboxstatus.json';
        $options=array(
            'method'=>'GET'
        );
        $res=$this->send_command($url, $options);
        if($res['result']!=0) {
            return $res;
        }
        $allkeys=array(
            'dev'=>array(
                'title'=>'Статус устройства',
                'data'=>array(
                    'accumLevel'=>'Заряд аккумулятора',
                    'dsn'=>'dsn устройства',
                    'ipAddresses'=>'ip адрес устройства',
                    'versionCode'=>'Код версии ПО ККТ',
                    'versionText'=>'Версия ПО ККТ',
                    'printerStatus'=>'Статус принтера'
                )
            ),
            'fs'=>array(
                'title'=>'Статус ФН',
                'data'=>array(
                    'availableDocs'=>'Примерное количество документов, которые еще может принять ФН',
                    'currentFfd'=>'Версия ФФД ФН',
                    'cycle'=>'Номер текущей смены',
                    'cycleCloseDt'=>'Дата закрытия последней смены (если не открыта)',
                    'cycleIsOpen'=>'Признак открытой смены',
                    'cycleOpenDt'=>'Дата открытия текщей или последней закрытой смены',
                    'error'=>'Ошибка получения статуса ФН, если есть',
                    'freeBufSize'=>'Размер свободной области (в килобайтах) для записи документов',
                    'isDebug'=>'Режим отладки',
                    'labelBufUsed'=>'Процент заполнения области хранения уведомлений о реализации маркированных товаров для ОИСМ',
                    'lifeTime'=>array(
                        'title'=>'Данные о времени жизни ФН',
                        'data'=>array(
                            'availableRegs'=>'Доступно перерегистраций',
                            'expiredDays'=>'Осталось дней до окончания срока действия',
                            'expiredDt'=>'Дата окончания срока действия',
                            'regsCount'=>'Выполнено перерегистраций'
                        )
                    ),
                    'notifications'=>array(
                        'title'=>'Статус отправки уведомлений',
                        'data'=>array(
                            'firstNotification'=>'Первое неотправленное уведомление',
                            'firstNotificationDt'=>'Дата первого неотправленного уведомления',
                            'memFullPercent'=>'Процент заполнения области хранения уведомлений',
                            'notSendedCount'=>'Количество уведомлений в очереди',
                            'status'=>'Состояние по передачи уведомлений'
                        )
                    ),
                    'receipt'=>'Количество чеков в смене',
                    'release'=>'Строка исполнения ФН',
                    'status'=>array(
                        'title'=>'Текущее состояние ФН',
                        'data'=>array(
                            'lastDocDt'=>'Дата последнего документа',
                            'lastFd'=>'Номер последнего документа',
                            'phase'=>'Фаза жизни ФН',
                            'serial'=>'Заводской номер ФН'
                        )
                    ),
                    'supportedFfd'=>'Поддерживаемый ФФД',
                    'transport'=>array(
                        'title'=>'Состояние обмена с ОФД',
                        'data'=>array(
                            'firstDocDt'=>'Дата первого неотправленного ОФД',
                            'firstDocNumber'=>'Номер первого неотправленного ФД',
                            'messagesCount'=>'Количество неотправленных документов',
                            'ofdMessageReading'=>'Статус чтения документа',
                            'status'=>'Статус обмена'
                        )
                    ),
                    'version'=>'Версия ФН'
                )
            ),
            'hasNotPrinted'=>'Признак наличия недопечатанного документа',
            'model'=>array(
                'title'=>'Данные модели ККТ',
                'data'=>array(
                    'model'=>'Код модели',
                    'modelName'=>'Наименование модели',
                    'serial'=>'Заводской номер ККТ',
                    'srvModel'=>'ИД модели на сервере umka365',
                    'version'=>'Версия ККТ'
                ),
            ),
            'reg'=>array(
                'title'=>'Регистрационные данные ККТ',
                'data'=>array(
                    'additionalParam'=>'Дополнительный параметр',
                    'additionalParamData'=>'Значение дополнительного параметра',
                    'address'=>'Адрес',
                    'cashboxFfd'=>'ФФД кассового ящика',
                    'cashier'=>'Кассир',
                    'cashierInn'=>'ИНН кассира',
                    'email'=>'E-mail кассира',
                    'ofdInn'=>'ИНН ОФД',
                    'ofdName'=>'ОФД',
                    'ownerInn'=>'ИНН организации',
                    'ownerName'=>'Организация'
                )
            )
        );
        function checkv($v) {
            if(is_bool($v)) {
                if($v==true) {
                    $v='true';
                }
                else {
                    $v='false';
                }
            }
            if($v==null) {
                $v='NULL';
            }
            return $v;
        }
        function print_value($data, $keys) {
            foreach($data as $k=>$v) {
                if(!isset($keys[$k]) || $keys[$k]=='') {
                    $keys[$k]=$k;
                }
                if(!is_array($v) && !is_array($keys[$k])) {
                    echo '<p><b>'.$keys[$k].'</b>: '.checkv($v).'</p>';
                }
                elseif($v && is_array($keys[$k])) {
                    echo '<p align="center"><b>'.$keys[$k]['title'].'</b></p>';
                    print_value($v, $keys[$k]['data']);
                }
                elseif(!empty($v)) {
                    //echo '<p align="center"><b>'.$keys[$k].'</b></p>';
                    foreach ($v as $vk => $vv) {
                        echo'<p><b>'.$vk.'</b>: '.checkv($vv).'</p>';
                    }
                }
            }
        }
        print_value($res['status'], $allkeys);
        exit();
    }

    // Внесение или изъятие из кассы
    function imde($data) {
        if($data[1]>0) {
            $url='/introduction.json';
        }
        else {
            $url='/payout.json';
        }
        $url.='?cash='.abs($data[1]);
        $options=array(
            'method'=>'GET'
        );
        return $this->send_command($url, $options);
    }

    // Открытие чека
    function b($data) {
        $url='/'.$this->ticket_types[$data[1]]['url'];
        if((int)$data[3]==0) $url.='?silent=1';
        $this->ticket=array(
            'url'=>$url,
            'method'=>'POST',
            'document'=>array(
                'paymentAttr'=>(int)$this->ticket_types[$data[1]]['type'],
                'amount'=>number_format($data[4]/100, 2, '.', ''),
                'tax'=>(int)$this->sno[$this->sets['sno']]
            )
        );
        $cashier=explode('{{user_inn}}', $data[2]);
        if($cashier[0]) {
            $this->ticket['document']['cashier']=$cashier[0];
        }
        if($cashier[1] && mb_strlen($cashier[1])==12) {
            $this->ticket['document']['cashierInn']=$cashier[1];
        }

        $res['result']=0;
        return $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->ticket['document']['buyerPhone']=$data[3];
                $res['result']=0;
            }
            else {
                $res['result']=1;
                $res['message']='Чек не открыт';
                $this->f3->get('logs_model')->save_log($res['message'].' (Касса)', 'error');
            }
        }
        elseif($data[1]==1173) {
            if(is_array($this->ticket)) {
                $this->ticket['document']['correctionReason']['isIndependent']=((int)$data[3]==0?1:0);
                $this->f3->set('COOKIE.correction_isIndependent', $this->ticket['document']['correctionReason']['isIndependent']);
                $res['result']=0;
            }
            else {
                $res['result']=1;
                $res['message']='Чек не открыт';
                $this->f3->get('logs_model')->save_log($res['message'].' (Касса)', 'error');
            }
        }
        elseif($data[1]==1178) {
            if(is_array($this->ticket)) {
                $this->ticket['document']['correctionReason']['date']=date('Y-m-d', $data[3]);
                $this->f3->set('COOKIE.correction_date', $this->ticket['document']['correctionReason']['date']);
                $res['result']=0;
            }
            else {
                $res['result']=1;
                $res['message']='Чек не открыт';
                $this->f3->get('logs_model')->save_log($res['message'].' (Касса)', 'error');
            }
        }
        elseif($data[1]==1179) {
            if(is_array($this->ticket)) {
                $this->ticket['document']['correctionReason']['docNumber']=$data[3];
                $this->f3->set('COOKIE.correction_docNumber', $this->ticket['document']['correctionReason']['docNumber']);
                $res['result']=0;
            }
            else {
                $res['result']=1;
                $res['message']='Чек не открыт';
                $this->f3->get('logs_model')->save_log($res['message'].' (Касса)', 'error');
            }
        }
        elseif($data[1]==1192) {
            $this->ticket['document']['additionalParam']=$data[3];
        }
        else {
            $res['result']=1;
            $res['message']='Команда неизвестна '.implode(', ', $data);
            $this->f3->get('logs_model')->save_log($res['message'].' (Касса)', 'error');
        }
        return  $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) {
        if(is_array($this->ticket)) {

            $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;

            $item=array(
                'paymentType'=>(int)$line['psr'],
                'type'=>(int)$line['ppr'],
                'name'=>$line['title'],
                'unit'=>(int)$line['units'],
                'price'=>number_format($line['price']/100, 2, '.', ''),
                'quantity'=>number_format($line['quantity']/1000, 3, '.', ''),
                'vatRate'=>(int)$line['vat'],
            );

            if(isset($line['country_alpha2']) && $line['country_alpha2']!='' && mb_strlen($line['country_alpha2'])==2) {
                $item['countryCode']=$line['country_alpha2'];
                $item['declarationNumber']=$line['gtd'];
            }

            if($line['partner']) {
                if(!isset($line['partner']['type'])) $line['partner']['type']=5;
                $item['agentFlag']=pow(2, $line['partner']['type']);
                if($line['partner']['inn']) {
                    $item['providerInn']=$line['partner']['inn'];
                }
                if($line['partner']['phone']) {
                    $item['providerData']=array(
                        'phones'=>array('+'.$line['partner']['phone']),
                        'name'=>$line['partner']['name']
                    );
                }
            }

            if(($line['mark'] || $line['mark']!='') && $line['egais']!=1) {
                if($line['reqId']!='' && $line['reqTimestamp']>0) {
                    $item['industryProperties']=array(
                        array(
                            'fedId'=>'030',
                            'date'=>'2023-11-23',
                            'docNumber'=>'1944',
                            'value'=>'UUID='.$line['reqId'].'&Time='.$line['reqTimestamp']
                        )
                    );
                    if($line['inst']!='') {
                        $item['industryProperties'][0]['value'].='&Inst='.$line['inst'];
                    }
                    if($line['version']!='') {
                        $item['industryProperties'][0]['value'].='&Ver='.$line['version'];
                    }
                }
                if($line['gp']!='furs') {
                    $res=$this->check_mark(array(1=>$line['mark'], 2=>$line['quantity']/1000, 3=>$line['units']), 255);
                    if(!$res['document']) {
                        $res=array(
                            'document'=>array(
                                'actualStatus'=>1,
                                'clFlags'=>1,
                                'reqCode2105'=>0,
                                'reqDt'=>date('Y-m-d').'T'.date('H:i:s'),
                                'reqResult'=>0,
                                'rawLabel'=>str_replace('{{U+003B}}', ';', $line['mark'])
                            ),
                            'result'=>0
                        );
                    }
                    if(!$res['document']['unit']) {
                        $res['document']['unit']=(int)$line['units'];
                    }
                    $item['labelCheckResult']=$res['document'];
                }

                $item['productCode']=array(
                    'type'=>$line['gp']=='furs'?'1307':'1300',
                    'data'=>$this->mark(str_replace('{{U+003B}}', ';', $line['mark'])),
                );
                $item['crc']=mb_substr(crc32(str_replace('{{U+003B}}', ';', $line['mark'])), -4);
                $this->ticket['document']['labledOperations'][]=$item;
            }
            else {
                $this->ticket['document']['operations'][]=$item;
            }
            $res['result']=0;
        }
        else {
            $res['result']=1;
            $res['message']='Чек не открыт';
            $this->f3->get('logs_model')->save_log($res['message'].' (Касса)', 'error');
        }
        return  $res;
    }

    // Оплата чека
    function tmde($data) {
        if(!is_array($this->ticket)) {
            $res['result']=1;
            $res['message']='Чек не открыт';
            $this->f3->get('logs_model')->save_log($res['message'].' (Касса)', 'error');
            return $res;
        }
        if(count($data)==3) {
            $this->ticket['document'][$this->paymethods[$data[2]]]=number_format($data[1]/100, 2, '.', '');
        }
        else {
            if(count($data)==7) $end=4;
            else $end=5;
            $this->ticket['document']['tax']=(int)$this->sno[$data[count($data)-1]];
            for($j=0; $j<$end; $j++) {
                if($j==0 || $data[$j+1]>0) {
                    $this->ticket['document'][$this->paymethods[$j]]=number_format($data[$j+1]/100, 2, '.', '');
                }
            }
        }
        $url=$this->ticket['url'];
        if($url=='/correction.json') {
            if(!isset($this->ticket['document']['correctionReason'])) {
                $this->ticket['document']['correctionReason']=array(
                    'isIndependent'=>$this->f3->get('COOKIE.correction_isIndependent'),
                    'date'=>$this->f3->get('COOKIE.correction_date'),
                    'docNumber'=>$this->f3->get('COOKIE.correction_docNumber'),
                );
                $this->clear_session=true;
                if(!$this->ticket['document']['correctionReason']['docNumber']);
            }
            if(!$this->ticket['document']['correctionReason']['docNumber'])$this->ticket['document']['correctionReason']['docNumber']=0;
        }
        $options=array(
            'method'=>$this->ticket['method'],
            'content'=>json_encode(array('document'=>$this->ticket['document']))
        );
        return array('result'=>0);
        //$this->ticket=false;
        //return $this->send_command($url, $options);
    }

    // Аннулирование открытого чека
    function g($data) {
        return array('result'=>0);
    }

    // открыть денежный ящик
    function open_cash_box($data) {
        return array('result'=>0);
    }

    // Продолжить печать чека
    function continue_print($data) {
        $url='/printnotprinted.json';
        $options=array(
            'method'=>'GET'
        );
        $res=$this->send_command($url, $options);
        if($res['result']==268435456) {
           $res['result']=0; 
        }
        return $res;
    }

    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;
        }
        $url='/labelcheck.json';
        $vars=array(
            'document'=>array(
                'checkFlags'=>$checkFlags,
                'excpectedStatus'=>$status,
                'quantity'=>str_replace(',', '.', $data[2]),
                'rawLabel'=>$data[1],
                'unit'=>(int)$data[3]
            )
        );

        $options=array(
            'method'=>'POST',
            'content'=>json_encode($vars)
        );
        $res=$this->send_command($url, $options);
        $res['marking_error']=true;
        if($res['result']>0) {
            return $res;
        }
        $errors=array(
            'st2109'=>array(
                2=>'Планируемый статус товара некорректен',
                3=>'Оборот товара приостановлен'
            ),
            'reqCode2105'=>array(
                1=>'Запрос имеет некорректный формат',
                2=>'Указанный в запросе код маркировки имеет некорректный формат (не распознан)'
            ),
            'reqResult'=>array(
                0=>'Результат проверки КП КМ отрицательный',
                2=>'Статус товара некорректен'
            )
        );
        $res['results']=$res;
        $reqResult=(string)base_convert($res['document']['reqResult'], 16, 2);
        if($res['document']['st2109']>1) {
            $res['check_mark']=false;
            $res['message']=$errors['st2109'][$res['document']['st2109']];
            $res['result']=1;
        }
        if($res['document']['reqCode2105']>0) {
            if(!$res['check_mark']) $res['message'].='; ';
            $res['message'].=$errors['reqCode2105'][$res['document']['reqCode2105']];
            $res['check_mark']=false;
            $res['result']=1;
        }
        $reqResult_0=(int)mb_substr($reqResult, 0, 1);
        $reqResult_2=(int)mb_substr($reqResult, 2, 1);
        if($reqResult_0==0) {
            if(!$res['check_mark']) $res['message'].='; ';
            $res['message'].=$errors['reqResult'][0];
            $res['check_mark']=false;
            $res['result']=1;
        }
        if($reqResult_2==0) {
            if(!$res['check_mark']) $res['message'].='; ';
            $res['message'].=$errors['reqResult'][2];
            $res['check_mark']=false;
            $res['result']=1;
        }
        $res['marking_error']=true;
        return $res;
    }

    function preitem($data) {
        $this->pre_item=true;
        $this->ticket=true;
        return array('result'=>0);
    }

    function preitem_end($data) {
        $this->pre_item=false;
        return array('result'=>0);
    }

    function postitem($data) {
        $this->post_item=true;
        return array('result'=>0);
    }

    function postitem_end($data) {
        $this->post_item=false;
        if(!$this->ticket || !is_array($this->ticket)) {
            return array('result'=>0);
        }
        $url=$this->ticket['url'];
        if($this->header) {
            $this->ticket['document']['header']=$this->header;
        }
        if($this->footer) {
            $this->ticket['document']['footer']=$this->footer;
        }
        if(mb_strpos($this->ticket['url'], 'correction')>0 || mb_strpos($this->ticket['url'], 'receipt')>0) {
            $vars=json_encode(array('document'=>$this->ticket['document']));
        }
        elseif($this->ticket['url']=='/print/string.bb') {
            $vars=$this->header.$this->ticket['content'].$this->footer;
        }
        else {
            $vars=json_encode($this->ticket['document']);
        }
        $options=array(
            'method'=>$this->ticket['method'],
            'content'=>$vars
        );
        $this->ticket=false;
        $this->post_item=false;
        $this->header=false;
        $this->footer=false;
        return $this->send_command($url, $options);
    }

    function beep($data) {
        return $this->print_font(array(1=>4, 2=>'Бип-Бип-БИИП'));
    }

    // Печать по номеру ФД
    function printdoc($data) {
        $url='/tlvdoc.json?silent=0&fd='.$data[1];
        $options=array(
            'method'=>'GET'
        );
        $res=$this->send_command($url, $options);
        return $res;
    }

    function tlvdoc($data) {
        $url='/tlvdoc.json?silent=1&fd='.$data[1];
        $options=array(
            'method'=>'GET'
        );
        $res=$this->send_command($url, $options);
        return $res;
    }

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

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

        $false=0;
        $str=explode("\n", $strs);
        if($str[0]!='continue_print') {
            $res=$this->hasNotPrinted();
            if($res['result']!=0) {
                echo '<p class="false">'.$res['message'].'</p>';
                $this->f3->get('logs_model')->save_log($res['message'].' (Касса)', 'error');
                echo $continue_html= \Template::instance()->render('fr_error.htm');
                exit();
            }
            if($res['status']===true) {
                $res=$this->continue_print(null);
                if($res['result']!=0) {
                    echo '<p class="false">'.$res['message'].'</p>';
                    $this->f3->get('logs_model')->save_log($res['message'].' (Касса)', 'error');
                    echo $continue_html= \Template::instance()->render('fr_error.htm');
                    exit();
                }
            }
        }
        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.' (Касса)', 'error');
                $false++;
                continue;
            }
            if($res['document']['docNumber']!=0 && $res['document']['fiscalCode']!=0) {
                $this->fd=$res['document']['docNumber'];
                $this->fp=$res['document']['fiscalCode'];
                $this->kkm_time=strtotime($res['document']['dt']);
                $this->f3->set('fn', $res['document']['fsNumber']);
                $res['result']=0;
            }
            if($res['result']!=0) {
                $error_txt.='<p class="false">'.$res['message'].'</p>';
                $this->f3->get('logs_model')->save_log($res['message'].' (Касса)', 'error');
                $false++;
            }
            elseif($view) {
                echo '<p class="true">Команда выполнена успешно: '.$ss[0].'</p>';
            }
        }
        if($this->f3->get('fr_error_ignore')) {
            return array(
                'success'=>false,
                'error'=>$error_txt
            );
        }
        if($false>0 && !$view && !$res['marking_error']) {
            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;
        }

        if((int)$res['result']==0) {
            $res['success']=true;
            if($this->clear_session==true) {
                $this->f3->clear('COOKIE.correction_isIndependent');
                $this->f3->clear('COOKIE.correction_date');
                $this->f3->clear('COOKIE.correction_docNumber');
            }
        }
        else {
            $res['success']=false;
        }
        return $res;
    }

    // Проверить наличие недопечатанного документа
    function hasNotPrinted() {
        $url='/cashboxstatus.json';
        $options=array(
            'method'=>'GET'
        );
        $res=$this->send_command($url, $options);
        if($res['result']!=0) {
            return $res;
        }
        return array(
            'result'=>0,
            'status'=>$res['status']['hasNotPrinted']
        );
    }

    // Проверить смену
    function change_status() {
        $url='/cashboxstatus.json';
        $options=array(
            'method'=>'GET'
        );
        $res=$this->send_command($url, $options);
        if($res['result']!=0) {
            return $res;
        }

        if($res['status']['hasNotPrinted']===true) {
            $this->continue_print(null);
        }

        // closed - закрыта
        // opened - открыта
        // expired - истекла
        if($res['status']['fs']['cycleIsOpen']===false) {
            $status='closed';
        }
        elseif(strtotime($res['status']['fs']['cycleOpenDt'])+3600*24>time()) {
            $status='opened';
        }
        else {
            $status='expired';
        }

        return $status;
    }

    function check_fnstatus() {
        $url='/cashboxstatus.json';
        $options=array(
            'method'=>'GET'
        );
        $res=$this->send_command($url, $options);
        if($res['result']!=0) {
            return $res;
        }

        return array(
            'success'=>true,
            'kkm'=>'Нева',
            'ffd'=>$this->sets['version'],
            'inn'=>$res['status']['reg']['ownerInn'],
            'kpp'=>'',
            'org_name'=>$res['status']['reg']['ownerName'],
            'org_address'=>$res['status']['reg']['paymentAddress'],
            'ofd_n'=>$res['status']['fs']['transport']['messagesCount'],
            'fn_end'=>strtotime($res['status']['fs']['lifeTime']['expiredDt'])
        );
    }

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

    public $errors=array(
        1=>'Команда не поддерживается',
        2=>'Некорректный формат суммы',
        4=>'Некорректные данные кассира',
        5=>'Некорректный тип транзакции',
        6=>'Некорректный ФД',
        7=>'Пустой идентификатор транзакции',
        8=>'Некорректное значение реквиита',
        9=>'Вставлен ФН, зарегистрированный в составе другой ККТ',
        10=>'Некореектный заводской номер ККТ',
        11=>'Некорректное значение реквизита',
        12=>'Некорректное значение ИНН пользователя',
        13=>'Некорректное значение РНМ',
        14=>'Доступ запрещен',
        15=>'Сервис ядра не доступен',
        16=>'Ошибка записи заводского номера',
        17=>'Заводской номер уже записан',
        18=>'Нет заводского номера',
        19=>'Не установлен фискальный модуль',
        20=>'Ошибка фискализации ККТ',
        21=>'Ошибка чтения из БД',
        22=>'Ошибка записи в БД',
        23=>'ККТ не фискализирована',
        24=>'Смена уже открыта',
        25=>'Смена закрыта',
        26=>'Смена превысила 24 часа',
        27=>'Ошибка кодирования чека',
        28=>'Сумма оплаты картой превышает сумму по чеку',
        29=>'Не достаточно средств для оплаты чека',
        30=>'Пустой статус ФН',
        31=>'Ошибка обмена с ФН',
        32=>'В ФН есть неотправленные документы',
        33=>'Не хватает реквизитов',
        34=>'Не указана цена единицы предмета расчета',
        35=>'Некорректное значение цены единицы предметы расчета',
        36=>'Некорректное значение цены предмета расчета',
        37=>'Некорректное значение количества предмета расчета',
        38=>'Нет связи с сервером проверки КМ',
        39=>'Некорректное значение данных агента',
        40=>'Некорректное значение ИНН поставщика',
        41=>'Не указаны данные поставщика',
        42=>'Некорректное значение единицы измерения предмета расчета',
        43=>'Некорректное значение ставки НДС',
        44=>'Ошибка сервера обновления ключей',
        45=>'В чеке нет предметов расчета',
        46=>'Чек не оплачен',
        47=>'Переплата по чеку',
        48=>'Указана некорректная налоговая система',
        49=>'Некорректная сумма по чеку',
        50=>'Не указана причина коррекции',
        51=>'Слишком длинный код маркировки',
        52=>'Работа с кодами маркировки не поддерживается',
        53=>'Не указан код маркировки',
        54=>'Неожиданный реквизит',
        55=>'Нет связи с сервером проверки КМ',
        56=>'Ошибка проверки кода маркироваки в ФН',
        57=>'Ошибка проверки кода маркировки на сервере ОИСМ',
        58=>'Только для автономного режима',
        59=>'Ошибка создания файла экспорта уведомлений в автономном режиме',
        60=>'Текущая версия ФФД ФН не поддерживается',
        61=>'Текущая версия ФФД не поддерживается',
        62=>'Ошибка чтения версии ФФД',
        63=>'Ошибка внутреннего хранилища',
        64=>'Документ с таким externalId уже существует',
        65=>'В ККТ есть недопечатанный документ',
        66=>'Нет запрашиваемых данных',
        67=>'Ошибка загрузки статуса обновления ключей',
        975=>'Ошибка принтера',
        1000=>'Ошибка принтера',
        1001=>'Ошибка принтера',
        1002=>'Ошибка печати текста',
        1003=>'Ошибка печати изображения',
        1004=>'Принтер занят',
        1005=>'Нет бумаги',
        1006=>'Ошибка принтера',
        1007=>'Ошибка принтера',
        1008=>'Перегрев головки принтера',
        1009=>'Ошибка API принтера',
        1010=>'Нет шрифтов для принтера',
        1011=>'Ошибка контроллера принтера',
        1281=>'Неизвестная команда ФН (01h)',
        1282=>'Другое состояние ФН (02h)',
        1283=>'Отказ ФН (03h)',
        1284=>'Отказ КС (04h)',
        1285=>'Параметры команды не соответствуют сроку жизни ФН (05h)',
        1286=>'Переполнение ФН (06h)',
        1287=>'Некорректная дата и/или время (07h)',
        1288=>'Нет запрошенных данных (08h)',
        1289=>'Некорректное значение параметров команды (09h)',
        1290=>'Некорректная команда (0Ah)',
        1291=>'Неразрешенные реквизиты (0Bh)',
        1292=>'Дублирование данных (0Ch)',
        1293=>'Отсутствуют данные, необходимые для корректного учета в ФН (0Dh)',
        1294=>'Количество позиций в документе, превысило допустимый предел (0Eh)',
        1296=>'Превышение размеров TLV данных (10h)',
        1297=>'Нет транспортного соединения (11h)',
        1298=>'Исчерпан ресурс ФН (12h)',
        1300=>'Ограничение ресурса ФН(14h)',
        1301=>'Таймаут передачи документа (15h)',
        1302=>'Превышена продолжительность смены (16h)',
        1303=>'Некорректные данные о промежутке времени между фискальными документами (17h)',
        1304=>'Некорректный реквизитпереданный ККТ в ФН (18h)',
        1305=>'Реквизит не соответствует установкам при регистрации (19h)',
        1312=>'Код обработки ответа в ФН: некорректный ответ (20h)',
        1315=>'Отрицательный ответ сервиса обновления ключей проверки КМ(23h)',
        1316=>'Неизвестный ответ сервиса обновления ключей проверки кодов проверки (24h)',
        1328=>'Требуется повтор процедуры обновления ключей проверки КМ (30h)',
        1330=>'Запрещена работа с маркированным товарами (32h)',
        1331=>'Неверная последовательность команд при работе с маркированными товарами (33h)',
        1332=>'Работа с маркированными товарами временно заблокирована (34h)',
        1333=>'Переполнена таблица проверки кодов маркировки (35h)',
        1334=>'Превышен период 90 дней современи последнего обновления ключей проверки (36h)',
        1340=>'В блоке TLV отсутствуют необходимые реквизиты (3Ch)',
        1342=>'В реквизите 2007 содержится КМ, который ранее не проверялся в ФН (3Eh)',
        1533=>'Ошибка разбора ответа от ФН',
        1534=>'Нет связи с ФН',
        1535=>'Неопределенная ошибка ФН'
    );
}
