// Скрипт автоматизации работой с фискальными регистраторами под управлением драйвера Штрих-М 
// Copyright (C)

// Печать чека. Если программа не находит данной процедуры, то выполняет просто скрипт. Оставлено для совместимости со старыми скриптами
// При успешном завершении переменная Result должна вернуть 0. Иначе - код ошибки ККМ и ResultDescription - текст ошибки
// procedure Cash_Execute(NeedConnect :boolean = true);  

// процедура вернет серийный номер кассы, номер сессии и последний номер документа
// procedure Cash_GetNumbers(NeedConnect :boolean = true);

// процедура проверяет и, если необходимо - инициализирует драйвер устройства. В случае ошибки вызовет exception 
// procedure Cash_CheckDevice;

// процедура устанавливает связь с сервером. В случае ошибки вернет false, а также установит ResultCode и Result_Description 
// procedure Cash_Connect;

// процедура разрывает связь с сервером 
// 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);

// все нижеперечисленные переменные могут быть определены в параметрах работы программы и проверяются перед выполнением на их наличие там.
// если в параметрах не найдено, то используются настройки по-умолчанию 

var 
      CASH_DeviceName :string ='AddIn.DrvFR';
      CASH_KKMSYSPASSWORD :string = ''; 
      CASH_STDUSERPASSWORD  :string = ''; // пароль кассира. Если не указан, то берется пароль пользователя программы. ДОлжен состоять только из цифр. Системный же пароль вбивается в настройках программы на закладке розницы.
      CASH_USERNAME_MANUAL :boolean =  false; // имя кассира, указывать ли вручную, взяв его из имени пользователя. Не зависит от пароля кассира и выбора кассира в аппарате. Пока используется только в чеках.
      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;
      CASH_TypeCloseBN :integer = 1; // подмена стандартных типов оплаты данными
      CASH_TypeCloseKredit :integer = 1;
      CASH_TypeCloseSert :integer = 3;
      
      CASH_CutType :integer = 0;  //0- не отрезать (не контролировать); 1-полная отрезка; 2-неполная отрезка
      
      CASH_OPENDRAWER_NAL :boolean = false; // открывать ли ящик в нале
      
      CASH_TrimNameGoods :boolean = false; // Обрезать ли наименование товара по длине строки
      
      // Длина строки. 0 - без обрезания и учета длины 
      // <0 - взять автоматически средствами драйвера      
      CASH_StringLength :integer = -1;

      CASH_SHOWNDS :boolean = true; // отображать ли НДС
      CASH_SHOWNDSSUMMARY :boolean = false;//посылать сумму НДС на аппарат (поддерживается не всеми аппаратами, теми, что не поддерживают, игнорируется, похоже)
      CASH_NDS0TYPE :integer = 1; // тип НДС0
      CASH_NDS10TYPE :integer = 2;
      CASH_NDS18TYPE :integer = 3;
      CASH_NDS20TYPE :integer = 4;
      CASH_CANDIVIDEDIVNUM :boolean = true; // можно ли делить количество и цену для фасованных товаров

      CASH_SAVEMARKS :integer = 2;  // сохранять ли марку в кассе 0 - нет. 4 -через GTIN и SerialNumber, 1 - жесткая заливка HEX,  2 - вручную формируем тэг и передаем в hex-строке
      
      Cash_Logging :boolean = true; // Сохранять лог операции своими средствами (для тестирования)

      CASH_NODISCONNECT :boolean = true; // Производить ли дисконнект по требованию
      CASH_PRINTM :boolean = false; // печатать ли символ [М] в наименовании
      CASH_CHECKMARKS :boolean = true; // Производить ли проверку марок перед их отправкой?

      CASH_CANFLOATQUANTITY :boolean = false; // допустимо ли дробное количество (не касается товаров, помеченных как весовые)
const
      FNN  = 'NN';       
      FName = 'name';
      FMARKS_BARCODES = 'MARKS_BARCODES';
      FQuantity = 'kol';
      FPrice = 'cena';
      FPriceOut = 'prodcena';
      FSection = 'section';
      FNDS = 'NDS';
      FSUMMARY = 'SUMMA';
      FFloatBarcode = 'FloatBarcode';
      FDivNum = 'DivNum';

{
var
    CardsPrintCutLineLength :integer; // сколько надо пропустить, чтобы линия отреза была там, где надо
    CardsPrintCutLine :boolean; // Надо ли резать (некоторые аппараты не поддерживают. по-умолчанию - true
    CardsPrintCutLineSleep :integer; // на месте отреза сделать слип, в секундах
}    

var
  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;  

  VarReceptSeria: string = '';
  VarReceptNumber: string = '';
  VarReceptDate: TDateTime = 0;
  
  TaxType :integer = 0;

  //GISMT_REQID: string;
  //GISMT_REQTIMESTAMP: string;
  
var
  // Возвращаемые параметры, могут быть использованы вызывающим скриптом или программой  
  ResultCode : integer =0; // результат, код ошибки, если не 0
  // Переменная ResultDescription возвращает текст кода Result
  ResultDescription :string = '';
  ResultCodeExtended :integer = 0; // расширенная ошибка согласно стандарту OPOS
  
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_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_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_USERNAME_MANUAL := appinifile.readbool('advanced','CASH_USERNAME_MANUAL',dbinifile.readbool('advanced','CASH_USERNAME_MANUAL',CASH_USERNAME_MANUAL));
   CASH_SAVEMARKS := appinifile.readinteger('advanced','CASH_SAVEMARKS',dbinifile.readinteger('advanced','CASH_SAVEMARKS',CASH_SAVEMARKS));
   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;


procedure Cash_SaveLog(StrValue :string; LogAlways :boolean = false);
begin
  if Cash_Logging or LogAlways then
  with TLogFile.create('cashshtrihm.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.ResultCode;
  ResultCodeExtended := 0;
  ResultDescription := VarDevice.ResultCodeDescription;
  result := resultcode=0;
  
  //if (ResultCode<>0) and  needexcept then raiseException('Ошибка: ' + inttostr(resultcode) + ': ' + resultDescription);
  if (ResultCode<>0) then
  begin
    strval := FuncName+': ошибка: ' + inttostr(resultcode) + ': ' + resultDescription; 
    cash_savelog(strval,true);
    if needexcept then raiseException(strval);
  end;
  
end;


procedure Cash_CheckDevice;
begin
  if varIsNull(VarDevice) then 
  begin
    VarDevice := CreateOleObject(CASH_DeviceName); 
    //VarDevice.ApplicationHandle := Application.Handle;
  end;
  //Cash_CheckError;
end;
                                   
procedure Cash_Connect(NeedSysPassword :boolean = false);
var strvalue :string; 
begin
  Cash_CheckDevice;

  if Needsyspassword 
  then begin
    strvalue := CASH_KKMSYSPASSWORD;
  end
  else begin
    StrValue := CASH_STDUSERPASSWORD;
    if strvalue = '' then StrValue := USERPROGRAMPASS; 
  end;
  if strvalue = '' then StrVAlue := GetPasswordDialog;
  if strvalue ='' then raiseException('Ошибка: не указан пароль');

  if strvalue<>inttostr(strtointprotected(strvalue)) then raiseException('Ошибка: Должен быть числовой пароль');
  
  VarDevice.Password := StrValue; Cash_CheckError('cash_connect:password');
  VarDevice.Connect; Cash_CheckError('cash_connect:connect');
end;

procedure Cash_disconnect;
begin
  if VarDevice.Connected  and not CASH_NODISCONNECT then 
  begin
    cash_savelog('cash_disconnect');
    VarDevice.Disconnect; 
  end;
end;

procedure Cash_GetNumbers(NeedConnect :boolean = true);
begin
    if needConnect then Cash_Connect; 
    try
      ResultSErialNumber := VarDevice.SerialNumber; Cash_CheckError('cash_getnumbers:serialnumber');
      Cash_SaveLog('SerialNumber: '+ResultSerialNumber); 
      ResultSessionNumber := VarDevice.SessionNumber; Cash_CheckError('cash_getnumbers:sessionnumber');
      Cash_SaveLog('SessionNumber: '+ResultSessionNumber); 
      ResultDocumentNumber := VarDevice.DocumentNumber; {не уверен, надо проверять} Cash_CheckError('cash_getnumbers:documentnumber');
      Cash_SaveLog('DocumentNumber: '+IntTostr(ResultDocumentNumber)); 
    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
      VarDevice.CutType := asboolean(CASH_CutType-1); Cash_CheckError('Cash_cut:cuttype');
      VarDevice.CutCheck; Cash_CheckError('Cash_cut:CutCheck');
    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 :integer;
    pDivNum :integer;
    IsFloatBarcode :boolean;

    pName :string;
    DetailCash :integer;
    Index :integer;
    Summ2 :currency;
    StrValue,StrVAlue1,StrValue2 :string;
    EmailSeller :string;
    stringlength :integer;
    NDS :integer;
    NalogIndex :integer;
    INN :string;
    pmarks_barcodes :string;
    pnn :string;
    MarkData :variant;
    Mark_BC,Mark_Num,Batch,ExpDate :string;
    ByteVal :byte;
    MDLP_STORAGE_ID :string;

    NEED_CHECK_KKM_COUNT: Integer; // количество марок, которые надо проверять в ККМ. Пока тут у нас все марки, которые не ЕГАИС
    MARKING_COUNT: Integer; // всего товаров с марками
    MDLP_COUNT :integer; // количество товаров с марками для МДЛП (лекарства)
    TOBACCO_COUNT :integer; // табак
    MILK_COUNT: Integer;  //молоко
    WATER_COUNT: Integer;  // вода
    ASEPT_COUNT: Integer; // антисептики
    MIZD_COUNT: Integer; // медизделия
    BAD_COUNT: Integer;  // бады

    VarValue :variant;
    StrQNT :string;
    
    CDS :TClientDataset;
    tc,tc1,tc2 :int64;
    WFCM :TMyWait;
    IsFrac :boolean;
    ik,ikc :integer;
    ThField: TField;
begin
   cash_savelog('cash_execute');
   DetailCash := Appinifile.readinteger('common','DetailCash',dbinifile.readinteger('common','DetailCash',0));  //это стандартная настройка на закладке "розница" 
   
   
   CDS := TClientDataset.Create(SelfScript);
   
   dataset.FieldDefs.update;
   for i := 0 to dataset.fieldcount-1 do
     cds.fielddefs.add(dataset.fields[i].fieldname,
                       dataset.fields[i].DataType,
                       dataset.fields[i].Size,
                       false);

   if dataset.findfield(fmarks_barcodes)=nil then
     cds.fielddefs.add(fmarks_barcodes,ftstring,1000,false);

   cds.FieldDefs.Add('tmp_ismarking',ftsmallint,0,False); // все марки не ЕГАИС
   cds.fielddefs.add('tmp_need_check_kkm',ftsmallint,0,false); // все проверяемые в ККМ
   cds.fielddefs.add('tmp_ismdlp',ftsmallint,0,false); // медикаменты
   cds.fielddefs.add('tmp_istobacco',ftsmallint,0,false);
   cds.fielddefs.add('tmp_ismilk',ftsmallint,0,false);
   cds.fielddefs.add('tmp_iswater',ftsmallint,0,false);
   cds.fielddefs.add('tmp_ismizd',ftsmallint,0,false);
   cds.fielddefs.add('tmp_isasept',ftsmallint,0,false);
   cds.fielddefs.add('tmp_isbad',ftsmallint,0,false);
   cds.fielddefs.add('tmp_quantity_divnum',ftinteger,0,false);    
   cds.fielddefs.add('tmp_check_result',ftstring,1000,false);
   cds.createdataset;
   cds.LogChanges := false; 
   
   MARKING_COUNT := 0;
   NEED_CHECK_KKM_Count := 0;
   MDLP_COUNT := 0;
   TOBACCO_COUNT := 0;
   MILK_Count := 0;
   WATER_COUNT := 0;
   MIZD_COUNT := 0;
   ASEPT_COUNT := 0;
   BAD_COUNT := 0;
   dataset.disablecontrols;
   try
     dataset.first;
     while not dataset.eof do
     begin

       cds.append;
       for i := 0 to dataset.fieldcount-1 do
         cds.fields[i].asvariant := dataset.fields[i].asvariant;

       cds.fieldbyname(fdivnum).asinteger := coalesce_integer(cds.fieldbyname(FDivNum).asinteger,1);    
       cds.fieldbyname('tmp_quantity_divnum').asinteger := round(cds.fieldbyname(fquantity).asfloat * cds.fieldbyname(fdivnum).asinteger);

       if (dataset.findfield(fmarks_barcodes)=nil) 
          and (DataSetMarks<>nil) 
          and  datasetmarks.locate('idprimary',dataset.fieldbyname('idprimary').asstring,0) 
       then 
         cds.fieldbyname(fmarks_barcodes).asstring := datasetmarks.fieldbyname('barcode').asstring;

       if (CASH_SAVEMARKS<>0) then
       begin
         if not NNInFilterEGAIS(dataset.fieldbyname(FNN).asstring) // егаисовские товары нас не интересуют 
            and not NNInFilterEGAISBEER(dataset.fieldbyname(FNN).asstring)
            and (cds.fieldbyname(fmarks_barcodes).asstring>'') then 
         begin
           inc(MARKING_COUNT);
           cds.fieldByName('tmp_ismarking').asInteger := 1;
           
           inc(NEED_CHECK_KKM_COUNT);
           cds.fieldbyname('tmp_need_check_kkm').asInteger := 1;
           
         
           if NNInFilterTobacco(dataset.fieldbyname(fnn).asstring) then
           begin 
             inc(TOBACCO_COUNT);
             cds.fieldbyname('tmp_istobacco').asinteger := 1;
           end
           else if NNInFilterMilk(dataset.fieldbyname(fnn).asstring) then
           begin 
             inc(MILK_COUNT);
             cds.fieldbyname('tmp_ismilk').asinteger := 1;
           end
           else if NNInFilterWater(dataset.fieldbyname(fnn).asstring) then
           begin 
             inc(WATER_COUNT);
             cds.fieldbyname('tmp_iswater').asinteger := 1;
           end
           else if NNInFilterMIZD(dataset.fieldbyname(fnn).asstring) then
           begin 
             inc(MIZD_COUNT);
             cds.fieldbyname('tmp_ismizd').asinteger := 1;
           end
           else if NNInFilterASEPT(dataset.fieldbyname(fnn).asstring) then
           begin 
             inc(ASEPT_COUNT);
             cds.fieldbyname('tmp_isasept').asinteger := 1;
           end
           else if NNInFilterBAD(dataset.fieldbyname(fnn).asstring) then 
           begin
             inc(BAD_COUNT);
             cds.fieldbyname('tmp_isbad').asinteger := 1;
           end
           else if NNIsNotOtherMarks(Dataset.FieldByName(fnn).asString) then
           begin
             inc(MDLP_COUNT);
             cds.fieldbyname('tmp_ismdlp').asinteger := 1;
           end;
           
           
           if cds.fieldbyname('tmp_quantity_divnum').asinteger>cds.fieldbyname(FDIVNUM).asinteger then 
           begin
             Cash_SaveLog('Отмена чека: имеются товары для МДЛП с количеством >1',true);
             raiseexception('Имеются товары для МДЛП с количеством >1');
           end;
         end;
       end;
         
       cds.post;
       dataset.next;
     end;
     dataset.first;
   finally
     dataset.enablecontrols;
   end;
   
   MDLP_STORAGE_ID := asstring(BDEQueryValue('select mdlp_storage_id from agents where id = :id',[STORAGE_ID]));
   // если mdlp_storage_id пуст, то проверим сразу наличие товаров с марками и остановим скрипт, если имеются таковые
   if (CASH_SAVEMARKS<>0)
      and (MDLP_COUNT>0) 
      and (mdlp_storage_id='')  then 
   begin  
     Cash_SaveLog('Отмена чека: имеются товары для МДЛП, но у склада не указан его код в МДЛП',true);
     raiseexception('Имеются товары для МДЛП, но у склада не указан его код в МДЛП');
   end;


   if needconnect then 
     Cash_Connect;
   try
      VarDevice.getecrstatus; Cash_CheckError('Cash_Execute:getecrstatus');
      if (VarDevice.ECRMode=4) then 
        begin 
          VarDevice.OpenSession; Cash_CheckError('Cash_Execute:OpenSession'); 
          Cash_SaveLog('Открытие смены');
          
          {VarDevice.getecrstatus;
          Cash_SaveLog('VarDevice.ecrmode='+asstring(VarDevice.ecrmode));
          Cash_SaveLog('VarDevice.ecradvancedmode='+asstring(VarDevice.ecradvancedmode));
          sleep(10000);
          VarDevice.getecrstatus;
          Cash_SaveLog('sleep(10000)');
          Cash_SaveLog('VarDevice.ecrmode='+asstring(VarDevice.ecrmode));
          Cash_SaveLog('VarDevice.ecradvancedmode='+asstring(VarDevice.ecradvancedmode));}
        end;
      
      //else    
       // еще идет печать предыдущего документа, это возможно, если мы делим чек,
       // то повисим (до 15 секунд)
       for i := 1 to 10 do
       begin
         VarDevice.getecrstatus; Cash_CheckError('Cash_Execute:getecrstatus');
         if {(VarDevice.ecrmode=8) and} (VarDevice.ecradvancedmode=5)  then sleep(3000)  else break;
       end;
       Cash_CheckError('Cash_Execute:1');
      
       if CASH_CHECKMARKS and (NEED_CHECK_KKM_COUNT>0) then
       begin
         tc := gettickcount;
         WFCM := TMyWait.create('Идет проверка марок в ККМ',selfscript,nil,true);
         try
           WFCM.progressbarmax := mdlp_count;
           WFCM.StopKey := 27;
           wfcm.AdvancedText := 'Для прерывания поиска нажмите ESC';
           WFCM.progressbarmax := NEED_CHECK_KKM_COUNT;
           Cash_SaveLog('Проверка марок');
           //Cash_CheckShiftState(false,false);
           
           cds.first;
           while not cds.eof do
           begin
             if cds.fieldbyname('tmp_NEED_CHECK_KKM').asinteger>0 then
             begin
               Cash_SaveLog('Марка: '+stringreplace(cds.fieldbyname(fmarks_barcodes).asstring,#29,'{SG}',true,true));
               tc1 := gettickcount;
               
               VarDevice.BarcodeHex := StringToHex(cds.fieldbyname(fmarks_barcodes).asstring,false);

               isFrac := 
                  (cds.fieldbyname(fdivnum).asinteger>1)
                  and (cds.fieldbyname('tmp_quantity_divnum').asinteger>0) 
                  and (cds.fieldbyname('tmp_quantity_divnum').asinteger<cds.fieldbyname(fdivnum).asinteger);

               
               if IsFrac then begin
                 VarDevice.ItemStatus := 2; cash_checkerror('ItemStatus');
                
                 VarDevice.TagNumber := 2108; cash_checkerror('2108');
                 VarDevice.TagType := 0; cash_checkerror('TagType');
                 VarDevice.TagValueInt := 0; cash_checkerror('TagValueInt');
                 VarDevice.GetTagAsTLV; cash_checkerror('GetTagAsTLV');
                 StrValue := VarDevice.TLVDataHEX; cash_checkerror('TLVDataHEX');
                  
                 VarDevice.TagNumber := 1023; cash_checkerror('1023');
                 VarDevice.TagType := $0000000B; cash_checkerror('TagType');
                 VarDevice.TagValueFVLND := 1; cash_checkerror('TagValueFVLND');
                 VarDevice.GetTagAsTLV; cash_checkerror('GetTagAsTLV');
                 StrValue := StrValue+ VarDevice.TLVDataHEX; cash_checkerror('TLVDataHEX');


                 VarDevice.TagNumber := 1291; cash_checkerror('1291');
                 VarDevice.TagType := $00000008; cash_checkerror('TagType');
                 VarDevice.FNBeginSTLVTag; cash_checkerror('FNBeginSTLVTag');
                 VarDevice.TagNumber := 1293; cash_checkerror('1293');
                 VarDevice.TagType := $00000004; cash_checkerror('TagType');
                 VarDevice.TagValueVLN := cds.fieldbyname('tmp_quantity_divnum').asstring; cash_checkerror('TagValueVLN');
                 VarDevice.FNAddTag; cash_checkerror('FNAddTag');
                 VarDevice.TagNumber := 1294; cash_checkerror('1294');
                 VarDevice.TagType := $00000004; cash_checkerror('TagType');
                 VarDevice.TagValueVLN := cds.fieldbyname(fdivnum).asstring; cash_checkerror('TagValueVLN');
                 VarDevice.FNAddTag; cash_checkerror('FNAddTag');
                 VarDevice.TagNumber := 1291; cash_checkerror('1291');
                 VarDevice.TagType := $00000008; cash_checkerror('TagType');
                 VarDevice.GetTagAsTLV; cash_checkerror('GetTagAsTLV');
                 StrVAlue := StrValue + VarDevice.TLVDataHex; cash_checkerror('TLVDataHex'); 

                 VarDevice.TLVDataHEX := StrValue;

               end
               else begin   
                 VarDevice.ItemStatus := 1; cash_checkerror('ItemStatus');
                 VarDevice.TLVDataHEX := '';
               end;
               
               VarDevice.FNCheckItemBarcode; cash_checkerror('FNCheckItemBarcode');
               
               strvalue := asstring(VarDevice.KMServerCheckingStatus); cash_checkerror('KMServerCheckingStatus');
               cds.edit;
               cds.fieldbyname('tmp_check_result').asstring := strvalue;
               cds.post;
               Cash_SaveLog(StrValue);
             
               // Подтверждаем реализацию товара с указанным КМ
               //VarDevice.DeclineMarkingCode(); cash_checkerror('DeclineMarkingCode');
               VarDevice.FNacceptMarkingCode;  cash_checkerror('FNAcceptMarkingCode');
               wfcm.incprogress;
               if WFCM.CheckStop then
               begin
                 Cash_SaveLog('Оператор остановил проверку марок');
                 raiseexception('Оператор остановил проверку марок');
               end;               


               tc2 := gettickcount;        
               with TLogFile.create('cashcheckmarktimes.log',false,1000000) do 
               try
                 AddStr(inttostr(tc2-tc1));
               finally
                 free
               end;
               cash_savelog('Время проверки марки: '+inttostr(tc2-tc1)+' мс.');
               tc1 := tc2;
             end;
             
             cds.next;
           end;
         finally
           FreeAndNil(WFCM)
         end;
         tc2 := gettickcount;               
         cash_savelog('Время проверки всех марок: '+inttostr(tc2-tc)+' мс.');
       end;
       
       Cash_GetNumbers(false); 

       if CASH_stringlength <0 then 
         stringlength := 0;   

       EmailSeller := appIniFile.readstring('cash','CashOnlineEmailSeller',DBIniFile.readstring('cash','CashOnlineEmailSeller',''));
       
       VArDevice.CheckType := ifthen_integer(IsReturn,2,0); Cash_CheckError('Cash_Execute:CheckType');
       VarDevice.OpenCheck; Cash_CheckError('Cash_Execute:OpenCheck');


       if (CASH_SAVEMARKS<>0) and (MDLP_COUNT>0) then
       begin
         // взято отсюда https://infostart.ru/1c/articles/1192569/
         // см также примеры https://docs.google.com/document/d/1m3gHyKvM0gBMPgpzG-1cFsfVDnecouCtzQBJVDooGuM/edit#heading=h.z4tw3du13anc

         //VarDevice.FNOperation; 
         VarDevice.TagNumber := 1084; Cash_CheckError('Cash_Execute:1084');
         VarDevice.FNBeginSTLVTag; Cash_CheckError('Cash_Execute:FNBeginSTLVTag');
         VarValue := VarDevice.TagID; Cash_CheckError('Cash_Execute:TagID GET');
         
         VarDevice.TagID := VarValue; Cash_CheckError('Cash_Execute:TagID SET');          

         VarDevice.TagNumber := 1085; Cash_CheckError('Cash_Execute:1085');
         VarDevice.TagType := 7; Cash_CheckError('Cash_Execute:TagType');
         VarDevice.TagValueStr := 'mdlp'; Cash_CheckError('Cash_Execute:TagValueStr');
         VarDevice.FNAddTag(); Cash_CheckError('Cash_Execute:FNAddTag');
          
         VarDevice.TagID := VarValue; Cash_CheckError('Cash_Execute:TagID SET');
         VarDevice.TagNumber := 1086; Cash_CheckError('Cash_Execute:1086');
         VarDevice.TagType := 7; Cash_CheckError('Cash_Execute:TagType');
         VarDevice.TagValueStr := 'sid'+mdlp_storage_id+'&'; Cash_CheckError('Cash_Execute:TagValueSTr');
         VarDevice.FNAddTag(); Cash_CheckError('Cash_Execute:FNAddTag');
                                          
         VarDevice.FNSendSTLVTag();  Cash_CheckError('Cash_Execute:FNSendSTLVTag');
         

 
         // Передаем отраслевой реквизит
         VarDevice.TagNumber := 1261; Cash_CheckError('Cash_Execute:1261');
         VarDevice.FNBeginSTLVTag; Cash_CheckError('Cash_Execute:FNBeginSTLVTag');

         VarDevice.TagNumber := 1262; Cash_CheckError('Cash_Execute:1262');
         VarDevice.TagType := $00000007; Cash_CheckError('Cash_Execute:TagType');
         VarDevice.TagValueStr := '020'; Cash_CheckError('Cash_Execute:TagValueStr');
         VarDevice.FNAddTag; Cash_CheckError('Cash_Execute:FNAddTag');

         VarDevice.TagNumber := 1263; Cash_CheckError('Cash_Execute:1263');
         VarDevice.TagType := $00000007; Cash_CheckError('Cash_Execute:TagType');
         VarDevice.TagValueStr := bvformatdatetime('dd/mm/yyyy',date(),'','.'); Cash_CheckError('Cash_Execute:TagValueStr');
         VarDevice.FNAddTag; Cash_CheckError('Cash_Execute:FNAddTag');

         VarDevice.TagNumber := 1264; Cash_CheckError('Cash_Execute:1264');
         VarDevice.TagType := $00000007; Cash_CheckError('Cash_Execute:TagType');
         VarDevice.TagValueStr := inttostr(ResultDocumentNumber); Cash_CheckError('Cash_Execute:TagValueStr');
         VarDevice.FNAddTag; Cash_CheckError('Cash_Execute:FNAddTag');

         VarDevice.TagNumber := 1265; Cash_CheckError('Cash_Execute:1265');
         VarDevice.TagType := $00000007; Cash_CheckError('Cash_Execute:TagType');
         
         StrValue2 := 'tm=mdlp';
         
         if VarReceptSeria>'' then
           StrValue2 := StrValue2 + '&ps='+StringReplace(VarReceptSeria,'&','&&',True,True);
         if VarReceptNumber>'' then
           StrValue2 := StrValue2 + '&dn='+StringReplace(VarReceptNumber,'&','&&',True,True); //&&781 ??
         if VarReceptDate<>0 then
           StrValue2 := StrValue2 + '&dd='+FormatDateTime('yymmdd',VarReceptDate);
         

         StrValue2 := StrValue2 + '&sid='+mdlp_storage_id+'&';
          
         VarDevice.TagValueStr := StrValue2; Cash_CheckError('Cash_Execute:TagValueStr');
         //VarDevice.TagValueStr := 'tm=mdlp&sid='+mdlp_storage_id+'&'; Cash_CheckError('Cash_Execute:TagValueStr');
         VarDevice.FNAddTag; Cash_CheckError('Cash_Execute:FNAddTag');

         VarDevice.TagNumber := 1261;
         VarDevice.FNSendSTLVTag();  Cash_CheckError('Cash_Execute:FNSendSTLVTag1');

       end
       else if (VarReceptSeria>'') or (VarReceptNumber>'') or (VarReceptDate<>0) then
       begin
         Cash_SaveLog('Ошибка: Указан рецепт, но нет ни одного маркированного товара, либо не указана опция сохранения марок. Сохранение чека невозможно.');
         raiseexception('Ошибка: Указан рецепт, но нет ни одного маркированного товара, либо не указана опция сохранения марок. Сохранение чека невозможно.');
       end;

       if Email<>''
       then begin
          //Отправка данных о покупателе
          VarDevice.CustomerEmail := Email; Cash_CheckError('Cash_Execute:email');  //В качестве параметра можно передавать или телефон (+7хххххххххх) или e-mail: test@test.com
          VarDevice.FNSendCustomerEmail; Cash_CheckError('Cash_Execute:fnsendcustomeremail');
       end;
       if EmailSEller<>'' // адрес электронной почты отправителя чека 
       then begin
          //Отправка данных о продавце
          //VarDevice.AttrNumber := 1117; Cash_CheckError;
          //VarDevice.AttrValue := EmailSeller; Cash_CheckError; 
          //VarDevice.WriteAttribute; Cash_CheckError; 
       end;

       
       if Cash_UserName_Manual and (UserProgram>'')
       then begin
          VarDevice.TagNumber := 1021; Cash_CheckError('Cash_Execute:1021');
          VarDevice.TagType := 7; Cash_CheckError('Cash_Execute:1021:7');
          VarDevice.TagValueStr := UserProgram; Cash_CheckError('Cash_Execute:1021:TagVAlueStr');
          VarDevice.FNSendTag; Cash_CheckError('Cash_Execute:1021:fnSendTag');
       end;

       if UserProgramID=0 then INN := '' else INN := UserProgramINN;

       if Cash_UserName_Manual and (INN>'')
       then begin
          VarDevice.TagNumber := 1203; Cash_CheckError('Cash_Execute:1203');
          VarDevice.TagType := 7; Cash_CheckError('Cash_Execute:1203:7');
          VarDevice.TagValueStr := INN; Cash_CheckError('Cash_Execute:1203:TagValueStr');
          VarDevice.FNSendTag; Cash_CheckError('Cash_Execute:1203:FNSendTag');
       end;    
       // печать чека

       cds.disablecontrols;
       try
         cash_savelog('Товары');
         pSummaryCash := 0;
         pSummaryDiscount := 0;      // для варианта без детализации

         cds.First;
         while not cds.eof do
         begin

           isFrac := 
              (cds.fieldbyname(fdivnum).asinteger>1)
              and (cds.fieldbyname('tmp_quantity_divnum').asinteger>0) 
              and (cds.fieldbyname('tmp_quantity_divnum').asinteger<cds.fieldbyname(fdivnum).asinteger);

           if isfrac and (cds.fieldbyname('tmp_ismdlp').asinteger>0)  then 
           begin          
             ikc := cds.fieldbyname('tmp_quantity_divnum').asinteger;
             if ikc<1 then ikc := 1;
           end
           else ikc := 1;
  
           for ik := 1 to ikc do
           begin
           
             pQuantityDivNum := cds.fieldbyname('tmp_quantity_divnum').asinteger;
           
             pPrice := cds.fieldbyname(FPrice).asCurrency;
             cash_savelog('Цена:'+floattostr(pprice));
             pPriceOut := cds.fieldbyname(fPriceOut).ascurrency;
             if pPriceOut < pPrice then  pPriceOut := pPrice;
             cash_savelog('Цена прод:'+floattostr(ppriceout));
  
             pQuantity := cds.fieldbyname(FQuantity).AsFloat;
             pQntFrac := bvroundto(frac(pQuantity),-6);
             cash_savelog('pQntFrac: '+asstring(pQntFrac));
             pDivNum := coalesce_integer(cds.fieldbyname(FDivNum).asinteger,1);
             cash_savelog('pDivNum: '+asstring(pDivNum));
             //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 := cds.fieldbyname(FFloatBarcode).asinteger<>0; 
             
             
             if cds.fieldbyname('tmp_ismarking').asinteger>0 then
             begin
               if isFrac then 
               begin
                 pPriceOut := pPriceOutDivNum;
                 pPrice := pPriceDivNum;
                 pQuantity := 1;
                 pQuantityDivNum := 1;
                 cash_savelog('МДЛП деленый товар: '+floattostr(pQuantity)+' : '+floattostr(pprice)+' : '+floattostr(ppriceout)); 
               end
               else  
                 cash_savelog('МДЛП неделеный товар: '+floattostr(pQuantity)+' : '+floattostr(pprice)+' : '+floattostr(ppriceout)); 
             end
             else if pQntFrac<>0 then // есть дробное количество
             begin
               if (DetailCash=0) // построчная детализация
                  and CASH_CANDIVIDEDIVNUM // можно делить при печати фасованные товары 
                  and not IsFloatBarcode // товар не является весовым 
                  and (pDivNum<>1) // товар делится
               then begin // надо вывести целое количество фасованных единиц по цене фасованной единицы
                 pPriceOut := pPriceOutDivNum;
                 pPrice := pPriceDivNUm;
                 pQuantity := 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
               else
                 cash_savelog('Товар: '+floattostr(pQuantity)+' : '+floattostr(pprice)+' : '+floattostr(ppriceout));
             end;
             
             {
             if (DetailCash<>0) // печать без детализации 
                or IsFloatBarcode and (pQntFrac<>bvroundto(pQntFrac,-3)) // Предохранимся от количеств, которые не понимает касса, оставляем 1xСумма
                or not IsFloatBarcode and (pQntFrac<>0) and (
                   Not CASH_CANDIVIDEDIVNUM // нельзя делить фасованные, указано в настройках
                   or (pDivNum=1)   // товар не является делящимся
                   or (pQuantityDivNum<>bvroundto(pQuantityDivNum)) // число 2.999997 в фасовке еще допускается, но 2.999 - уже странно
                   or (pPriceOutDivNum <> bvRoundTo(pPriceOutDivNum,-2))) // проблемы с ценой фасованной, лишние знаки после запятой
             then begin
               cash_savelog('Печать без детализаци');
               pPriceOut := pQuantity * pPriceOut;
               pPrice := pQuantity * pPrice;
               pQuantity := 1;   
               cash_savelog('1 x Сумма: '+formatfloat(',0.00#######',pQuantity)+' : '+formatfloat(',0.00#######',pprice)+' : '+formatfloat(',0.00#######',ppriceout));
             end
             else if (pQntFrac<>0) and not IsFloatBarcode and (pDivNum<>1) // невесовой товар, но делится
             then begin
                  pPriceOut := pPriceOutDivNum;
                  pPrice := pPriceDivNUm;
                  pQuantity := bvroundto(pQuantityDivNum); 
                  cash_savelog('Фас.кол-во x цена за фасов.ед.: '+formatfloat(',0.00#######',pQuantity)+' : '+formatfloat(',0.00#######',pprice)+' : '+formatfloat(',0.00#######',ppriceout));
             end;}
  
             pPriceOut := bvroundto(pPriceOut, -2,rpTruncate);
             pPrice := bvroundto(pPrice, -2,rpTruncate);
  
             pSummary := pQuantity * pPrice;
             pSummaryOut := pQuantity * pPriceOut;
  
             pSummaryCash := pSummaryCash + pSummary;
             pSummaryDiscount := pSummaryDiscount + (pSummaryOut - pSummary);
             pDiscount := bvRoundTo(pSummaryOut-pSummary,-2);
             
             cash_savelog('Суммы: pSummary='+formatfloat(',0.00#######',pSummary)+' : pSummaryOut='+formatfloat(',0.00#######',pSummaryOut)+' : pDiscount='+formatfloat(',0.00#######',pDiscount)+' : pSummaryCash='+formatfloat(',0.00#######',pSummaryCash));
  
             if (bvroundto(pSummary, -2) > 0)
             then begin
  
               pname := cds.fieldbyname(FName).asstring;
  			 
               if CASH_PRINTM and (cds.fieldbyname('tmp_NEED_CHECK_KKM').asinteger>0) then 
                  pName := '[M+] '+pName; 
  			 
               if (stringlength>0) and cash_trimnamegoods then pname := copy(pname,1,stringlength);
               
               PNN := cds.fieldbyname(FNN).asstring;
  
               if cds.findfield(fmarks_barcodes)<>nil then pmarks_barcodes := cds.fieldbyname(fmarks_barcodes).asstring 
               else if (DataSetMarks<>nil) and  datasetmarks.locate('idprimary',cds.fieldbyname('idprimary').asstring,0) 
               then pmarks_barcodes := datasetmarks.fieldbyname('barcode').asstring
               else 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_savelog('параметры товара:');
                 VarDevice.StringForPrinting := pName; Cash_CheckError('Cash_Execute:Name');
  
                 cash_savelog('Количество: '+floattostr(pQuantity));
                 VarDevice.Quantity := pQuantity; Cash_CheckError('Cash_Execute:Quantity');
                 
                 cash_savelog('Цена: '+floattostr(ifthen_currency(CASH_DiscountType<>0,pPriceOut,pPrice)));
                 VarDevice.Price := ifthen_currency(CASH_DiscountType<>0,pPriceOut,pPrice); Cash_CheckError('Cash_Execute:Price');
  
                 thField := cds.findfield(fsection);
                 if thfield<>nil then VarDevice.Department := coalesce_integer(thField.asinteger,CurrentSection,1)
                 else VarDevice.Department := coalesce_integer(CurrentSection,1); 
                 Cash_CheckError('Cash_Execute:Section');
  
                 if Cash_ShowNDS
                 then begin
                   cash_savelog('НДС');
                   NDS := cds.fieldbyname('nds').asinteger;
                   NalogIndex := ifthen_integer(NDS=20,CASH_NDS20TYPE,ifthen_integer(NDS=18,CASH_NDS18TYPE,ifthen_integer(NDS=10,CASH_NDS10TYPE,CASH_NDS0TYPE)));
  
                   VarDevice.Tax1 := NalogIndex;
                   VarDevice.Tax2 := 0;
                   VarDevice.Tax3 := 0;
                   VarDevice.Tax4 := 0;
                   
                   //if cash_showNDSSUMMARY then VarDevice.Summ := bvroundto(cds.fieldbyname(FSummary).asfloat *NDS/(100+NDS),-2);                        
                   Cash_CheckError('Cash_Execute:nds');
                   
                 end;
                 
                 VarDevice.MeasureUnit := 0; Cash_CheckError('Cash_execute;MeasureUnit');
                 
  
                 if IsFrac and (cds.fieldbyname('tmp_ismarking').asinteger>0) then 
                 begin
                   cash_savelog('IsFrac: '+asstring(IsFrac));
                   VarDevice.DivisionalQuantity := true; Cash_CheckError('Cash_execute;DivisionalQuantity');  
                   VarDevice.Numerator := 1; //cds.fieldbyname('tmp_quantity_divnum').asstring; cash_checkerror('NUMERATOR');
                   VarDevice.Denominator := cds.fieldbyname(fdivnum).asstring; cash_checkerror('DENOMINATOR');
                     
                   cash_savelog('quantity_divnum: '+cds.fieldbyname('tmp_quantity_divnum').asstring);
                   cash_savelog(fdivnum+': '+cds.fieldbyname(fdivnum).asstring);
                                      
                 end
                 else begin                    
                   VarDevice.DivisionalQuantity := false; Cash_CheckError('Cash_execute;DivisionalQuantity');
                 end;  
               
                 VarDevice.FNOperation;  Cash_CheckError('Cash_execute;FNOperation'); //if IsReturn then VarDevice.ReturnSale else VarDevice.Sale; Cash_CheckError('Cash_Execute:returnsale');
  
                 VarDevice.StringForPrinting := ''; Cash_CheckError('Cash_Execute:str2');
  
  
                 if (CASH_SAVEMARKS<>0) and (pmarks_barcodes>'')
                    and not NNInFilterEGAIS(pNN) and not NNInFilterEGAISBEER(pNN) 
                 then begin
  
                   (*if NNInFilterTOBACCO(pNN) then
                   begin
                      { https://docs.google.com/document/d/1m3gHyKvM0gBMPgpzG-1cFsfVDnecouCtzQBJVDooGuM/edit#heading=h.z4tw3du13anc
                       может пригодиться?
                      Driver.FNOperation();
                      Driver.BarCode = "010173456789433921GPiOI99wtmAtM91002492/r+UHmeqRXXfjCTLC2j8MauSavTwXGF/kkzKpaKQ50ZNvoLatTkTeHfGO6vLvGtTlQP0c5MOk0X15Wp3JYp6KA==";
                      Driver.FNSendItemBarcode();
                      }
                   end
                   else*) 
                   begin
                     cash_savelog('SaveMarks='+inttostr(cash_savemarks));
                     cash_savelog('mark='+stringreplace(pmarks_barcodes,#29,'{SG}',true,true));
                     
                     VarDevice.BarcodeHex := StringToHex(pMarks_barcodes,false);
                     VarDevice.FNSendItemBarcode; Cash_CheckError('Cash_execute;FNSendItemBarcode'); 


                     (* это из атола. где здесь размещение резульатта проверки марки в ККМ?
                     if cds.FieldByName('TMP_NEED_CHECK_KKM').asInteger<>0 then
                     begin
                         
                         if CASH_CHECKMARKS then 
                           IntValue := strtointprotected(cds.fieldbyname('tmp_check_result').asstring)
                         else  
                           IntValue := 0; //VarDevice.getParamInt(VarDevice.LIBFPTR_PARAM_MARKING_CODE_ONLINE_VALIDATION_RESULT); cash_checkerror('GET LIBFPTR_PARAM_MARKING_CODE_ONLINE_VALIDATION_RESULT');
                         
                         VarDevice.setParam(VarDevice.LIBFPTR_PARAM_MARKING_CODE_ONLINE_VALIDATION_RESULT, IntValue); cash_checkerror('LIBFPTR_PARAM_MARKING_CODE_ONLINE_VALIDATION_RESULT');
                         VarDevice.setParam(VarDevice.LIBFPTR_PARAM_MARKING_PROCESSING_MODE, 0);  cash_checkerror('LIBFPTR_PARAM_MARKING_PROCESSING_MODE');
                     end;
                     *)


                     // Версия оформления STLVTag, как с 1260. Как правильно?  
                     // Передаем отраслевой реквизит
                     if cds.fieldbyName('tmp_ismdlp').asInteger <> 0 then
                     begin
                       VarDevice.TagNumber := 1262; Cash_CheckError('Cash_Execute:1262');
                       VarDevice.TagType := $00000007; Cash_CheckError('Cash_Execute:TagType');
                       VarDevice.TagValueStr := '020'; Cash_CheckError('Cash_Execute:TagValueStr');
                       VarDevice.FNSendTagOperation; Cash_CheckError('Cash_Execute:FNSendTagOperation');
              
                       VarDevice.TagNumber := 1263; Cash_CheckError('Cash_Execute:1263');
                       VarDevice.TagType := $00000007; Cash_CheckError('Cash_Execute:TagType');
                       VarDevice.TagValueStr := bvformatdatetime('dd/mm/yyyy',date(),'','.'); Cash_CheckError('Cash_Execute:TagValueStr');
                       VarDevice.FNSendTagOperation; Cash_CheckError('Cash_Execute:FNSendTagOperation');
              
                       VarDevice.TagNumber := 1264; Cash_CheckError('Cash_Execute:1264');
                       VarDevice.TagType := $00000007; Cash_CheckError('Cash_Execute:TagType');
                       VarDevice.TagValueStr := inttostr(ResultDocumentNumber); Cash_CheckError('Cash_Execute:TagValueStr');
                       VarDevice.FNSendTagOperation; Cash_CheckError('Cash_Execute:FNSendTagOperation');
            
                       StrValue2 := 'tm=mdlp';
                       if DataSet.FieldByName('IsNeedRecept').asInteger<>0 then
                       begin
                         if VarReceptSeria>'' then
                           StrValue2 := StrValue2 + '&ps='+StringReplace(VarReceptSeria,'&','&&',True,True);
                         if VarReceptNumber>'' then
                           StrValue2 := StrValue2 + '&dn='+StringReplace(VarReceptNumber,'&','&&',True,True); //&&781 ??
                         if VarReceptDate<>0 then
                           StrValue2 := StrValue2 + '&dd='+FormatDateTime('yymmdd',VarReceptDate);
                       end;
                       StrValue2 := StrValue2 + '&sid='+mdlp_storage_id+'&';

                       VarDevice.TagNumber := 1265; Cash_CheckError('Cash_Execute:1265');
                       VarDevice.TagType := $00000007; Cash_CheckError('Cash_Execute:TagType');
                       VarDevice.TagValueStr := StrValue2; Cash_CheckError('Cash_Execute:TagValueStr');
                       VarDevice.FNSendTagOperation; Cash_CheckError('Cash_Execute:FNSendTagOperation');
                     end
                     else if {(GISMT_REQID>'') and (GISMT_REQTIMESTAMP>'')} (CDS.FindField('GISMT_CHECKED')<>nil) and (CDS.FieldByName('GISMT_CHECKED').asINteger<>0) then
                     begin  
                       cash_savelog('Ввод реквизита 1260 для ГИС МТ');
                       
                       VarDevice.TagNumber := 1262; Cash_CheckError('Cash_Execute:1262');
                       VarDevice.TagType := $00000007; Cash_CheckError('Cash_Execute:TagType');
                       VarDevice.TagValueStr := '030'; Cash_CheckError('Cash_Execute:TagValueStr');
                       VarDevice.FNSendTagOperation; Cash_CheckError('Cash_Execute:FNSendTagOperation');
              
                       VarDevice.TagNumber := 1263; Cash_CheckError('Cash_Execute:1263');
                       VarDevice.TagType := $00000007; Cash_CheckError('Cash_Execute:TagType');
                       VarDevice.TagValueStr := bvformatdatetime('dd/mm/yyyy',date(),'','.'); Cash_CheckError('Cash_Execute:TagValueStr');
                       VarDevice.FNSendTagOperation; Cash_CheckError('Cash_Execute:FNSendTagOperation');
              
                       VarDevice.TagNumber := 1264; Cash_CheckError('Cash_Execute:1264');
                       VarDevice.TagType := $00000007; Cash_CheckError('Cash_Execute:TagType');
                       VarDevice.TagValueStr := inttostr(ResultDocumentNumber); Cash_CheckError('Cash_Execute:TagValueStr');
                       VarDevice.FNSendTagOperation; Cash_CheckError('Cash_Execute:FNSendTagOperation');
            

                       VarDevice.TagNumber := 1265; Cash_CheckError('Cash_Execute:1265');
                       VarDevice.TagType := $00000007; Cash_CheckError('Cash_Execute:TagType');
                       StrValue2 := 'UUID='+CDS.FieldByName('GISMT_REQID').asSTring+'&Time='+CDS.FieldByName('GISMT_REQTIMESTAMP').asSTring; //+'&';
                       
                       if CDS.FieldByName('GISMT_INST').asString>'' then
                         StrValue2 := StrValue2 + '&Inst='+CDS.FieldByName('GISMT_INST').asString;
                       if CDS.FieldByName('GISMT_VERSION').asString>'' then
                         StrValue2 := StrValue2 + '&Ver='+CDS.FieldByName('GISMT_VERSION').asString;
                       
                       VarDevice.TagValueStr := StrValue2; Cash_CheckError('Cash_Execute:TagValueStr');
                       VarDevice.FNSendTagOperation; Cash_CheckError('Cash_Execute:FNSendTagOperation');
                       
                     end;
                   end
                 end
                 else begin
                   strvalue := asstring(BDEQueryValue('select tag1162 from tovar where nn = :nn',[pNN]));
                   if strvalue>'' then 
                   begin
  
                     if CASH_SAVEMARKS = 2 then
                     begin
                       cash_savelog('nntag1162='+strvalue);
                       strvalue := IntToHex(STrToInt64Protected(strvalue),6);
                       strvalue := StrPadLeft(strvalue,12,'0');
                       StrVAlue := '450D'+StrValue;
                       cash_savelog('nntag1162_hex2='+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)); // пробелы между знаками поставим
    
                       // по варианту CASH_SAVEMARKS=2
                       //VarDevice.FNOperation; Cash_CheckError('Cash_Execute:1162:FNOperation');
                       VarDevice.TagNumber := 1162; Cash_CheckError('Cash_Execute:1162');
                       VarDevice.TagType := 9; Cash_CheckError('Cash_Execute:1162:tagtype 9');
                       //VarDevice.TagValueBin := MarkData; Cash_CheckError('Cash_Execute:1162:TagVAlueBin');
                       VarDevice.TagValueBinHex := StrValue; Cash_CheckError('Cash_Execute:tagValueBinHex');
                       VarDevice.FNSendTagOperation; {VarDevice.FNSendTag;} Cash_CheckError('Cash_Execute:1162:fnSendTagOperation:1162-1');
                     end
                     else begin
                       //VarDevice.FNOperation; Cash_CheckError('Cash_Execute:1162:FNOperation');
                       VarDevice.MarkingType := $450D; Cash_CheckError('Cash_Execute:1162:MarkingType'); //EAN-13
                       VarDevice.BarCode := StrValue; Cash_CheckError('Cash_Execute:1162:Barcode'); 
                       VarDevice.FNSendItemCodeData; Cash_CheckError('Cash_Execute:1162:FNSendItemCodeData');
                     end;
  
  
                     // печать тэга 1162 для всяких марлей (немаркируемые)
                     // https://docs.google.com/document/d/1m3gHyKvM0gBMPgpzG-1cFsfVDnecouCtzQBJVDooGuM/edit#heading=h.z4tw3du13anc
                     // см.также пример в руководстве для программиста
  
                     // Driver.FNOperation;
                     // Driver.MarkingType := $450D; //EAN-13
                     // Driver.BarCode := '4606203090785';
                     // Driver.FNSendItemCodeData;
  
  
                     //VarDevice.FNOperation;
                     //VarDevice.MarkingType := $450D; //EAN-13
                     //VarDevice.BarCode := StrVAlue;
                     //VarDevice.FNSendItemCodeData;
                   end;
                 end;
  
                 if CASH_DiscountType=1
                 then begin
                   cash_savelog('CASH_DiscountType:1');  
                   if bvroundto(pSummaryOut - pSummary, -2) <> 0 then
                   begin
                     VarDevice.Summ1 := bvroundto(pSummaryOut - pSummary, -2); Cash_CheckError('Cash_Execute:summ1');
                     cash_savelog('Summ1:'+formatfloat(',0.00#######',pSummaryOut - pSummary));
                     VarDevice.Summ2 := 0; Cash_CheckError('Cash_Execute:summ2');
                     VarDevice.Summ3 := 0; Cash_CheckError('Cash_Execute:summ3');
                     VarDevice.Summ4 := 0; Cash_CheckError('Cash_Execute:summ4');
                     VarDevice.Discount; Cash_CheckError('Cash_Execute:Discount');
                   end;
                 end;
                 if (pDiscount <> 0) and CASH_PrintStringDiscountSummary        
                 then begin  
                   cash_savelog('(pDiscount <> 0) and CASH_PrintStringDiscountSummary');   
                   //VarDevice.Caption := 'В том числе скидка: '+bvCurrtostrF(pDiscount)+#13+'Цена без скидки: '+bvCurrToStrF(pPriceOut);
                   StrValue := 'Скидка: ';
                   cash_savelog('Скидка: ');  
                   strvalue1 := bvcurrtostrf(pdiscount);
                   cash_savelog('strvalue1='+asstring(strvalue1)); 
                   
                   if stringlength>0 then StrValue1 := StrPad(strvalue1,stringlength-length(strvalue),' ',true);
         
                   VarDevice.StringForPrinting := strvalue+strvalue1; Cash_CheckError('Cash_Execute:STr3');
                   cash_savelog('VarDevice.StringForPrinting='+asstring(strvalue)+ asstring(strvalue1));
                    
                   VarDevice.PrintString; Cash_CheckError('Cash_Execute:STr4');
                   cash_savelog('Cash_Execute:STr4');
                    
                   VarDevice.StringForPrinting := ''; Cash_CheckError('Cash_Execute:STr5');
                   cash_savelog('Cash_Execute:STr5');
                 end;
  
                 if (pDiscount <> 0) and CASH_PrintStringPriceOut        
                 then begin   
                   cash_savelog('(pDiscount <> 0) and CASH_PrintStringPriceOut');
                   //VarDevice.Caption := 'В том числе скидка: '+bvCurrtostrF(pDiscount)+#13+'Цена без скидки: '+bvCurrToStrF(pPriceOut);
                   StrValue := 'Цена без скидки: ';
                   cash_savelog('Цена без скидки: ');
                   strvalue1 := bvcurrtostrf(pPriceOut);
                   cash_savelog('strvalue1='+asstring(strvalue1));
                    
                   if stringlength>0 then StrValue1 := StrPad(strvalue1,stringlength-length(strvalue),' ',true);
                   VarDevice.StringForPrinting := strvalue+ strvalue1; Cash_CheckError('Cash_Execute:str6');
                   cash_savelog('Cash_Execute:str6');
                   VarDevice.PrintString; Cash_CheckError('Cash_Execute:str7');
                   cash_savelog('Cash_Execute:STr7');
                   VarDevice.StringForPrinting := ''; Cash_CheckError('Cash_Execute:str8');
                   cash_savelog('Cash_Execute:STr8');
                 end;
               end;
               Cash_CheckError('Cash_Execute:3');
               cash_savelog('Cash_Execute:3');
             end;
           end;

           cds.next;
         end;           

         if (DetailCash = 2) // Без детализации
         then begin
           cash_savelog('DetailCash = 2 Без детализации');
           
           if (pSummaryCash > 0) then
           begin
             VarDevice.Quantity := 1; Cash_CheckError('Cash_Execute:QNT1');
             cash_savelog(' VarDevice.Quantity := 1');
             
             thField := cds.findfield(fsection);
             if thfield<>nil then VarDevice.Department := coalesce_integer(thField.asinteger,CurrentSection,1)
             else VarDevice.Department := coalesce_integer(CurrentSection,1); 
             Cash_CheckError('Cash_Execute:Section1');
             Cash_savelog('Cash_Execute:Section1');
             
             VarDevice.Price := ifthen_currency(CASH_DiscountType<>0, bvroundto(pSummaryCash + pSummaryDiscount, -2,rpTruncate), bvroundto(pSummaryCash, -2,rpTruncate)); Cash_CheckError('Cash_Execute:SummaryCash');

             if isReturn then VarDevice.ReturnSale else VarDevice.Sale; Cash_CheckError('Cash_Execute:ReturnSale'); 

             if (CASH_DiscountType=1)and ( bvroundto(pSummaryDiscount, -2) <> 0) then
             begin
               VarDevice.Summ1 := bvroundto(pSummaryDiscount, -2); Cash_CheckError('Cash_Execute:Summ1_1');
               Cash_savelog('Cash_Execute:Summ1_1');
               Cash_savelog('Cash_Execute:Summ1_1'+formatfloat(',0.00#######',bvroundto(pSummaryDiscount, -2)));
               
               
               VarDevice.Summ2 := 0; Cash_CheckError('Cash_Execute:Summ2_1');
               VarDevice.Summ3 := 0; Cash_CheckError('Cash_Execute:Summ3_1');
               VarDevice.Summ4 := 0; Cash_CheckError('Cash_Execute:Summ4_1');
               VarDevice.Discount; Cash_CheckError('Cash_Execute:Discount');
             end;

             if (pSummaryDiscount <> 0) and CASH_PrintStringDiscountSummary        
             then begin     
               //VarDevice.Caption := 'В том числе скидка: '+bvCurrtostrF(pDiscount)+#13+'Цена без скидки: '+bvCurrToStrF(pPriceOut);
               StrValue := 'Скидка: ';
               strvalue1 := bvcurrtostrf(pSummaryDiscount);
               if stringlength>0 then StrValue1 := StrPad(strvalue1,stringlength-length(strvalue),' ',true);
               VarDevice.StringForPrinting := strvalue+ strvalue1; Cash_CheckError('Cash_Execute:Str1_1');
               VarDevice.PrintString; Cash_CheckError('Cash_Execute:Str1_2');
               VarDevice.StringForPrinting := ''; Cash_CheckError('Cash_Execute:Str1_3');
             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.StringForPrinting := strvalue+ strvalue1; Cash_CheckError('Cash_Execute:Str1_4');
               VarDevice.PrintString; Cash_CheckError('Cash_Execute:Str1_5');
               VarDevice.StringForPrinting := ''; Cash_checkError('Cash_Execute:Str1_6');
             end;

           end;
         end;

         if (CASH_DiscountType=2) and (bvroundto(pSummaryDiscount, -2) <> 0) 
         then begin
           VarDevice.Summ1 := bvroundto(pSummaryDiscount, -2); Cash_CheckError('Cash_Execute:Summ3_1');
           VarDevice.Summ2 := 0; Cash_CheckError('Cash_Execute:Summ3_2'); 
           VarDevice.Summ3 := 0; Cash_CheckError('Cash_Execute:Summ3_3');                     
           VarDevice.Summ4 := 0; Cash_CheckError('Cash_Execute:Summ3_4');
           //VarDevice.Destination := 0;
           VarDevice.Discount; Cash_CheckError('Cash_Execute:Discount3');
         end;



         if (pSummaryDiscount <> 0) and CASH_PrintStringDiscountOnCheck        
         then begin     
           StrValue := 'Итого скидка по чеку: ';
           strvalue1 := bvcurrtostrf(pSummaryDiscount);
           if stringlength>0 then StrValue1 := StrPad(strvalue1,stringlength-length(strvalue),' ',true);
           VarDevice.StringForPrinting := strvalue+ strvalue1; Cash_CheckError('Cash_Execute:Str21');
           VarDevice.PrintString; Cash_CheckError('Cash_Execute:Str22');
           VarDevice.StringForPrinting := ''; Cash_CheckError('Cash_Execute:Str23');
         end;

       finally
         cds.enablecontrols;
       end;
       cds.first;

       

///////////////////////////////////////////////

       if VarActionNal and  (bvroundto(PaySummary, -2) <> 0) then
       begin
         Summ2 := bvroundto(Paysummary, -2);
         cash_savelog('Paysummary='+formatfloat(',0.00#######',Paysummary));
         
         //VarDevice.Delivery;
       end
       else
         begin
           Summ2 :=  bvroundto(pSummaryCash, -2 ); // +0.01;
           cash_savelog('pSummaryCash='+formatfloat(',0.00#######',pSummaryCash));
           cash_savelog('Summ2='+formatfloat(',0.00#######',Summ2));
         end;  


       case DOC_TYPE_CLOSE of
         1 : Index := CASH_TypeCloseBN;
         2 : Index := CASH_TypeCloseKredit;
         3 : Index := CASH_TypeCloseSert;
         else  Index := 0;
       end;

       case Index of
         1: begin
             VarDevice.Summ1 := 0; Cash_CheckError('Cash_Execute:Summ4_1');
             VarDevice.Summ2 := Summ2; Cash_CheckError('Cash_Execute:Summ4_2');
             cash_savelog('Cash_Execute:Summ4_2');
             cash_savelog('Summ2='+formatfloat(',0.00#######',Summ2));
             VarDevice.Summ3 := 0; Cash_CheckError('Cash_Execute:Summ4_3');
             VarDevice.Summ4 := 0; Cash_CheckError('Cash_Execute:Summ4_4');
            end;
         2: begin
             VarDevice.Summ1 := 0; Cash_CheckError('Cash_Execute:Summ5_1');
             VarDevice.Summ2 := 0; Cash_CheckError('Cash_Execute:Summ5_2');
             VarDevice.Summ3 := Summ2; Cash_CheckError('Cash_Execute:Summ5_3');
             cash_savelog('Cash_Execute:Summ5_3');
             cash_savelog('Summ2='+formatfloat(',0.00#######',Summ2));
             VarDevice.Summ4 := 0; Cash_CheckError('Cash_Execute:Summ5_4');
            end;
         3: begin
             VarDevice.Summ1 := 0; Cash_CheckError('Cash_Execute:Summ6_1');
             VarDevice.Summ2 := 0; Cash_CheckError('Cash_Execute:Summ6_2');
             VarDevice.Summ3 := 0; Cash_CheckError('Cash_Execute:Summ6_3');
             VarDevice.Summ4 := Summ2; Cash_CheckError('Cash_Execute:Summ6_4');
             cash_savelog('Cash_Execute:Summ6_4');
             cash_savelog('Summ2='+formatfloat(',0.00#######',Summ2));
            end;
         else begin
             VarDevice.Summ1 := Summ2; Cash_CheckError('Cash_Execute:Summ7_1');
             cash_savelog('Cash_Execute:Summ7_1');
             cash_savelog('Summ2='+formatfloat(',0.00#######',Summ2));
             VarDevice.Summ2 := 0; Cash_CheckError('Cash_Execute:Summ7_2');
             VarDevice.Summ3 := 0; Cash_CheckError('Cash_Execute:Summ7_3');
             VarDevice.Summ4 := 0; Cash_CheckError('Cash_Execute:Summ7_4');
            end;
       end;
       
       VarDevice.Summ5 := 0; Cash_CheckError('Cash_Execute:Summ5');
       VarDevice.Summ6 := 0; Cash_CheckError('Cash_Execute:Summ6');
       VarDevice.Summ7 := 0; Cash_CheckError('Cash_Execute:Summ7');
       VarDevice.Summ8 := 0; Cash_CheckError('Cash_Execute:Summ8');
       VarDevice.Summ9 := 0; Cash_CheckError('Cash_Execute:Summ9');
       VarDevice.Summ10 := 0; Cash_CheckError('Cash_Execute:Summ10');
       VarDevice.Summ11 := 0; Cash_CheckError('Cash_Execute:Summ11');
       VarDevice.Summ12 := 0; Cash_CheckError('Cash_Execute:Summ12');
       VarDevice.Summ13 := 0; Cash_CheckError('Cash_Execute:Summ13');
       VarDevice.Summ14 := 0; Cash_CheckError('Cash_Execute:Summ14');
       VarDevice.Summ15 := 0; Cash_CheckError('Cash_Execute:Summ15');
       VarDevice.Summ16 := 0; Cash_CheckError('Cash_Execute:Summ16');
       
       VarDevice.RoundingSumm := 0; Cash_CheckError('Cash_Execute:RoundingSumm');

       if Taxtype>0 then 
       begin 
         VarDevice.TaxType := TaxType; Cash_CheckError('Cash_Execute:TaxType');
         //VarDevice.FNCloseCheckEx; Cash_CheckError('Cash_Execute:FNCloseCheckEx');
       end
       else begin
         //VarDevice.CloseCheck; Cash_CheckError('Cash_Execute:CloseCheck');
       end;


       // Тестовое аннулирование чека. закомментить последние 4 строки
       //VArDevice.CancelCheck;
       //resultcode := -100;
       //resultdescription := 'Тестовое аннулирование чека'; 
       //exit;// test
       //raiseexception('тест');
       
       cash_savelog('FNCloseCheckEx');
       VarDevice.FNCloseCheckEx; Cash_CheckError('Cash_Execute:FNCloseCheckEx');
       cash_savelog('-');
       
       Cash_SaveLog('cash_cut');
       Cash_Cut(false); Cash_CheckError('Cash_Execute:Cash_cut11');
       Cash_SaveLog('-завершено');

       if (index=0) and CASH_OPENDRAWER_NAL
       then Cash_OpenDrawer(false); 

   finally
     if needconnect then Cash_Disconnect; 
   end;
   Cash_SaveLog('cash_execute:exit');
end;

procedure Cash_CancelCheck(NeedConnect :boolean = true);
var StrValue: string;
begin
  StrValue := '';
  try
    if NeedConnect then //Cash_Connect(CASH_KKMSYSPASSWORD>'');
    try
      IncStrCR(StrValue,'ПОДКЛЮЧЕНИЕ');
      Cash_Connect(CASH_KKMSYSPASSWORD>'');
      IncStrCr(StrValue,'- успешно');
    except
      IncStrCr(StrValue,ExceptMessage);
    end;
    try
      (*
      if CASH_KKMSYSPASSWORD='' then 
      begin
        if VArDevice.ECRMode = 8 then // мы в документе
          VarDevice.CancelCheck; Cash_CheckError('Cash_Execute:CancelCheck');
      end
      else  
        VarDevice.SysAdminCancelCheck; Cash_CheckError('Cash_Execute:SysAdminCancelCheck');
      *)  
      //if VArDevice.ECRMode = 8 then // мы в документе
      try
        IncStrCr(StrValue,'ОТМЕНА ЧЕКА ПОЛЬЗОВАТЕЛЕМ: ');
        VarDevice.Password := CASH_KKMSYSPASSWORD;
        VarDevice.CancelCheck; Cash_CheckError('CancelCheck');
        IncStrCr(StrValue,'- успешно');
      except
        IncStrCr(StrValue,ExceptMessage);
      end;
      try
        IncStrCr(StrValue,'ОТМЕНА ЧЕКА АДМИНИСТРАТОРОМ: ');
        VarDevice.SysAdminCancelCheck; Cash_CheckError('SysAdminCancelCheck');
        IncStrCr(StrValue,'- успешно');
      except
        IncStrCr(StrValue,ExceptMessage);
      end;
    finally
      if NeedConnect then
      try
        IncStrCr(StrValue,'ОТКЛЮЧЕНИЕ:');  
        Cash_disconnect;
        IncStrCr(StrValue,'- успешно');
      except
        IncStrCr(StrValue,ExceptMessage);
      end;
    end;
  finally
    bvMessage(StrValue);
  end;
end;

procedure Cash_RepeatCheck(NeedConnect :boolean = true);
begin
  if NeedConnect then //Cash_Connect;
  begin
    Cash_CheckDevice;
    Cash_Connect;
  end;
  try
    VarDevice.ContinuePrint; Cash_CheckError('Cash_Execute:ContinuePrint');
  finally
    if NeedConnect then Cash_disconnect;
  end;
end;


procedure Cash_DoSummary(IsIn :boolean; NeedConnect :boolean = true);
var i :integer;
begin
  if needconnect then Cash_Connect;
  try
    // еще идет печать предыдущего документа, это возможно, если мы делим чек,
    // то повисим (до 15 секунд)
    for i := 1 to 30 do
    begin
       VarDevice.getecrstatus; Cash_checkerror('Cash_Execute:dosummary:getecrstatus');
       if (VarDevice.ecrmode=8) and (VarDevice.ecradvancedmode=5)  then sleep(3000)  else break;
    end;
  
    VarDevice.summ1 := Summary; Cash_CheckError('Cash_Execute:dosummary:summ1');
  
    if IsIn then VarDevice.CashIncome else VarDevice.CashOutcome; Cash_CheckError('Cash_Execute:dosummary:cashoutcome');
  
    Cash_Cut(false); Cash_CheckError('Cash_Execute: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_OpenDrawer(NeedConnect :boolean = true);
begin
  if NeedConnect then Cash_Connect;
  try
    VarDevice.OpenDrawer; Cash_CheckError('Cash_Execute:OpenDrawer');
  finally
    if NeedConnect then Cash_disconnect;
  end;
end;

procedure Cash_XReport(NeedConnect :boolean = true);
begin
  if NeedConnect then Cash_Connect(true);
  try
    VarDevice.PrintReportWithoutCleaning; Cash_CheckError('Cash_Execute:PrintReportWithoutCleaning');
  finally
    if NeedConnect then Cash_disconnect;
  end;
end;

procedure Cash_ZReport(NeedConnect :boolean = true);
begin
  if NeedConnect then Cash_Connect(true);
  try
    VarDevice.PrintReportWithCleaning; Cash_CheckError('Cash_Execute:PrintReportWithCleaning');
  finally
    if NeedConnect then Cash_disconnect;
  end;
end;

procedure Cash_SectionReport(NeedConnect :boolean = true);
begin
  if NeedConnect then Cash_Connect(true);
  try
    VarDevice.PrintDepartmentReport;Cash_CheckError('Cash_Execute:PrintDepartmentReport');
  finally
    if NeedConnect then Cash_disconnect;
  end;
end;

procedure Cash_UsersReport(NeedConnect :boolean = true);
begin
  if NeedConnect then Cash_Connect;
  try
    VarDevice.PrintCashierReport; Cash_CheckError('Cash_Execute:PrintCashierReport');
  finally
    if NeedConnect then Cash_disconnect;
  end;
end;

procedure Cash_RegistersReport(NeedConnect :boolean = true; ByListOfRegisters :boolean = true);

var
  FiskregSHTCashReg :string ='120,121,123,125,127,129,131,185,187,189,'+
                             '191,193,195,197,199,209,211,213,215,225,'+
                             '227,229,231,241,242,243,244,245,246,247,'+
                             '248,  0,  2,  4,  6,  8, 10, 64, 66, 68,'+
                             ' 70, 72, 74, 76, 78, 88, 90, 92, 94,104,'+
                             '106,108,110';

  FiskregSHTOperationReg :string = '  0,  2,  4,  6,  8, 10, 64, 66, 68, 70,'+
                                   ' 72, 74, 76, 78, 80, 82,136,138,140,142,'+
                                   '144,146,148,150,152,153,154,155,156,157,'+
                                   '158,159,160,161,162,163,164,165,166,167,'+
                                   '168,169,170,171,172,173,174,175,176,177,'+
                                   '178';

var i :integer;
    List :Tstringlist;
    StrVal1,StrVal2 :string;
    //FloatVal :double;
    //IntVal :integer;
    StrVAl :string;
    CNT :integer;
    Num :integer;
begin
  if (ResultRegisterListNames=nil) or (ResultRegisterListValues=nil) 
  then raiseException('Ошибка в Cash_RegistersReport: Не проинициализированы контейнеры для результатов функции');

  if NeedConnect then Cash_Connect(true);
  try
    resultregisterlistnames.clear;
    resultregisterlistvalues.clear;
  
    List := tstringlist.create;
    with TMyWait.create('Идет чтение регистров',selfscript) do
    try
      if ByListOfRegisters then  
      begin
        stringtolist(FiskregSHTCashReg,List,',');
        cnt := list.count;
      end
      else cnt := 256;
      
      for  i:= 0 to CNT-1  do
      begin
        progressbarpos := i * 50 div CNT;
        if ByListOfRegisters then Num := strtointprotected(list[i]) else Num := i;
        VarDevice.RegisterNumber :=  Num;
        StrVal1 := inttostr(Num)+': ' + VarDevice.NameCashReg;
        VarDevice.GetCashReg;
        if varDevice.resultcode<>0
        then StrVal2 := VarDevice.resultcodedescription
        else begin
          strVal := asstring(VarDevice.ContentsOfCashRegister);
          if VarDevice.resultcode <> 0   
          then StrVal2 := VarDevice.ResultCodeDescription
          else StrVal2 := StrVal;
        end;
        ResultRegisterListNames.add(StrVal1);
        ResultRegisterListValues.add(StrVal2);
      end;

      if ByListOfRegisters then 
      begin
        stringtolist(FiskregSHTOperationReg,List,',');
        cnt := list.count;
      end
      else Cnt := 256;
            
      
      for  i:= 0 to CNT-1  do
      begin
        progressbarpos := i * 50 div CNT + 50;
        if ByListOfRegisters then Num := strtointprotected(list[i]) else Num := i;
        VarDevice.RegisterNumber := Num;
        StrVal1 := inttostr(Num)+': ' + VarDevice.NameOperationReg;
        VarDevice.GetOperationReg;
        if varDevice.resultcode<>0
        then StrVal2 := VarDevice.resultcodedescription
        else begin
          StrVal := asstring(VarDevice.ContentsOfOperationRegister);
          if VarDevice.resultcode <> 0   
          then StrVal2 := VarDevice.ResultCodeDescription
          else StrVal2 := StrVal;
        end;
        ResultRegisterListNames.add(StrVal1);
        ResultRegisterListValues.add(StrVal2);
      end;
      //ResultREgisterListnames.SaveToFile('c:\temp\11.txt');
      //ResultREgisterListValues.SaveToFile('c:\temp\12.txt');
    finally
      free;
      list.free
    end;
    
    Cash_CheckError('Cash_Execute:reg');
  finally
    if NeedConnect then Cash_disconnect;
  end;
end;

procedure Cash_ShowProperties;
begin
    Cash_CheckDevice;
    VarDevice.ShowProperties;
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;
  }
  
  
  //CreateHint('PrintChequeShtrihM TextValue =: '+TextValue,'Тест',60);
  //StringToFile('c:\temp\1.txt',TExtVAlue);
    Cash_SaveLog('PrintChequeShtrihM');
    Cash_SaveLog(Strings.text);
    
    Cash_SaveLog(
      'NeedConnect:'+asstring(NeedConnect)+';'+
      'RepeatPrint:'+asstring(RepeatPrint)+';'+
      'CutAfter:'+asstring(CutAfter)+';'+  
      'CutLine:'+asstring(CutLine)+';'+  
      'CutLineText:'+asstring(CutLineText)+';'+  
      'CutLineLength:'+asstring(CutLineLength)+';'+  
      'CutLineSleep:'+asstring(CutLineSleep));
    
  
  
    Cash_SaveLog('PrintChequeShtrihM:1');
    if Strings.text ='' then 
      Exit;

    VarDevice.UseReceiptRibbon:=True; Cash_CheckError('Cash_Execute:UseReceiptRibbon');
    VarDevice.UseJournalRibbon:=False; Cash_CheckError('Cash_Execute:UseJournalRibbon');

    Cash_SaveLog('PrintChequeShtrihM:2');

    for k := 1 to ifthen_integer(repeatprint>1,repeatprint,1) do
    begin
      Cash_saveLog('PrintChequeSHTRIHM:RepeatPrint:'+IntTostr(k));
  
      NeedCutLine := false;
      incCutLine := 0;
      for i := 0 to strings.count-1 do
      begin
        Cash_SaveLog('PrintChequeSHTRIHM:2.1'+inttostr(i)+':'+strings[i]);
        VarDevice.StringForPrinting:=strings[i]; Cash_CheckError('Cash_Execute:PrintChequeSHTRIHM:2.1');
        Cash_SaveLog('PrintChequeSHTRIHM:2.2.'+inttostr(i));
        VarDevice.PrintString; Cash_CheckError('Cash_Execute:PrintChequeSHTRIHM:2.2');
        Cash_SaveLog('PrintChequeSHTRIHM:2.3.'+inttostr(i));
        //VarDevice.PrintWideString
        if pos(CutLineText,strings[i])=1 //if list[i] = Cash_CutLineText
        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('PrintChequeSHTRIHM:2.3');
              VarDevice.CutType:=False; // false полная отрезка, true - неполная
              Cash_SaveLog('PrintChequeSHTRIHM:2.4');
              VarDevice.CutCheck; // отрезка
            end;
            if CutLineSleep>0 then Sleep(CutLineSleep*1000);
            NeedCutLine := false;
            IncCutLine := 0;
          end;
        end;
      end;
  
      Cash_SaveLog('PrintChequeShtrihM:3');
  
  
      if CutAfter and NeedCutLine and (IncCutLine<CutLineLength)
      then begin
        Cash_SaveLog('PrintChequeSHTRIHM:4');
        VarDevice.StringQuantity:=CutLineLength-IncCutLine; //пропустить строки
        Cash_SaveLog('PrintChequeSHTRIHM:5');
        VarDevice.FeedDocument;
        if CutLine then
        begin
          Cash_SaveLog('PrintChequeSHTRIHM:6');
          VarDevice.CutType:=False; // false полная отрезка, true - неполная
          Cash_SaveLog('PrintChequeSHTRIHM:7');
          VarDevice.CutCheck; // отрезка
        end;
        if CutLineSleep>0 then Sleep(CutLineSleep*1000);
      end;
    end;


    //VarDevice.UseSlipDocument:=false;
    //VarDevice.StringQuantity:=3; //пропустить строки
    //VarDevice.FeedDocument;

    //VarDevice.CutType:=False; // false полная отрезка, true - неполная
    //VarDevice.PrintDocumentTitle;
    //VrDevice.CutCheck; // отрезка

    // Проверка PrintCliche, раскомментировать три строки здесь
    //VarDevice.CutType:=False; // false полная отрезка, true - неполная
    //VarDevice.PrintCliche;
    //VarDevice.CutCheck; // отрезка

    Cash_SaveLog('PrintChequeShtrihM:exit');
  finally  
    if NeedConnect then 
      Cash_disconnect;
  end;
end;

begin
  // инициализация модуля. выполняется перед выполнением каких-либо функций и для установки параметров по-умолчанию
  Cash_InitVars;
  cash_checkdevice;
  Cash_checkerror('start');
  // конец инициализации
  
  
{  
   // тест
  //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.
