Experiência na integração de caixa Atol com negociação própria de CRM

Recentemente, nas bilheterias on-line, muita emoção, em 1º de julho de 2019, o último adiamento termina, então tive que lidar com esse problema. Aqueles que possuem 1C ou outro sistema, especialmente, não podem se esforçar, mas se você tiver seu próprio sistema auto-escrito, a integração com caixas registradoras on-line também cairá sobre seus ombros.

Minha experiência é útil para integração com caixas eletrônicos da Atol no modo de troca de dados de rede, seu programa pode enviar dados para o servidor da web da Atol tanto no host local quanto na rede local; você pode enviá-los até pelo navegador AJAX, ou pelo servidor via CURL, portanto, não importa em que idioma seu software corporativo esteja escrito, tudo é multiplataforma.

Eu me deparei com a bilheteria do Atol 30f - é uma máquina de escrever tão simples com uma caixa preta (FN), é perfeita quando toda a lógica do pedido está em software externo, e não no software embutido na caixa. Além disso, dispositivos desse tipo são relativamente baratos, em comparação com os equivalentes do Android.

Eu também gostaria de observar que os “especialistas” de algumas empresas envolvidas no suporte não sabem que o Atoll da versão 10 possui um servidor da web embutido no driver que aceita trabalhos JSON; além disso, esse driver também pode ser instalado no Linux, julgando pelo número de soluções prontas nas framboesas, posso assumir que também pode ser instalado lá, na distribuição da 10ª versão do driver, o instalador do braço está presente.

O esquema planejado é mais ou menos assim: existe um CRM que roda no servidor da rede local, é aberto a partir de navegadores, a partir do servidor, as verificações serão enviadas ao PHP por meio de curl e impressas na caixa registradora. E o próprio caixa eletrônico está conectado a qualquer computador no Windows na mesma rede.

Eles dizem que, se você não ativar o caixa, ele poderá funcionar no modo impressora e imprimir que o cheque é inválido, mas não pude verificar, tive que fazer um centavo de vendas e devoluções.

O driver da décima versão é baixado aqui .

Antes da instalação, é necessário instalar o Java com a mesma profundidade de bits que o driver, caso contrário, o servidor Web da caixa de seleção não estará disponível, se você instalar o driver KKT de 64 bits e, em seguida, o Java x64.

Parece que, logicamente, você precisa instalar um driver de 64 bits em um sistema de 64 bits, mas alguns softwares de 32 bits não poderão trabalhar com ele (como isso se aplica ao 1C se for de 32 bits).



No final da instalação, há uma marca de seleção - para configurar o servidor da Web, se não estiver instalado, é necessário acessar o navegador em 127.0.0.1 : 16732 / settings, marque a caixa "ativar servidor" e salve.





Depois disso, você precisa reiniciar o servidor através de START-> ATOL-> restart ...

Também quero avisar imediatamente que, se você iniciar o servidor da Web, os aplicativos locais não poderão acessar o CCP, trabalho por muito tempo, instalo o driver, execute o teste do driver do Cct e ele me diz que a porta está ocupada e é isso, liguei para o suporte técnico do vendedor local eles disseram que não sabemos o que fazer, então sobrecarreguei o computador dez vezes, reinstalei o driver, nada ajuda.

Em geral, depois de ativar e reiniciar o servidor, e antes disso, você desligou o servidor e verificou a impressão de texto sem formatação pelo utilitário fornecido, ou apenas verificou a conexão, pode prosseguir.

Esse serviço web não possui nenhuma proteção por senha; portanto, você precisa configurar imediatamente o firewall do Windows ou outro software para que apenas os computadores necessários possam acessar a porta 16732; na minha situação, este é o servidor no qual o CRM está sendo executado.

A comunicação com o serviço da web geralmente é um tópico separado, muito interessante ...

  1. Gere um uuid exclusivo para o trabalho
  2. Enviamos a tarefa usando o método POST
  3. Estamos travando no serviço da web, aguardando o resultado da tarefa com nosso UUID, pode ser que por alguns segundos nossa tarefa tenha um status de espera ou um erro poderá ocorrer se algo estiver errado na solicitação ...

Depois, darei uma versão de trabalho, adequada para situações em que o pagamento é apenas um método, e não tanto em dinheiro e em parte, mas também usa o sistema tributário padrão, o IVA ainda não foi calculado, eu queria adicionar o código e distribuí-lo, mas Acho que ainda existem pessoas que precisam dessas informações antes de 1º de julho do que depois. Devo dizer imediatamente que a classe precisa de refinamento, muito processamento bruto e sem erros, tudo foi feito em algumas horas sem levar em conta a leitura da documentação, esse código é mais um exemplo e eu aconselho você a estudar a documentação em detalhes e adaptá-la aos seus processos específicos.

código php para um exemplo de trabalho com API (usado apenas para fins educacionais)
<?php Class AtolWebDriver { protected $addr="127.0.0.1",$port="16732"; public $timeout = 30; //  public $operator; function __construct($addr=false,$port=false) { if ($addr!==false) $this->addr=$addr; if ($port!==false) $this->port=$port; } public function CallAPI($method, $data,$_url="/requests") { $url = "http://".$this->addr.":".$this->port.$_url; $curl = curl_init($url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl,CURLOPT_TIMEOUT, $this->timeout); $headers = ['Content-Type: application/json']; curl_setopt($curl,CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data)); $resp = curl_exec($curl); $data = json_decode($resp,1); $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); $res= [$data,$code,$resp]; print_r($res); return $res; } //   public function get_res($uuid) { $ready = false; $cnt=0; $res_url = '/requests/'.$uuid; while (!$ready && ++$cnt<60) { usleep(500000); // ,     list($res,$code,$resp) = $this->CallAPI('GET',[],$res_url); $ready = ($res['results'][0]['status'] == 'ready'); if ($ready) return $res; } return false; //    } //  public function add_req($uuid,$req) { return $this->CallAPI('POST', ['uuid'=>$uuid,'request'=>$req]); } //  id   public function gen_uuid() { return exec('uuidgen -r'); } //      public function atol_task($type,$req=[]) { $req['type'] = $type; $uuid = $this->gen_uuid(); $req = $this->add_req($uuid,$req); if ($req[1]!='201') return false; //  $res = $this->get_res($uuid); //  if ($res===false || !isset($res['results'][0])) return false; return $res['results'][0]; } /*    */ //  public function get_shift_status() { $res = $this->atol_task('getShiftStatus'); if ($res===false) return false; //closed / opened / expired return $res['result']['shiftStatus']['state']; } //  public function open_shift() { $status = $this->get_shift_status(); //e ,    if ($status=="expired") $this->close_shift(); if ($status=="opened") return "    "; $res = $this->atol_task('openShift',['operator'=>$this->operator]); } //  public function close_shift() { $status = $this->get_shift_status(); if ($status=="closed") return "    "; $res = $this->atol_task('closeShift',['operator'=>$this->operator]); } public function items_prepare($items) { $res_items = []; $summ = 0; while ($item = array_shift($items)) { $res_item = $item; if (!isset($item['type'])) $res_item['type']="position"; if (isset($item['price']) && isset($item['quantity'])) { $res_item['amount'] = $item['price']*$item['quantity']; $res_item['tax'] = ['type'=>'none']; $summ+=$res_item['amount']; } $res_items[] = $res_item; } return [$res_items,$summ]; } // sell,  sellReturn public function fiskal($type_op="sell",$items,$pay_type="cash") { $data = []; $data['operator'] = $this->operator; $data['payments'] = []; list($data['items'],$summ) = $this->items_prepare($items); //+++       $data['payments'][] = ['type'=>$pay_type,'sum'=>$summ]; $res = $this->atol_task($type_op,$data); } } //  ip   web-  ,      $atol = new AtolWebDriver('192.168.100.10'); //    $atol->operator = ['name'=>'.']; //       $items = []; $items[] = ['name'=>' ','price'=>0.7,'quantity'=>1]; $items[] = ['name'=>' ','price'=>0.4,'quantity'=>1]; //  $atol->open_shift(); //  $atol->fiskal("sell",$items); sleep(10); // ,     //     , ..    $atol->fiskal("sellReturn",$items); //     sleep(20); // ,   $atol->close_shift(); 


Existem algumas falhas aqui que eu vou corrigir

  1. Frações de arredondamento ao calcular os valores, você precisa arredondar para centavos, caso contrário, você pode obter 1.000000001 ou 0.999999999
  2. Com a grafia correta do restante da lógica do programa, isso geralmente não ocorre, mas durante os testes me deparei com o fato de que a tarefa retornou um resultado de erro e eu estava esperando por pronto

Bem, no processo de implementação, tenho medo de detectar muitos outros erros, por exemplo, se uma tarefa travar por muito tempo no status de espera, é melhor removê-la da fila, caso contrário, as tarefas subseqüentes ficarão suspensas por vários minutos, eu peguei uma falha uma vez, não esperava que ela fosse impressa e aqui estou, mas ele pulou e imprimiu imediatamente dois cheques seguidos enviados anteriormente ...

Em geral, é possível coletar aquisições no site no futuro, se elas não tiverem verificações on-line, até que você decida qual aquisição se danificar. Mas a solução é que, mais provavelmente como uma idéia para uma solução, o tempo dirá como essa bilheteria se enraizará.

Aviso , para quem lê o artigo de forma desatenta e não é muito competente na questão da segurança - este serviço da Web não possui criptografia (https), não possui autorização , mesmo que seja usado apenas na rede local - configure a proteção para acesso à porta.

Source: https://habr.com/ru/post/pt457684/


All Articles