1
atol.php in trunk/common/cri/atol – MultiMag

source: trunk/common/cri/atol/atol.php @ 986

Last change on this file since 986 was 986, checked in by blacklight, 8 months ago
  • Добвлен вывод печати организации в печатную форму коммерческого предложения
  • Проведён рефакторинг печатных форм документов
File size: 18.4 KB
Line 
1<?php
2//      MultiMag v0.2 - Complex sales system
3//
4//      Copyright (C) 2005-2018, BlackLight, TND Team, http://tndproject.org
5//
6//      This program is free software: you can redistribute it and/or modify
7//      it under the terms of the GNU Affero General Public License as
8//      published by the Free Software Foundation, either version 3 of the
9//      License, or (at your option) any later version.
10//
11//      This program is distributed in the hope that it will be useful,
12//      but WITHOUT ANY WARRANTY; without even the implied warranty of
13//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14//      GNU Affero General Public License for more details.
15//
16//      You should have received a copy of the GNU Affero General Public License
17//      along with this program.  If not, see <http://www.gnu.org/licenses/>.
18//
19
20namespace CRI\Atol;
21
22class atol {
23var $buf;
24    var $len;
25    var $pass;
26    var $recv;
27   
28    protected $atolBuffer;
29    protected $password = [0, 0];   
30    protected $cur_tld = 0;
31    protected $last_answer = null;
32    protected $async_results = [];
33    protected $result_flags = self::F_NOFLAGS;
34    protected $test_mode = false;
35    protected $section = 0;
36
37    // Flags
38    const F_NOFLAGS = 0x00;
39    const F_NEED_RESULT = 0x01;
40    const F_IGNORE_ERROR = 0x02;
41    const F_WAIT_ASYNC_DATA = 0x04;
42   
43    // Check types
44    const CT_IN = 1;
45    const CT_IN_RETURN = 2;
46    const CT_OUT = 4;
47    const CT_OUT_RETURN = 5;
48    const CT_CORRECTION_IN = 7;
49    const CT_CORRECTION_OUT = 8;
50
51    public function __construct() {
52        $this->atolBuffer = new AtolBuffer([$this, 'asyncReceiveData'], [$this, 'asyncReceiveError']);
53    }
54
55    public function __destruct() {
56    }
57   
58    public function setResultFlags($flags) {
59        $this->result_flags = $flags;
60    } 
61
62    public function setTestMode(bool $flag) {
63        $this->test_mode = $flag;
64    }
65   
66    public function setPassword($password) {
67        $this->password = $this->intToBCD($password, 2);
68    }
69   
70    public function setSection($section) {
71        $this->section = $section;
72    }
73
74    public function asyncReceiveData(int $tld, array $data) {
75        $this->async_results[$tld] = [
76            'state' => 'result',
77            'data' => $data
78        ];
79    }
80   
81    public function asyncReceiveError(int $tld, array $data) {
82        $this->async_results[$tld] = [
83            'state' => 'error',
84            'data' => $data
85        ];
86    }
87
88    public function connect($connect_line) {       
89        $this->atolBuffer->connect($connect_line);
90        $this->atolBuffer->abort();
91    }
92   
93    public function abortBuffer() {
94        $this->atolBuffer->abort();
95    }
96   
97    public function cmdBeep() {
98        $data = [$this->password[0], $this->password[1], 0x47];
99        $this->cur_tld++;
100        $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
101    }
102   
103    public function cmdRequestDeviceType() {
104        $data = [$this->password[0], $this->password[1], 0xA5];
105        $this->cur_tld++;
106        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
107    }
108   
109    public function requestDeviceType() {
110        $this->result_flags = self::F_NEED_RESULT;
111        $this->cmdRequestDeviceType();       
112        $this->atolBuffer->dispatchData(1);
113        $ret = $this->getFreeAsyncResult($this->cur_tld);
114        if($ret['data'][0]!=0) {
115            throw new AtolHLException("Нет связи");
116        }
117        $ret = $ret['data'];
118        $len = count($ret);
119        $name = '';
120        for($i=11;$i<$len;$i++) {
121            $name .= chr($ret[$i]);
122        }
123        $name = iconv('CP866', 'UTF-8', $name);
124        return [
125            'protocol' => $ret[1],
126            'type' => $ret[2],
127            'model' => $ret[3],
128            'mode' => (int)sprintf("%02X%02X", $ret[4], $ret[5]),
129            'version' => (int)sprintf("%02X%02X%02X%02X%02X", $ret[6], $ret[7], $ret[8], $ret[9], $ret[10]),
130            'name' => $name,
131        ];
132    }
133   
134    public function cmdGetState() {
135        $data = [$this->password[0], $this->password[1], 0x3F];
136        $this->cur_tld++;
137        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
138    }
139   
140    public function requestGetState() {
141        $this->result_flags = self::F_NEED_RESULT;
142        $this->cmdGetState();       
143        $this->atolBuffer->dispatchData(1);
144        $ret = $this->getFreeAsyncResult($this->cur_tld);
145        if($ret['state']=='error') {
146            throw new AtolHLException("Ошибка получения статуса");
147        }
148        $ret = $ret['data'];
149        if($ret[0]!=0x44) {
150            throw new AtolHLException("Неверная сигнатура ответа");
151        }
152        return [
153            'kashier' => (int)sprintf("%02X", $ret[1]),
154            'numInRoom' => $ret[2],
155            'date' => sprintf("20%02X-%02X-%02X", $ret[3], $ret[4], $ret[5]),
156            'time' => sprintf("%02X:%02X:%02X", $ret[6], $ret[7], $ret[8]),
157            'flags' => [
158                'isFiscal' => $ret[9] & 0x01 ? true:false,
159                'session' => $ret[9] & 0x02 ? true:false,
160                'cashDrawerOpen' => $ret[9] & 0x04 ? true:false,
161                'paper' => $ret[9] & 0x08 ? true:false,
162                'cover' => $ret[9] & 0x20 ? true:false,
163                'activeFiscalDrive' => $ret[9] & 0x40 ? true:false,
164                'battery' => $ret[9] & 0x80 ? true:false,
165            ],
166            'factoryNumber' => sprintf("%02X%02X%02X%02X", $ret[10], $ret[11], $ret[12], $ret[13]),
167            'model' => $ret[14],
168            'state' => $ret[17]%10,
169            'substate' => floor($ret[17]/10),
170            'lastCheckNumber' => (int)sprintf("%02X%02X", $ret[18], $ret[19]),
171            'lastSessionNumber' => (int)sprintf("%02X%02X", $ret[20], $ret[21]),
172            'checkState' => $ret[22],
173            'checkSum' => (int)sprintf("%02X%02X%02X%02X%02X", $ret[23], $ret[24], $ret[25], $ret[26], $ret[27]),
174            'pointPosition' => $ret[28],
175            'interface' => $ret[29],
176        ];
177    }
178   
179    public function cmdGetStateCode() {
180        $data = [$this->password[0], $this->password[1], 0x45];
181        $this->cur_tld++;
182        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
183    }
184   
185    public function requestGetStateCode() {
186        $this->result_flags = self::F_NEED_RESULT;
187        $this->cmdGetStateCode();
188        $this->atolBuffer->dispatchData(1);
189        $ret = $this->getFreeAsyncResult($this->cur_tld);
190        if($ret['state']=='error') {
191            throw new AtolHLError($ret['data'][1],$ret['data'][2]);
192        }
193        $ret = $ret['data'];
194        if($ret[0]!=0x55) {
195            throw new AtolHLException("Неверная сигнатура ответа");
196        }
197        return [
198            'state' => $ret[1]%10,
199            'substate' => floor($ret[1]/10),
200            'flags' => [
201                'paper' => $ret[2] & 0x01 ? true : false,
202                'printerConnected' => $ret[2] & 0x01 ? false : true,
203                'cutterError' => $ret[2] & 0x04 ? true : false,
204                'printerOverheat' => $ret[2] & 0x08 ? true : false,
205                'paper' => $ret[2] & 0x01 ? true : false,
206            ]
207        ];
208    }
209   
210    public function cmdEnterToMode($mode, $password) {
211        $k_pass = $this->intToBCD($password, 4);
212        $data = [$this->password[0], $this->password[1], 0x56, $mode];
213        $data = array_merge($data, $k_pass);
214        $this->cur_tld++;
215        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
216    }
217   
218    public function requestEnterToMode($mode, $password) {
219        $this->cmdEnterToMode($mode, $password);
220        $this->atolBuffer->dispatchData(3);
221        $ret = $this->getFreeAsyncResult($this->cur_tld);
222        if($ret['state']=='error') {
223            throw new AtolHLError($ret['data'][1],$ret['data'][2]);
224        }
225        $ret = $ret['data'];
226        if($ret[0]!=0x55) {
227            throw new AtolHLException("Неверная сигнатура ответа");
228        }
229        return $ret;
230    }
231   
232    public function cmdExitFromMode() {
233        $data = [$this->password[0], $this->password[1], 0x48];
234        $this->cur_tld++;
235        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
236    }
237   
238    public function requestExitFromMode() {
239        $this->cmdExitFromMode();
240        $this->atolBuffer->dispatchData(3);
241        $ret = $this->getFreeAsyncResult($this->cur_tld);
242        if($ret['state']=='error') {
243            throw new AtolHLError($ret['data'][1],$ret['data'][2]);
244        }
245        $ret = $ret['data'];
246        if($ret[0]!=0x55) {
247            throw new AtolHLException("Неверная сигнатура ответа");
248        }
249        return $ret;
250    }
251
252    // ----------- Команды режима регистрации -------------------------------
253   
254    public function cmdNewSession() {
255        $flags = 0;
256        if($this->test_mode) {
257            $flags |= 0x01;
258        }
259        $data = [$this->password[0], $this->password[1], 0x9A, $flags, 0];
260        $this->cur_tld++;
261        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
262    }
263   
264    public function requestNewSession() {
265        $this->result_flags = self::F_NEED_RESULT;
266        $this->cmdNewSession();
267        $this->atolBuffer->dispatchData(3);
268        $ret = $this->getFreeAsyncResult($this->cur_tld);
269        if($ret['state']=='error') {
270            throw new AtolHLError($ret['data'][1],$ret['data'][2]);
271        }
272        $ret = $ret['data'];
273        if($ret[0]!=0x55) {
274            throw new AtolHLException("Неверная сигнатура ответа");
275        }
276        return $ret;
277    }
278   
279    public function cmdOpenCheck(int $type, bool $no_print = false) {
280        $flags = 0;
281        if($this->test_mode) {
282            $flags |= 0x01;
283        }
284        if($no_print) {
285            $flags |= 0x04;
286        }
287        $data = [$this->password[0], $this->password[1], 0x92, $flags, $type];
288        $this->cur_tld++;
289        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
290    }
291   
292    public function requestOpenCheck(int $type, bool $no_print = false) {
293        $this->result_flags = self::F_NEED_RESULT;
294        $this->cmdOpenCheck($type, $no_print);
295        $this->atolBuffer->dispatchData(1);
296        $ret = $this->getFreeAsyncResult($this->cur_tld);
297        if($ret['state']=='error') {
298            throw new AtolHLError($ret['data'][1],$ret['data'][2]);
299        }
300        $ret = $ret['data'];
301        if($ret[0]!=0x55) {
302            throw new AtolHLException("Неверная сигнатура ответа");
303        }
304        return $ret;
305    }
306   
307    public function cmdRegisterNomenclature(string $name, float $price, float $count, int $type = 0, int $sign = 0, int $size = 0, int $tax = 0) {
308        $flags = 0;
309        if($this->test_mode) {
310            $flags |= 0x01;
311        }       
312        $data = [$this->password[0], $this->password[1], 0xE6, $flags];
313        $data = array_merge($data, $this->stringToArray($name, 64));
314        $data = array_merge($data, $this->intToBCD($price*100, 6));
315        $data = array_merge($data, $this->intToBCD($count*1000, 5));
316        $data = array_merge($data, [$type, $sign]);
317        $data = array_merge($data, $this->intToBCD($size*100, 6));
318        $data = array_merge($data, [$tax, $this->section]);
319        $data = array_merge($data, $this->intToBCD(0, 17));
320        $this->cur_tld++;
321        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
322    }
323   
324    public function requestRegisterNomenclature(string $name, float $price, float $count, int $type = 0, int $sign = 0, int $size = 0, int $tax = 0) {
325        $this->result_flags = self::F_NEED_RESULT;
326        $this->cmdRegisterNomenclature($name, $price, $count, $type, $sign, $size, $tax);
327        $this->atolBuffer->dispatchData(1);
328        $ret = $this->getFreeAsyncResult($this->cur_tld);
329        if($ret['state']=='error') {
330            throw new AtolHLError($ret['data'][1],$ret['data'][2]);
331        }
332        $ret = $ret['data'];
333        if($ret[0]!=0x55) {
334            throw new AtolHLException("Неверная сигнатура ответа");
335        }
336        return $ret;
337    }
338   
339    public function cmdCloseCheck(int $type, float $sum) {
340        $flags = 0;
341        if($this->test_mode) {
342            $flags |= 0x01;
343        }
344        $data = [$this->password[0], $this->password[1], 0x4A, $flags, $type];
345        $data = array_merge($data, $this->intToBCD($sum*100, 5));
346        $this->cur_tld++;
347        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
348    }
349   
350    public function requestCloseCheck(int $type, float $sum) {
351        $this->result_flags = self::F_NEED_RESULT;
352        $this->cmdCloseCheck($type, $sum);
353        $this->atolBuffer->dispatchData(5);
354        $ret = $this->getFreeAsyncResult($this->cur_tld);
355        if($ret['state']=='error') {
356            throw new AtolHLError($ret['data'][1],$ret['data'][2]);
357        }
358        $ret = $ret['data'];
359        if($ret[0]!=0x55) {
360            throw new AtolHLException("Неверная сигнатура ответа");
361        }
362        return $ret;
363    }
364   
365    public function cmdBreakCheck() {
366        $data = [$this->password[0], $this->password[1], 0x59];
367        $this->cur_tld++;
368        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
369    }
370   
371    public function requestBreakCheck() {
372        $this->result_flags = self::F_NEED_RESULT;
373        $this->cmdBreakCheck();
374        $this->atolBuffer->dispatchData(5);
375        $ret = $this->getFreeAsyncResult($this->cur_tld);
376        if($ret['state']=='error') {
377            throw new AtolHLError($ret['data'][1],$ret['data'][2]);
378        }
379        $ret = $ret['data'];
380        if($ret[0]!=0x55) {
381            throw new AtolHLException("Неверная сигнатура ответа");
382        }
383        return $ret;
384    }
385   
386    public function cmdInCash(float $sum) {
387        $flags = 0;
388        if($this->test_mode) {
389            $flags |= 0x01;
390        }
391        $data = [$this->password[0], $this->password[1], 0x49, $flags];
392        $data = array_merge($data, $this->intToBCD($sum*100, 5));
393        $this->cur_tld++;
394        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
395    }
396   
397    public function requestInCash(float $sum) {
398        $this->result_flags = self::F_NEED_RESULT;
399        $this->cmdInCash($sum);
400        $this->atolBuffer->dispatchData(5);
401        $ret = $this->getFreeAsyncResult($this->cur_tld);
402        if($ret['state']=='error') {
403            throw new AtolHLError($ret['data'][1],$ret['data'][2]);
404        }
405        $ret = $ret['data'];
406        if($ret[0]!=0x55) {
407            throw new AtolHLException("Неверная сигнатура ответа");
408        }
409        return $ret;
410    }
411   
412    // -------- Команды режима отчёта без гашения ------------------
413    public function cmdXREport($type) {
414        settype($type, 'int');
415        $data = [$this->password[0], $this->password[1], 0x67, $type];
416        $this->cur_tld++;
417        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
418    }
419   
420    public function requestXREport($type) {
421        $this->result_flags = self::F_NEED_RESULT;
422        $this->cmdXREport($type);
423        $this->atolBuffer->dispatchData(1);
424        $ret = $this->getFreeAsyncResult($this->cur_tld);
425        if($ret['state']=='error') {
426            throw new AtolHLError($ret['data'][1],$ret['data'][2]);
427        }
428        $ret = $ret['data'];
429        if($ret[0]!=0x55) {
430            throw new AtolHLException("Неверная сигнатура ответа");
431        }
432        return $ret;
433    }
434   
435   
436    // -------- Команды режима отчёта с гашением ------------------
437    public function cmdZREport() {
438        $data = [$this->password[0], $this->password[1], 0x5A];
439        $this->cur_tld++;
440        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
441    }
442   
443    public function requestZREport() {
444        $this->result_flags = self::F_NEED_RESULT;
445        $this->cmdZREport();
446        $this->atolBuffer->dispatchData(1);
447        $ret = $this->getFreeAsyncResult($this->cur_tld);
448        if($ret['state']=='error') {
449            throw new AtolHLError($ret['data'][1],$ret['data'][2]);
450        }
451        $ret = $ret['data'];
452        if($ret[0]!=0x55) {
453            throw new AtolHLException("Неверная сигнатура ответа");
454        }
455        return $ret;
456    }
457     
458    // -------- Команды режима запроса к ФН ------------------
459    public function cmdRePrintDocument($num) {
460        $data = [$this->password[0], $this->password[1], 0xAB];
461        $data = array_merge($data, $this->intToBCD($num, 5));
462        $this->cur_tld++;
463        return $this->atolBuffer->add($this->result_flags, $this->cur_tld, $data);
464    }
465   
466    public function requestRePrintDocument($num) {
467        $this->result_flags = self::F_NEED_RESULT;
468        $this->cmdRePrintDocument($num);
469        $this->atolBuffer->dispatchData(1);
470        $ret = $this->getFreeAsyncResult($this->cur_tld);
471        $this->assertErrors($ret);
472        return $ret;
473    }
474   
475    protected function assertErrors($res) {
476        if($res['state']=='error') {
477            throw new AtolHLError($res['data'][1],$res['data'][2]);
478        }
479        $res = $res['data'];
480        if($res[0]!=0x55) {
481            throw new AtolHLException("Неверная сигнатура ответа");
482        }
483    }
484
485
486    protected function stringToArray(string $str, int $size) {
487        $str = iconv('UTF-8', 'CP866', $str);
488        $str = substr($str, 0, $size);
489        $str = str_split($str);
490        foreach($str as $i=>$s) {
491            $str[$i] = ord($s);
492        }
493        while(count($str)<$size) {
494            $str[] = 0;
495        }
496        return $str;
497    }
498
499
500    protected function intToBCD($number, $size) {
501        $res = [];
502        settype($number, 'int');
503        for($i=$size-1;$i>=0;$i--) {
504            $code = $number%10 | ((floor($number/10)%10)<<4);
505            $number = floor($number/100);
506            $res[$i] = $code;
507        }
508        ksort($res);
509        return $res;
510    }   
511
512   
513    protected function getFreeAsyncResult($tld) {
514        if(!array_key_exists($tld, $this->async_results)) {
515            throw new AtolException('Нет данных этой команды');
516        }       
517        $ret = $this->async_results[$this->cur_tld];
518        unset($this->async_results[$this->cur_tld]);
519        return $ret;
520    }
521           
522 
523
524}
525
Note: See TracBrowser for help on using the repository browser.