Класс TBtrFile (потомок TbFile) с поддержкой журнализации и фильтром записей

Автор:krupennikov
Дата:09.04.2015
Просмотров:2776
Скачиваний:650
Оценка: - , Оценок - 0
Скачать (zip-файл; Размер - 32751)

Обсудить в форуме

Описание

Класс, являющийся наследником от TbFile, c расширенными возможностями. Возможность фильтрации записей, как просто путем задания условий на поля, так и возможность отобрать определенный диапазон записей, путем задания границ диапазона, то есть первой и последней записи при данном ключе. Возможна и комбинация этих двух способов фильтрации, то есть сначала задаем диапазон, а затем дополнительные условия фильтрования.
Класс поддерживает журнализацию всех изменений в БД с записью в operlog.dbt (для RS-Bank), при написании этого функционала был частично использован класс xOperLog из данной библиотеки (Хондожко И.В., ИС-Банк). Как и у первоисточника, контрольная сумма не заполняется (однако никаких проблем за время эксплуатации с 2009-го года это не вызвало).
В прилагаемом архиве находятся следующие файлы:
_tbtrfile.mac - класс TBtrFile
_common_const.mac, _kbdcodes.mac - всякие константы
_func_lib.mac - используемые функции и процедуры
_common_classes.mac - библиотека вспомогательных классов
_tfields.mac - классы для стандартных типов данных
_toperlog - класс, используемый для журнализации
Во вспомогательных файлах осталось много не относящегося к данному примеру, но вычищать все основательно просто нет времени. Впрочем, там много всякой нужной всячины, может кому что и пригодится.

В файле _tfiles.mac содержится ряд примеров использования класса, например, arhdoc за заданный диапазон дат, кассовые символы для документа, счета по маске и по балансовому счету, и.т.д.
Ниже подробно разбирается один из этих примеров - архивные документы.

Класс этот активно мной используется где-то с 2009-го или 2010-го года, если нужно открывать постоянную таблицу БД на запись - только его и использую, не приходится озабачиваться журнализацией. Но operlog приходится архивировать регулярно, растет в разы быстрее, чем на дистрибутиве. Из серьезных недостатков класса - сильно тормозит метод NRecords при большом числе записей, так как работает тупым перебором. В ряде случаев советую переопределить этот метод с использованием SQL, это гораздо быстрее, но, к сожалению, не универсально и требует писать запрос руками для каждого конкретного случая. Сгенерить запрос на автомате базируясь только на том, как задается фильтрация, у меня не получилось. Еще один тонкий момент - если задали границы диапазона по какому-то ключу, менять его уже нельзя, получится полная ерунда. Надеюсь, эта разработка кому-нибудь да пригодится.

Текст примера

 class (TBtrFile) TFArhDoc(_bdate:@date, _edate:@date, _chap:@integer, _code_curr:@integer, _mode:@string)
  var BegDate  :date     = NVL(_bdate, ZERO_DATE);         // начальная дата 
  var EndDate  :date     = NVL(_edate, {curdate} - 1);     // конечная дата
  var Chapter  :integer  = NVL(_chap, CHAPTER_BALANCE);    // глава (по умолчанию 1-я)
  var Code_Curr:integer  = NVL(_code_curr, RUR);           // код валюты (по умолчанию рубли)
  var Mode     :string   = NVL(_mode, "R");                // режим открытия файла
  /******************************************************/
  macro Filter(rec)
    // отбрасываем записи с Result_Carry 23
    if  (rec.rec.Result_Carry == RES_CARRY_DELETED)
       return FALSE;
    end;
    // отбрасываем записи не подходящие по дате проводки
    if  ((rec.rec.Date_Carry < BegDate) or (rec.rec.Date_Carry > EndDate))
       return FALSE;
    end;
    // фильтруем по коду валюты
    if  ((Code_Curr > 0) and (rec.rec.Code_Currency != Code_Curr))
       return FALSE;
    end;
    // фильтруем по главе
    if  (GetDocChapter(rec) != Chapter)
       return FALSE;
    end;

    return Filter(rec);
  end;
  /******************************************************/
  // номер ключа зависит от таблицы с документами
  // временный объект, таблица будет выбрана по главе и валюте
  local var iKey :integer  = BoolToInt(Chapter != CHAPTER_BALANCE);
  local var ffTmp:object   = SelectDocFile(Code_Curr, Chapter, DOC_TYPE_ARH, "TBtrFile", Mode);
  // вызов конструктора базового класса
  initTBtrFile(ffTmp.tblName, Mode, iKey, ffTmp.FileName, ffTmp.dicName);
  // флаг пустой выборки
  bEmpty = TRUE;

  // ищем по ключу первую запись диапазона (и удовлетворяющую фильтр) 
  _extObj.Clear();
  if  (Chapter != CHAPTER_BALANCE)
     _extObj.rec.Chapter = Chapter;
  end;
  _extObj.rec.Date_Carry = BegDate;
  if  (not _extObj.GetLT())
     _extObj.Rewind();
  end;
  while(_extObj.Next() 
  and  (GetDocChapter(_extObj) == Chapter) 
  and  (_extObj.rec.Date_Carry <= EndDate))
     if  (Filter(_extObj))
        // запись найдена, фиксируем ее как первую и одновременно как последнюю запись диапазона
        FirstRec = TCRecord(this.tblName, this.dicName, this.KeyNum);
        LastRec  = TCRecord(this.tblName, this.dicName, this.KeyNum);
        copy (FirstRec, _extObj);
        copy (LastRec , _extObj);
        bEmpty = FALSE;
        break;
     end;
  end;

  if  (bEmpty)
     return;
  end; 

  // ищем с другой стороны последнюю запись, и если нашли и она не совпадает с первой - переопределяем
  //верхнюю границу диапазона
  _extObj.Clear();
  if  (Chapter != CHAPTER_BALANCE)
     _extObj.rec.Chapter = Chapter;
  end;
  _extObj.rec.Date_Carry = EndDate;
  if  (not _extObj.GetGT())
     _extObj.Rewind();
  end;
  while(_extObj.Prev() 
  and  (GetDocChapter(_extObj) == Chapter) 
  and  (_extObj.rec.Date_Carry >= BegDate))
     if  (Filter(_extObj))
        copy (LastRec , _extObj);
        break;
     end;
  end;

end;