Код:
#include "..\LACK\LS\PRG\laks.ch"
MEMV oKkm
* ---------------------------------------------------------------------------
* Головной модуль программы
PROC Main()
LOCA nVal:=0, cVal:="", nSel:=1
PRIV oKkm
// Глобальные системные установки пакета.
SET DATE GERMAN
SET DELE ON
SET ESCA ON
SET SCOR OFF
SET WRAP ON
SET CENT ON
// Стандартная цветовая гамма программы
PRIV cMainc_c := "GR+/B,N+/W,,,N/W", cMainc_m := "W/N,N/W,,,N/W+"
PRIV cColor_c := "N/W,GR+/B,,,N/G", cColor_m := "W/N,N/W,,,N/W+"
PRIV cError_c := "N/R+,W/N,,,W/B", cError_m := "W+/N,N/W,,,W/N"
PRIV cHelpc_c := "N/G,R/G,,,N/G+", cHelpc_m := "N/W,W/N,,,N/W+"
PRIV cOther_c := "N+/G,W/N,,,B/G", cOther_m := "N/W,W/N,,,N/W"
PRIV cMainc := IF(ISCOLOR(),cMainc_c,cMainc_m)
PRIV cColor := IF(ISCOLOR(),cColor_c,cColor_m)
PRIV cError := IF(ISCOLOR(),cError_c,cError_m)
PRIV cHelpc := IF(ISCOLOR(),cHelpc_c,cHelpc_m)
PRIV cOther := IF(ISCOLOR(),cOther_c,cOther_m)
PRIV l_mus := TRUE
CLS
// Декларирование структуры с обработкой ошибок выполнения - недоступность драйвера ККМ
TRY
oKkm := CreateObject('AddIn.FprnM45')
CATCH
fErrQuit("Не могу открыть драйвер Атол! Возможно он не установлен на ПК",cError )
RETU
END
// Занимаем порт ККМ и проверяем его доступность - может быть занят мной или другими службами
oKkm:DeviceEnabled := TRUE
IF oKkm:ResultCode <> 0 // При возврате ошибки занимания порта 14 - Занят порт (в драйверах usb-ethernet занимает порт)
ErrMess("Возвращен код ошибки при резервировании порта = "+Alltrim(Str(oKkm:ResultCode)),cError)
RETU
ENDI
// Получаем состояние ККМ - возврат кода ошибки или 0
IF oKkm:GetStatus <> 0
ErrMess("Возвращено неопределенное состояние ККМ. Код ошибки = "+Alltrim(Str(oKkm:GetStatus)),cError)
RETU
ENDI
//*** Проверяем на всякий случай ККМ на фискализированность, что бы не испортить рабочую ККМ
IF oKkm:Fiscal
IF !Fyn("ККМ фискализирована! Вы действительно хотите продолжить?",cError) THEN RETU
ENDI
//*** Циклический вызов меню задач кассового аппарата - изолированные функции низкого уровня
DO WHIL TRUE
CLS
// Пополняемое меню задач для кассового аппарата
@ 1,1 PROM "Открыть смену - выставляю глобальный флаг"
@ 2,1 PROM "Проверить наличие открытых чеков- закрыть"
@ 3,1 PROM "Входим в режим кассира. Пример для продаж"
@ 4,1 PROM "Прод. товаров с печатью чека-общий подход"
@ 5,1 PROM "Внесение или выплата денег из кассы.Общий"
@ 6,1 PROM "Резерв... "
@ 7,1 PROM "Резерв... "
@ 8,1 PROM "Резерв... "
@ 9,1 PROM "Провер. наличие открытого чека. Закрываем"
@10,1 PROM "В случае открытой смены печатаем z-отчет "
@11,1 PROM "Печать Х-отчета. Не закрывается опердень!"
@12,1 PROM "Закрыть смену - убираю глобальный флаг"
MENU TO nSel
IF nSel = 0 THEN EXIT
// Для меня новенькое - осваиваю новый тип блока обработки
SWITCH nSel
CASE 1 // Открыть смену - выставляю глобальный флаг
pOpenSmena()
CASE 3 // Входим в режим кассира. Например для продаж
pInpKassir()
CASE 4 // Продажа товаров с печатью чека - общий алгоритмический подход и описание
pPayWar()
CASE 5 // Внесение или выплата денег из кассы. Делаю процедуру общего назначения с передачей пароля
pInOutCash("30",300.00,Fyn("Вы желаете внести деньги в кассу?",cHelpc))
CASE 9 // Проверяем наличие открытого чека. Закрываем его. Чеки всегда нужно закрывать
pCloseCheck()
CASE 10 // В случае открытой смены печатаем z-отчет
pPrintZ()
CASE 11 // В случае открытой смены печатаем Х-отчет - это не закрывает смену и не гасит кассу
pPrintX()
DEFAULT
EXIT
END
END
//*** ОСВОБОЖДАЕМ ПАРОЛИ - выходим в режим выбора, чтобы кто-то под введенными паролями не сделал что нибуть нехорошее
// В принципе - это не освобождение паролей, а очистка установленного режима с очисткой всех свойств, включая пароли
IF oKkm:ResetMode <> 0
ErrMess("Ошибка при освобождении паролей. Код ошибки = "+Alltrim(Str(oKkm:ResetMode)),cError)
RETU
ENDI
//*** ОСВОБОЖДАЕМ ПОРТ - освобождаем порт
oKkm:DeviceEnabled := FALSE
IF oKkm:ResultCode <> 0
ErrMess("Ошибка при освобождении ПОРТА. Код ошибки = "+Alltrim(Str(oKkm:ResultCode)),cError)
RETU
ENDI
// Очищаю объект и закрываю программу
oKkm := NIL
fErrQuit("Спасибо за работу со мной!",cMainc)
RETU
****************************************************************************************************
* НАБОР ЭЛЕМЕНТАРНЫХ ПРОЦЕДУР, ВЫЗЫВАЕМЫХ ИЗ ТЕСТОВОГО МЕНЮ ПРОГРАММЫ
****************************************************************************************************
* --------------------------------------------------------------------------------------------------
* Открыть смену - выставляю глобальный флаг
STAT PROC pOpenSmena()
IF oKkm:SessionOpened
ErrMess("Смена уже открыта!",cError)
RETU
ENDI
//???
RETU
* --------------------------------------------------------------------------------------------------
* Продажа товаров с печатью чека - общий алгоритмический подход и описание последовательности
/*
Примерная последовательность вызовов:
1. Открыть чек
2. Регистрация первой позиции
3. Скидка на первую позицию - у меня без скидок
4. Регистрация второй позиции
5. Надбавка на весь чек
6. Прием оплаты
7. Закрытие чека
*/
STAT PROC pPayWar()
LOCA nI:=0, nL:=0
LOCA cPass := "1" // Пароль кассира
LOCA aWar := {} // Список товаров для продаж, очищенный от скидок
LOCA cCust := "+79276214504" // Можно передавать или телефон (+7хххххххххх) или e-mail: test @ test.com
// У меня будет список, созданный при формировании чека, а здесь делаю в виде массива
aadd(aWar,{"Водка беленькая 0.4",1,320.00})
aadd(aWar,{"Шампанское роза ветров полупьяная",2,730.00})
aadd(aWar,{"Закусь",1.34,123.75})
// 1. Открытие чека. Определение пароля и задание режима работы кассы
oKkm:Password := cPass // Устанавливаем пароль кассира для режима регистрации продаж
oKkm:Mode := 1 // Входим в режим регистрации - Mode = 1
IF oKkm:SetMode <> 0 // Ошибка
ErrMess("Ошибка при установке режима регистрации. Код ошибки = "+Alltrim(Str(oKkm:SetMode)),cError)
RETU
ENDI
// 2. Определение свойств чека и открываю чек
oKkm:CheckState() // Определяю состояние предыдущего чека
oKkm:CancelCheck() // Для профилактики отправляю метод аннулирования "предыдущего" чека
oKkm:CheckType := 0 // Чек продажи
IF oKkm:OpenCheck <> 0 // Открываю чек
ErrMess("Ошибка при открытии чека. Код ошибки = "+Alltrim(Str(oKkm:OpenCheck)),cError)
RETU
ENDI
// Отправка данных о покупателе, если он это потребовал до момента создания чека и дал свои атрибуты
oKkm:CustomerEmail := cCust
IF oKkm:FNSendCustomerEmail <> 0 THEN ErrMess("Ошибка отправки данных о покупателе",cError)
// 3. В цикле регистрирую каждую позицию чека
nL := Len(aWar)
FOR nI := 1 TO nL
oKkm:Name := HB_OEMTOANSI(aWar[nI,1]) // Наименование товара
oKkm:Price := aWar[nI,3] // Цена, как число
oKkm:Quantity := aWar[nI,2] // Количество - возможно дробное с точкой
oKkm:Department := 1 // Всегда указывается отдел (раздел)
IF oKkm:Registration <> 0 // Регистрация строки продажи
ErrMess("Ошибка регистрации строки продажи. Код ошибки = "+Alltrim(Str(oKkm:Registration)),cError)
ENDI
NEXT nI
// 4. Закрытие чека наличными без ввода полученной от клиента суммы
oKkm:TypeClose := 0
IF oKkm:CloseCheck <> 0 THEN ErrMess("Ошибка при закрытии чека. Код ошибки = "+Alltrim(Str(oKkm:CloseCheck)),cError)
RETU
* -------------------------------------------------------------------------------------------------
* Проверяем наличие открытого чека. Закрываем его. Чеки всегда нужно закрывать
STAT PROC pCloseCheck()
IF oKkm:CheckState <> 0 // Признак открытости чека
IF oKkm:CancelCheck <> 0 // Ошибка при закрытии чека
ErrMess("Ошибка при попытке закрытия до этого открытого чека. Код ошибки = "+Alltrim(Str(oKkm:CancelCheck)),cError)
RETU
ENDI
ENDI
RETU
* -------------------------------------------------------------------------------------------------
* В случае открытой смены печатаем z-отчет
STAT PROC pPrintZ()
// Если смена открыта снимаем Z-отчет... Наверное это просто тестовый пример, а при текущей работе данного делать не нужно и выполнять команду по отдельному заданию?
IF !oKkm:SessionOpened
ErrMess("Нет открытой смены! Нельзя напечатать Z-отчет!",cError)
RETU
ENDI
// Для снятия z-отчета необходимы права администратора. Пароли задаваеются при настройке драйвера
oKkm:Password := "30" // // Устанавливаем пароль системного администратора ККМ (30)
// Входим в режим отчетов с гашением - нужно изучить все возможные параметры Mode
oKkm:Mode := 3
IF oKkm:SetMode <> 0
ErrMess("Ошибка при установке режима ККМ - отчет с гашением. Код ошибки = "+Alltrim(Str(oKkm:SetMode)),cError)
RETU
ENDI
// Снимаем z-отчет... Просто печатается на ФР (ReportType??? - тип отчета)
oKkm:ReportType := 1
IF oKkm:Report <> 0
ErrMess("Ошибка отправки на печать регламентного типа отчета. Код ошибки = "+Alltrim(Str(oKkm:Report)),cError)
RETU
ENDI
RETU
* --------------------------------------------------------------------------------------------------
* В случае открытой смены печатаем Х-отчет - это не закрывает смену и не гасит кассу???
STAT PROC pPrintX()
// Если смена открыта снимаем X-отчет без гашения...
IF !oKkm:SessionOpened
ErrMess("Нет открытой смены! Нельзя напечатать Z-отчет!",cError)
RETU
ENDI
// Проведение и печать Х-отчета - он вроде бы не закрывает кассовый день X - отчет
oKkm:Password := "29" // АДМИНИСТРАТОРЫ БЫВАЮТ РАЗНЫЕ! устанавливаем пароль администратора ККМ
// Входим в режим отчетов без гашения - нужно изучить все возможные параметры Mode
oKkm:Mode := 2
IF oKkm:SetMode <> 0
ErrMess("Ошибка при установке режима ККМ - отчет с гашением. Код ошибки = "+Alltrim(Str(oKkm:SetMode)),cError)
RETU
ENDI
// Снимаем X-отчет... Просто печатается на ФР (ReportType??? - тип отчета)
oKkm:ReportType := 2
IF oKkm:Report <> 0
ErrMess("Ошибка отправки на печать регламентного типа отчета. Код ошибки = "+Alltrim(Str(oKkm:Report)),cError)
RETU
ENDI
RETU
* --------------------------------------------------------------------------------------------------
* Внесение или выплата денег из кассы. Делаю процедуру общего назначения с передачей пароля. Эдесь
* приведена четкая и правильная последовательность задания свойств и вызовов методов или операций
* с анализом возникновения ошибок при их использовании
* Параметры:
* cPass - пароль для режима
* nSum - сумма внесения или выплаты
* lInCash - ИСТИНА, если внесение, иначе выплата указанной суммы
STAT PROC pInOutCash(cPass,nSum,lInCash)
LOCA nResult:=0
// Задаем свойства для метода SetMode
oKkm:Password := Alltrim(cPass) // Пароль для режима
oKkm:Mode := 1 // Режим регистрации = 1 (деньги и чеки)
// Попытка вызвать метод установки режима и выход при ошибке
IF oKkm:SetMode <> 0
ErrMess("Ошибка при установке режима ККМ - регистрация. Код ошибки = "+Alltrim(Str(oKkm:SetMode)),cError)
RETU
ENDI
// Меняю только свойство суммы для операции
oKkm:summ := nSum
// Вызываю операцию и определяю код возвращаемой ошибки, если таковая будет
nResult := IF( lInCash, oKkm:CashInCome, oKkm:CashOutCome )
// Даём только сообщение, т.к. нужно ещё будет очистить свойства режима
IF nResult <> 0 THEN ErrMess("Ошибка при регистрации с внесением или выдачей денег. Код ошибки = "+Alltrim(Str(nResult)),cError)
// Очищаем свойства режима, в частности активные пароли на кассе
IF oKkm:ResetMode <> 0 THEN ErrMess("Ошибка при очистке свойств режима регистрации. Код ошибки = "+Alltrim(Str(oKkm:ResetMode)),cError)
RETU
* --------------------------------------------------------------------------------------------------
* Входим в режим кассира. Например для ввода продаж - задаётся пароль кассира и получаем допуск
* Все настройки (пароли) задаются в режимах настройки драйвера фискального регистратора
STAT PROC pInpKassir()
// Входим в режим регистрации пользователя на ФР. Устанавливаем пароль кассира ("1")
oKkm:Password := "1"
// Входим в режим регистрации - Mode = 1... Это работы кассира? Каждый раз нужно проверять, что команда отработала без ошибок
oKkm:Mode := 1
IF oKkm:SetMode <> 0
ErrMess("Ошибка при установке режимов ККМ - работа кассира. Код ошибки = "+Alltrim(Str(oKkm:SetMode)),cError)
RETU
ENDI
RETU