// Скрипт автоматизации работой с фискальными регистраторами под управлением драйвера АТОЛ 
// Copyright (C)

// Печать чека. Если программа не находит данной процедуры, то выполняет просто скрипт. Оставлено для совместимости со старыми скриптами
// При успешном завершении переменная Result должна вернуть 0. Иначе - код ошибки ККМ и ResultDescription - текст ошибки
// procedure Cash_Execute(NeedConnect :boolean = true) :boolean;  

// процедура вернет серийный номер кассы
// procedure Cash_GetNumbers(NeedConnect :boolean = true);

// процедура вернет номер сессии
// procedure Cash_GetSessionNumber(NeedConnect :boolean = true) :string;

// процедура проверяет и, если необходимо - инициализирует драйвер устройства (OLЕ-объект). В случае ошибки вызовет exception 
// procedure Cash_CheckDevice;

// процедура устанавливает связь с сервером. В случае ошибки вернет false, а также установит ResultCode и Result_Description 
// procedure Cash_Connect :boolean;

// процедура разрывает связь с сервером 
// procedure Cash_Disconnect;

// процедура отрезки чека
// procedure Cash_Cut(NeedConnect :boolean = true);

// процедура отмены зависшего чека
// procedure Cash_CancelCheck(NeedConnect :boolean = true);

// процедура "продолжить печать"
// procedure Cash_RepeatCheck(NeedConnect :boolean = true);

// процедура показа параметров устройства
// procedure Cash_ShowProperties;

// процедура вносить сумму в кассу
// procedure Cash_DoSummaryIn(NeedConnect :boolean = true);
// процедура изымает суммы из кассы
// procedure Cash_DoSummaryOut(NeedConnect :boolean = true);


// процедура открывает ящик
//procedure Cash_OpenDrawer(NeedConnect :boolean = true);

// Отчет с гашением
// procedure Cash_ZReport(NeedConnect :boolean = true);
// Отчет с без гашения
// procedure Cash_XReport(NeedConnect :boolean = true);
// Отчет по секциям
// procedure Cash_SectionReport(NeedConnect :boolean = true);
// Отчет по кассирам
// procedure Cash_UsersReport(NeedConnect :boolean = true);

// Отчет по регистрам (общий отчет)
// procedure Cash_RegistersReport(NeedConnect :boolean = true);

// все нижеперечисленные переменные могут быть определены в параметрах работы программы и проверяются перед выполнением на их наличие там.
// если в параметрах не найдено, то используются настройки по-умолчанию 


// поднять любую настройку из кассы (по её номеру, см. в приложении к АТОЛ "Настройки ККТ"). Требуется подключение к кассе.
// function Cash_GetSettingValue(SettingNumber :integer) :string; 

// поднять из нашей локальной инишки настройки драйвера 
// (основные настройки, отображающиеся на первой странице в настройках драйвера),
// не хранятся в кассе, требуется их восстановление и сохранение, 
// подключение к кассе не требуется.
// procedure cash_RestoreSettings;  

// сохранить настройки подключения для драйвера в нашей локальной инишке
// подключение к кассе не требуется, эти настройки в кассе не хранятся
// procedure Сash_SaveSettings;

var
  // Округление при обрезании цен и сумм
  DefRoundParam :  integer  = rpDown; // было rpTruncate // (rpDown = -1, rpNearest = 0, rpUp = 1, rpTruncate = 2,rpDEFAULT = 100);


var 
      CASH_DeviceName :string =  'AddIn.Fptr10';
      CASH_KKMSYSPASSWORD :string = ''; 
      CASH_STDUSERPASSWORD  :string = ''; // пароль кассира. Если не указан, то берется пароль пользователя программы. ДОлжен состоять только из цифр. Системный же пароль вбивается в настройках программы на закладке розницы.
      CASH_USERNAME_MANUAL :boolean =  false; // имя кассира, указывать ли вручную, взяв его из имени пользователя. Не зависит от пароля кассира и выбора кассира в аппарате. Пока используется только в чеках.
      CASH_DEFAULT_USERNAME :string = ''; // кассир по-умолчанию. Если он не определен, то используется пользователь программы
      CASH_DEFAULT_USERINN :string = '';  // ИНН кассира по-умолчанию
      CASH_DiscountType :integer = 0; // 0 - скидка не отображается, 1 - детальная скидка, 2  - скидка на весь чек
      CASH_PrintStringDiscountSummary :boolean = true; // печатать строку с размером скидки  
      CASH_PrintStringPriceOut :boolean = true; // печатать строку со строкой без скидки
      CASH_PrintStringDiscountOnCheck :boolean = true; // печатать строку со скидкой на чек  

      CASH_NameBadCharList :string = '';  // символы в наименовании на замену
      CASH_NameReplaceCharList :string = ''; // символы в наименовании, которыми надо заменить плохие.   

      CASH_TypeCloseNal :integer = 0; // LIBFPTR_PT_CASH
      CASH_TypeCloseBN :integer = 1; // LIBFPTR_PT_ELECTRONICALLY подмена стандартных типов оплаты данными
      CASH_TypeCloseKredit :integer = 1;
      CASH_TypeCloseSert :integer = 2; // LIBFPTR_PT_PREPAID = 2, LIBFPTR_PT_CREDIT = 3, LIBFPTR_PT_OTHER = 4 ....
     
      CASH_CutType :integer = 0;  //0- не отрезать (не контролировать); 1-полная отрезка; 2-неполная отрезка
      
      CASH_OPENDRAWER_NAL :boolean = false; // открывать ли ящик в нале

      CASH_TrimNameGoods :boolean = false; // Обрезать ли наименование товара по длине строки
      
      // Длина строки. 0 - без обрезания и учета длины 
      // <0 - взять автоматически средствами драйвера      
      CASH_StringLength :integer = -1;
      
      Cash_Protocol3 :boolean = true;
      
      CASH_SHOWNDS :boolean = true; // отображать ли НДС
      CASH_SHOWNDSSUMMARY :boolean = false;//посылать сумму НДС на аппарат (поддерживается не всеми аппаратами, теми, что не поддерживают, игнорируется, похоже)
      CASH_NDS0TYPE :integer = 6; //LIBFPTR_TAX_VAT0 // тип НДС0
      CASH_NDS10TYPE :integer = 2; //LIBFPTR_TAX_VAT10
      CASH_NDS18TYPE :integer = 7; //LIBFPTR_TAX_VAT18
      CASH_NDS20TYPE :integer = 7; //LIBFPTR_TAX_VAT20
      CASH_CANDIVIDEDIVNUM :boolean = true; // можно ли делить количество и цену для фасованных товаров (приводить к фасованным единицам и ценам)
      CASH_CANFLOATQUANTITY :boolean = false; // допустимо ли дробное количество (не касается товаров, помеченных как весовые)
      
      //CASH_SESSIONREGISTER :integer = 53; // 21 или 27, или 28, или 53 // раньше использовал 21, но потом стал давать 0

      CASH_SHOWADVANCEDERRORTEXT :boolean = true; // выводить ли в cash_checkerror дополнительный хинт с ошибкой
      CASH_PRINTCLIENT :boolean = false; // выводить ли имя и инн покупателя (атрибуты 1227 и 1228)
      CASH_SAVEMARKS :integer = 3;  // сохранять ли марку в кассе
      CASH_CHECKDATEENDFN :boolean = true; // проверять ли срок действия ФН
      CASH_CHECKDATETIME  :boolean = true; // проверять ли дату-время ККМ
      
      CASH_MAXMINUTESDIFF :integer = 10; // максимальное расхождение в минутах между временем ПК и временем кассы, после превышения которого будет выдано предупрежденеие

      Cash_Logging :boolean = true; // Сохранять лог операции своими средствами (для тестирования)
    //CashDRVAdvLogging = false; // включить дополнительное логгирование, которой ведет драйвер АТОЛ  см. %APPDATA%/ATOL/drivers10/fptr10_log.properties
    
      CASH_NODISCONNECT :boolean = true; // Производить ли дисконнект по требованию
      CASH_PRINTM :boolean = false; // печатать ли символ [М] в наименовании


const       
      FNN = 'NN';       
      FName = 'name';
      FMARKS_BARCODES = 'MARKS_BARCODES';
      FQuantity = 'kol';
      FPrice = 'cena';
      FPriceOut = 'prodcena';
      FPriceOut1 = 'rs_cena';
      FSection = 'section';
      FNDS = 'NDS';
      FSUMMARY = 'SUMMA';
      FFloatBarcode = 'FloatBarcode';
      FDivNum = 'DivNum';

{

var
    CardsPrintCutLineLength :integer; // сколько надо пропустить, чтобы линия отреза была там, где надо
    CardsPrintCutLine :boolean; // Надо ли резать (некоторые аппараты не поддерживают. по-умолчанию - true
    CardsPrintCutLineSleep :integer; // на месте отреза сделать слип, в секундах
}    


var
  IsInvoice :boolean = false; // true - идет печать из накладной
  DATASET :TDataset = nil; // датасет с товарами. Набор полей смотрите в чеке розницы
  DatasetMarks :tDataset = nil;
  Summary :Currency = 0; // сумма по чеку
  PaySummary :Currency = 0; // Внесено денег
  DiscountSummary :currency = 0; // сумма надбавки/скидки
  Discount   :double = 0; // процент надбавки/скидки
  DiscountIsLiveneed   :double = 0; // процент надбавки/скидки для ЖНВЛС
  DiscountMG   :double = 0; // процент надбавки/скидки от наценки
  DiscountIsLiveneedMG   :double = 0; // процент надбавки/скидки от наценки для ЖНВЛС
  DiscountSummary :double = 0; // сумма надбавки/скидки
  ClientName :string = ''; // имя контрагента
  ClientID :integer = 0; // код контрагента
  AddInfo :string = ''; // доп. инфо к контрагенту
  Doc_Reason :string = ''; // код основания в чеке
  Doc_Reason_Name :string = ''; // наименование основания в чеке
  Doc_Reason_FILTERED :string = ''; // код основания, по которому отфильтрован чек
  Doc_Reason_FILTERED_Name :string = ''; // наименование отфильтрованного основания в чеке
  DOC_TYPE_CLOSE :integer = 0; //
  SummaNotNullField :double = 0; // Сумма строк с ненулевым полем, установленным согласно настройкам
  EMAIL :string = ''; // телефон или емейл покупателя, для онлайн-касс
  user_name :string = ''; // имя кассира
  user_id :integer = 0; // ID кассира  
  IsReturn :boolean = false; // Это чек возврата, используется при пробитии чека
  Number_DOC :integer = 0; // Номер документа
  
  Storage_ID :integer = 0; // Номер склада

  VarActionNal :boolean = false;
  VarActionBN :boolean = false;
  VarActionKredit :boolean = false;
  VarActionSert :boolean = false;

  VarActionDiskont :boolean = false;
  VarActionDiskont1 :boolean = false;
  VarActionPension :boolean = false;
  VarActionVIP :boolean  = false;
  VarActionInsurance :boolean  = false;
  

  CurrentSection :integer =0; // секция для всего товара
  
  VarDevice :Variant = null; // устройство печати. Если не указано, то определять самостоятельно. Может быть использовано приложением для получения от этого скрипта и передачи другим скриптам

  ResultSerialNumber :string = '';   // серийный номер, возвращаемый процедурой Cash_GetSerialNumber и Cash_execute (если этого требуют настройки программы)  
  ResultSessionNumber :string = '';  // номер сессии, возвращаемый процедурой Cash_GetSessionNumber и Cash_execute (если этого требуют настройки программы)
  ResultDocumentNumber :integer = 0; // номер следующего документа  

  ResultRegisterListNames :TStrings = nil; 
  ResultRegisterListValues :TSTrings = nil;  

  TaxType :integer = 0;

 
var
  // Возвращаемые параметры, могут быть использованы вызывающим скриптом или программой  
  ResultCode : integer =0; // результат, код ошибки, если не 0
  // Переменная ResultDescription возвращает текст кода Result
  ResultDescription :string = '';
  ResultCodeExtended :integer = 0; // расширенная ошибка согласно стандарту OPOS

procedure Cash_SaveLog(StrValue :string; LogAlways :boolean = false);
begin
  if Cash_Logging or LogAlways then
  with TLogFile.create('cashatol.log',false,10000000) do
  try
    AddStr(StrValue);
  finally
    Free;
  end;
end;
function Cash_CheckError(FuncName :string; NeedExcept :boolean = true) :boolean; // Проверка ошибки драйвера. Вылетаем на exception, если была ошибка
var strval :string;
begin
  ResultCode := VarDevice.ErrorCode;
  ResultCodeExtended := 0;
  ResultDescription := VarDevice.ErrorDescription;
  result := resultcode=0;
  if CASH_SHOWADVANCEDERRORTEXT then
  begin
    if (ResultCode in [1,2,4]) then  CreateHintW('Проверьте, включена ли касса','Рекомендации по ошибке ('+inttostr(resultcode)+': '+resultdescription+')')
    else if (resultcode = 82) then createhintw('Чек открыт: Попробуйте провести операцию отмены зависшего чека');
  end;
  
  if (ResultCode<>0) then
  begin
    strval := FuncName+': ошибка: ' + inttostr(resultcode) + ': ' + resultDescription; 
    cash_savelog(strval,true);
    if needexcept then raiseException(strval);
  end;
end;
  
  
function CASH_GET_DATETIME {(NeedConnect :boolean = true)}:tdatetime;  
begin
  //if NeedConnect then  Cash_Connect;
  try
    VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_DATE_TIME); 
    VarDevice.queryData; cash_checkerror('CASH_GET_DATETIME:QueryData');
    Result := VarDevice.getParamDateTime(VarDevice.LIBFPTR_PARAM_DATE_TIME); cash_checkerror('CASH_GET_DATETIME:1');
  finally
    //if NeedConnect then Cash_disconnect;
  end;
end;

function CASH_GET_FN_LAST_DATETIME {(NeedConnect :boolean = true)}:tdatetime;  
begin
  //if NeedConnect then  Cash_Connect;
  try
    VarDevice.setParam(VarDevice.LIBFPTR_PARAM_FN_DATA_TYPE, VarDevice.LIBFPTR_FNDT_VALIDITY);
    VarDevice.fnqueryData; cash_checkerror('CASH_GET_FN_LAST_DATETIME:fnQueryData'); 
    Result := asfloat(VarDevice.getparamdatetime(VarDevice.LIBFPTR_PARAM_DATE_TIME)); cash_checkerror('CASH_GET_FN_LAST_DATETIME:1');
  finally
    //if NeedConnect then Cash_disconnect;
  end;  
end; 


  
procedure Cash_InitVars;
begin
   CASH_DeviceName :=  appinifile.readstring('advanced','CASH_DEVICENAME',dbinifile.readstring('advanced','CASH_DEVICENAME',CASH_DEVICENAME));

   CASH_KKMSYSPASSWORD := Appinifile.readstring('advanced','CASH_KKMSYSPASSWORD',DBINIFILE.readstring('advanced','CASH_KKMSYSPASSWORD',appinifile.readstring('common','KKMSYSPASSWORD',CASH_KKMSYSPASSWORD)));
   CASH_STDUSERPASSWORD := Appinifile.readstring('advanced','CASH_STDUSERPASSWORD',DBINIFILE.readstring('advanced','CASH_STDUSERPASSWORD',Appinifile.readstring('common','KKMUSERPASSWORD',CASH_STDUSERPASSWORD)));
   CASH_DEFAULT_USERNAME := Appinifile.readstring('advanced','CASH_DEFAULT_USERNAME',DBINIFILE.readstring('advanced','CASH_DEFAULT_USERNAME',CASH_DEFAULT_USERNAME));
   CASH_DEFAULT_USERINN := Appinifile.readstring('advanced','CASH_DEFAULT_USERINN',DBINIFILE.readstring('advanced','CASH_DEFAULT_USERINN',CASH_DEFAULT_USERINN));
   
   CASH_DiscountType  := appinifile.readinteger('advanced','CASH_DISCOUNTTYPE',dbinifile.readinteger('advanced','CASH_DISCOUNTTYPE',CASH_DiscountType));
   CASH_PrintStringDiscountSummary := appinifile.readbool('advanced','CASH_PrintStringDiscountSummary',dbinifile.readbool('advanced','CASH_PrintStringDiscountSummary',CASH_PrintStringDiscountSummary));   
   CASH_PrintStringPriceOut := appinifile.readbool('advanced','CASH_PrintStringPriceOut',dbinifile.readbool('advanced','CASH_PrintStringPriceOut',CASH_PrintStringPriceOut));
   CASH_PrintStringDiscountOnCheck := appinifile.readbool('advanced','CASH_PrintStringDiscountOnCheck',dbinifile.readbool('advanced','CASH_PrintStringDiscountOnCheck',CASH_PrintStringDiscountOnCheck));  
   CASH_NameBadCharList := appinifile.readstring('advanced','CASH_NameBadCharList',dbinifile.readstring('advanced','CASH_NameBadCharList',CASH_NameBadCharList));
   CASH_NameReplaceCharList  := appinifile.readstring('advanced','CASH_NameReplaceCharList',dbinifile.readstring('advanced','CASH_NameReplaceCharList',CASH_NameReplaceCharList));   
   CASH_TypeCloseNal := appinifile.readinteger('advanced','CASH_TypeCloseNal',dbinifile.readinteger('advanced','CASH_TypeCloseNal',CASH_TypeCloseNal));
   CASH_TypeCloseBN := appinifile.readinteger('advanced','CASH_TypeCloseBN',dbinifile.readinteger('advanced','CASH_TypeCloseBN',CASH_TypeCloseBN));
   CASH_TypeCloseKredit := appinifile.readinteger('advanced','CASH_TypeCloseKredit',dbinifile.readinteger('advanced','CASH_TypeCloseKredit',CASH_TypeCloseKredit));
   CASH_TypeCloseSert := appinifile.readinteger('advanced','CASH_TypeCloseSert',dbinifile.readinteger('advanced','CASH_TypeCloseSert',CASH_TypeCloseSert));
   CASH_CutType := appinifile.readinteger('advanced','CASH_CutType',dbinifile.readinteger('advanced','CASH_CutType',CASH_CutType));
   CASH_TrimNameGoods := appinifile.readbool('advanced','CASH_TrimNameGoods',dbinifile.readbool('advanced','CASH_TrimNameGoods',CASH_TrimNameGoods));
   CASH_StringLength := appinifile.readinteger('advanced','CASH_StringLength',dbinifile.readinteger('advanced','CASH_StringLength',CASH_StringLength));
   CASH_OPENDRAWER_NAL := appinifile.readbool('advanced','CASH_OPENDRAWER_NAL',dbinifile.readbool('advanced','CASH_OPENDRAWER_NAL',CASH_OPENDRAWER_NAL));
   CASH_Protocol3 := appinifile.readbool('advanced','CASH_Protocol2',dbinifile.readbool('advanced','CASH_Protocol3',CASH_Protocol3));
   CASH_SHOWNDS := appinifile.readbool('advanced','CASH_SHOWNDS',dbinifile.readbool('advanced','CASH_SHOWNDS',CASH_SHOWNDS));
   CASH_SHOWNDSSUMMARY := appinifile.readbool('advanced','CASH_SHOWNDSSUMMARY',dbinifile.readbool('advanced','CASH_SHOWNDSSUMMARY',CASH_SHOWNDSSUMMARY));
   CASH_NDS0TYPE := appinifile.readinteger('advanced','CASH_NDS0TYPE',dbinifile.readinteger('advanced','CASH_NDS0TYPE',CASH_NDS0TYPE));
   CASH_NDS10TYPE := appinifile.readinteger('advanced','CASH_NDS10TYPE',dbinifile.readinteger('advanced','CASH_NDS10TYPE',CASH_NDS10TYPE));
   CASH_NDS18TYPE := appinifile.readinteger('advanced','CASH_NDS18TYPE',dbinifile.readinteger('advanced','CASH_NDS18TYPE',CASH_NDS18TYPE));
   CASH_NDS20TYPE := appinifile.readinteger('advanced','CASH_NDS20TYPE',dbinifile.readinteger('advanced','CASH_NDS20TYPE',CASH_NDS20TYPE));
   CASH_CANDIVIDEDIVNUM := appinifile.readbool('advanced','CASH_CANDIVIDEDIVNUM',dbinifile.readbool('advanced','CASH_CANDIVIDEDIVNUM',CASH_CANDIVIDEDIVNUM));
   CASH_CANFLOATQUANTITY := appinifile.readbool('advanced','CASH_CANFLOATQUANTITY',dbinifile.readbool('advanced','CASH_CANFLOATQUANTITY',CASH_CANFLOATQUANTITY));
   //CASH_USERNAME_MANUAL := appinifile.readbool('advanced','CASH_USERNAME_MANUAL',dbinifile.readbool('advanced','CASH_USERNAME_MANUAL',CASH_USERNAME_MANUAL));
   //CASH_SESSIONREGISTER  := appinifile.readinteger('advanced','CASH_SESSIONREGISTER',dbinifile.readinteger('advanced','CASH_SESSIONREGISTER',CASH_SESSIONREGISTER));
   CASH_SHOWADVANCEDERRORTEXT := appinifile.readbool('advanced','CASH_SHOWADVANCEDERRORTEXT',dbinifile.readbool('advanced','CASH_SHOWADVANCEDERRORTEXT',CASH_SHOWADVANCEDERRORTEXT));
   CASH_PRINTCLIENT := appinifile.readbool('advanced','CASH_PRINTCLIENT',dbinifile.readbool('advanced','CASH_PRINTCLIENT',CASH_PRINTCLIENT));
   //CASH_SAVEMARKSTOBACCO := appinifile.readinteger('advanced','CASH_SAVEMARKSTOBACCO',dbinifile.readinteger('advanced','CASH_SAVEMARKSTOBACCO',CASH_SAVEMARKSTOBACCO));
   //CASH_SAVEMARKSMED := appinifile.readinteger('advanced','CASH_SAVEMARKSMED',dbinifile.readinteger('advanced','CASH_SAVEMARKSMED',CASH_SAVEMARKSMED));
   CASH_SAVEMARKS := appinifile.readinteger('advanced','CASH_SAVEMARKS',dbinifile.readinteger('advanced','CASH_SAVEMARKS',CASH_SAVEMARKS));
   CASH_CHECKDATEENDFN := appinifile.readbool('advanced','CASH_CHECKDATEENDFN',dbinifile.readbool('advanced','CASH_CHECKDATEENDFN',CASH_CHECKDATEENDFN));
   CASH_CHECKDATETIME := appinifile.readbool('advanced','CASH_CHECKDATETIME',dbinifile.readbool('advanced','CASH_CHECKDATETIME',CASH_CHECKDATETIME));
   CASH_MAXMINUTESDIFF := appinifile.readinteger('advanced','CASH_MAXMINUTESDIFF',dbinifile.readinteger('advanced','CASH_MAXMINUTESDIFF',CASH_MAXMINUTESDIFF));
   CASH_Logging := appinifile.readbool('advanced','CASH_LOGGING',dbinifile.readbool('advanced','CASH_LOGGING',CASH_LOGGING));
   CASH_NODISCONNECT := appinifile.readbool('advanced','CASH_NODISCONNECT',dbinifile.readbool('advanced','CASH_NODISCONNECT',CASH_NODISCONNECT));
   CASH_PRINTM := appinifile.readbool('advanced','CASH_PRINTM',dbinifile.readbool('advanced','CASH_PRINTM',CASH_PRINTM));

   
end;



// поднять любую настройку из кассы (по её номеру, см. в приложении к АТОЛ "Настройки ККТ"). Требуется подключение к кассе.
function Cash_GetSettingValue(SettingNumber :integer) :string; 
begin
  VarDevice.setParam(VarDevice.LIBFPTR_PARAM_SETTING_ID, SettingNumber); cash_checkerror('Cash_GetSettingValue:1');
  VarDevice.readDeviceSetting; cash_checkerror('Cash_GetSettingValue:ReadDeviceSettings');
  result := VarDevice.GetParamString(VarDevice.LIBFPTR_PARAM_SETTING_VALUE); Cash_checkerror('Cash_GetSettingValue:2');
end; 

// поднять из нашей локальной инишки настройки драйвера 
// (основные настройки, отображающиеся на первой странице в настройках драйвера),
// не хранятся в кассе, требуется их восстановление и сохранение, 
// подключение к кассе не требуется.
procedure cash_RestoreSettings;  
var StrValue :string;
    FName :string;
begin
  //StrValue:= appinifile.ReadString('ATOL','SETTINGS','');
  FName := getappLocalDataFolder+'atol10.cfg';
  if fileexists(fName) then StrValue := FileToString(FName) else StrVAlue := '';
  if strvalue>'' then
  begin
    //strvalue := stringreplace(strvalue,'<-CR->',#13,true,true);
    //strvalue := stringreplace(strvalue,'<-LF->',#10,true,true);
    VarDevice.SetSettings(StrValue); cash_checkerror('cash_RestoreSettings:SetSettings');
    
    if CASH_KKMSYSPASSWORD>'' then
    begin
      StrValue := VarDevice.getSingleSetting(VarDevice.LIBFPTR_SETTING_ACCESS_PASSWORD);
      if StrValue<>CASH_KKMSYSPASSWORD then VarDevice.setSingleSetting(VarDevice.LIBFPTR_SETTING_ACCESS_PASSWORD, CASH_KKMSYSPASSWORD);
      VarDevice.applySingleSettings;    
    end;
    if CASH_STDUSERPASSWORD>'' then
    begin
      StrValue := VarDevice.getSingleSetting(VarDevice.LIBFPTR_SETTING_USER_PASSWORD);
      if StrValue<>CASH_STDUSERPASSWORD then VarDevice.setSingleSetting(VarDevice.LIBFPTR_SETTING_USER_PASSWORD, CASH_STDUSERPASSWORD);
      VarDevice.applySingleSettings;    
    end;
  end;
end;

// сохранить настройки подключения для драйвера в нашей локальной инишке
// подключение к кассе не требуется, эти настройки в кассе не хранятся
procedure cash_SaveSettings;
var StrValue :string;
begin
  StrValue := VarDevice.GetSettings;
  cash_checkerror('cash_savesettings:getsettings');
  StringToFile(getappLocalDataFolder+'atol10.cfg',StrValue);
  {
  strvalue := stringreplace(strvalue,#13,'<-CR->',true,true);
  strvalue := stringreplace(strvalue,#10,'<-LF->',true,true);
  appinifile.WriteString('ATOL','SETTINGS',strvalue);}
end;

procedure Cash_CheckDevice;
begin
  if varIsNull(VarDevice) then 
  begin
    VarDevice := CreateOleObject(CASH_DeviceName); 
    //VarDevice.ApplicationHandle := Application.Handle;
    Cash_RestoreSettings; 
  end;
end;


procedure Cash_Connect(NeedLogin :boolean = true);
var strvalue,StrValue1,strvalue2 :string;
    boolval :boolean;
    CashDefaultDevice :integer;
    dt :tdatetime;
    intval :int64;
begin
  Cash_CheckDevice;
  
  //Boolval := VarDevice.isopened; cash_checkerror;
  //if not Boolval then 
  begin 
    VarDevice.open; Cash_CheckError('cash_connect:open');
  end;

  if NeedLogin then // если всегда прописывать юзера, то при зависшем чеке получим ошибку программирования параметра 1021
  begin
  
    if Cash_DEFAULT_USERNAME>'' then
    begin
      StrValue := CASH_DEFAULT_USERNAME;
      StrValue1 := CASH_DEFAULT_USERINN; 
    end
    else begin
      StrValue := UserProgram;
      StrValue1 := UserProgramINN;
    end; 
    
    //strvalue2 := VarDevice.GetParamString(1021); cash_checkerror; возвращается пустая строка, функция бесполезна 
    //showmessage(strvalue2);
    //if StrValue2<>StrValue then 
    begin  
      VarDevice.setParam(1021,StrVAlue); Cash_CheckError('cash_connect:1021');
    end;
  
    if StrValue1>'' then 
    begin
      //strvalue2 := VarDevice.GetParamString(1203); cash_checkerror;
      //showmessage(strvalue2);
      //if strvalue2<>StrVAlue1 then
      begin
        VarDevice.setParam(1203,StrVAlue1); Cash_CheckError('cash_connect:1203');
      end;
    end;
  
    VarDevice.OperatorLogin; Cash_CheckError('cash_connect:OperatorLogin');
  end;

  if CASH_CHECKDATEENDFN then 
  begin
    if  appinifile.readdate('Atol10','date_check_fn',0) <>date then
    try
      DT := CASH_GET_FN_LAST_DATETIME;
      if DT <=0 then bvmessageerror('Срок действия ФН закончился '+DateToStr(DT)+'. Сообщите в бухгалтерию!')
      else if DT-date <=30  then bvmessagewarning('ФН заканчивается '+DateToStr(DT)+', через '+inttostr(daysbetween(DT,date))+' дней'+'.','Сообщите в бухгалтерию!');

      Appinifile.writedate('Atol10','date_check_fn',date)
    except
      createhinte(exceptmessage,'Не могу проверить срок действия ФН на кассе');    
    end;
  end;

  if CASH_CHECKDATETIME then 
  begin
    if  appinifile.readdate('Atol10','date_check_dt',0)<>date then
    try
      dt := CASH_GET_DATETIME;
      intval := minutesbetween(dt,now);
      if intval>CASH_MAXMINUTESDIFF then CreatehintW ('Время в кассе и на компьтере сильно расходится: время компьютера: '+formatdatetime('dd/mm/yyyy hh:nn',now)+', время в кассе: '+formatdatetime('dd/mm/yyyy hh:nn',dt),'Надо исправить время в кассе.');

      Appinifile.writedate('Atol10','date_check_dt',date)
    except
      createhinte(exceptmessage,'Не могу проверить текущие дату и время на кассе');    
    end;
  end;
  
end;

procedure Cash_disconnect;
begin
  cash_savelog('cash_disconnect');
  if VarDevice.isopened  and not CASH_NODISCONNECT then 
  begin
    cash_savelog('disconnected');
    VarDevice.Close; cash_checkerror('cash_disconnect',false);
  end;
end;

procedure Cash_GetNumbers(NeedConnect :boolean = true);
begin
  if needConnect then Cash_Connect;
  try
    VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_STATUS); Cash_CheckError('cash_GetNumbers:1');
    VarDevice.queryData; Cash_CheckError('cash_GetNumbers:2');
  
    resultSerialNumber := VarDevice.GetParamString(VarDevice.LIBFPTR_PARAM_SERIAL_NUMBER); Cash_CheckError('cash_GetNumbers:3');
    ResultSessionNumber := VarDevice.GetParamString(VarDevice.LIBFPTR_PARAM_SHIFT_NUMBER); Cash_CheckError('cash_GetNumbers:4');
    ResultDocumentNumber := VarDevice.GetParamInt(VarDevice.LIBFPTR_PARAM_DOCUMENT_NUMBER { или VarDevice.LIBFPTR_PARAM_RECEIPT_NUMBER?}); Cash_CheckError('cash_GetNumbers:5'); 
  finally  
    if NeedConnect then Cash_disconnect;
  end;
end;

procedure Cash_Cut(NeedConnect :boolean = true);
begin
    if CASH_CutType=0 then exit; // нет отрезки
    
    if NeedConnect then Cash_Connect;
    try
      if CASH_CutType=1 then VarDevice.LIBFPTR_PARAM_CUT_TYPE :=VarDevice.LIBFPTR_CT_FULL   //0- не отрезать (не контролировать); 1-полная отрезка; 2-неполная отрезка
      else VarDevice.LIBFPTR_PARAM_CUT_TYPE :=VarDevice.LIBFPTR_CT_PART;
      Cash_CheckError('cash_cut:1');
      
      VarDevice.cut; Cash_CheckError('cash_cut:2');
    finally
      if NeedConnect then Cash_disconnect;
    end;
end;

procedure Cash_OpenDrawer(NeedConnect :boolean = true);
begin
  if NeedConnect then Cash_Connect;
  try
    VarDevice.OpenDrawer; Cash_CheckError('cash_openDrawer');
  finally
    if NeedConnect then Cash_disconnect;
  end;
end;

procedure Cash_Execute(NeedConnect :boolean = true);
var i :integer;
    pSummaryCash,pSummaryDiscount :currency;
    pSummary,pSummaryOut :currency;
    pDiscount :currency;
    pPrice,pPriceOut :currency;
    pPriceOutDivNum :currency;
    pPriceDivNum :currency;
    pQuantity :Double;
    pQNTFrac :double;
    pQuantityDivNum :double;
    pDivNum :integer;
    IsFloatBarcode :boolean;

    PNN :string;
    pName :string;
    pmarks_barcodes :string;
    DetailCash :integer;
    Index :integer;
    Summ2 :currency;
    StrValue,StrVAlue1,StrValue2 :string;
    EmailSeller :string;
    stringlength :integer;
    NDS :integer;
    thField :TField;
    IntValue :integer;
    INN :string;
    MARK_BC,MARK_NUM :string;
    MarkData :variant;
    ByteVal :byte;
    
    BATCH,EXPDATE :string;
    
    MDLP_STORAGE_ID :string;
    
    MDLP_COUNT :integer;
    
    VarValue :variant;
    strQNT :string;
begin
   Cash_SaveLog('cash_execute');
   DetailCash := Appinifile.readinteger('common','DetailCash',dbinifile.readinteger('common','DetailCash',0));  //это стандартная настройка на закладке "розница" 

       
   MDLP_STORAGE_ID := asstring(BDEQueryValue('select mdlp_storage_id from agents where id = :id',[STORAGE_ID]));

   MDLP_COUNT := 0;       
   // если mdlp_storage_id пуст, то проверим сразу наличие товаров с марками и остановим скрипт, если имеются таковые
   if (CASH_SAVEMARKS<>0) then
   begin
     dataset.disablecontrols;
     try
       dataset.first;
       while not dataset.eof do
       begin
         if not NNInFilterEGAIS(dataset.fieldbyname('nn').asstring) // егаисовские товары нас не интересуют 
            and not NNInFilterEGAISBEER(dataset.fieldbyname('nn').asstring)
            and not NNInFilterTOBACCO(dataset.fieldbyname('nn').asstring)
         then begin        
           if dataset.findfield(fmarks_barcodes)<>nil then pmarks_barcodes := dataset.fieldbyname(fmarks_barcodes).asstring 
           else if (DataSetMarks<>nil) and  datasetmarks.locate('idprimary',dataset.fieldbyname('idprimary').asstring,0) 
           then pmarks_barcodes := datasetmarks.fieldbyname('barcode').asstring
           else pMarks_barcodes := '';
           
           if pmarks_barcodes>'' then inc(MDLP_COUNT); 
         end;
         dataset.next;
       end;      
       dataset.first;
     finally
       dataset.enablecontrols;
     end;
     
     if (mdlp_storage_id='') and (MDLP_COUNT>0) then 
     begin  
       Cash_SaveLog('Отмена чека: имеются товары для МДЛП, но у склада не указан его код в МДЛП',true);
       raiseexception('Имеются товары для МДЛП, но у склада не указан его код в МДЛП');
     end;
   end;
   
   if needconnect then Cash_Connect;
   try
       Cash_GetNumbers(false);

       if CASH_stringlength <0 then 
       begin
         VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_RECEIPT_LINE_LENGTH); Cash_CheckError('Cash_Execute:LineLength1');
         VarDevice.queryData; cash_checkerror('Cash_Execute:LineLength2');
         StringLength  := VarDevice.getParamInt(VarDevice.LIBFPTR_PARAM_RECEIPT_LINE_LENGTH);
         Cash_CheckError('Cash_Execute:LineLength3');
       end;

       if (CASH_SAVEMARKS<>0) and (MDLP_COUNT>0) then
       begin
         VarDevice.SetParam(1085,'mdlp');
         Cash_CheckError('Cash_Execute:1085');
         VarDevice.SetParam(1086,'sid'+mdlp_storage_id+'&');
         Cash_CheckError('Cash_Execute:1086');
         VarDevice.utilFormTlv;
         Cash_CheckError('Cash_Execute:1084:utilFormTlv');
         VarValue := VarDevice.getParamByteArray(VarDevice.LIBFPTR_PARAM_TAG_VALUE);
         Cash_CheckError('Cash_Execute:1084:LIBFPTR_PARAM_TAG_VALUE');
       end;
       

       if IsReturn then VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL_Return) 
       else VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL);
       Cash_CheckError('Cash_Execute:SetReceiptType');

       if (CASH_SAVEMARKS<>0) and (MDLP_COUNT>0) then
       begin
         VarDevice.setNonPrintableParam(1084, VarValue);         
         Cash_CheckError('Cash_Execute:1084');
       end;
       


       if Email<>'' then 
       begin
         VarDevice.setParam(1008, EMail); 
         Cash_CheckError('Cash_Execute:1008'); 
       end;

       EmailSeller := appIniFile.readstring('cash','CashOnlineEmailSeller',DBIniFile.readstring('cash','CashOnlineEmailSeller',''));
       if EmailSEller<>'' then // адрес электронной почты отправителя чека 
       begin
          //Отправка данных о продавце
         VarDevice.setParam(1117, EMailSeller); 
         Cash_CheckError('Cash_Execute:1117'); 
       end;
       
       if CASH_PRINTCLIENT and (Clientid<>0) then
       begin
         StrValue := asstring(BDEQueryValue('select name from agents where id = :id',[Clientid]));
         if strvalue>'' then
         begin 
           //Отправка имени покупателя
           VarDevice.setParam(1227, StrVAlue); 
           Cash_CheckError('Cash_Execute:1227'); 
         end; 
         StrValue := asstring(BDEQueryValue('select inn from agents where id = :id',[Clientid]));
         if strvalue>'' then
         begin 
           //Отправка инн покупателя
           VarDevice.setParam(1228, EMailSeller); 
           Cash_CheckError('Cash_Execute:1228'); 
         end; 
       end;

       case DOC_TYPE_CLOSE of
         1 : Index := CASH_TypeCloseBN;
         2 : Index := CASH_TypeCloseKredit;
         3 : Index := CASH_TypeCloseSert;
         else  Index := 0;
       end;

       VarDevice.setParam(VarDevice.LIBFPTR_PARAM_PAYMENT_TYPE, Index); cash_checkerror('Cash_Execute:CAsh_typeClose');
       
       //fptr.setParam(fptr.LIBFPTR_PARAM_RECEIPT_ELECTRONICALLY, True);


       if Taxtype>0 then 
       begin 
	     Cash_SaveLog('Cash_Execute:1055:TaxType='+inttostr(TaxType));
         //Cash_CheckError('Cash_Execute:1055:TaxType='+inttostr(TaxType));
         VarDevice.SetParam(1055,TaxType); //Применяемая система налогообложения (СНО)
         Cash_CheckError('Cash_Execute:1055');
       end;
   

       cash_savelog('OpenReceipt');

       VarDevice.openReceipt; Cash_CheckError('Cash_Execute:OpenReceipt');
       
       //raiseexception('test');

       // печать чека

       cash_savelog('Товары');
       dataset.disablecontrols;
       try
         pSummaryCash := 0;
         pSummaryDiscount := 0;      // для варианта без детализации

         Dataset.First;
         while not Dataset.eof do
         begin
           pPrice := Dataset.fieldbyname(FPrice).asCurrency;
           cash_savelog('Цена:'+floattostr(pprice));
           thField := dataset.findfield(fPriceOut);
           if thField=nil then thField := dataset.findfield(fpriceout1);
           if thfield=nil then pPriceOut := 0 else  pPriceOut := thField.ascurrency;

           if pPriceOut < pPrice then  pPriceOut := pPrice;
           cash_savelog('Цена прод:'+floattostr(ppriceout));
 
           pQuantity := dataset.fieldbyname(FQuantity).AsFloat;
           pQntFrac := bvroundto(frac(pQuantity),-6);
           pDivNum := coalesce_integer(dataset.fieldbyname(FDivNum).asinteger,1);
           pQuantityDivNum := bvroundto( pQuantity * pDivNum,-3); // 4-6 знаки оставим несущественными, число 2.999997 в фасовке еще допускается, округлим
           pPriceOutDivNum := bvroundto( pPriceOut / pDivNum,-4);
           pPriceDivNum := bvroundto( pPrice / pDivNum,-4);
           cash_savelog('Количество: '+floattostr(pQuantity)+' : '+floattostr(ppricedivnum));
           

           IsFloatBarcode := dataset.fieldbyname(FFloatBarcode).asinteger<>0; 

           //cash_savelog('Параметры: '+asstring(DetailCash)+' : '+asstring(IsFloatBarcode)+' : '+asstring(pQNTFrac)+' : '+asstring(CASH_CANDIVIDEDIVNUM)+' : '+asstring(pdivnum)+' : '+asstring(pQuantityDivNUm)+' : '+asstring(pPriceOutDivNUm));

           if pQntFrac<>0 then // есть дробное количество
           begin
             if (DetailCash=0) // построчная детализация
                and CASH_CANDIVIDEDIVNUM // можно делить при печати фасованные товары 
                and not IsFloatBarcode // товар не является весовым 
                and (pDivNum<>1) // товар делится
             then begin // надо вывести целое количество фасованных единиц по цене фасованной единицы
               pPriceOut := pPriceOutDivNum;
               pPrice := pPriceDivNUm;
               pQuantity := bvroundto(pQuantityDivNum);
               cash_savelog('Фас.кол-во x цена за фасов.ед.: '+floattostr(pQuantity)+' : '+floattostr(pprice)+' : '+floattostr(ppriceout)); 
             end
             else if 
               (DetailCash<>0) // печать без детализации 
               or not CASH_CANFLOATQUANTITY // или нельзя дробные количества
               or IsFloatBarcode and (pQntFrac<>bvroundto(pQntFrac,-3)) // Для весовых - предохранимся от количеств, которые не понимает касса, оставляем 1xСумма
               //or (pQuantityDivNum<>bvroundto(pQuantityDivNum)) // число 2.999997 в фасовке еще допускается, но 2.999 - уже странно
               //or (pPriceOutDivNum <> bvRoundTo(pPriceOutDivNum,-2))
             then begin // необходимо распечатать 1 штуку по цене, равной полной сумме
               pPriceOut := pQuantity * pPriceOut;
               pPrice := pQuantity * pPrice;
               pQuantity := 1;
               cash_savelog('1 x Сумма: '+floattostr(pQuantity)+' : '+floattostr(pprice)+' : '+floattostr(ppriceout));
             end;
           end;
           
           pPrice := bvroundto(pPrice, -2,DefRoundParam);
           if pprice<>0 then  pPriceOut := bvroundto(pPriceOut, -2,DefRoundParam) else ppriceout := 0; // нулевая цена, значит и скидку нам не надо отображать
           

           pSummary := pQuantity * pPrice;
           pSummaryOut := pQuantity * pPriceOut;

           pSummaryCash := pSummaryCash + pSummary;
           pSummaryDiscount := pSummaryDiscount + (pSummaryOut - pSummary);
           pDiscount := bvRoundTo(pSummaryOut-pSummary,-2);
           
           cash_savelog('Суммы: '+floattostr(pSummary)+' : '+floattostr(pSummaryOut)+' : '+floattostr(pDiscount));

           if pquantity>0 //  (bvroundto(pSummary, -2) > 0) // возможна печать нулевых цен, поэтому проверку делаем только по количеству
           then begin

             pname := dataset.fieldbyname(FName).asstring;
             if (stringlength>0) and cash_trimnamegoods then pname := copy(pname,1,stringlength);
             
             PNN := dataset.fieldbyname(FNN).asstring;

             cash_savelog('Наименование: '+pname+', '+PNN);

             if dataset.findfield(fmarks_barcodes)<>nil then pmarks_barcodes := dataset.fieldbyname(fmarks_barcodes).asstring 
             else if (DataSetMarks<>nil) and  datasetmarks.locate('idprimary',dataset.fieldbyname('idprimary').asstring,0) 
             then pmarks_barcodes := datasetmarks.fieldbyname('barcode').asstring
             else pMarks_barcodes := '';
             
             cash_savelog('Марка: '+pmarks_barcodes);
             
             
             for i := 1 to min(length(CASH_NameBadCharList), length(CASH_NameReplaceCharList)) do 
               if pos(CASH_NameBadCharList[i], pname) > 0
                 then pname := stringreplace(pname, CASH_NameBadCharList[i], CASH_NameReplaceCharList[i],true,true);

             cash_savelog('Наименование1: '+pname);

             if detailcash <> 2
             then begin                 // детализация чека
               
               cash_savelog('cash_savemarks='+inttostr(cash_savemarks));
               
               if (CASH_SAVEMARKS<>0) and (pmarks_barcodes>'')
                  and not NNInFilterEGAIS(pNN) and not NNInFilterEGAISBEER(pNN) 
               then begin
                 cash_savelog('mark='+pmarks_barcodes);

                 if NNInFilterTOBACCO(pNN) then
                 begin
                 
                    cash_savelog('mark_tobacco='+pmarks_barcodes);
                    Mark_BC := copy(pmarks_barcodes,1,14);
                    if (length(MARK_BC)<>14) or (StrToInt64Protected(MARK_BC)=0) then raiseexception('Неверный код марки для табачной продукции (штрихкод)');
                    cash_savelog('mark_bc_tobacco='+mark_bc);
                    
                    MARK_NUM := copy(pmarks_barcodes,15,7);
                    if length(mark_num)<>7 then raiseexception('Неверный код марки для табачной продукции (серия)');
                    cash_savelog('mark_num_tobacco='+mark_num);

                    if CASH_PRINTM then pName := '[M] '+pName; 

                    cash_savelog('mark_bc1='+mark_bc);
                    if cash_savemarks=1 then 
                    begin
                      
                      MarkData := VarArrayCreate([0,length(pmarks_barcodes)-1],VarByte);
                      for i := 1 to length(pmarks_barcodes) do VarArrayPut(MarkData, ord(pmarks_barcodes[i]), [i-1]); 
                      {begin
                        //ByteVal := ord(pmarks_barcodes[i]);  // HexToByte(strvalue); 
                        VarArrayPut(MarkData, ord(pmarks_barcodes[i]), [i-1]);
                      end;}
                      VarDevice.SetParam(VarDevice.LIBFPTR_PARAM_MARKING_CODE,MarkData); cash_checkerror('Cash_Execute:LIBFPTR_PARAM_MARKING_CODE');
                      //VarDevice.SetParam(1162,MarkData); cash_checkerror('Cash_Execute:1162-3');
                      MarkData := unassigned;
                    end
                    else if CASH_SAVEMARKS=2 then // набираем сами, как в драйвере 8 (через строку), передаем как строку 
                    begin
                      Mark_BC := IntToHex(STrToInt64Protected(Mark_BC),6);
                      Mark_BC := StrPadLeft(Mark_BC,12,'0');
                      cash_savelog('mark_bc1='+mark_bc);
  
                      Mark_num:= stringtohex(Mark_NUM);
                      cash_savelog('mark_num1='+mark_num);
                    
                      StrVAlue := '0005'+Mark_BC+Mark_NUM;
                      cash_savelog('mark_hex1='+strvalue);
                      
                      for i := 1 to length(StrVAlue) div 2-1 do strvalue := copy(strvalue,1,i*2+i-1) + ' '+ copy(strvalue,i*2+i,length(strvalue)); // пробелы между знаками поставим
                      
                      VarDevice.SetParamStrHex(1162,StrValue); cash_checkerror('Cash_Execute:1162-2'); // или setNonPrintableParamStrHex()
                    end
                    else if CASH_SAVEMARKS=3 then // набираем сами, как в драйвере 8, передаем через массив, как в драйвере 10 
                    begin
                    
                      Mark_BC := IntToHex(STrToInt64Protected(Mark_BC),6);
                      Mark_BC := StrPadLeft(Mark_BC,12,'0');
                      cash_savelog('mark_bc2='+mark_bc);
  
                      Mark_num:= stringtohex(Mark_NUM);
                      cash_savelog('mark_num2='+mark_num);
                    
                      StrVAlue := '0005'+Mark_BC+Mark_NUM;
                      cash_savelog('mark_hex2='+strvalue);
                      
                      //for i := 1 to length(StrVAlue)-1 do strvalue := copy(strvalue,1,i*2+i-1) + ' '+ copy(strvalue,i*2+i,length(strvalue)); // пробелы между знаками поставим
  
                      MarkData := VarArrayCreate([0,14],VarByte); //2+6(бинар.формат штрихкода)+7 = 15 
                      for i := 0 to 14 do 
                      begin
                        ByteVal := HexToByte(strvalue); 
                        delete(StrVAlue,1,2);
                        VarArrayPut(MarkData, ByteVal, [i]);
                      end;
                      VarDevice.SetParam(1162,MarkData); cash_checkerror('Cash_Execute:1162-3');
                      MarkData := unassigned;
                    end
                    else  
                    begin // 4 
                      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_NOMENCLATURE_TYPE, VarDevice.LIBFPTR_NT_TOBACCO);
                      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_GTIN, MARK_BC);
                      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_SERIAL_NUMBER, MARK_NUM);
                      VarDevice.utilFormNomenclature;
                      MarkData := VarDevice.getParamByteArray(VarDevice.LIBFPTR_PARAM_TAG_VALUE);
                      //showmessage(asstring(vartype(MarkData))+':'+varTypeAsText(VarType(MarkData))+':'+asstring(VarArrayHighBound(MarkData,1)));
                      VarDevice.SetParam(1162,MarkData); cash_checkerror('Cash_Execute:1162-4');
                      MarkData := unassigned;
                    end;
                    //VarDevice.SetParam(1162,strValue); cash_checkerror; // должен быть массив байтов, а не строка. как передать массив? та же проблема в 8 версии
                 end
                 else begin
                 
                   cash_savelog('DecodeMarkMed='+asstring(MDLPDecodeMarkMed(pmarks_barcodes,MARK_BC,MARK_NUM,BATCH,EXPDATE)));  {and NNInFilterMED(pNN)} // если декодируется, то считать медикаментами
                   // не будем требоватЬ, чтобы вернуло true, ибо часть марок левые, а продать надо. Со временем, может, можно будет требование это вернуть
                   cash_savelog('mark_bc_med='+mark_bc);
                   cash_savelog('mark_num_med='+mark_num);
                   cash_savelog('batch_med='+BATCH);
                   cash_savelog('expdate_med='+EXPDATE);
                    

                   if (mark_bc>'') and (mark_num>'') then 
                   begin
                     if (pQuantity<1) and (pdivnum>1) then strQNT := inttostr(round(pquantitydivnum))+'/'+inttostr(pdivnum)+'&'
                     else strqnt := ''; 
                    
                     VarDevice.setNonPrintableParam(1191, 'mdlp'+strqnt); // или SetParam?
                     
                     if CASH_SAVEMARKS = 1 then
                     begin
                       MarkData := VarArrayCreate([0,length(pmarks_barcodes)-1],VarByte);
                       for i := 1 to length(pmarks_barcodes) do VarArrayPut(MarkData, ord(pmarks_barcodes[i]), [i-1]); 
                       {begin
                         //ByteVal := ord(pmarks_barcodes[i]);  // HexToByte(strvalue); 
                         VarArrayPut(MarkData, ord(pmarks_barcodes[i]), [i-1]);
                       end;}
                       VarDevice.SetParam(VarDevice.LIBFPTR_PARAM_MARKING_CODE,MarkData); cash_checkerror('Cash_Execute:LIBFPTR_PARAM_MARKING_CODE');
                       //VarDevice.SetParam(1162,MarkData); cash_checkerror('Cash_Execute:1162-3');
                       MarkData := unassigned;
                     end
                     else if CASH_SAVEMARKS = 4 then
                     begin // 4 
                       VarDevice.setParam(VarDevice.LIBFPTR_PARAM_NOMENCLATURE_TYPE, VarDevice.LIBFPTR_NT_MEDICINES); // посмотреть значение Medicines этого. может оно и есть префикс?
                       // VarDevice.setParam(VarDevice.LIBFPTR_PARAM_NOMENCLATURE_TYPE, $444D);  // может префикс так вписать? 
                       VarDevice.setParam(VarDevice.LIBFPTR_PARAM_GTIN, MARK_BC);
                       VarDevice.setParam(VarDevice.LIBFPTR_PARAM_SERIAL_NUMBER, MARK_NUM);
                       VarDevice.utilFormNomenclature;
                       MarkData := VarDevice.getParamByteArray(VarDevice.LIBFPTR_PARAM_TAG_VALUE);
                       //showmessage(asstring(vartype(MarkData))+':'+varTypeAsText(VarType(MarkData))+':'+asstring(VarArrayHighBound(MarkData,1)));
                       VarDevice.SetParam(1162,MarkData); cash_checkerror('Cash_Execute:1162-4');
                       MarkData := unassigned;
                     end
                     else begin //2,3
                       Mark_BC := IntToHex(STrToInt64Protected(Mark_BC),6);
                       Mark_BC := StrPadLeft(Mark_BC,12,'0');
                       cash_savelog('mark_bc2='+mark_bc);
   
                       Mark_num:= stringtohex(Mark_NUM);
                       cash_savelog('mark_num2='+mark_num);
                     
                       StrVAlue := {'0003'} '444D'+Mark_BC+Mark_NUM;
                       cash_savelog('mark_hex2='+strvalue);
      
                       if CASH_SAVEMARKS = 2 then
                       begin
                         for i := 1 to length(StrVAlue) div 2-1 do strvalue := copy(strvalue,1,i*2+i-1) + ' '+ copy(strvalue,i*2+i,length(strvalue)); // пробелы между знаками поставим
                         VarDevice.SetParamStrHex(1162,StrValue); cash_checkerror('Cash_Execute:1162-2'); // или setNonPrintableParamStrHex()
                       end
                       else if CASH_SAVEMARKS = 3 then 
                       begin
                         MarkData := VarArrayCreate([0,20],VarByte); //2+6(бинар.формат штрихкода)+13 = 21
                         for i := 0 to 20 do 
                         begin
                           ByteVal := HexToByte(strvalue); 
                           delete(StrVAlue,1,2);
                           VarArrayPut(MarkData, ByteVal, [i]);
                         end;
                         VarDevice.SetParam(1162,MarkData); cash_checkerror('Cash_Execute:1162-3');
                         MarkData := unassigned;
                       end
                       else RaiseException('Не определен тип сохраненния марок CASH_SAVEMARKS: '+inttostr(cash_savemarks));
                     end
                   end
                   else raiseException('Присутствует марка, но её разбор прошел неудачно. Из-за требования печати марки сохранение чека невозможно.'); //raiseException('Товар не опознан как один из маркируемых, но присутствует марка и требование печати марки. Сохранение чека невозможно');
                 end; 
               end
               else begin
                 cash_savelog('tag1162?');

                 // проверим на tag1162 в справочнике товаров (Поле для заполнения тэга 1162 для медизделий (или других?) Постановление Правительства РФ от 16.04.2020 N 521 "О внесении изменений в постановление Правительства Российской Федерации от 21 февраля 2019 г. N 174")
                 strvalue := asstring(BDEQueryValue('select tag1162 from tovar where nn = :nn',[pNN]));
                 if strvalue>'' then 
                 begin
                   cash_savelog('nntag1162='+strvalue);
                   strvalue := IntToHex(STrToInt64Protected(strvalue),6);
                   strvalue := StrPadLeft(strvalue,12,'0');
                   StrVAlue := '450D'+StrValue;
                   cash_savelog('nntag1162_hex2='+strvalue);
  
                   if CASH_SAVEMARKS = 2 then
                   begin
                     for i := 1 to length(StrVAlue) div 2-1 do strvalue := copy(strvalue,1,i*2+i-1) + ' '+ copy(strvalue,i*2+i,length(strvalue)); // пробелы между знаками поставим
                     VarDevice.SetParamStrHex(1162,StrValue); cash_checkerror('Cash_Execute:nntag1162-2'); // или setNonPrintableParamStrHex()
                   end
                   else begin
                     MarkData := VarArrayCreate([0,7],VarByte); //2+6(бинар.формат штрихкода)
                     for i := 0 to 7 do 
                     begin
                       ByteVal := HexToByte(strvalue); 
                       delete(StrVAlue,1,2);
                       VarArrayPut(MarkData, ByteVal, [i]);
                     end;
                     VarDevice.SetParam(1162,MarkData); cash_checkerror('Cash_Execute:nntag1162-3');
                     MarkData := unassigned;
                   end;
                 end;
               end;
               
               //cash_savelog(asstring(VarDevice.GetParamStr(1162))); cash_checkerror('Cash_Execute:1162-get');
               
               cash_savelog('параметры товара:');

               VarDevice.setParam(VarDevice.LIBFPTR_PARAM_COMMODITY_NAME, pName); Cash_CheckError('Cash_Execute:NAME');  
               VarDevice.setParam(VarDevice.LIBFPTR_PARAM_QUANTITY, pQuantity); Cash_CheckError('Cash_Execute:Quantity');
               VarDevice.setParam(VarDevice.LIBFPTR_PARAM_PRICE, ifthen_currency(CASH_DiscountType<>0,pPriceOut,pPrice)); Cash_CheckError('Cash_Execute:Price');

               thField := dataset.findfield(fsection);
               if thfield<>nil then IntValue :=  coalesce_integer(thField.asinteger,CurrentSection,1) else IntValue := CurrentSection;
               VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DEPARTMENT, IntValue); Cash_CheckError('Cash_Execute:Department');

               
               if Cash_ShowNDS then 
               begin
                 cash_savelog('НДС');
                 NDS := dataset.fieldbyname('nds').asinteger;
                 IntValue := ifthen_integer(NDS=20,CASH_NDS20TYPE,ifthen_integer(NDS=18,CASH_NDS18TYPE,ifthen_integer(NDS=10 ,CASH_NDS10TYPE,CASH_NDS0TYPE)));
                 VarDevice.setParam(VarDevice.LIBFPTR_PARAM_TAX_TYPE, IntVAlue);
                 Cash_CheckError('Cash_Execute:NDS');
                 if cash_showNDSSUMMARY then 
                 begin
                   VarDevice.SetParam(VArDevice.LIBFPTR_PARAM_TAX_SUM,bvroundto(dataset.fieldbyname(FSummary).asfloat *NDS/(100+NDS),-2));                        
                   Cash_CheckError('Cash_Execute:NDSSummary');
                 end;
               end;
               
               cash_savelog('cash_discounttype:'+inttostr(cash_discounttype)+', '+floattostr(pdiscount));
               VarDevice.SetParam(VarDevice.LIBFPTR_PARAM_INFO_DISCOUNT_SUM,ifthen_currency(cash_DiscountType<>0,pDiscount,0));  Cash_CheckError('Cash_Execute:Discount');

               VarDevice.setParam(1212, 1); Cash_CheckError('Cash_Execute:1212'); //commodity - товар
               VarDevice.setParam(1214, 4); Cash_CheckError('Cash_Execute:1214'); //fullPayment - полный расчет
                                                 

               cash_savelog('registration');
               VarDevice.Registration; Cash_CheckError('Cash_Execute:Registration');
               //raiseexception('test');

               if (pDiscount <> 0) and CASH_PrintStringDiscountSummary        
               then begin     
                 cash_savelog('pdiscount='+floattostr(pdiscount)); 
                 //VarDevice.Caption := 'В том числе скидка: '+bvCurrtostrF(pDiscount)+#13+'Цена без скидки: '+bvCurrToStrF(pPriceOut);
                 StrValue := 'Скидка: ';
                 strvalue1 := bvcurrtostrf(pdiscount);
                 if stringlength>0 then StrValue1 := StrPad(strvalue1,stringlength-length(strvalue),' ',true);
                 VarDevice.setParam(VarDevice.LIBFPTR_PARAM_TEXT, StrValue+StrValue1); cash_checkerror('Cash_Execute:TEXT');
                 VarDevice.printText; cash_checkerror('Cash_Execute:PrintText');
               end;

               if (pDiscount <> 0) and CASH_PrintStringPriceOut        
               then begin     
                 //VarDevice.Caption := 'В том числе скидка: '+bvCurrtostrF(pDiscount)+#13+'Цена без скидки: '+bvCurrToStrF(pPriceOut);
                 StrValue := 'Цена без скидки: ';
                 strvalue1 := bvcurrtostrf(pPriceOut);
                 cash_savelog('Цена без скидки: '+strvalue1);
                 if stringlength>0 then StrValue1 := StrPad(strvalue1,stringlength-length(strvalue),' ',true);
                 VarDevice.setParam(VarDevice.LIBFPTR_PARAM_TEXT, StrValue+StrValue1); cash_checkerror('Cash_Execute:TEXT1-1');
                 VarDevice.printText; cash_checkerror('Cash_Execute:PrintText1-1');
                 cash_savelog('-напечатано');
               end;

               {
               if Cash_ShowNDS
               then begin
                 VarDevice.Destination := 1; Cash_CheckError;
                 VarDevice.TaxTypeNumber := ifthen_integer(dataset.fieldbyname('nds').asinteger=18,CASH_NDS18TYPE,
                                          ifthen_integer(dataset.fieldbyname('nds').asinteger=10,CASH_NDS10TYPE,CASH_NDS0TYPE)); 
                 Cash_CheckError;
                 
               end;
               }
             end;
             Cash_CheckError('Cash_Execute:next');
           end;

           cash_savelog('Строка завершена');
           Dataset.next;
         end;
                                             
         if (DetailCash = 2) // Без детализации
         then begin
           cash_savelog('DetailCash = 2');
           if (pSummaryCash > 0) then
           begin
             VarDevice.setParam(VarDevice.LIBFPTR_PARAM_QUANTITY, 1); Cash_CheckError('Cash_Execute:QUANTITY2');
             
             thField := dataset.findfield(fsection);
             if thfield<>nil then IntValue :=  coalesce_integer(thField.asinteger,CurrentSection,1) else IntValue := CurrentSection;
             VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DEPARTMENT, IntValue); 
             Cash_CheckError('Cash_Execute:SECTION2');

             VarDevice.setParam(VarDevice.LIBFPTR_PARAM_PRICE, ifthen_currency(CASH_DiscountType<>0, bvroundto(pSummaryCash + pSummaryDiscount, -2,DefRoundParam), bvroundto(pSummaryCash, -2,defRoundParam))); 
             Cash_CheckError('Cash_Execute:Discount2');

             //VarDevice.SetParam(VarDevice.LIBFPTR_PARAM_INFO_DISCOUNT_SUM,ifthen_currency(cash_DiscountType<>0, bvRoundto(pSummaryDiscount,-2),0));  Cash_CheckError;
             //VarDevice.DiscountType := 0; Cash_CheckError;
             //VarDevice.DiscountValue := ifthen_currency(cash_DiscountType<>0, bvRoundto(pSummaryDiscount,-2),0);  Cash_CheckError;

             VarDevice.Registration; Cash_CheckError('Cash_Execute:Registration2'); 

             if (CASH_DiscountType=1)and ( bvroundto(pSummaryDiscount, -2) <> 0) then
             begin
               VarDevice.SetParam(VarDevice.LIBFPTR_PARAM_INFO_DISCOUNT_SUM,bvroundto(pSummaryDiscount, -2));  Cash_CheckError('Cash_Execute:DiscountSum2');
               //VArDevice.Summ :=  bvroundto(pSummaryDiscount, -2); Cash_CheckError;
               //VarDevice.Destination := 1; Cash_CheckError; // 0 - для всего чека, 1 - для последней операции
               //VarDevice.SummDiscount; Cash_CheckError;
             end;
             
             
             if (pSummaryDiscount <> 0) and CASH_PrintStringDiscountSummary then 
             begin     
               StrValue := 'Скидка: ';
               strvalue1 := bvcurrtostrf(pSummaryDiscount);
               if stringlength>0 then StrValue1 := StrPad(strvalue1,stringlength-length(strvalue),' ',true);
               VarDevice.setParam(VarDevice.LIBFPTR_PARAM_TEXT, StrValue+StrValue1); cash_checkerror('Cash_Execute:Text2');
               VarDevice.printText; cash_checkerror('Cash_Execute:PrintText2');
             end;

             if (pDiscount <> 0) and CASH_PrintStringPriceOut        
             then begin     
               //VarDevice.Caption := 'В том числе скидка: '+bvCurrtostrF(pDiscount)+#13+'Цена без скидки: '+bvCurrToStrF(pPriceOut);
               StrValue := 'Итого без скидки: ';
               strvalue1 := bvcurrtostrf(pSummaryCash);
               if stringlength>0 then StrValue1 := StrPad(strvalue1,stringlength-length(strvalue),' ',true);
               VarDevice.setParam(VarDevice.LIBFPTR_PARAM_TEXT, StrValue+StrValue1); cash_checkerror('Cash_Execute:Text2-2');
               VarDevice.printText; cash_checkerror('Cash_Execute:PrintText2-2');
             end;
             
           end;
         end;

         if (CASH_DiscountType=2) and (bvroundto(pSummaryDiscount, -2) <> 0) then 
         begin
           cash_savelog('Cash_DiscountType=2, psummaryDiscount='+floattostr(pSummaryDiscount));
           VarDevice.SetParam(VarDevice.LIBFPTR_PARAM_INFO_DISCOUNT_SUM,bvroundto(pSummaryDiscount, -2));  Cash_CheckError('Cash_Execute:DiscountSum3');
           //VarDevice.Summ := bvroundto(pSummaryDiscount, -2); Cash_CheckError;
           //VarDevice.Destination := 0;
           //VarDevice.SummDiscount; Cash_CheckError;
         end;
         
         if (pSummaryDiscount <> 0) and CASH_PrintStringDiscountOnCheck then 
         begin     
           StrValue := 'Итого скидка по чеку: ';
           strvalue1 := bvcurrtostrf(pSummaryDiscount);
           cash_savelog(StrVAlue+strvalue1);
           
           if stringlength>0 then StrValue1 := StrPad(strvalue1,stringlength-length(strvalue),' ',true);
           VarDevice.setParam(VarDevice.LIBFPTR_PARAM_TEXT, StrValue+StrValue1); cash_checkerror('Cash_Execute:Text3');
           VarDevice.printText; cash_checkerror('Cash_Execute:PrintText3');
         end;
       finally
         Dataset.enablecontrols;
       end;
       Dataset.first;

///////////////////////////////////////////////

       if VarActionNal then Summ2 := bvroundto(Paysummary, -2)  else Summ2 := 0; // bvroundto(pSummaryCash, -2 );

       cash_savelog('Summ2: '+floattostr(summ2));

       case DOC_TYPE_CLOSE of
         1 : Index := CASH_TypeCloseBN;
         2 : Index := CASH_TypeCloseKredit;
         3 : Index := CASH_TypeCloseSert;
         else  Index := 0;
       end;
       cash_savelog('PaymentType: '+inttostr(index));

       if Summ2<>0 then
       begin
         VarDevice.setParam(VarDevice.LIBFPTR_PARAM_PAYMENT_TYPE, Index); cash_checkerror('Cash_Execute:PaymentType');
         VarDevice.setParam(VarDevice.LIBFPTR_PARAM_PAYMENT_SUM, Summ2);  cash_checkerror('Cash_Execute:PaymentSum');
         VarDevice.payment; cash_checkerror('Cash_Execute:Payment');
       end;
    
       
       if (index=0) and CASH_OPENDRAWER_NAL   then 
       begin
         cash_savelog('OpenDrawer');
         Cash_OpenDrawer(false); 
       end;
       


       case DOC_TYPE_CLOSE of
         1 : Index := CASH_TypeCloseBN;
         2 : Index := CASH_TypeCloseKredit;
         3 : Index := CASH_TypeCloseSert;
         else  Index := 0;
       end;

       cash_savelog('PaymentType1: '+inttostr(index));
       VarDevice.setParam(VarDevice.LIBFPTR_PARAM_PAYMENT_TYPE, Index); cash_checkerror('Cash_Execute:PaymentType1');
       
       //raiseexception('тест');

       cash_savelog('CloseReceipt');
       VarDevice.CloseReceipt; //Cash_CheckError('Cash_Execute:CloseReceipt');
       cash_savelog('-');

       if VarDevice.checkDocumentClosed<>0 then
       While VarDevice.checkDocumentClosed <> 0 do
       begin
          cash_savelog('-not closed');
          Cash_CheckError('Cash_Execute:CheckDocumentClosed',false);
          if getConfirm('Сохранение чека ещё не завершено: '+#13+
                        'Ошибка: '+inttostr(ResultCode)+': '+ResultDescription,
                        '','Повторить/продолжить','Отменить')<>mrOk 
          then break;
       end
       else cash_savelog('-closed');
       
       if not VarDevice.getParamBool(VarDevice.LIBFPTR_PARAM_DOCUMENT_CLOSED) then // если чек не закрыт, то отменяем его.
       begin
         Cash_SaveLog('Отмена чека',true);
         VarDevice.CancelReceipt;
         cash_checkError('Cash_Execute:CancelReceipt');
         raiseexception('Чек отменен!');
       end;

       Cash_SaveLog('Проверка статуса печати');
       if not VarDevice.getParamBool(VarDevice.LIBFPTR_PARAM_DOCUMENT_PRINTED) then
       begin
         Cash_SaveLog('Ожидание окончания печати');
         while VarDevice.continuePrint<>0 do 
         begin
           cash_checkError('Cash_Execute:ContinuePrint',false);
           if getConfirm('Чек сохранён, но его печать не завершена:'+#13+
                        'Ошибка: '+inttostr(ResultCode)+': '+ResultDescription,
                        '','Продолжить печать','Выйти')<>mrOk 
           then break;
         end;
         Cash_SaveLog('- печать завершена');
       end;

       Cash_SaveLog('cash_cut');
       Cash_Cut(false); Cash_CheckError('Cash_Execute:Cash_Cut',false);
       Cash_SaveLog('-завершено');
     
   finally
       if needconnect then cash_disconnect; 
   end;
   Cash_SaveLog('cash_execute:exit');
end;

procedure Cash_CancelCheck(NeedConnect :boolean = true);
begin
  if NeedConnect then Cash_Connect(false);
  try
    {
    VarDevice.GetStatus; Cash_CheckError;
    if (VarDevice.CheckState <> 0) then VarDevice.CancelCheck; 
    Cash_CheckError;
    }
    
    VarDevice.checkDocumentClosed; Cash_CheckError('Cash_CancelCheck:CheckDocumentClosed');
     
    if not VarDevice.getParamBool(VarDevice.LIBFPTR_PARAM_DOCUMENT_CLOSED) then // если чек не закрыт, то отменяем его.
    begin
       VarDevice.CancelReceipt;
       cash_checkError('Cash_CancelCheck:CheckDocumentClosed');
    end;
  finally
    if NeedConnect then  Cash_disconnect;
  end;
end;

procedure Cash_RepeatCheck(NeedConnect :boolean = true);
begin
  cash_savelog('Cash_RepeatCheck: функция не реализована для данного драйвера',true);
  raiseexception('Cash_RepeatCheck: функция не реализована для данного драйвера'); 
end;


procedure Cash_DoSummary(IsIn :boolean; NeedConnect :boolean = true);
var i :integer;
begin
  if needconnect then Cash_Connect;
  try
    VarDevice.SetParam(VarDevice.LIBFPTR_PARAM_SUM,Summary); cash_checkerror('Cash_DoSummary:Summary');
    if isIn then VarDevice.CashIncome else VarDevice.CashOutcome; Cash_CheckError('Cash_DoSummary:CashOutcome');
    Cash_Cut(false); Cash_CheckError('Cash_DoSummary:cash_cut');
  finally
    if needconnect then cash_disconnect;
  end;
end;

procedure Cash_DoSummaryIn(NeedConnect :boolean = true);
begin
  Cash_DoSummary(true,NeedConnect);
end;

procedure Cash_DoSummaryOut(NeedConnect :boolean = true);
begin
  Cash_DoSummary(false,NeedConnect);
end;


procedure Cash_XReport(NeedConnect :boolean = true);
begin
  if NeedConnect then Cash_Connect;
  try
    VarDevice.setParam(VarDevice.LIBFPTR_PARAM_REPORT_TYPE, VarDevice.LIBFPTR_RT_X);
    VarDevice.Report; Cash_CheckError('cash_xreport');
  finally
    if NeedConnect then Cash_disconnect;
  end;
end;

procedure Cash_ZReport(NeedConnect :boolean = true);
begin
  if NeedConnect then  Cash_Connect;
  try
    VarDevice.setParam(VarDevice.LIBFPTR_PARAM_REPORT_TYPE, VarDevice.LIBFPTR_RT_CLOSE_SHIFT);
    VarDevice.Report; Cash_CheckError('Cash_zreport');
  finally
    if NeedConnect then Cash_disconnect;
  end;
end;

procedure Cash_SectionReport(NeedConnect :boolean = true);
begin
  if NeedConnect then Cash_Connect;
  try
    VarDevice.setParam(VarDevice.LIBFPTR_PARAM_REPORT_TYPE, VarDevice.LIBFPTR_RT_DEPARTMENTS);
    VarDevice.Report; Cash_CheckError('Cash_SectionReport');
  finally
    if NeedConnect then Cash_disconnect;
  end;
end;

procedure Cash_UsersReport(NeedConnect :boolean = true);
begin
  if NeedConnect then  Cash_Connect;
  try
    VarDevice.setParam(VarDevice.LIBFPTR_PARAM_REPORT_TYPE, VarDevice.LIBFPTR_RT_OPERATORS);
    VarDevice.Report; Cash_CheckError('Cash_UsersReport');
  finally
    if NeedConnect then Cash_disconnect;
  end;
end;

procedure Cash_RegistersReport(NeedConnect :boolean = true);

var i :integer;
    List :Tstringlist;
    ResultVal :string; //,ResultVal1,ResultVal2,ResultVal3,ResultVal4,ResultVal5,ResultVal6 :string;
    FloatVal :double;
    IntVal :integer;
    DTVal :tdatetime;
    BoolVal :boolean;
begin
  if (ResultRegisterListNames=nil) or (ResultRegisterListValues=nil) 
  then raiseException('Ошибка в Cash_RegistersReport: Не проинициализированы контейнеры для результатов функции');

  if NeedConnect then  Cash_Connect;

  List := tstringlist.create;
  with TmyWait.create('Идет сбор данных по регистрам',selfscript) do  
  try

    //VarDevice.mode := 0; Cash_CheckError;
    //VarDevice.SetMode; Cash_CheckError;
    
    resultregisterlistnames.clear;
    resultregisterlistvalues.clear;       
    

    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_SHIFT_TOTALS); 
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:8',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM));
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма продаж (чеки продажи):');  ResultRegisterListValues.add(ResultVal);

    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_SHIFT_TOTALS); 
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL_RETURN);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:9',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM));
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма возвратов (чеки возврата продажи):');  ResultRegisterListValues.add(ResultVal);

    

    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_PAYMENT_SUM); 
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL);
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_PAYMENT_TYPE, VarDevice.LIBFPTR_PT_CASH);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:10',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма продаж (наличными):');  ResultRegisterListValues.add(ResultVal);


    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_PAYMENT_SUM); 
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL_RETURN);
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_PAYMENT_TYPE, VarDevice.LIBFPTR_PT_CASH);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:11',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма возвратов (наличными):');  ResultRegisterListValues.add(ResultVal);


    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_PAYMENT_SUM); 
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL);
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_PAYMENT_TYPE, VarDevice.LIBFPTR_PT_ELECTRONICALLY);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:12',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма продаж (безнал):');  ResultRegisterListValues.add(ResultVal);

   progressbarpos := 15;
   
    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_PAYMENT_SUM); 
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL_RETURN);
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_PAYMENT_TYPE, VarDevice.LIBFPTR_PT_ELECTRONICALLY);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:13',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма возвратов (безнал):');  ResultRegisterListValues.add(ResultVal);


    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_PAYMENT_SUM); 
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL);
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_PAYMENT_TYPE, VarDevice.LIBFPTR_PT_CREDIT);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:14',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма продаж (кредит):');  ResultRegisterListValues.add(ResultVal);


    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_PAYMENT_SUM); 
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL_RETURN);
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_PAYMENT_TYPE, VarDevice.LIBFPTR_PT_CREDIT);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:15',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма возвратов (кредит):');  ResultRegisterListValues.add(ResultVal);

    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_CANCELLATION_SUM ); 
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:16',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма отмененных чеков:');  ResultRegisterListValues.add(ResultVal);

    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_CANCELLATION_SUM ); 
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL_RETURN);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:17',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма отмененных возвратов:');  ResultRegisterListValues.add(ResultVal);



    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_CASHIN_SUM ); 
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:18',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма внесений:');  ResultRegisterListValues.add(ResultVal);

    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_CASHOUT_SUM ); 
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_RECEIPT_TYPE, VarDevice.LIBFPTR_RT_SELL_RETURN);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:19',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма выплат:');  ResultRegisterListValues.add(ResultVal);
    
    //////////////////

    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_CASH_SUM); 
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:20',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма наличности:');  ResultRegisterListValues.add(ResultVal);

    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_REVENUE); 
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:21',false) then ResultVal := ResultDescription 
      else ResultVal := bvcurrtostrf(VarDevice.getParamDouble(VarDevice.LIBFPTR_PARAM_SUM)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Сумма выручки:');  ResultRegisterListValues.add(ResultVal);
    
  progressbarpos := 30;
  
    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_DATE_TIME); 
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:22',false) then ResultVal := ResultDescription 
      else ResultVal := VarDevice.getParamDateTime(VarDevice.LIBFPTR_PARAM_DATE_TIME); //datetimetostr(VarDevice.getParamDateTime(VarDevice.LIBFPTR_PARAM_DATE_TIME)); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Текущие дата и время:');  ResultRegisterListValues.add(ResultVal);


    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_SHIFT_STATE); 
      VarDevice.queryData;
      
      if not cash_checkError('cash_registers_report:23',false) then raiseexception(ResultDescription) 
      else begin
        try
          IntVal := VarDevice.GetParamInt(VarDevice.LIBFPTR_PARAM_SHIFT_STATE);
          if IntVal = VarDevice.LIBFPTR_SS_CLOSED then ResultVal := 'Смена закрыта'
          else if IntVal = VarDevice.LIBFPTR_SS_OPENED then ResultVal := 'Смена Открыта' 
          else if IntVal = VarDevice.LIBFPTR_SS_EXPIRED then ResultVal := 'Смена истекла (>24 часов)'
          else ResultVal := 'Неизвестное значение: '+inttostr(IntVal);
        except
          resultval := ExceptionMessage
        end;
        ResultRegisterListNames.add('Состояние смены:');  ResultRegisterListValues.add(ResultVal);
        
        try
          resultval := VarDevice.GetParamString(VarDevice.LIBFPTR_PARAM_SHIFT_NUMBER);
        except
          resultval := ExceptionMessage
        end; 
        ResultRegisterListNames.add('Номер смены:');  ResultRegisterListValues.add(resultval);

        try
          resultval := VarDevice.getParamDateTime(VarDevice.LIBFPTR_PARAM_DATE_TIME);
        except
          resultval := ExceptionMessage
        end; 
        ResultRegisterListNames.add('Время истечения текущей смены:');  ResultRegisterListValues.add(resultval);
          
      end; 
    except
      ResultRegisterListNames.add('Ошибка запроса состояния смены:');  ResultRegisterListValues.add(ExceptionMessage);
    end; 
  
    
    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_STATUS); 
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:2_1',false) then raiseexception(resultDescription)
      else begin
        try
          ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_SERIAL_NUMBER); 
        except
          resultval := ExceptionMessage
        end; 
        ResultRegisterListNames.add('Заводской номер ККМ:');  ResultRegisterListValues.add(ResultVal);

        try
          ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_SHIFT_NUMBER); 
        except
          resultval := ExceptionMessage
        end; 
        ResultRegisterListNames.add('Текущий номер сессии:');  ResultRegisterListValues.add(ResultVal);
    
        try
          ResultVal := VarDevice.getParamstring(VarDevice.LIBFPTR_PARAM_DOCUMENT_NUMBER); 
        except
          resultval := ExceptionMessage
        end; 
        ResultRegisterListNames.add('Текущий номер документа:');  ResultRegisterListValues.add(ResultVal);
      end; 
    except
      ResultRegisterListNames.add('Запрос статуса ККМ - ошибка:');  ResultRegisterListValues.add(ExceptionMessage);
    end;
       

    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_MODEL_INFO);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:1',false) then raiseexception(ResultVal+':'+ResultDescription)
      else begin
          try
            ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_MODEL); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Номер модели ККМ:');  ResultRegisterListValues.add(ResultVal);

          try
            ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_MODEL_NAME); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Наименование ККМ:');  ResultRegisterListValues.add(ResultVal);

          try
            ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_UNIT_VERSION); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Версия ККМ:');  ResultRegisterListValues.add(ResultVal);
          
          
          try
            ResultVal := VarDevice.Version; 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Версия драйвера:');  ResultRegisterListValues.add(ResultVal);
          
      end; 
    except
      ResultRegisterListNames.add('Запрос модели ККМ:');  ResultRegisterListValues.add(ExceptMessage);
    end; 
    
    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_UNIT_VERSION);
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_UNIT_TYPE, VarDevice.LIBFPTR_UT_CONFIGURATION);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:2',false) then raiseexception(ResultDescription)
      else begin
          try
            ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_UNIT_VERSION ); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Версия прошивки:');  ResultRegisterListValues.add(ResultVal);
          try
            ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_UNIT_RELEASE_VERSION ); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Версия релиза:');  ResultRegisterListValues.add(ResultVal);
      end; 
    except
      ResultRegisterListNames.add('Запрос версии модуля:');  ResultRegisterListValues.add(ExceptMessage);
    end; 

    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_RECEIPT_LINE_LENGTH);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:3',false) then ResultVal := ResultDescription 
      else ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_RECEIPT_LINE_LENGTH); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Ширина ленты (симв.):');  ResultRegisterListValues.add(ResultVal);


    progressbarpos := 75;
    
    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_FN_DATA_TYPE, VarDevice.LIBFPTR_FNDT_OFD_EXCHANGE_STATUS);
      VarDevice.fnQueryData;
      Boolval := cash_checkError('cash_registers_report:4',false); 
      if not boolval then ResultVal := ResultDescription 
      else ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_OFD_EXCHANGE_STATUS); 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Статус обмена с ОФД:');  ResultRegisterListValues.add(ResultVal);
    if boolval then
    begin
      try
        ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_DOCUMENTS_COUNT); 
      except
        resultval := ExceptionMessage
      end; 
      ResultRegisterListNames.add('Кол-во неотправленных документов в ОФД:');  ResultRegisterListValues.add(ResultVal);
      try
        //ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_DATE_TIME);  
        ResultVal := VarDevice.getParamDateTime(VarDevice.LIBFPTR_PARAM_DATE_TIME);
      except
        resultval := ExceptionMessage
      end; 
      ResultRegisterListNames.add('Дата и время первого не отправленного документа в ОФД:');  ResultRegisterListValues.add(ResultVal);
    end;
    
    
    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_DATA_TYPE, VarDevice.LIBFPTR_DT_LAST_SENT_OFD_DOCUMENT_DATE_TIME);
      VarDevice.queryData;
      if not cash_checkError('cash_registers_report:5_0',false) then ResultVal := ResultDescription  
      else ResultVal := VarDevice.getParamDateTime(VarDevice.LIBFPTR_PARAM_DATE_TIME) 
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Дата и время последнего успешного обмена с ОФД:');  ResultRegisterListValues.add(ResultVal);
 
    
    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_FN_DATA_TYPE, VarDevice.LIBFPTR_FNDT_VALIDITY);
      VarDevice.fnqueryData;
      if not cash_checkError('cash_registers_report:5_1',false) then ResultVal := ResultDescription 
      else ResultVal := VarDevice.getParamDateTime(VarDevice.LIBFPTR_PARAM_DATE_TIME);
      
    //registrationsRemain := fptr.getParamInt(fptr.LIBFPTR_PARAM_REGISTRATIONS_REMAIN);
    //registrationsCount  := fptr.getParamInt(fptr.LIBFPTR_PARAM_REGISTRATIONS_COUNT);
    //dateTime:           := fptr.getParamDateTime(fptr.LIBFPTR_PARAM_DATE_TIME);    
       
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Срок действия ФН:');  ResultRegisterListValues.add(ResultVal);
    
    
    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_FN_DATA_TYPE, VarDevice.LIBFPTR_FNDT_FN_INFO); 
      VarDevice.fnQueryData;
      if not cash_checkError('cash_registers_report:5_2',false) then raiseexception(resultDescription)
      else begin
        try
          ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_SERIAL_NUMBER); 
        except
          resultval := ExceptionMessage
        end; 
        ResultRegisterListNames.add('Серийный номер ФН:');  ResultRegisterListValues.add(ResultVal);

        try
          ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_FN_VERSION); 
        except
          resultval := ExceptionMessage
        end; 
        ResultRegisterListNames.add('Версия ФН:');  ResultRegisterListValues.add(ResultVal);
    
        try
          IntVal := VarDevice.getParamInt(VarDevice.LIBFPTR_PARAM_FN_TYPE);
          
          if IntVal =  VarDevice.LIBFPTR_FNT_UNKNOWN then ResultVal := 'неизвестная (не удалось получить)'
          else if intVal = VarDevice.LIBFPTR_FNT_DEBUG then resultVal := 'отладочная версия'
          else if IntVal = VarDevice.LIBFPTR_FNT_RELEASE then ResultVal := 'боевая версия'
          else ResultVal := 'неизвестное значение: '+inttostr(intval);
           
        except
          resultval := ExceptionMessage
        end; 
        ResultRegisterListNames.add('Тип ФН:');  ResultRegisterListValues.add(ResultVal);
        


        try
          IntVal := VarDevice.getParamInt(VarDevice.LIBFPTR_PARAM_FN_STATE);

          if IntVal =  VarDevice.LIBFPTR_FNS_INITIAL then ResultVal := 'настройка ФН'
          else if intVal = VarDevice.LIBFPTR_FNS_CONFIGURED then resultVal := 'готовность к активации'
          else if IntVal = VarDevice.LIBFPTR_FNS_FISCAL_MODE then ResultVal := 'фискальный режим'
          else if IntVal = VarDevice.LIBFPTR_FNS_POSTFISCAL_MODE then ResultVal := 'постфискальный режим'
          else if IntVal = VarDevice.LIBFPTR_FNS_ACCESS_ARCHIVE then ResultVal := 'доступ к архиву'
          else ResultVal := 'неизвестное значение: '+inttostr(intval);
           
        except
          resultval := ExceptionMessage
        end; 
        ResultRegisterListNames.add('Состояние ФН:');  ResultRegisterListValues.add(ResultVal);

        ResultVal := '';
        try
          BoolVal := VarDevice.getParamBool(VarDevice.LIBFPTR_PARAM_FN_NEED_REPLACEMENT);
          if BoolVal then IncStr(ResultVal,'Требуется срочная замена ФН',', '); 

          BoolVal := VarDevice.getParamBool(VarDevice.LIBFPTR_PARAM_FN_RESOURCE_EXHAUSTED); 
          if BoolVal then IncStr(ResultVal,'Исчерпан ресурс ФН',', '); 

          BoolVal := VarDevice.getParamBool(VarDevice.LIBFPTR_PARAM_FN_OFD_TIMEOUT); 
          if BoolVal then IncStr(ResultVal,'Превышено время ожидания запроса от ОФД',', '); 

          BoolVal := VarDevice.getParamBool(VarDevice.LIBFPTR_PARAM_FN_MEMORY_OVERFLOW); 
          if BoolVal then IncStr(ResultVal,'Память ФН переполнена',', '); 

          BoolVal := VarDevice.getParamBool(VarDevice.LIBFPTR_PARAM_FN_CRITICAL_ERROR); 
          if BoolVal then IncStr(ResultVal,'Критическая ошибка ФН',', '); 

          BoolVal := VarDevice.getParamBool(VarDevice.LIBFPTR_PARAM_FN_MEMORY_OVERFLOW); 
          if BoolVal then IncStr(ResultVal,'Память ФН переполнена',', '); 

          BoolVal := VarDevice.getParamBool(VarDevice.LIBFPTR_PARAM_FN_MEMORY_OVERFLOW); 
          if BoolVal then IncStr(ResultVal,'Память ФН переполнена',', ');
          
          ResultRegisterListNames.add('Замечания по ФН:');  ResultRegisterListValues.add(ResultVal);
           
        except
          resultval := ExceptionMessage
        end; 
      end; 
    except
      ResultRegisterListNames.add('Запрос параметров ФН - ошибка:');  ResultRegisterListValues.add(ExceptionMessage);
    end;
    
    
    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_FN_DATA_TYPE, VarDevice.LIBFPTR_FNDT_FFD_VERSIONS);
      VarDevice.fnQueryData;
      if not cash_checkError('cash_registers_report:6',false) then ResultVal := ResultDescription 
      else ResultVal := VarDevice.getParamInt(VarDevice.LIBFPTR_PARAM_DEVICE_FFD_VERSION);    
    except
      resultval := ExceptionMessage
    end; 
    ResultRegisterListNames.add('Версия ФФД ККТ:');  ResultRegisterListValues.add(ResultVal);
    
    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_FN_DATA_TYPE, VarDevice.LIBFPTR_FNDT_ERRORS);
      VarDevice.fnQueryData;
    
      if not cash_checkError('cash_registers_report:7',false) then ResultVal := ResultDescription 
      else begin
          try
            ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_NETWORK_ERROR ); 
          except
            resultval := ExceptionMessage
          end; 
      
          try
            incstr(resultval,asstring(VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_NETWORK_ERROR_TEXT )),': '); 
          except
            incstr(resultval,ExceptionMessage,': ');
          end; 
          ResultRegisterListNames.add('Ошибка сети:');  ResultRegisterListValues.add(ResultVal);
          
          try
            ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_OFD_ERROR ); 
          except
            resultval := ExceptionMessage
          end; 
          
          try
            incstr(ResultVal,asstring(VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_OFD_ERROR_TEXT )),': '); 
          except
            incstr(resultval,ExceptionMessage,': ');
          end; 
          ResultRegisterListNames.add('Текст ошибки ОФД:');  ResultRegisterListValues.add(ResultVal);
      end; 
    except
      ResultRegisterListNames.add('Ошибки сети:');  ResultRegisterListValues.add(ExceptMessage);
    end; 

    progressbarpos := 90;

    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_FN_DATA_TYPE, VarDevice.LIBFPTR_FNDT_REG_INFO);
      VarDevice.fnQueryData;
    
      if not cash_checkError('cash_registers_report:8',false) then ResultVal := ResultDescription 
      else begin
          try
            ResultVal := VarDevice.getParamString(1018); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('ИНН организации:');  ResultRegisterListValues.add(ResultVal);
    
          try
            ResultVal := VarDevice.getParamString(1048); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Название организации:');  ResultRegisterListValues.add(ResultVal);
    
          try
            ResultVal := VarDevice.getParamString(1117); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('EMAIL организации:');  ResultRegisterListValues.add(ResultVal);
    

          try
            ResultVal := VarDevice.getParamString(1187); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Адрес места расчетов:');  ResultRegisterListValues.add(ResultVal);

          try
            ResultVal := VarDevice.getParamString(1037); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Регистрационный номер устройства:');  ResultRegisterListValues.add(ResultVal);

          try
            ResultVal := VarDevice.getParamString(1017); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('ИНН ОФД:');  ResultRegisterListValues.add(ResultVal);
    
          try
            ResultVal := VarDevice.getParamString(1046); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Название ОФД:');  ResultRegisterListValues.add(ResultVal);

          try
            ResultVal := VarDevice.getParamString(1209); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Версия ФФД:');  ResultRegisterListValues.add(ResultVal);
 
          {          
          1060	Адрес сайта ФНС	string
          1009	Адрес расчетов	string
          1062	Системы налогообложения	int
          1057	Признак агента	int
          1001	Признак автоматического режима	bool
          1036	Номер автомата	string
          1002	Признак автономного режима	bool
          1056	Признак шифрования	bool
          1108	Признак ККТ для расчетов в сети Интернет	bool
          1109	Признак расчетов за услуги	bool
          1110	Признак АС БСО	bool
          1126	Признак проведения лотерей	bool
          1193	Признак проведения азартных игр	bool
          1207	Признак подакцизного товара	bool
          1221	Признак установки принтера в автомате	bool
          }
      end;
    except
      ResultRegisterListNames.add('Запрос реквизитов регистрации ККТ');  ResultRegisterListValues.add(ExceptMessage);
    end; 
    
    progressbarpos := 95;

    try
      VarDevice.setParam(VarDevice.LIBFPTR_PARAM_FN_DATA_TYPE, VarDevice.LIBFPTR_FNDT_LAST_RECEIPT);
      VarDevice.fnQueryData;
    
      if not cash_checkError('cash_registers_report:9',false) then ResultVal := ResultDescription 
      else begin
          try
            ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_DOCUMENT_NUMBER); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Последний документ в ФН: номер:');  ResultRegisterListValues.add(ResultVal);

          try
            ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_DATE_TIME); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Последний документ в ФН: дата-время:');  ResultRegisterListValues.add(ResultVal);
    
          try
            ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_RECEIPT_SUM); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Последний документ в ФН: сумма:');  ResultRegisterListValues.add(ResultVal);
    
          try
            ResultVal := VarDevice.getParamString(VarDevice.LIBFPTR_PARAM_FISCAL_SIGN); 
          except
            resultval := ExceptionMessage
          end; 
          ResultRegisterListNames.add('Последний документ в ФН: фискальный признак:');  ResultRegisterListValues.add(ResultVal);
    
      end;
    except
      ResultRegisterListNames.add('Запрос данных последнего чека');  ResultRegisterListValues.add(ExceptMessage);
    end; 
    
    progressbarpos := 100;
  finally  
    if NeedConnect then Cash_disconnect;
    list.free;
    free
  end;
end;

procedure Cash_ShowProperties;
var StrVal :string;
    IntVal :integer;
begin
  Cash_CheckDevice;
  IntVal := VarDevice.ShowProperties(VarDevice.LIBFPTR_GUI_PARENT_NATIVE,0); cash_checkError('Cash_ShowProperties');
  if IntVal=0 then 
  begin
    Cash_SaveSettings; Cash_CheckError('Cash_ShowProperties:SaveSettings');
  end;
end;


procedure Cash_PrintText(Strings :tStrings;
                         NeedConnect :boolean = true;
                         RepeatPrint :integer =1; // число повторов слипа (если поступил одинарный слип, то повторим его печать)
                         CutAfter :boolean=false; // обрезать ли слип после печати
                         CutLine :boolean =false; // Надо ли резать (некоторые аппараты не поддерживают. по-умолчанию - true
                         CutLineText :string =''; // линия отреза содержит
                         CutLineLength :integer = 0; // сколько надо пропустить, чтобы линия отреза была там, где надо
                         CutLineSleep :integer = 0 // на месте отреза сделать слип, в секундах. Требуется, если аппарат не имеет отрезки, тогда эти секунды дадут возможность оторвать руками
                         );
var 
    i :integer;
    k :integer;
    incCutLine :integer;
    NeedCutLine :boolean; // Надо резать, после того, как IncCutLIne станет равной CardsPrintCutLineLength
begin
  if needConnect then Cash_Connect;
  try
  { тест 
    ShowMessage(Strings.text+#13+
                'NeedConnect:'+asstring(NeedConnect)+#13+
                'RepeatPrint:'+asstring(RepeatPrint)+#13+
                'CutAfter:'+asstring(CutAfter)+#13+  
                'CutLine:'+asstring(CutLine)+#13+  
                'CutLineText:'+asstring(CutLineText)+#13+  
                'CutLineLength:'+asstring(CutLineLength)+#13+  
                'CutLineSleep:'+asstring(CutLineSleep));
    exit;
  }
  
  
  
    Cash_SaveLog('PrintTextAtol');
    Cash_SaveLog(strings.text);
    if trim(strings.text) ='' then exit;
  
    Cash_SaveLog('PrintTextAtol:2');

  
    for k := 1 to ifthen_integer(repeatprint>1,repeatprint,1) do
    begin
      NeedCutLine := false;
      incCutLine := 0;
      Cash_SaveLog('PrintTextAtol:3');

      for i := 0 to strings.count-1 do
      begin
        Cash_SaveLog('PrintTextAtol:3.1.'+inttostr(i));
        VarDevice.setParam(VarDevice.LIBFPTR_PARAM_TEXT, Strings[i]); //VarDevice.Caption := List[i]; 
        cash_checkerror('Cash_PrintText');
        Cash_SaveLog('PrintTextAtol:3.2.'+inttostr(i));
        
          //Перенос LIBFPTR_PARAM_TEXT_WRAP может принимать следующие значения:
            //LIBFPTR_TW_NONE - не переносить. Текст будет обрезаться по ширине ленты
            //LIBFPTR_TW_WORDS - переносить по словам. Разделителями считаются символы:
               //пробел точка запятая точка с запятой тире знак вопроса восклицательный знак закрывающая скобка
              //закрывающая фигурная скобка            закрывающая квадратная скобка            
            //LIBFPTR_TW_CHARS - переносить по символам
    
        VarDevice.setParam(VarDevice.LIBFPTR_PARAM_TEXT_WRAP, VarDevice.LIBFPTR_TW_CHARS); {VarDevice.TextWrap := 1;} 
        cash_checkerror('Cash_PrintText:1');//Перенос текста по словам
    
            //LIBFPTR_ALIGNMENT_LEFT - по левому краю
            //LIBFPTR_ALIGNMENT_CENTER - по центру
            //LIBFPTR_ALIGNMENT_RIGHT - по правому краю    
    
        VarDevice.setParam(VarDevice.LIBFPTR_PARAM_ALIGNMENT, VarDevice.LIBFPTR_ALIGNMENT_LEFT); {VarDevice.Alignment := 0; } 
        cash_checkerror('Cash_PrintText:2'); // выравнивание текста в строке
        
        VarDevice.printText;  {VArDevice.PrintString;}   
        cash_checkerror('Cash_PrintText:3');
        
        if pos(CutLineText,strings[i])=1
        then begin
          NeedCutLine := true;
          IncCutLine := 1;
        end
        else if NeedCutLIne then
        begin
          inc(IncCutLine);
          if IncCutLine>=CutLineLength
          then begin
            if CutLine then
            begin
              Cash_SaveLog('PrintTextAtol:3.3.'+inttostr(i));
              
                    //Выравнивание LIBFPTR_PARAM_CUT_TYPE может принимать следующие значения:
                    //LIBFPTR_CT_FULL - полная отрезка
                    //LIBFPTR_CT_PART - частичная отрезка              

              VarDevice.SetParam(VarDevice.LIBFPTR_PARAM_CUT_TYPE,VarDevice.LIBFPTR_CT_PART); Cash_checkerror('Cash_PrintText:4');
              VarDevice.Cut; cash_checkerror('Cash_PrintText:5');// отрезка
              
            end;
            if CutLineSleep>0 then Sleep(CutLineSleep*1000);
            NeedCutLine := false;
            IncCutLine := 0;
          end;
        end;
      end;
  
      Cash_SaveLog('PrintTextAtol:4');
      if CutAfter and NeedCutLine and (IncCutLine<CutLineLength)
      then begin
        //VarDevice.StringQuantity:=CardsPrintCutLineLength-IncCutLine; //пропустить строки
        //VarDevice.FeedDocument;
        for i := 1 to (CutLineLength-IncCutLine) do
        begin
          Cash_SaveLog('PrintTextAtol:4.1.'+inttostr(i));
          VarDevice.setParam(VarDevice.LIBFPTR_PARAM_TEXT, '');  //VarDevice.Caption := ''; 
          cash_checkerror('Cash_PrintText:6');
          
          Cash_SaveLog('PrintTextAtol:4.2.'+inttostr(i));
          VarDevice.printText; //VArDevice.PrintString;   
          cash_checkerror('Cash_PrintText:7');
        end;
        if CutLine then
        begin
          //VarDevice.CutType:=False; // false полная отрезка, true - неполная
          //VarDevice.CutCheck; // отрезка
        end;
        if CutLineSleep>0 then Sleep(CutLineSleep*1000);
      end;
    end;
    Cash_SaveLog('PrintTextAtol:5');
    vardevice.printCliche; //VarDevice.PrintHeader;   
    cash_checkerror('Cash_PrintText:8');
    Cash_SaveLog('PrintTextAtol:6');
    if CutLine then
    begin
      Cash_SaveLog('PrintTextAtol:6.1');
           //VarDevice.partialcut;   cash_checkerror;

      VarDevice.SetParam(VarDevice.LIBFPTR_PARAM_CUT_TYPE,VarDevice.LIBFPTR_CT_PART); Cash_checkerror('Cash_PrintText:9');
      VarDevice.Cut; cash_checkerror('Cash_PrintText:10');// отрезка
    end;

    if CutLineSleep>0 then Sleep(CutLineSleep*1000);

    Cash_SaveLog('PrintTextAtol:exit');
  finally  
    if NeedConnect then Cash_disconnect;
  end;
end;


var List :tstringlist;
begin
  // инициализация модуля. выполняется перед выполнением каких-либо функций и для установки параметров по-умолчанию
  Cash_InitVars;
  cash_checkdevice;
  cash_checkerror('init');
  // конец инициализации

  
  (*
  list := tstringlist.create;
  try
     list.loadfromfile('');
  
     Cash_PrintText(list, //Strings :tStrings;
                    true,//       NeedConnect :boolean = true;
                    1,//       RepeatPrint :integer =1; // число повторов слипа (если поступил одинарный слип, то повторим его печать)
                    true,//       CutAfter :boolean=false; // обрезать ли слип после печати
                    true,//       CutLine :boolean =false; // Надо ли резать (некоторые аппараты не поддерживают. по-умолчанию - true
                    '=====',//       CutLineText :string =''; // линия отреза содержит
                    0,//       CutLineLength :integer = 0; // сколько надо пропустить, чтобы линия отреза была там, где надо
                    0 //       CutLineSleep :integer = 0 // на месте отреза сделать слип, в секундах. Требуется, если аппарат не имеет отрезки, тогда эти секунды дадут возможность оторвать руками
                           );
  finally
    list.free;
  end;
  *)

  //cash_connect;
  //showmessage(datetimetostr(CASH_GET_DATETIME));
  //showmessage(datetimetostr(CASH_GET_FN_LAST_DATETIME));
  
  //showmessage(asstring(VarDevice.LIBFPTR_PT_ELECTRONICALLY));
  
  //cash_connect;

  //showmessage(vardevice.isopened);  
  //cash_xreport(true);
  
{  
   // тест
  //cash_getnumbers; 
  //CreateHint('Результат : '+inttostr(ResultCode)+': '+ResultDescription);
  //createhint(asstring(resultserialnumber));
  //createhint(asstring(resultsessionnumber));
  //createhint(asstring(resultdocumentnumber));
   
   Dataset := TClientDAtaset.create(selfscript);
   with tclientdataset(Dataset) do
   begin
     fielddefs.clear;
     fielddefs.add(FName,ftstring,100,false);
     fielddefs.add(FQuantity,ftfloat,0,false);
     fielddefs.add(FPrice,ftcurrency,0,false);
     fielddefs.add(FSUMMARY,ftfloat,0,false);
     fielddefs.add(FPriceOut,ftcurrency,0,false);
     fielddefs.add(FSection,ftinteger,0,false);
     fielddefs.add(FNDS,ftinteger,0,false);
     createdataset;
     logchanges := false;

     append;
     fieldbyname(FName).asstring := 'Товар1 Товар1 Товар1 Товар1 Товар1 Товар1 Товар1 Товар1 Товар1 Товар1 Товар1 Товар1 Товар1 Товар1';
     fieldbyname(FQuantity).asfloat := 2;
     fieldbyname(FPrice).asfloat := 1.30;
     fieldbyname(FPriceOut).asfloat := 1.50;
     fieldbyname(FSummary).asfloat := fieldbyname(fquantity).asfloat * fieldbyname(fprice).asfloat;
     fieldbyname(FNDS).asinteger := 0;
     post;
     append;
     fieldbyname(FName).asstring := 'Товар2';
     fieldbyname(FQuantity).asfloat := 2;
     fieldbyname(FPrice).asfloat := 0.90;
     fieldbyname(FPriceOut).asfloat := 1.00;
     fieldbyname(FSummary).asfloat := fieldbyname(fquantity).asfloat * fieldbyname(fprice).asfloat;
     fieldbyname(FNDS).asinteger := 10;
     post;
     
     append;
     fieldbyname(FName).asstring := 'Товар3';
     fieldbyname(FQuantity).asfloat := 2;
     fieldbyname(FPrice).asfloat := 1.10;
     fieldbyname(FPriceOut).asfloat := 1.20;
     fieldbyname(FSummary).asfloat := fieldbyname(fquantity).asfloat * fieldbyname(fprice).asfloat;
     fieldbyname(FNDS).asinteger := 18;
     post;
     
     
   end;

  CASH_SHOWNDS := true;
   
  PaySummary := 100;
  EMAIL := 'borzov@solr.ru';
  IsReturn := false; 
  //CASH_UserPassword := '30';
  //CASH_SysPassword := '30'; 

  VarActionNal := true;
  VarActionBN := false;
  VarActionKredit := false; 
  VarActionSert := false;

  VarActionDiskont := false;
  VarActionDiskont1 := false;
  VarActionPension := false;
  VarActionVIP := false;
  VarActionInsurance := false;
  
  cash_execute();
  CreateHint('Результат : '+inttostr(ResultCode)+': '+ResultDescription);
}

end.
