Архив метки: АТОЛ 55ф

Костылище при работе с ККМ в Розница 2.2.

С какого-то момента на одном из компьютеров с 1С, перестали печататься чеки на онлайн-кассе. Причем ошибок 1С никаких не показывала.  Причем при открытии/закрытии смен, чеки с Z отчетами выдавались.  Бился два дня с разнообразными настройками и поисками решения. Грешил и на ОС, и на 1С и на ККМ. Сегодня утром уже даже отладчиком 1С стал код шерстить, чтобы понять на каком моменте, что-то не так. Нашел:

&НаКлиенте
Процедура НапечататьЧекКлиент()
	Перем Отказ, ИдентификаторУстройства, ИспользоватьКассуБезПодключенияОборудования, РаспределениеВыручкиПоСекциям;
	
	ПодготовитьИПроверитьПечатьЧека(Отказ, ИдентификаторУстройства, ИспользоватьКассуБезПодключенияОборудования, РаспределениеВыручкиПоСекциям);
	
	Если Отказ Тогда
		Возврат
	КонецЕсли;
!!!!!!!! ошибка здесь!!!!!
	Если НЕ ИспользоватьПодключаемоеОборудование ИЛИ ИспользоватьКассуБезПодключенияОборудования Тогда
		
		Если НомерДокументаКассыККМ[Объект.КассаККМ] <> Неопределено Тогда
			Объект.НомерЧекаККМ  = НомерДокументаКассыККМ[Объект.КассаККМ];
		Иначе
			Объект.НомерЧекаККМ  = ПорядковыйНомерПродажи;
		КонецЕсли;
		Объект.ПробитЧек     = Истина;
		
		Модифицированность = Истина;

ИспользоватьПодключаемоеОборудование = ВСЕГДА = ЛОЖЬ.  Причем только когда работаешь в 1С именно на этом ПК. На других всё ок. Почему? Не понятно. Галочка стоит:

Место, где заполняется значение нашел:

// Проверяет, что включена ф.о "Использовать подключаемое оборудование" и авторизовался пользователь,
// а не внешний пользователь.
Функция ИспользоватьПодключаемоеОборудование() Экспорт
	
	Возврат ПолучитьФункциональнуюОпцию("ИспользоватьПодключаемоеОборудование") И ТипЗнч(Пользователи.АвторизованныйПользователь()) = Тип("СправочникСсылка.Пользователи");
	
КонецФункции

И оно возвращает ИСТИНА! В общем, бился, бился..

Пока прибил всё это костылем:

&НаКлиенте
Процедура НапечататьЧекКлиент()
	Перем Отказ, ИдентификаторУстройства, ИспользоватьКассуБезПодключенияОборудования, РаспределениеВыручкиПоСекциям;
	
	ПодготовитьИПроверитьПечатьЧека(Отказ, ИдентификаторУстройства, ИспользоватьКассуБезПодключенияОборудования, РаспределениеВыручкиПоСекциям);
	
	Если Отказ Тогда
		Возврат
	КонецЕсли;
	// грибов
	// совершенно не понятный глюк
	ИспользоватьПодключаемоеОборудование=истина;
	//
	Если НЕ ИспользоватьПодключаемоеОборудование ИЛИ ИспользоватьКассуБезПодключенияОборудования Тогда

Работа с Атол 55Ф из python, ветка драйверов 10.х

В продолжение предыдущей статьи о работе с Атол чрез python. Там описывалась работа с драйверами из ветки ДТО 9.х Всё хорошо, но в них не поддерживаются «электронные чеки». Собственно из-за чего всё и затевалось у меня. К сожалению работа с драйверами 10.х существенно отличается от 9.х Но к счастью есть ресурс http://integration.atol.ru, в котором достаточно всё хорошо разжевано с примерами. Правда без учета специфики Linux.

Итак, что нужно для того что-бы «поработать» с драйверами в Ubuntu.

1) Штатно установить драйвера deb из папки installer
2) В зависимости от того, скольки битная система, скопировать в папку проекта содержимое linux-86 или linux-x64. Если драйвера установлены верно, то при запуске fptr10_t.sh можно будет увидеть что-то вроде:

3) В архиве с драйверами ищем libfptr10.py и копируем в файл проекта python

4) Далее если мы будем для инициализации драйвера использовать код из примеров вида:

#!/usr/bin/python2
# -*- coding: utf-8 -*-


import sys
import os
from libfptr10 import IFptr


, то получим ошибку:

root@pavelpc:~/reserved/getcoding/atol/test/ver10# ./kkt_10.py
Traceback (most recent call last):
  File "./kkt_10.py", line 7, in <module>
    from libfptr10 import IFptr
ImportError: dynamic module does not define init function (initlibfptr10)

На сколько мне, не сильно подкованному в python и cpp, удалось выяснить, проблема в не верно скомпилированном libfptr10.so Возможно в следующих версиях драйвера она уйдет.

А пока, выходом у меня является дописание функционального кода в конце файла libfptr10.py, после описания класса IFptr

Например:

LIBRARY_PATH = os.path.dirname(os.path.abspath(__file__))
fptr = IFptr(os.path.join(LIBRARY_PATH, "libfptr10.so"))
print (fptr)

fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_MODEL, str(IFptr.LIBFPTR_MODEL_ATOL_55F))
fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_PORT, str(IFptr.LIBFPTR_PORT_TCPIP))
fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_IPADDRESS, "192.168.0.99")
fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_IPPORT,"5555")
fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_LIBRARY_PATH,"/home/pavel/reserved/getcoding/atol/test/ver10")
fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_ACCESS_PASSWORD,"0")
fptr.setSingleSetting(IFptr.LIBFPTR_SETTING_USER_PASSWORD,"30")
res=fptr.applySingleSettings()
print (res);

fptr.open()

isOpened = fptr.isOpened()
print ("Opened:"+str(isOpened));

fptr.setParam(IFptr.LIBFPTR_PARAM_DATA_TYPE, IFptr.LIBFPTR_DT_STATUS)
fptr.queryData()
operatorID      = fptr.getParamInt(IFptr.LIBFPTR_PARAM_OPERATOR_ID)
print (operatorID)
logicalNumber   = fptr.getParamInt(IFptr.LIBFPTR_PARAM_LOGICAL_NUMBER)
shiftState      = fptr.getParamInt(IFptr.LIBFPTR_PARAM_SHIFT_STATE)
serialNumber    = fptr.getParamString(IFptr.LIBFPTR_PARAM_SERIAL_NUMBER)
modelName       = fptr.getParamString(IFptr.LIBFPTR_PARAM_MODEL_NAME)
firmwareVersion = fptr.getParamString(IFptr.LIBFPTR_PARAM_UNIT_VERSION)
print (modelName)

Кое-что уже выводит:

Фискализация чеков через онлайн-кассу АТОЛ 55Ф на ОС Ubuntu, python

Прилетела задача фискализировать поступления платежей через оплату услуг с карточек через интернет, из личного кабинета абонента, оплаты абонента посредством Сбербанк-онлайн и т.п. Из ТУ : операционная система семейства Linux.

Было рассмотрено несколько вариантов решения:

1) Сервис Атол-Онлайн. Плюсы: простота интеграции, очень вменяемое API. Минусы — со второго года — выходит очень дорого по сравнению со «своей железкой»

2)PAYONLINE-01-ФА. Плюсы: прям в «базе» нет принтера, что удешевляет. Минусы: нет толкового описания работы с драйверами, примеров, нет драйверов под LInux

3) Атол-55Ф. Плюсы: драйвера под Linux, вменяемое описание работы с примерами на Java, cpp. Плюс натолкнулся на пример работы https://github.com/parshin/kkt с этой железкой. Минусы — дорого по сравнению с 2) но дешево по сравнению с 1)

Собственно драйвера ставятся стандартно. Далее пример пробития чека на python

#!/usr/bin/python2.7
# -*- coding: utf-8 -*-

# import web
import sys
import os
import json
import logging

from conf import *	    # загружаем настройки
import dto9fptr		    # загружаем заголовки драйвера

def DeviceSetup(driver,params):    
    driver.put_DeviceSingleSetting("Port", params["Port"])
    driver.put_DeviceSingleSetting("IPAddress", params["IPAddress"])
    driver.put_DeviceSingleSetting("IPPort", params["IPPort"])
    driver.put_DeviceSingleSetting("Model", params["Model"])
    driver.put_DeviceSingleSetting("Protocol", params["Protocol"])
    driver.put_DeviceSingleSetting("AccessPassword", params["AccessPassword"])
    driver.put_DeviceSingleSetting("UserPassword", params["UserPassword"])
    driver.put_DeviceSingleSetting("Protocol", params["Protocol"])
    driver.put_DeviceSingleSetting('SearchDir', os.path.dirname(DTO_LIB_NAME))
    driver.ApplySingleSettings()
    driver.put_DeviceEnabled(True)
    return 0

def GetCurrentParamDevice(driver):
    result = driver.GetStatus()    
    if result[0]!=0:
	print t_err+unicode(result[1])+t_off
	logging.info(result[1])
	exit(1)
    result = driver.get_DeviceSettings()
    print t_mess+'Текщие настройки: '+t_off + str(result)
    if str(result)=="None":
	etxt="Драйвер не подключился к устройству"
	print t_err+etxt+t_off
	logging.info(etxt)
	exit(1)
    return result

def print_check(driver, check_data):
    res=0
    mess="check_data: " + str(check_data)
    
    print t_mess+mess+t_off
    logging.info(mess)
    
    result = driver.put_DeviceEnabled(True)
    logging.info("put device enabled: " + repr(result).decode("unicode_escape"))
    result_code = driver.get_ResultCode()
    result_description = driver.get_ResultDescription()
    if result_code != 0:
        logging.error("  result code:" + repr(result_code).decode("unicode_escape"))
        logging.error("  result description:" + repr(result_description).decode("unicode_escape"))
	print t_err+str(result_code)+t_off
	print t_err+result_description+t_off
        exit(1)  
    print t_ok+result_description+t_off		
    
    print t_mess+"- переводим ККМ в режим регистрации"+t_off
    result = driver.put_Mode(1)
    logging.info("put mode 1:" + repr(result).decode("unicode_escape"))
    result_code = driver.get_ResultCode()
    result_description = driver.get_ResultDescription()
    if result_code != 0:
        logging.error("  result code:" + repr(result_code).decode("unicode_escape"))
        logging.error("  result description:" + repr(result_description).decode("unicode_escape"))
	print t_err+str(result_code)+t_off
	print t_err+result_description+t_off
    print t_ok+result_description+t_off					   
	
    print t_mess+"- создаем новый документ"+t_off
    result = driver.NewDocument()	
    logging.info("new document:" + repr(result).decode("unicode_escape"))
    result_code = driver.get_ResultCode()
    result_description = driver.get_ResultDescription()
    if result_code == -3822:
        logging.error("  result code:" + repr(result_code).decode("unicode_escape"))
        logging.error("  result description:" + repr(result_description).decode("unicode_escape"))
        logging.error("  trying close shift...")
	print t_err+str(result_code)+t_off
	print t_err+result_description+t_off
	exit(1)
    print t_ok+result_description+t_off	
    
    print t_mess+"- выставляем что тип чека - продажа"+t_off
    result = driver.put_CheckType(1)
    logging.info("put check type:" + repr(result).decode("unicode_escape"))
    result_code = driver.get_ResultCode()
    result_description = driver.get_ResultDescription()
    if result_code != 0:
        logging.error("  result code:" + repr(result_code).decode("unicode_escape"))
        logging.error("  result description:" + repr(result_description).decode("unicode_escape"))
	print t_err+str(result_code)+t_off
	print t_err+result_description+t_off
	exit(1)
    print t_ok+result_description+t_off	
	
    print t_mess+"- открываем чек"+t_off
    result = driver.OpenCheck()
    logging.info("open check:" + repr(result).decode("unicode_escape"))
    result_code = driver.get_ResultCode()
    result_description = driver.get_ResultDescription()
    if result_code != 0:
        logging.error("  result code:" + repr(result_code).decode("unicode_escape"))
        logging.error("  result description:" + repr(result_description).decode("unicode_escape"))
	print t_err+str(result_code)+t_off
	print t_err+result_description+t_off
	exit(1)
    print t_ok+result_description+t_off	
    
    print t_mess+"- регистрация позиций"+t_off
    logging.info("registering positions...")
    
    for position in check_data:
	print "--- №:"+str(position["num"])
	print "--- Дата:"+str(position["docdate"])
	print "--- Всего:"+str(position["summdoc"])
	for goods in position["goods"]:
	    print "---- Товар:"+str(goods["name"])
	    result = driver.put_Name(goods["name"].decode('utf8'))
	    print "---- Цена:"+str(goods["price"])
	    result = driver.put_Price(goods["price"])
	    print "---- Количество:"+str(goods["quiantity"])
	    result = driver.put_Quantity(goods["quiantity"])
	    result = driver.put_TaxNumber(3) 
	    print "---- Сумма:"+str(goods["summ"])
	    result_code = driver.put_PositionSum(goods["summ"]) 
	    print "---- Регистрация позиции:"
	    result_code = driver.Registration() 	    
	    if result_code[0] != 0:
		logging.error("  result code:" + repr(result_code).decode("unicode_escape"))
		logging.error("  result description:" + repr(result_description).decode("unicode_escape"))	    
		print t_err+str(result_code)+t_off
		print t_err+result_description+t_off
		exit(1)
	

	print t_mess+"- прием оплаты"+t_off
	result = driver.put_TypeClose(1)  # Тип оплаты - Платежная карта
	logging.info("put type close:" + repr(result).decode("unicode_escape"))
	result_code = driver.get_ResultCode()
	result_description = driver.get_ResultDescription()
	if result_code != 0:
	    logging.error("  result code:" + repr(result_code).decode("unicode_escape"))
	    logging.error("  result description:" + repr(result_description).decode("unicode_escape"))	    
	    print t_err+str(result_code)+t_off
	    print t_err+result_description+t_off
	    exit(1)

	print t_mess+"- сумма оплаты"+t_off
	result = driver.put_Summ(position["summdoc"])
	logging.info("put summ:" + repr(result).decode("unicode_escape"))
	result_code = driver.get_ResultCode()
	result_description = driver.get_ResultDescription()
	if result_code != 0:
	    logging.error("  result code:" + repr(result_code).decode("unicode_escape"))
	    logging.error("  result description:" + repr(result_description).decode("unicode_escape"))
	    print t_err+str(result_code)+t_off
	    print t_err+result_description+t_off
	    exit(1)

	print t_mess+"- регистрация платежа"+t_off
	result = driver.Payment()
	logging.info("pyment:" + repr(result).decode("unicode_escape"))
	result_code = driver.get_ResultCode()
	result_description = driver.get_ResultDescription()
	if result_code != 0:
	    logging.error("  result code:" + repr(result_code).decode("unicode_escape"))
	    logging.error("  result description:" + repr(result_description).decode("unicode_escape"))
	    print t_err+str(result_code)+t_off
	    print t_err+result_description+t_off
	    exit(1)

	print t_mess+"- проверка фиксации "+t_off
	result = driver.get_ResultCode()
	logging.info("payment result code:" + repr(result).decode("unicode_escape"))
	result_code = driver.get_ResultCode()
	result_description = driver.get_ResultDescription()
	if result_code != 0:
	    logging.error("  result code:" + repr(result_code).decode("unicode_escape"))
	    logging.error("  result description:" + repr(result_description).decode("unicode_escape"))
	    print t_err+str(result_code)+t_off
	    print t_err+result_description+t_off
	    exit(1)

    print t_mess+"- закрытие чека "+t_off
    result = driver.CloseCheck()
    logging.info("close check:" + repr(result).decode("unicode_escape"))
    result_code = driver.get_ResultCode()
    result_description = driver.get_ResultDescription()
    if result_code != 0:
        logging.error("  result code:" + repr(result_code).decode("unicode_escape"))
        logging.error("  result description:" + repr(result_description).decode("unicode_escape"))
        print t_err+str(result_code)+t_off
        print t_err+result_description+t_off
        exit(1)

    print t_mess+"- получение номера чека "+t_off
    result = driver.get_CheckNumber()
    logging.info("check number:" + repr(result).decode("unicode_escape"))
    result_code = driver.get_ResultCode()
    result_description = driver.get_ResultDescription()
    if result_code != 0:
        logging.error("  result code:" + repr(result_code).decode("unicode_escape"))
        logging.error("  result description:" + repr(result_description).decode("unicode_escape"))
        print t_err+str(result_code)+t_off
        print t_err+result_description+t_off
        exit(1)


    exit(1)
    return res

os.environ['LD_LIBRARY_PATH'] = os.path.dirname(os.path.realpath(__file__))
logging.info(os.path.dirname(os.path.realpath(__file__)))
logging.info(os.environ)

print t_mess+"Фискализация чеков v 1.0 (c) 2018 by Pavel Gribov http://грибовы.рф"+t_off

DTO_LIB_NAME = '/opt/ATOL/drivers9/bin/libfptr.so'
VERSION = 15
driver = dto9fptr.Fptr(DTO_LIB_NAME, VERSION)

print t_mess+"Драйвер:"+t_off+str(driver)
print t_mess+"Параметры:"+t_off+str(params)

# устанавливаем параметры соединения
DeviceSetup(driver,params)
# Читаем текущие настройки
cursettings=GetCurrentParamDevice(driver)
# печатаем чек
check_data=[]

goodsa=[]
goodsa.append({"name":"Телематические услуги связи 1","price":0.01,"quiantity":1,"summ":0.01});
goodsa.append({"name":"Телематические услуги связи 2","price":0.01,"quiantity":1,"summ":0.01});

check_data.append({"num":"1","docdate":"07.05.2018","summdoc":0.02,"goods":goodsa});

print check_data
print_check(driver, check_data)

driver.Beep()


#Закончили работу
driver.put_DeviceEnabled(False)
del driver

conf.py:

params = {
    "Port": "TCPIP",
    "IPAddress": "192.168.0.99",
    "IPPort": "5555",
    "Model": "62",
    "AccessPassword": "0",
    "UserPassword": "30",
    "Protocol": "2",
    "LogFileName": "/var/log/kkt.log"
}
t_mess="\x1b[33m"
t_err="\x1b[31m"
t_ok="\x1b[32m"
t_off="\x1b[0m"

Далее осталась рутина, отключить физическую печать, доделать автоматическое открытие-закрытие смены и т.п. Но в принципе всё уже есть в https://github.com/parshin/kkt

В файле conf.py нужно прописать параметры подключения к ККТ IP адрес, порт, путь к лог файлу и т.д. Параметр «Model» смотрим в Руководстве программиста приложение 7 модели ККМ ???(у меня 47 не подошло, подошло 62, почему не понял).

VERSION = 15 — это для драйверов 9.12.1. Возможно потом нужно будет менять на 16 и т.д.

P.S. Решение применимо и для других аппаратов АТОЛ с минимальными изменениями.