Собственная реализация функционала печати чеков по 54-ФЗ из 1С Розница 2.2

Собственно причиной сподвигнувшей меня на написание данного «велосипеда» послужило то, для того чтобы распечатался полноценный чек из 1С, не используюя РМК, нужно очень много телодвижений от менеджера.  А именно: создать документ реализации, на основании его создать документ «Чек» или «ПКО». Если печатать чек прямо из документа реализации, то в чеке выходят не понятные надписи «Кредит».

РМК внедрить в моих условиях не удалось, т.к. менеджеры по продажам курсируют между различными ПК, заходя на каждом из них под своей учеткой, А в штатном РМК нет возможности выбрать на какой из принтеров печатать чек. А может и есть, но я покрутив его день, так и не смог настроить полноценную работу, чтобы при переходе с ПК на ПК, печатало на разные ККМ.

В итоге было решено разработать собственную методику печати чеков:

  1. Менеджер в документе «Реализация товаров и услуг» (или «Возврат») нажимает кнопку «Печатать чек»
  2. 1С записывает в базу данных MySQL данные о чеке, с указанием на какую ККМ его выводить (на основании имени ПК, на котором создавался документ)
  3. На сервере UBUNTU крутится скрипт, который смотрит новые чеки в базе MySQL и если таковые находит — печатает их.

Всё достаточно просто.

Обработчик 1С в общем модуле:

Процедура ЗаписатьВБазуMySQLДокумент(документсс,имякомпа)  Экспорт	    
	касса=1;
	Если имякомпа="serverrdp" тогда касса=2;конецесли; //касса Анна/Алёна
	Если имякомпа="ANNA" тогда касса=2;конецесли; //касса Анна/Алёна
	Если имякомпа="manager" тогда касса=2;конецесли; //касса Анна/Алёна
	Если имякомпа="SergCach" тогда касса=1;конецесли; //касса Дуров
	документ=документсс;	
	если документ.Проведен=Ложь тогда
		сообщить("Документ не проведен, печатать чек не буду!");
		возврат;
	конецесли;	
	
	
	ИмяODBC = "sieufhleriufs";
	ИмяБазы = "erf8se9r8f";
	ИмяПользователя = "serifjsoeir";
	Пароль = "serkfherj";

	Connection = Новый COMОбъект("ADODB.Connection");
	СтрокаПодключения = "DRIVER={MySQL ODBC 8.0 Unicode Driver};DATABASE=" + ИмяБазы + ";PWD=" + Пароль + ";PORT=3306;SERVER=" + ИмяODBC + ";UID=" + ИмяПользователя + ";";
	Connection.Open(СокрЛП(СтрокаПодключения)); 

	ВидЧека=1; // Возврат
	//сообщить(документ.Ссылка.Метаданные().Имя);
	если документ.Ссылка.Метаданные().Имя="РеализацияТоваров" тогда ВидЧека=0;конецесли;
	
	ТипОплаты=0; //наличными
	//проверяем, а есть ли связанный документ эквайринга?
	 Запрос = Новый Запрос;
  	 Запрос.Текст = 
		"ВЫБРАТЬ
		|	ОплатаОтПокупателяПлатежнойКартойРасшифровкаПлатежа.ДокументРасчетовСКонтрагентом КАК ДокументРасчетовСКонтрагентом
		|ИЗ
		|	Документ.ОплатаОтПокупателяПлатежнойКартой.РасшифровкаПлатежа КАК ОплатаОтПокупателяПлатежнойКартойРасшифровкаПлатежа
		|ГДЕ
		|	ОплатаОтПокупателяПлатежнойКартойРасшифровкаПлатежа.ДокументРасчетовСКонтрагентом.Ссылка = &Ссылка"	;
	Запрос.УстановитьПараметр("Ссылка", документ.Ссылка);
	РезультатЗапроса = Запрос.Выполнить();	
	ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
	Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
		ТипОплаты=1; //электронно
	конеццикла;	
		 продавец=документ.Продавец.Наименование;
		 инн=документ.Продавец.инн;
	для каждого стр из документ.Товары цикл
		ном=стр.номенклатура.Наименование;
		цена=Формат(стр.цена,"ЧГ=");
		сумма=Формат(стр.сумма,"ЧГ=");
		сумма=СтрЗаменить(сумма,",",".");
		цена=СтрЗаменить(цена,",",".");
		количество=Формат(стр.количество,"ЧГ=");
		//type_pay - способ расчета (электронно, налом)
		Запрос="insert into checks_1c (id,docnum,dt,goods,cost,seller,inn,kassa,type_pay,type_sell,result,cnt,summ) values (null,'"+Документ.Номер+"',now(),'"+ном+"',"+цена+",'"+продавец+"','"+инн+"',"+касса+","+ТипОплаты+","+ВидЧека+",0,"+количество+","+сумма+")";
		//Сообщить(Запрос);
		РезультатЗапроса = Connection.Execute(Запрос); 
	конеццикла;		
	сообщить("Есть вероятность что чек сейчас распечатается ("+имякомпа+")...");	
конецпроцедуры

Обработчик в документах Реализации и Возврата:

&НаСервере
Процедура ПробитьЧекНаСервереГрибов(документ,имякомпа)
	ОбщийМодульГрибов.ЗаписатьВБазуMySQLДокумент(документ,имякомпа);	
КонецПроцедуры
&НаКлиенте
Процедура ПробитьЧек(Команда)
	        
	//грибов. переделка печати чеков
	ддк=Объект;
	ПробитьЧекНаСервереГрибов(ддк,ИмяКомпьютера());	     
	//Если Объект.ПробитЧек Тогда
	//	ТекстСообщения = НСтр("ru = 'Чек уже пробит на фискальном регистраторе!'");
	//	ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщения);
	//	Возврат;
	//КонецЕсли;
	//
	//ОбработчикОповещения = Новый ОписаниеОповещения("ОповещениеВопросПроведениеПередПечатьюЧека", ЭтотОбъект);
	//
	//Если ФинансыКлиент.ПроверитьВозможностьПечатиЧека(ОбработчикОповещения, ЭтотОбъект) Тогда
	//	НапечататьЧекКлиент();
	//КонецЕсли;
	
КонецПроцедуры

Скрипт PHP:

#!/usr/bin/php
<?php
/* 
 * (с) 2018 Грибов Павел
 * http://грибовы.рф * 
 * Если исходный код найден в сети - значит лицензия GPL v.3 * 
 * Сей дивный скрипт листает очередь не фискализированных платежей, и понемножку фискализирует...
 */

$debug=true;
///////////
include_once ("sql.php");

$fl = fopen("/tmp/sales_list_1c.lock", "w"); 
if( ! ( $fl && flock( $fl, LOCK_EX | LOCK_NB ) ) ) {
    die("--копия скрипта уже запущена!");	
};

function PutLog($txt){
 global $debug;
  $dt=Date("d-m-Y H:i:s");
   if ($debug==true){echo $dt." | "."$txt\n";}; 
};
    
$lb=new Tsql();
$lb->connect("жшыоа укшща","ыщазук","зцушказщуыш","щукшыащу","-connect to base");

while (true) {
    //ищу не проведенный номер документа
    $sql="select * from checks_1c where result=0 group by docnum";
    $result = $lb->ExecuteSQL($sql,'--ищу не фискализированные продажи с группировкой по номеру документа') or die("Ошибка (1)!".mysqli_error($lb->idsqlconnection));
     while($row = mysqli_fetch_array($result)) {
	 $docnum=$row["docnum"];
	 //собираю чек
	 $check=array();
	 $check["summ"]=0;     
	 $check["seller"]=$row["seller"];
	 $check["inn"]=$row["inn"];
	 $check["kassa"]=$row["kassa"];
	 $check["dt"]=$row["dt"];
	 $check["docnum"]=$row["docnum"];
	 $check["type_sell"]=$row["type_sell"]; //0 - реализация, 1 - возврат
	 $check["type_pay"]=$row["type_pay"]; //0 - нал, 1 - электронно

	 $check["goods"]=array();              
	 $sql="select * from checks_1c where docnum='$docnum' and result=0";
	 $result2 = $lb->ExecuteSQL($sql,'--собираю чек') or die("Ошибка (2)!".mysqli_error($lb->idsqlconnection));
	  while($row2 = mysqli_fetch_array($result2)) {	  
              $good=array();
	      $good["name"]=$row2["goods"];
	      $good["cost"]=$row2["cost"];
	      $good["summ"]=$row2["summ"];
              $good["cnt"]=$row2["cnt"];
	      $check["summ"]=$check["summ"]+$row2["summ"];
              $check["goods"][]=$good;
	  };
	  var_dump($check);
	  PutLog("-----пробую фискализировать и распечатать чек");
	  $jsonparam= base64_encode(json_encode($check));
	  $com="/home/pavel/online_kassa/sale_1c.py --sale $jsonparam";    
	  PutLog($com);
	    $output=array();
	    $res=exec($com,$output,$ret);
	    foreach ($output as $value) {echo "$value \n";};
	    if ($ret==99){
		PutLog("----Хорошо! Фискализация удачная.. Меняю статус чека в базе НОС");
		$sql="update checks_1c set result=1 where docnum='$docnum'";
		$result3 = $lb->ExecuteSQL($sql,'--Меняю статус чека в базе НОС') or die("Ошибка (3)!".mysqli_error($lb->idsqlconnection));
	    } else {
		PutLog("--Всё плохо....");
	    };     
     };
     PutLog("--поспим секундочку..");
     sleep(5);
    // die();
};

Скрипт Python:

#!/usr/bin/python3.5
# -*- coding: utf-8 -*-
import base64
import ctypes
import datetime
import json
from lib import IFptr
import os
import platform
from pprint import pprint
import sys
#fptr = IFptr("")

#get extr param
if len(sys.argv) == 1:
    print ("Возможные параметры:")
    print ("--sale base64 - запрос продажи/возврата")
    print ("--z f@i@o inn [num] - формирование Z-отчета и закрытие смены для кассира на номере кассы")
    exit(0)
arg = sys.argv[1]    
if arg=="--z":
    print ("Формируем Z-отчет и закрываем смену!")
    fio=sys.argv[2].replace("@"," ")
    inn=sys.argv[3]
    kassa=sys.argv[4]
    ip = "192.168.0.104" #касса по умолчанию
    if kassa == "2": ip = "192.168.0.104"; #алена
    if kassa == "1": ip = "192.168.0.103"; #дуров
    print("--будем печатать Z-Отчет на " + ip);    
    print ("ФИО:",fio)
    print ("ИНН:",inn)
    print ("Z-report")
    
    #connect to driver
    LIBRARY_PATH = os.path.dirname(os.path.abspath(__file__))
    fptr = IFptr(os.path.join(LIBRARY_PATH, "libfptr10.so"))
    #connect to ATOL
    fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_MODEL, str(IFptr.LIBFPTR_MODEL_ATOL_AUTO))
    fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_PORT, str(IFptr.LIBFPTR_PORT_TCPIP))
    fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_IPADDRESS, ip)
    fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_IPPORT, "5555")
    fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_ACCESS_PASSWORD, "0")
    fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_USER_PASSWORD, "30")

    fptr.applySingleSettings()
    #открываем соединение
    fptr.open()
    isOpened = fptr.isOpened()
    if isOpened == 0:
        print ("Не удалось открыть соединение с ККМ!")
        exit(1)
    
    fptr.setParam(1021,fio)
    fptr.setParam(1203, inn)
    fptr.operatorLogin()    
    # Отчет о закрытии смены
    fptr.setParam(IFptr.LIBFPTR_PARAM_REPORT_TYPE, IFptr.LIBFPTR_RT_CLOSE_SHIFT)
    fptr.report()
    
if arg == "--sale":
    bs64 = sys.argv[2];
    bs = base64.b64decode(bs64.encode('ascii')).decode("utf-8");
    print(bs.encode("utf-8"))
    param = json.loads(bs)
    ip = "192.168.0.104" #касса по умолчанию
    #ip = "192.168.0.227" #касса по умолчанию
    if param["kassa"] == "2": ip = "192.168.0.104"; #алена
    if param["kassa"] == "1": ip = "192.168.0.103"; #дуров
    print("--будем печатать на " + ip);
    #connect to driver
    LIBRARY_PATH = os.path.dirname(os.path.abspath(__file__))
    fptr = IFptr(os.path.join(LIBRARY_PATH, "libfptr10.so"))
    #connect to ATOL
    fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_MODEL, str(IFptr.LIBFPTR_MODEL_ATOL_AUTO))
    fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_PORT, str(IFptr.LIBFPTR_PORT_TCPIP))
    fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_IPADDRESS, ip)
    fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_IPPORT, "5555")
    fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_ACCESS_PASSWORD, "0")
    fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_USER_PASSWORD, "30")

    fptr.applySingleSettings()
    #открываем соединение
    fptr.open()
    isOpened = fptr.isOpened()
    if isOpened == 0:
        print ("Не удалось открыть соединение с ККМ!")
        exit(1)
        
    seller=param["seller"];
    inn=param["inn"]
    fptr.setParam(1021,seller)
    fptr.setParam(1203, inn)
    fptr.operatorLogin()

    print("Открываем чек");
    if param["type_sell"]=="0": # продажа
     print("-продажа");      
     fptr.setParam(IFptr.LIBFPTR_PARAM_RECEIPT_TYPE, IFptr.LIBFPTR_RT_SELL)
    else:
     print("-возврат");              
     fptr.setParam(IFptr.LIBFPTR_PARAM_RECEIPT_TYPE, IFptr.LIBFPTR_RT_SELL_RETURN)
    fptr.setParam(IFptr.LIBFPTR_PARAM_RECEIPT_ELECTRONICALLY, False)     
    fptr.setParam(1008, "office@ts35.ru")
    rz=fptr.openReceipt()
    if rz< 0:
      print("{} [{}]".format(fptr.errorCode(), fptr.errorDescription()))
      
    print("Регистрируем позиции");
    for goods in param["goods"]:
      nm=goods["name"]
      cost=goods["cost"]
      summ=goods["summ"]
      cnt=goods["cnt"]
      print (nm,cost,summ,cnt);
      fptr.setParam(IFptr.LIBFPTR_PARAM_COMMODITY_NAME,nm)
      fptr.setParam(IFptr.LIBFPTR_PARAM_PRICE, float(summ)/float(cnt))
      fptr.setParam(IFptr.LIBFPTR_PARAM_QUANTITY, cnt)
      fptr.setParam(IFptr.LIBFPTR_PARAM_TAX_TYPE, IFptr.LIBFPTR_TAX_NO)
      fptr.setParam(1212, 1) # товар
      fptr.setParam(1214, 4) # полный расчет
      fptr.registration()
    print("Регистрируем итог");      
    # Регистрация итога (отрасываем копейки)
    fptr.setParam(IFptr.LIBFPTR_PARAM_SUM, param["summ"])
    rz=fptr.receiptTotal()
    if rz < 0:
      print("{} [{}]".format(fptr.errorCode(), fptr.errorDescription()))

    print("Выбираем способ расчета");      
    if param["type_pay"]=="0":
     print("-нал");      
     fptr.setParam(IFptr.LIBFPTR_PARAM_PAYMENT_TYPE, IFptr.LIBFPTR_PT_CASH)
    else:
     print("-карта");      
     fptr.setParam(IFptr.LIBFPTR_PARAM_PAYMENT_TYPE, IFptr.LIBFPTR_PT_ELECTRONICALLY)

    fptr.setParam(IFptr.LIBFPTR_PARAM_PAYMENT_SUM, param["summ"])
    rz=fptr.payment()
    if rz < 0:
      print("{} [{}]".format(fptr.errorCode(), fptr.errorDescription()))

    print("Закрываем чек");      
    rz=fptr.closeReceipt()
    if rz < 0:
      print("{} [{}]".format(fptr.errorCode(), fptr.errorDescription()))

    if fptr.checkDocumentClosed() < 0 or rz<0:
      print ("Документ не закрылся!")
      del fptr
      exit(1)
    else:
      print ("Вроде чек распечатали..")
      del fptr
      exit(99)

    fptr.close()
    del fptr

Комментарии:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.