Страница 1 из 1

Импорт Табеля рабочего времени из файла

Добавлено: 07 авг 2007, 14:37
SergeyZhd
Здравствуйте!
Интересует вопрос кто нибудь заполняет табель из какого либо внешнего файла и как это делается?
в хелпе написано Стандартная форма но нигде её примеров нету
а текстовый файл не подходит, т.к заполнять будут руководители отделов и отдавать в кадры.
есть ли возможность загружать табель из какой либо наглядной формы типа таблицы?

Добавлено: 07 авг 2007, 15:13
voronov
Собственная доработка выгрузки из EXCEL, например, если наглядно надо.

Добавлено: 07 авг 2007, 16:46
edward_K
стандартная форма получается при печати Т12. Там есть еще алтернативный формат - о нем написано в доке и по F1. Наскока помню хорошо не получается(оссобенно с неявками).

Добавлено: 08 авг 2007, 17:53
SergeyZhd
пробовал выгружать форму т12 из галактики (лист 2) пишет некорректный формат

Добавлено: 14 авг 2007, 18:06
Maxx
Мы используем альтернативный формат текстового файла. Он наиболее подходящий. Есть итоговые часы. Но у нас очень много отклонений от графика и после импорта из текстового файла все равно приходится смотреть и корректировать. Насчет формата скажу, что он очень урезан. За день можно указать только часы или буквенное обозначение. Нельзя передать за день ночные и т.д.
Вопрос к voronov: можно подробнее рассказать как написали импорт в таблицы гал-ки?

Добавлено: 14 авг 2007, 22:45
voronov
Maxx писал(а): Вопрос к voronov: можно подробнее рассказать как написали импорт в таблицы гал-ки?
В основном, проблема была именно в том, каким образом все построить в эксель файле. Различные обозначения рабочего дня (а у клиента их используется ужастно много). Число отработанных дней. Обработка отклонений.
В результате работы месяцов подходящий шаблон был найден
А дальше просто:
1. Считываем из excel обозначение рабочих дней и проставляем его в галактику
2. Считаем число рабочих дней (ежедневно и в сумме) и тоже переносим в галку
3. Считываем отклонения. Удаляем все отклоения Галки за этот месяц и заменяем своими

Добавлено: 15 авг 2007, 08:39
PViP
Maxx, о каком ипорте в таблицы галактики идеь речь? Есть стандартная реализация импорта табеля из тхт файла(пункт меню Формирование табеля). Все подходы в принципе озвучены выше. Я делал табель в таблицах MS Excel, из Excel экспортировал в текстовый файл в "алтернативном" формате и затем ипотртировал этот файл в галку.

А вот о подводных камнях импорта: при импорте обозначения "ДО", не происходит его корректная обработка, тоесть если в галке нет приказа на отпуск, а мы его импортируем, то при переформировании не происходит удаление "ДО" из табеля, а должно бы быть! :sad:

Добавлено: 16 авг 2007, 01:59
Screw
Для справки: альтернативный формат был реализован как демонстрация возможностей API импорта табелей. Не очень, надо заметить, удачная (итоги по часам надо было расположить не в конце, а в начале, до подённых данных). В своей собственной реализации импортера можно реализовать сколь угодно сложный алгоритм разбора исходных данных. Однако "превращение" отклонений в "настоящие" отпуска, больничные и командировки - это не забота табельного импортера. Его дело - передача в Галактику подённых часов (в том числе ночных, вечерних, выходных и праздничных) и табельных отклонений (не путать с системными, для которых заведены соответствующие таблицы, например, "Отпуска"). К сожалению, API для генерации пакетов отпусков и прочих системных отклонений пока не существует, поэтому могу рекомендовать только непосредственную работу с БД.

Для особо интересующихся привожу описания объектных интерфейсов API импорта табелей и код импортера из формата "данные разделены символом ';' (альтернативный)".

Содержимое WTImport.vih

Код: Выделить всё

//******************************************************************************
//                                                      (с) корпорация Галактика
// Галактика 8.1 - Заработная плата
// Объектные инт-сы для импорта табеля из текстового файла
//******************************************************************************

#ifndef _WTIMPORT_INCLUDED_
#define _WTIMPORT_INCLUDED_

#ifdef ComponentVersion
#component "Z_WT"
#end

//------------------------------------------------------------------------------
#doc
  Объктный интерфейс итератора по отклонениям табеля учета рабочего времени.
#end
objinterface IWorkingTableVarianceIterator;
  #doc
    Устанавливает курсор на первое отклонение. Возвращает true, если
    позиционирование прошло успешно, в противном случае возвращает false.
  #end
  function First: boolean;

  #doc
    Установливает курсор на следующее отклонение. Возвращает true, если
    позиционирование прошло успешно, в противном случае возвращает false.
  #end
  function Next: boolean;

  #doc
    Возвращает в B содержимое буфера отклонения.
  #end
  procedure GetData(var B: type$VARIANCE);
end;

//------------------------------------------------------------------------------
#doc
  Объектный интерфейс импортера данных табеля учета рабочего времени.
#end
objinterface IWorkingTableImporter;

  #doc
    Открывает файл с данными.
  #end
  function OpenFile(FileName: string): boolean;

  #doc
    Закрывает файл с данными.
  #end
  procedure CloseFile;

  #doc
    Тестирует содержимое файла на соответствие формату.
  #end
  function TestFile: boolean;

  #doc
    Переходит к данным первого импортируего табеля. Возвращает true в случае
    успеха, иначе возвращает false.
  #end
  function First: boolean;

  #doc
    Переходит к данным следующего импортируемого табеля.  Возвращает true в
    случае успеха, иначе возвращает false.
  #end
  function Next: boolean;

  #doc
    Возвращает true, если в процессе загрузки данных очередного табеля
    произошла какая-либо ошибка.
  #end
  function ErrorFound: boolean;

  #doc
    Возвращает значение числового табельного номера лицевого счета.
  #end
  function GetClockNumber: longint;

  #doc
    Возвращает месяц, к которому относятся данные импортируемого табеля.
  #end
  function GetMonth: byte;

  #doc
    Возвращает год, к которому относятся данные импортируемого табеля.
  #end
  function GetYear: word;

  #doc
    Возвращает true, если данные о количестве часов вида HourKind за день D
    были представлены в файле импорта. Количество часов при этом передаётся в
    Hours. Возвращает false в противном случае.
  #end
  function GetDailyHours(D: byte; HourKind: byte; var Hours: double): boolean;

  #doc
    Возвращает true, если данные о суммарном количестве часов вида HourKind
    были представлены в файле импорта. Количество часов при этом передаётся в
    Hours. Возвращает false в противном случае.
  #end
  function GetMonthlyHours(HourKind: byte; var Hours: double): boolean;

  #doc
    Возвращает ссылку на итератор по табельным отклонениям.
  #end
  function GetVarianceIterator: IWorkingTableVarianceIterator;
end;

//------------------------------------------------------------------------------
#doc
  Объектный интерфейс описателя и загрузчика импортера данных табеля учета
  рабочего времени.</brief>

  <p>ВНИМАНИЕ: имена всех реализаций данного объектного интерфейса должны
  начинаться с "VWorkingTableImporterLoader_"!</p>

  <p>Интерфейс ввода параметров импорта табеля загружает список имен реализаций
  объектного интерфейса IWorkingTableImporterLoader, загружает каждую
  реализацию и вызывает ее метод GetFormat. Таким образом заполняется список
  форматов файлов.</p>

  <p>После того, как пользователь выбрал интересующий его формат и задал имя файла,
  инициализируется соответствующий загрузчик и вызывается его метод GetImporter.
  Далее вызываются методы импортера OpenFile и TestFile для открытия файла
  импорта и проверки корректности его формата.</p>

  <p>Вызов методов First и Next импортера означает подгрузку из файла и проверку
  корректности данных первого/следующего табеля. Если данные загружены
  корректно, посредством метода GetDailyHours выбираются часы за каждый день.
  Затем, при помощи метода GetMonthlyHours, - итоговые величины за месяц.
  Завершается цикл проходом по списку отклонений.</p>
#end
objinterface IWorkingTableImporterLoader;

  #doc
    Возвращает краткое описание поддерживаемого формата.</brief>

    <p>Используется для составления списка поддерживаемых форматов в интерфейсе
    настройки параметров импорта табелей учета рабочего времени.</p>
  #end
  function GetFormat: string;

  #doc
    Возвращает краткое описание синтаксиса записи импортируемых данных.
  #end
  function GetSyntax: string;

  #doc
    Возвращает ссылку на импортер, представленный данным загрузчиком.
  #end
  function GetImporter: IWorkingTableImporter;
end;

vipinterface VWorkingTableVarianceIterator implements IWorkingTableVarianceIterator
#ifdef ATL51
Licensed(Free)
#end
;
vipinterface VWorkingTableImporterLoader_CSV implements IWorkingTableImporterLoader
#ifdef ATL51
Licensed(Free)
#end
;
vipinterface vWorkingTableImporter_CSV implements IWorkingTableImporter
#ifdef ATL51
Licensed(Free)
#end
;
#end
Содержимое WTImport.vip

Код: Выделить всё

/*
 ╔═══════════════════════════════════════════════════════════════════════════╗
 ║                     (c) 1994,97 корпорация ГАЛАКТИКА                      ║
 ║ Проект        : ГАЛАКТИКА                                                 ║
 ║ Система       : Заработная плата                                          ║
 ║ Назначение    : Простая реализация расширяемого импорта табеля            ║
 ║ Ответственный : Корзюк Виталий Францевич                                  ║
 ╚═══════════════════════════════════════════════════════════════════════════╝
*/

#include WTIMPORT.VIH

table struct SVARIANCE = VARIANCE;

#doc
Простая реализация итератора отклонений от табеля
#end
interface VWorkingTableVarianceIterator cacheable;
  create view x;

  public function First: boolean;
  {
    First := getfirst SVARIANCE = tsOk;
  }

  public function Next: boolean;
  {
    Next := getnext SVARIANCE = tsOk;
  }

  public procedure GetData(var B: type$VARIANCE);
  {
    B := type$VARIANCE(SVARIANCE.BUFFER);
  }
end.

#doc
Простая реализация загрузчика импортера
#end
interface VWorkingTableImporterLoader_CSV cacheable;
  create view x;

  public function GetFormat: string;
  {
    GetFormat := 'Данные разделены символом ";" (альтернативный формат)' ;
  }

  public function GetSyntax: string;
  {
    GetSyntax :=
      '<строка> ::= <таб.н.> ";" <месяц> ";" <год> ";" [ <часы по дням> [ <итого часов> ] ]'#13 +
      '<часы по дням> ::= { [ <часы> ] ";" }'#13 +
      '<итого часов> ::= [ <вечерних> ] ";" [ [ ночных> ] ";" [ [ <праздничных> ] ";" [ [ <выходных> ] ";" ] ] ]';
  }

  public function GetImporter: IWorkingTableImporter;
  {
    var I: IWorkingTableImporter;
    LoadVipRef(I, 'VWorkingTableImporter_CSV');
    GetImporter := I;
  }
end.

#doc
Простая реализация импортера табеля из текстового файла
#end
interface VWorkingTableImporter_CSV '' EscClose;
  show at (,,,);

  table struct SDAILYDATA
  (
    DAY: byte,
    ABBREVIATURE: comp,
    WORKINGHOURS: double
  )
  with index
  (
    SDAILYDATA01 = DAY(unique)
  );

  table struct SMONTHLYHOURS
  (
    KIND: byte,
    HOURS: double
  )
  with index
  (
    SMONTHLYHOURS01 = KIND(unique)
  );

  create view ImporterView
    var
      ClockNumber: longint;
      CurMonth: byte;
      CurYear: word;
      DataDelimiter: char;
      ParseError: boolean;

      CurLine: longint;
      CurPos: integer;
    from
      SDAILYDATA
      , SMONTHLYHOURS
      , SVARIANCE
      , LSCHET
    ;

  file F;

  function ValidDataString(var S: string): boolean;
  {
    ValidDataString := false;
    S := Trim(S);
    if (S = '')
      exit;
    // комментарий
    if (SubStr(S, 1, 1) = '!')
      exit;
    ValidDataString := true;
  }

  function GetData(var S: string; var Data: string): boolean;
  {
    GetData := false;
    Data := '';
    if (CurPos > Length(S))
      exit;
    do
    {
      var C: string;
      C := SubStr(S, CurPos, 1);
      CurPos := CurPos + 1;
      if (C = DataDelimiter)
        break;
      else
        Data := Data + C;
    }
    while (CurPos <= Length(S));

    Data := Trim(Data);
    GetData := true;
  }

  function Position: string;
  {
    Position := '(стр. ' + string(CurLine) + ', поз. ' + string(CurPos - 1) + ')';
  }

  function ParseString(S: string): boolean;
  {
    ParseString := false;
    var Data: string;

    CurPos := 1;
    _try
    {
      if (GetData(S, Data))
      {
        CLockNumber := longint(Data);
        if (getfirst LSCHET where ((ClockNumber == LSCHET.TABN)) <> tsOk)
        {
          Displ('[x] Лицевой счет с табельным номером ' + string(ClockNumber) + ' не найден ' + Position);
          exit;
        }
      }
      else
      {
        Displ('[x] Ошибка импорта табельного номера ' + Position);
        exit;
      }

      if (GetData(S, Data))
      {
        CurMonth := byte(Data);
        if (CurMonth > 12) or (CurMonth = 0)
        {
          Displ('[x] Обнаружено недопустимое значение месяца: ' + string(CurMonth) + ' ' + Position);
          exit;
        }
      }
      else
      {
        Displ('[x] Ошибка импорта месяца ' + Position);
        exit;
      }

      if (GetData(S, Data))
      {
        CurYear := word(Data);
        if (CurYear = 0)
        {
          Displ('[x] Обнаружено недопустимое значение года: ' + string(CurYear) + ' ' + Position);
          exit;
        }
      }
      else
      {
        Displ('[x] Ошибка импорта года ' + Position);
        exit;
      }

      // импорт данных по дням месяца
      // некоторые дни могут быть пропущены - это допустимо
      var I: integer;
      for(I := 1; I <= Last_Day(Date(1, CurMonth, CurYear)); I := I + 1)
      {
        if (GetData(S, Data))
        {
          if (Data <> '')
          {
            ClearBuffer(#SDAILYDATA);
            SDAILYDATA.DAY := I;
            if (getfirst UOWRKTABEL where ((Data == UOWRKTABEL.NUM)) = tsOk)
              SDAILYDATA.ABBREVIATURE := UOWRKTABEL.NREC;
            else
              _try
              {
                SDAILYDATA.WORKINGHOURS := double(Data);
                if (SDAILYDATA.WORKINGHOURS > 24)
                {
                  Displ('[x] Обнаружено недопустимое количество отработанных часов: ' +
                    string(SDAILYDATA.WORKINGHOURS) + ' ' + Position);
                  exit;
                }
              }
              _except
                on ExNumberConvert:
                {
                  Displ('[x] Код "' + Data + '" не найден в классификаторе условных обозначений ' + Position);
                  Displ(S);
                  exit;
                }
            insert current SDAILYDATA;
          }
        }
      }

      if (GetData(S, Data))
      {
        SMONTHLYHOURS.KIND := hkEvening;
        SMONTHLYHOURS.HOURS := double(Data);
        insert current SMONTHLYHOURS;
      }

      if (GetData(S, Data))
      {
        SMONTHLYHOURS.KIND := hkNight;
        SMONTHLYHOURS.HOURS := double(Data);
        insert current SMONTHLYHOURS;
      }

      if (GetData(S, Data))
      {
        SMONTHLYHOURS.KIND := hkHoliday;
        SMONTHLYHOURS.HOURS := double(Data);
        insert current SMONTHLYHOURS;
      }

      if (GetData(S, Data))
      {
        SMONTHLYHOURS.KIND := hkWeekend;
        SMONTHLYHOURS.HOURS := double(Data);
        insert current SMONTHLYHOURS;
      }

      var LastAbbr: comp;
      LastAbbr := 0;

      _loop SDAILYDATA novisual
      {
        if (SDAILYDATA.ABBREVIATURE <> LastAbbr) and (LastAbbr <> 0)
        {
          insert current SVARIANCE;
          LastAbbr := 0;
        }
        if (SDAILYDATA.ABBREVIATURE <> 0)
        {
          if (SDAILYDATA.ABBREVIATURE <> LastAbbr)
          {
            ClearBuffer(#SVARIANCE);
            LastAbbr := SDAILYDATA.ABBREVIATURE;
            SVARIANCE.CUO := SDAILYDATA.ABBREVIATURE;
            SVARIANCE.KIND := 1;
            SVARIANCE.BEGINNING := SDAILYDATA.DAY;
          }
          SVARIANCE.ENDING := SDAILYDATA.DAY;
        }
      }
      if (LastAbbr <> 0)
        insert current SVARIANCE;

      ParseString := true;
    }
    _except
      on ExNumberConvert:
      {
        Displ('[x] Ошибка преобразования строки в число ' + Position);
        Displ(S);
      }
      on ExDatabase:
        Displ('[x] ' + ExploreException);
  }

  public function OpenFile(FileName: string): boolean;
  {
    OpenFile := false;
    _try
      OpenFile := F.OpenFile(FileName, stOpenRead);
    _except
      on ExFile:
        Displ('[x] ' + ExploreException);
  }

  public procedure CloseFile;
  {
    F.Close;
  }

  public function Next: boolean;
  {
    Next := false;
    CurMonth := 0;
    CurYear := 0;
    ClockNumber := 0;

    delete all SDAILYDATA;
    delete all SMONTHLYHOURS;
    delete all SVARIANCE;

    if (F.EOF)
      exit;
    do
    {
      var S: string;
      F.ReadLn(S);
      CurLine := CurLine + 1;
      if not ValidDataString(S)
        continue;
      ParseError := not ParseString(S);
      break;
    }
    while not F.EOF;
    Next := true;
  }

  public function ErrorFound: boolean;
  {
    ErrorFound := ParseError;
  }

  public function First: boolean;
  {
    First := false;
    F.Seek(0);
    CurLine := 1;
    First := Next;
  }

  public function TestFile: boolean;
  {
    TestFile := true;
  }

  public function GetClockNumber: longint;
  {
    GetClockNumber := ClockNumber;
  }

  public function GetMonth: byte;
  {
    GetMonth := CurMonth;
  }

  public function GetYear: word;
  {
    GetYear := CurYear;
  }

  public function GetDailyHours(D: byte; HourKind: byte; var Hours: double): boolean;
  {
    if (getfirst SDAILYDATA where ((D == SDAILYDATA.DAY)) = tsOk) and (HourKind = hkWorking) and (SDAILYDATA.ABBREVIATURE = 0)
    {
      Hours := SDAILYDATA.WORKINGHOURS;
      GetDailyHours := true;
    }
    else
      GetDailyHours := false;
  }

  public function GetMonthlyHours(HourKind: byte; var Hours: double): boolean;
  {
    if (getfirst SMONTHLYHOURS where ((HourKind == SMONTHLYHOURS.KIND)) = tsOk)
    {
      Hours := SMONTHLYHOURS.HOURS;
      GetMonthlyHours := true;
    }
    else
      GetMonthlyHours := false;
  }

  public function GetVarianceIterator: IWorkingTableVarianceIterator;
  {
    var WTVI: IWorkingTableVarianceIterator;
    LoadVipRef(WTVI, 'VWorkingTableVarianceIterator');
    GetVarianceIterator := WTVI;
  }

  HandleEvent
    cmOnVipLoad:
    {
      DataDelimiter := ';';
    }

    cmOnVipUnLoad:
    {
      CloseFile;
    }
  end;
end.

Добавлено: 16 авг 2007, 02:01
Screw
Посмотрел пост и аж огорчился: до чего ж некрасиво я реализовал работу с отклонениями :-(