Для формирования вида чеков, задания фискальных атрибутов и прочих параметров используется динамический код. В конфигурацию плагина прописывается нужный класс:
# динамический класс для формирования вида чека checkbuilder=ru.bitel.bgbilling.cashcheck.SimpleCheck
Есть возможность задать отдельный класс для отдельного ККТ или для отдельного маппинга по типу. Это может потребоваться для теста нового шаблона. Или для удобства, если фискализаторы совсем разные (например, локальный и онлайн-служба) - вместо проверки по ККТ внутри шаблона просто используются разные шаблоны.
# динамический класс для отдельного ККТ fr.1.checkbuilder=ru.bitel.bgbilling.cashcheck.CheckKKT1 # динамический класс для отдельного типа платежа pt.666.checkbuilder=ru.bitel.bgbilling.cashcheck.CheckTestType666
В таком случае порядок выбора класса следующий: ищется для маппинга, потом для ККТ, потом общий. Про случай настройки для маппинга есть оговорка: не работает для возвратов (в данный момент там нет маппинга), при ручном чеке проверяется что все платежи в чеке (если их несколько - например при распределении или просто при печати кучи платежей одного договора в один чек) нашли один и тот же настроенный дин.код.
Несколько примеров реализации класса идёт в комплекте с плагином. Подразумевается, что какой-либо класс обязательно должен быть найден и должен сработать. О работе с динамическим кодом можно прочитать в соответствующем разделе справки. Внутри можно проверить любые условия и сформировать чек любой формы для каждой позиции/платежа, добавляемой в чек. Класс должен расширять абстрактный класс ru.bitel.bgbilling.cashcheck.CheckMaker.
Методы динамического класса служат для настройки внешнего вида чеков: чека продажи, чека возврата, чека для автоматической печати платежей.
Позиция - это несколько строк, представляющие одну позицию в чеке, добавляются в объект check с помощью addPayment/addString
один раз должна присутствовать установка платежа addPayment( ), это будет "фискальная" строка, которая собственно представляет собой продажу, в этой строке может быть ещё одна строка, типа наименование продажи и отдел. Теоретически может не быть фискальной строки, в печать не выведется тогда платёж, но внимание - по всем остальным признакам платёж пометится напечатанным. Замечание касается работы с фискальным оборудованием. При печати на произвольном принтере (термопринтере или обычном) возможно формирование с помощью скриптов любых данных для вывода.
в остальных строках - по одному параметру String которая выведется
или: прямой текст указан, так и выведется
или: просто ничего (пробел), это будет пустая строка, типа вертикальная табуляция (например, для красоты при отделении блоков текста друг от друга и т.д.)
- для объекта check настраиваются дополнительные параметры: типы платежа, признаки электронного чека итд - см. следующий раздел.
Примеры кода:
добавление позиции
public class SimpleCheck extends CheckMaker { ... public void addPayment( Payment payment, Check check, Printer printer ) throws BGException { // 1) строка сумма-контракт, плюс отдел (если надо, то вычисляется в этом же скрипте) check.addPayment( payment.getSumma(), payment.getContractTitle(), 0 ); // 2) строка с каментом check.addString( payment.getContractComment() ); // 3) пустая строка check.addString( " " ); } ... }
завершение формирования
public class SimpleCheck extends CheckMaker { ... public void endCreate( Set<Integer> cids, Check check, Printer printer ) throws BGException { check.addString( "footer 1" ); check.addString( "footer 2" ); check.addString( "footer 3" ); } ... }
Обратите особое внимание, что в каждом скрипте формирования внешнего вида чека (а именно происходит формирование каждой отдельной позиции чека) обязательно должна присутствовать (как правило*) ровно одна команда addPayment для всех устройств, являющихся ККМ. Дополнительно может быть любое количество addString. Для устройств, представляющих обычный принтер, для FOP-устройств (см. ниже) и т.п. команда addPayment не нужна, так как там не происходит добавление продажи во внутреннюю память. Но сумма платежа будет считаться только для позиций, добавленных через addPayment.
* Исключение может быть для случаев, если вы специально хотите разбить один платёж на две позиции в чеке (например, с разными названиями или атрибутами), в этом случае нужно самостоятельно разбить сумму на части и добавить несколько addPayment, в логе платежей будет одна запись с итоговой суммой.
Далее приведём пример кода "добавление позиции" для формирования FO-документа, для FOP-драйвера. Эти строки соответствуют шаблону cashcheck_pko.xsl, находящемуся в стандартной поставке сервера печати.
import bitel.billing.common.*; import bitel.billing.server.admin.bean.*; public class SimpleCheck extends CheckMaker { ... public void addPayment( Payment payment, Check check, Printer printer ) throws BGException { //Сумма платежа: "120,00р." (делаем одну строку addPayment, чтобы общая сумма посчиталась) check.addPayment( payment.getSumma(), String.valueOf(payment.getSumma()) + "р.", 0 ); //Номер договора check.addString( payment.getContractTitle() ); //Фамилия клиента: "ИВАНОВ И.И." check.addString( payment.getContractComment() ); //Дата платежа: 20-04-2009 check.addString( TimeUtils.format( payment.getDate(), "dd-MM-yyyy" ) ); //Организация: ООО "ПРОВАЙДЕР" check.addString( "ООО \"ПРОВАЙДЕР\"" ); //ФИО кассира (пользователя биллинга) UserManager um = new UserManager( con ); User user = um.getUserByID( payment.getUserID() ); check.addString( user.getName() ); //Тип платежа: "СПД №" check.addString( "СПД №" ); //Сумма прописью: "Сто двадцать рублей 00 коп" check.addString( SummaToString.summaToString( String.valueOf(payment.getSumma()), true ) ); } ... }
В данном случае мы формируем 8 строк с произвольной информацией, которая передаётся в FOP-драйвер сервера печати, который из напрямую передаёт в FO-шаблон обычной линейной xml. Это позволяет формировать документ из любых строк, подготовленных в скрипте и из любого написанного шаблона.
Также в методы класса передаётся объект "printer" - объект "принтер", на который производится печать. Это может понадобиться, например, при наличии двух разных принтеров и желания печатать на них разного вида информацию и на разные принтеры использовать разные скрипты (например, для FOP и ККМ скрипты всегда разные будут). Например, можно получить ид принтера (такой, какой он в конфиге плагина):
int printerId = printer.getId();
В этом объекте есть метод switchPrinter, который позволяет переключить на нужный принтер внутри дин.кода, это может понадобиться в случаях, когда выбор принтера сложнее привязки "по типам". Весь конфиг принтера доступен через метод getConfig.
Последовательность вызова дин.кода:
Для чека продажи вызывается addPayment для каждого платежа биллинга, которые направлены на печать в один чек и в конце один раз endCreate. Оговорка про "для каждого платежа" - потому что из клиента биллинга есть возможность напечатать сразу несколько приходов биллинга в один чек, в виде нескольких продажных позиций. Если печатается просто по приходу платежа галочкой, то будет один вызов addPayment дин.кода.
Для чека возврата вызывается addPaymentRefund для одного возврата биллинга (возвраты только по отдельности можно печатать) и в конце endCreateRefund. В отличие от платёжного чека обычно нельзя печатать строки итд, потому должно быть там просто один addPayment внутри addPaymentRefund и установка доп.атрибутов (налоги, адрес покупателя итд).
Для чека, который печатается через механизм автопечати платежей - вызывается один раз addPaymentAuto для единственного платежа. Т.е. в отличие от методов штатной функциональности вызывается один раз для каждого кассового чека (т.к. в чек попадает всегда один платёж биллинга), так что подразумевает в себе логику и addPayment и endCreate. (Для возвратов в данный момент аналогичная ситуация, но исторически там осталось два метода). Про автопечать платежей см. соответствующий раздел.
Аналогично auto-платежам для delay-платежей реализуется метод addPaymentAutoDelay с такой же сигнатурой.