Недавно столкнулся с проблемой печати чеков ККМ с помощью компоненты "DatecsECR". Может, кому будет полезна моя публикация. Собственно задача стояла реализовать печать копии чеков ККМ. Углубившись в задачу и в мануал по работе с компонентой DatecsECR, понял, что только по последнему созданному чеку можно напечатать копию. Задача же стоит - печатать копию любого чека.
Для реализации такой возможности нужно где-то хранить номер и дату чека фискального регистратора. Я выбрал самый легкий вариант, в реквизитах документа «Чек ККМ». Если конфигурацию стоит на поддержке, лучше создать отдельный регистр сведений для этих целей.
Если нужно печать только сами чеки из 1С на фискальный регистратор, то дорабатывать ничего не надо.
Обработка не претендует на эталон и выложена для примера печати чеков ККМ, поэтому перед скачиванием посмотрите код, может, такой вариант вам и не подойдет.
Вот код процедуры печати чека:
ПолучитьТаблицуЧеков();
флЕстьОшибка = 0;
ECRFisc=Новый COMОбъект("DatecsECR.TECRFisc");
Для каждого СтрокаДокументаЧекККМ из ТаблицаЧеков цикл
флЕстьОшибка = 0;
Сообщить("Печать чека для РН "+СтрокаДокументаЧекККМ.Ссылка, СтатусСообщения.Информация);
// 1. откроем порт
Result = ECRFisc.SetComPort(1, 19200);
ЗаписьЖурналаРегистрации("Печать Чеков КММ на ФР", УровеньЖурналаРегистрации.Информация, Метаданные.Документы.ЧекККМ, СтрокаДокументаЧекККМ.ЧекККМ,
"Открыли COM порт. "+Строка(СтрокаДокументаЧекККМ.ЧекККМ));
Если Result <> 1 Тогда
//Если не Result тогда
Сообщить("Проблем подключения по COM Порту. Код ошибки: "+Строка(Result)+" ! Проверьте подключение!", СтатусСообщения.ОченьВажное);
Сообщить(" -1 - ошибка (неверный номер порта – меньше 1 или больше 8)" + Символы.ПС+
" -2 – неверно указана скорость" + Символы.ПС+
" -3 – порт занят" + Символы.ПС+
" -4 – невозможно создать блок управления устройством" + Символы.ПС+
" -5 – ошибка установки параметров порта" + Символы.ПС+
" -7 – проблемы с лицензированием");
флЕстьОшибка = 1 ;
Конецесли;
// Находим номер последнего запрограммированного артикула
Если флЕстьОшибка = 0 тогда
Код = ВернутьКодПоследнегоЗапрограммированногоАртикулаФР();
Если Код = 0 Тогда
// Свойство возвращает информацию о первом запрограммированном артикуле.
ПервыйКод = ECRFisc.firstart();
ЗаписьЖурналаРегистрации("Печать Чеков КММ на ФР", УровеньЖурналаРегистрации.Информация, Метаданные.Документы.ЧекККМ, СтрокаДокументаЧекККМ.ЧекККМ,
"Прочитали первый запрограммированный артикул. "+Строка(СтрокаДокументаЧекККМ.ЧекККМ));
Если СтрДлина(ПервыйКод) = 0 тогда
Код = 1;
Иначе
Для номерарт = 2 по 14800 цикл
//свойство возвращает информацию о следующем запрограммированном артикуле.
следкод = ECRFisc.nextart();
Если стрдлина(следкод) = 0 тогда
Код = номерарт; прервать;
Конецесли;
Конеццикла;
Конецесли;
ЗаписьЖурналаРегистрации("Печать Чеков КММ на ФР", УровеньЖурналаРегистрации.Информация, Метаданные.Документы.ЧекККМ, СтрокаДокументаЧекККМ.ЧекККМ,
"Нашли номер последнего запрограммированного артикула. "+Строка(СтрокаДокументаЧекККМ.ЧекККМ));
ЗаписатьКодПоследнегоДобавленногоАртикулаВФР(Код);
КонецЕсли;
// Если количество запрограммированных артикулов не привысило максимальное количество, то открываем фискальный чек
Если Код <= 14800 Тогда
//откроем фискальный чек на ФР
result = ECRFisc.OpenFiscReceipt(1,1,"0000");
Если не Result тогда
сообщить("Ошибка открытия чека продажи на ФР!");
флЕстьОшибка = 1 ;
//продолжить;
Иначе
// Объявляем переменные
старртикул = 0; ОбщаяСумма = 0;
// Перебираем все строки табличной части товары из Чека ККМ
ОбъектЧекККМ = СтрокаДокументаЧекККМ.ЧекККМ.ПолучитьОбъект();
Для Каждого СтрокаТЧ Из ОбъектЧекККМ.Товары Цикл
// код = код + 1;
Сумма = СтрокаТЧ.Сумма;
Цена = Сумма / СтрокаТЧ.Количество;
ОбщаяСумма = ОбщаяСумма + Сумма;
НаименованиеТовара = Лев(СокрЛП(СтрокаТЧ.Номенклатура),24);
// осуществим продажу по строке в чеке
// Добавляем артикул
res = ECRFisc.ProgramArt(192, Код, 1, Цена, "0000", НаименованиеТовара);
ЗаписьЖурналаРегистрации("Печать Чеков КММ на ФР", УровеньЖурналаРегистрации.Информация, Метаданные.Документы.ЧекККМ, СтрокаДокументаЧекККМ.ЧекККМ,
"Запрограммировали артикул "+Строка(НаименованиеТовара));
// В случае удачного добавления артикула, выводим строку на печать
Если res Тогда
Result = ECRFisc.SellArtCashDS(True, код, строкатч.количество, 0,0);
ЗаписьЖурналаРегистрации("Печать Чеков КММ на ФР", УровеньЖурналаРегистрации.Информация, Метаданные.Документы.ЧекККМ, СтрокаДокументаЧекККМ.ЧекККМ,
"Напечатали артикул "+Строка(НаименованиеТовара));
Если не Result тогда
Сообщить("Ошибка при формировании продажи и выводе информации в строке фискального чека!" ,СтатусСообщения.ОченьВажное);
флЕстьОшибка = 1;
ЗаписатьКодПоследнегоДобавленногоАртикулаВФР(0);
// Анулируем фискальный чек
ECRFisc.ReceiptCancel();
// Закрываем com порт
ECRFisc.CloseComPort(1);
Прервать;
Иначе
Код = Код +1;
ЗаписатьКодПоследнегоДобавленногоАртикулаВФР(Код);
Если Код > 14800 тогда
ЗаписатьКодПоследнегоДобавленногоАртикулаВФР(0);
Сообщить("Достигнуто максимальное количество программируемых артикулов = 14800!", СтатусСообщения.Важное);
Сообщить("Сделайте z-отчет!", СтатусСообщения.Важное);
// Анулируем фискальный чек
ECRFisc.ReceiptCancel();
// Закрываем com порт
ECRFisc.CloseComPort(1);
Прервать;
КонецЕсли;
КонецЕсли;
Иначе
Сообщить("Ошибка при добавлении нового артикула в ФР!" + Символы.ПС + "Товар: "+НаименованиеТовара ,СтатусСообщения.ОченьВажное);
флЕстьОшибка = 1;
ЗаписатьКодПоследнегоДобавленногоАртикулаВФР(0);
// Анулируем фискальный чек
ECRFisc.ReceiptCancel();
// Закрываем com порт
ECRFisc.CloseComPort(1);
Прервать;
КонецЕсли;
// Добавляем кодтовара из фискального регистратора в Строку Чека
СтрокаТЧ.КодТовараФР = Код;
КонецЦикла;
// Пишем Номер, ИД чека из ФР
Попытка
ОбъектЧекККМ.НомерЧекаФР = Строка(ECRFisc.NumbRec());
Исключение
КонецПопытки;
//регистрация продажи и закрытие чека
ECRFisc.Payment(0, 0);
ЗаписьЖурналаРегистрации("Печать Чеков КММ на ФР", УровеньЖурналаРегистрации.Информация, Метаданные.Документы.ЧекККМ, СтрокаДокументаЧекККМ.ЧекККМ,
"Сделали продажу "+Строка(ОбъектЧекККМ));
// Закрываем фискальный чек
Result = ECRFisc.CloseFiscReceipt();
ЗаписьЖурналаРегистрации("Печать Чеков КММ на ФР", УровеньЖурналаРегистрации.Информация, Метаданные.Документы.ЧекККМ, СтрокаДокументаЧекККМ.ЧекККМ,
"Закрыли фискальный чек "+Строка(ОбъектЧекККМ));
Если не Result Тогда
Сообщить("Ошибка при закрытии фискального чека!");
кодошибки = 1 ;
Иначе
// Пишем дату и время закрытия чека из ФР
ПОПЫТКА
ОбъектЧекККМ.ДатаЧекаФР = ТекущаяДата();
Исключение
КонецПопытки;
КонецЕсли;
//закроем порт
Result = ECRFisc.CloseComPort(1);
ЗаписьЖурналаРегистрации("Печать Чеков КММ на ФР", УровеньЖурналаРегистрации.Информация, Метаданные.Документы.ЧекККМ, СтрокаДокументаЧекККМ.ЧекККМ,
"Закрыли порт "+Строка(ОбъектЧекККМ));
Если не Result Тогда
Сообщить("Ошибка закрытия порта!");
кодошибки = 1 ;
КонецЕсли;
// Пишем Чек ККМ
ОбъектЧекККМ.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;
Иначе
Сообщить("Достигнуто максимальное количество программируемых артикулов!");
Сообщить("Сделайте z-отчет!");
ЗаписатьКодПоследнегоДобавленногоАртикулаВФР(0);
прервать;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Сообщить("Печать чеков закончена!");
Главная проблема состоит в том, что при добавлении нового чека в фискальный регистратор, нужно хранить код товара на стороне 1С. Иначе нужно при печати каждого чека искать код последнего добавленного товара в фискальный регистратор, что занимает огромное количество времени. Поэтому пока открыта обработка мы храним код последнего добавленного товара в фискальный регистратор. Так же для каждого Чека ККМ записываем Номер и Дату чека из Фискального регистратора. Что касается даты чека из ФР, то у используемой компоненты нет метода для возврата Даты, поэтому в момент печати чека сохраняем ТекущуюДату.
Вот Код печати Копии Чека:
ПолучитьТаблицуЧеков();
флЕстьОшибка = 0;
ECRFisc=Новый COMОбъект("DatecsECR.TECRFisc");
// Перебираем все строки и печатаем отмеченные чеки
Для каждого СтрокаДокументаЧекККМ из ТаблицаЧеков цикл
флЕстьОшибка = 0;
Сообщить("Печать копии чека для "+СтрокаДокументаЧекККМ.Ссылка, СтатусСообщения.Информация);
// 1. откроем порт
Result = ECRFisc.SetComPort(1, 19200);
Если Result <> 1 Тогда
Сообщить("Проблем подключения по COM Порту. Код ошибки: "+Строка(Result)+" ! Проверьте подключение!", СтатусСообщения.ОченьВажное);
Сообщить(" -1 - ошибка (неверный номер порта – меньше 1 или больше 8)" + Символы.ПС+
" -2 – неверно указана скорость" + Символы.ПС+
" -3 – порт занят" + Символы.ПС+
" -4 – невозможно создать блок управления устройством" + Символы.ПС+
" -5 – ошибка установки параметров порта" + Символы.ПС+
" -7 – проблемы с лицензированием");
флЕстьОшибка = 1 ;
Конецесли;
// Если подключили com порт, то открываем на печать не фискальный чек
Если флЕстьОшибка = 0 тогда
//откроем нефискальный чек на ФР
Result = ECRFisc.OpenNonFiscReceipt();
Если не Result тогда
Сообщить("Ошибка открытия нефискального чека на ФР!");
флЕстьОшибка = 1 ;
Иначе
// Объявляем переменные
ОбщаяСумма = 0;
// Печатаем шапку чека
Result = ECRFisc.PrntNonFiscal("Копия Чека № "+СокрЛП(СтрокаДокументаЧекККМ.ЧекККМ.НомерЧекаФР));
Попытка
ДатаЧекаФР = СтрокаДокументаЧекККМ.ЧекККМ.ДатаЧекаФР;
Result = ECRFisc.PrntNonFiscal("от "+СокрЛП(ДатаЧекаФР));
Исключение
КонецПопытки;
Result = ECRFisc.PrntNonFiscal("");
// Перебираем все строки табличной части товары из Чека ККМ
Для Каждого СтрокаТЧ Из СтрокаДокументаЧекККМ.ЧекККМ.Товары Цикл
Сумма = СтрокаТЧ.Сумма;
Цена = Сумма / СтрокаТЧ.Количество;
ОбщаяСумма = ОбщаяСумма + Сумма;
НаименованиеТовара1 = Лев(СтрокаТЧ.Номенклатура,24);
НаименованиеТовара2 = Сред(СтрокаТЧ.Номенклатура,25, 24);
// Выводим коментарий в нефискальный чек
Result = ECRFisc.PrntNonFiscal(НаименованиеТовара1);
Если НЕ ПустаяСтрока(НаименованиеТовара2) Тогда
Result = ECRFisc.PrntNonFiscal(НаименованиеТовара2);
КонецЕсли;
Result = ECRFisc.PrntNonFiscal(Формат(СтрокаТЧ.Количество,"ЧЦ=7; ЧДЦ=2")+ " " +
СокрЛП(СтрокаТЧ.ЕдиницаИзмерения) + " х"+Формат(Цена,"ЧЦ=10; ЧДЦ=2")+
" = "+Формат(Сумма,"ЧЦ=10; ЧДЦ=2"));
Если не Result тогда
Сообщить("Ошибка при печати нефискального чека!" ,СтатусСообщения.ОченьВажное);
флЕстьОшибка = 1;
// Закрываем нефискальный чек
ECRFisc.CloseNonFiscReceipt();
// Закрываем com порт
ECRFisc.CloseComPort(1);
Прервать;
КонецЕсли;
КонецЦикла;
// Выводим общую сумму по чеку
Result = ECRFisc.PrntNonFiscal("------------------------");
Result = ECRFisc.PrntNonFiscal("СУММА"+Прав(" " +Формат(ОбщаяСумма,"ЧЦ=10; ЧДЦ=2"),19));
Result = ECRFisc.PrntNonFiscal("ПДВ А 0,00% 0,00");
Result = ECRFisc.PrntNonFiscal("Сумма ПДВ"+Прав(" " +Формат(СтрокаДокументаЧекККМ.Ссылка.Товары.Итог("СуммаНДС"),"ЧЦ=10; ЧДЦ=2"),15));
Result = ECRFisc.PrntNonFiscal("------------------------");
Result = ECRFisc.PrntNonFiscal(" СЛУЖЕБНЫЙ ЧЕК ");
// Закрываем нефискальный чек
Result = ECRFisc.CloseNonFiscReceipt();
Если не Result Тогда
Сообщить("Ошибка при закрытии нефискального чека!");
кодошибки = 1 ;
КонецЕсли;
//закроем порт
Result = ECRFisc.CloseComPort(1);
Если не Result Тогда
Сообщить("Ошибка закрытия порта!");
кодошибки = 1 ;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Если кодошибки = 0 Тогда
СтрокаДокументаЧекККМ.флНапечатать = ложь;
КонецЕсли;
КонецЦикла;
Сообщить("Печать копии чеков закончена!");
Так же буду очень признателен за продуктивную критику по работе с компонентой, может, есть какие-то методы, которые я не учел (не нашел) и которые бы очень сильно облегчили разработку. К примеру, вытащить копию чека по номеру или вытащить дату чека.
Использование встроенных механизмов из типовых конфигураций специально не рассматривается ввиду автономности данной обработки, хотя и не универсальности.