СТАНДАРТНЫЕ ТРАНЗАКЦИИ: ИМПОРТ ДОКУМЕНТОВ

Задача интеграции АБС с внешними системами уверен хотя бы раз стояла перед вами. В АБС Бисквит имеется отдельный модуль посвященный этому вопросу - ОБМЕН С ВНЕШНИМИ СИСТЕМАМИ. Имеется так же документированный механизм импорта объектов в теговом формате.

Внешних систем огромное разнообразие и возможность экспорта данных есть почти во всех, но и форматы экспорта у всех систем разные и далеко не все из них соответствуют привычному АБС БИСКВИТ теговому формату. Это может быть текстовый формат, XML, DBF и т.д.

О том как научить АБС Бисквит понимать необходимый нам формат экспорта-импорта платежных документов мы здесь и поговорим.

Рассмотрим создание процедуры импорта платежных документов из внешних систем.

Для универсальности решения данной задачи разобьем ее на отдельные этапы (блоки):

  • обработка параметров транзакции (правило обмена);
  • чтение данных из файла (любой формат файла импорта);
  • заполнение временные таблицы w-op, w-op-entry и т.д.;
  • обработка шаблона транзакции соответствующего загружаемому документу;
  • валидация импортируемых документов;
  • перенос данных из временных таблиц в базу АБС;
  • печать ведомости загрузки.

Каждый из этих этапов может быть реализован в отельном инклюд-файле, тем самым мы получим возможность конструировать различные процедуры импорта документов.

И так начнем. Объявим необходимые нам переменные, временные таблицы и т.д.

{globals.i}
{imp-mci.def &nodef="/*"}

DEF VAR vCount      AS INT   NO-UNDO.
DEF VAR vErrCount   AS INT   NO-UNDO.
DEF VAR iErr        AS INT   NO-UNDO.
DEF VAR vError      AS CHAR  NO-UNDO.
DEF VAR vCodeClass AS CHAR  NO-UNDO.
DEF VAR vErrCode   AS CHAR  NO-UNDO.

DEF TEMP-TABLE tt-error NO-UNDO

FIELD err-message AS CHAR
.
DEF TEMP-TABLE tt-op
FIELD send-name AS CHAR
  FIELD send-acct AS CHAR
  FIELD ben-name  AS CHAR
  FIELD ben-acct  AS CHAR
  FIELD amt-rub   AS DECIMAL
  FIELD details   AS CHAR
.

DEF STREAM ImportStream.


ОБРАБОТКА ПАРАМЕТРОВ ТРАНЗАКЦИИ


Произведем анализ транзакции импорта и ее шаблонов. Из транзакции нам необходимо определить основные параметры импорта:

  • каталог импорта (каталог в котором будет осуществляться поиск файлов для импорта);
  • маска импортируемых файлов;
  • режим импорта рейс/автоматическая;
  • параметры документов (статус, счета т.д.) 
flag-go = 0.                    /* Вид импортируемых документов        */
{justasec}
{imp-mci.ch1 &NoStat = "YES"}  
{i-imp.del}

imp-mci.ch1 осуществляет обработку правила обмена и создает файл filelist.imp, в рабочем каталоге пользователя запустившего транзакцию, со списком импортируемых файлов, находящихся в каталоге импорта и попадающих под указанную маску, формирует запрос ввода номера рейса.

Возможность перезаписи рейса, с удалением загруженных ранее документов в рамках этого рейса осуществляется инклюд-файлом  {i-imp.del}.


ЧТЕНИЕ ДАННЫХ ИЗ ФАЙЛА ИМПОРТА


Для примера, мы будем импортировать внутренние  документы, например, загрузка реестра по выплате зарплаты, из следующего CSV файла со следующей структурой данных:

<Плательщик>;<ДБ>;<Получатель>;<КР>;<Сумма>;<Назначение платежа>

Приступим...

{debug.i "Чтение файлов из каталога"}
/* Цикл по файлам из каталога импорта */
INPUT STREAM FileListin FROM "filelist.imp".
REPEAT:
   /* Получение i-го файла в списке */                                               
   IMPORT STREAM FileListin file-name NO-ERROR.            
   /* Каждый файл обрабатываем в отдельной транзакции */
   DO TRANSACTION ON ERROR UNDO, LEAVE:
      INPUT STREAM ImportStream FROM VALUE(file-name).    
      /* Строка файла */
      REPEAT:       
         CREATE tt-op.
         /* Чтение i-ой записи из файла во временную таблицу tt-op */
         IMPORT STREAM ImportStream  DELIMITER ";" tt-op.  
      END.
      INPUT STREAM ImportStream CLOSE.
   END.
END.
INPUT STREAM FileListin CLOSE.
{debug.i "Чтение файлов из каталога:выполнено"}


ЗАПОЛНЕНИЕ ВРЕМЕННЫХ ТАБЛИЦЫ


Сформируем на основе импортированных записей таблицы w-op, w-op-entry.

{debug.i "Формирование w-op, w-op-entry"}
vCount = 0.
FOR EACH tt-op:
  vCount = vCount + 1.

  CREATE w-op.
  CREATE w-op-entry.

  ASSIGN
     w-op.op              = vCount             
     w-op.filial-id       = shFilial
     w-op.op-date         = TODAY
     w-op.doc-type        = IF op-template.doc-type NE ""
                            THEN op-template.doc-type
                            ELSE "01"
     w-op.doc-num         = STRING(vCount)
     w-op.user-id         = mail-user.user-id
     w-op.acct-cat        = "b"
     w-op.op-kind         = op-kind.op-kind
     w-op.op-status       = status-done
     w-op.inn             = ""
     w-op.doc-date        = in-op-date
     w-op.ins-date        = in-op-date
     w-op.due-date        = in-op-date
     w-op.contract-date   = in-op-date
w-op.bank-code-rec   = bank-mfo-9
     w-op.bank-code-send  = bank-mfo-9

     w-op.details         = tt-op.details
     w-op-entry.op        = vCount
     w-op-entry.op-entry  = 1
     w-op-entry.op-date   = w-op.op-date
     w-op-entry.user-id   = w-op.user-id
     w-op-entry.acct-cat  = w-op.acct-cat
     w-op-entry.op-status = w-op.op-status
     w-op-entry.acct-db   = IF op-template.acct-db EQ ?
                            THEN tt-op.send-acct
                            ELSE op-templ.acct-db       
     w-op-entry.acct-cr   = IF op-template.acct-cr EQ ?
                            THEN tt-op.ben-acct
                            ELSE op-templ.acct-cr
     w-op-entry.amt-rub   = tt-op.amt-rub
  .
END.
{debug.i "Формирование w-op, w-op-entry: выполнено"}

Таким образом, мы занесли импортируемые документы во временные таблицы w-op, w-op-entry.


ВАЛИДАЦИЯ ИМПОРТИРУЕМЫХ ДОКУМЕНТОВ


Следующим этапом нам необходимо провалидировать документы. Валидацию документов во временных таблицах w-op, w-op-entry процедурой imp-upd.p мы уже рассматривали подробно в отдельной статье Проверка реквизитов документов

{debug.i "Валидация импортируемых документов"}
RUN imp-upd.p (0, NO).  
{debug.i "Валидация импортируемых документов: выполнено"}

Здесь мы рассматриваем импорт однотипных, в плане типа платежа, документов и валидацию импортируемых документов проводим одновременно всех после занесения их во временные таблицы. Если бы мы импортировали реестр содержащий не однотипные документы (внутренние, ответные обороты и т.д.), валидацию необходимо бы было проводить по каждому документу в отдельности, предварительно определив тип платежа, например, по БИКу банка получателя, передавая соответствующие значение входных параметров процедуре imp-upd для каждого документа.

После валидации в поле w-op.op-error записи документа прописываются коды ошибок классификатора ошибок, по которым документ не прошел валидацию в формате <классификатор>:<код ошибки>,...

Код ошибки в классификаторе может быть классифицирован как ошибка (критическая), или как предупреждение. 

Документы имеющие "предупреждения" будем загружать со статусом "В - документ с ошибкой" для последующего анализа контролирующим сотрудником, а документы имеющие "ошибки" загружать вообще не будем и в последствии

vErrCount = 0.
FOR EACH w-op:
   IF w-op.op-error NE "" THEN
   DO:
      FIND FIRST w-op-entry WHERE w-op-entry.op EQ w-op.op NO-LOCK NO-ERROR.
/*
     ASSIGN
         status-done          = "В"
         w-op.op-status       = status-done.
         w-op-entry.op-status = status-done.
*/
      DO iErr = 1 TO NUM-ENTRIES(w-op.op-error):       
vError = ENTRY(iErr, w-op.op-error).
         ASSIGN
            vCodeClass = ENTRY(1,vError,":")
            vErrCode   = ENTRY(2,vError,":")
         .

Как вы можете заметить, присвоение статуса "В" документам у которых w-op.op-error <> "", т.е. имеющим какие-то ошибки, выявленных в процессе валидации, мы пока закомментировали. Для чего мы это сделали объясним чуть позже.

        /* 
Найдем ошибку в классификаторе и определим по нему
ошибка это
или предупреждение. Если это ошибка, удалим запись документа,
предварительно занеся информацию о нем во временную таблицу
tt-error
*/
         FIND FIRST code WHERE code.class EQ vCodeClass AND

                               code.code  EQ vErrCode
                         NO-LOCK NO-ERROR.
         /* Если это ошибка удаляем запись */
         IF AVAIL code AND code.val EQ "Ошибка" THEN
         DO:
            CREATE tt-error.
            ASSIGN
               tt-error.err-message = w-op.op-error
            .
            DELETE w-op.
            DELETE w-op-entry.
            vErrCount = vErrCount + 1.
            LEAVE.
         END.
      END.
   END.
END.


ВЕДОМОСТИ ЗАГРУЗКИ


Вывод ошибок в ведомость может осуществляться процедурой DispErr описанной в imp-all.pro, которая в свою очередь объявленна в imp-mci.def.

{imp-err.def}
FOR EACH w-op NO-LOCK:
   FIND FIRST w-op-entry WHERE w-op-entry.op EQ w-op.op NO-LOCK NO-ERROR.
   RUN DispErr.
END.
{preview.i &stream="stream err" {&*}}


ПЕРЕНОС ДАННЫХ ИЗ ВРЕМЕННЫХ ТАБЛИЦ В БАЗУ АБС


Для переноса документов из временных таблиц в базу в АБС Бисквит имеется ряд готовых иклюд-файлов, таких как:

  • imp-opcr.i
  • imp-opcrkas.i
  • imp-opcrmbk.i
  • imp-opcr.mci

Рассмотрение их принципиальных отличий оставлю пока вам самим. Все они несильно отличаются друг от друга и основным из них является imp-opcr.i, вот на нем мы и остановимся.

Тут есть ряд особых моментов:

Если flag-go <> 121 (что за 121 пока не знаю), то какой бы статус не был указан в w-op, он будет заменен на единый для всех status-done, который мы определили вначале процедуры.

Если flag-go = 1 или 2, то будет проведена проверка на ошибки и уже загруженному в АБС документу будет изменен статус на "В", а при flag-go = 1 еще и счет кредита будет заменен на nacct-db. Но это все только если ошибка - это действительно ошибка, а не предупреждение, например.

Если же ошибка окажется предупреждением, или вообще ничем (по классификатору ошибок), то ничего изменено не будет, и документ, у которого есть предупреждения, будет загружен со статусом status-done

При flag-go = 0 или 3 таких преобразований не произойдет, и всем документам будет проставлен единый статус равный, значению переменной status-done.

Есть 2-а варианта, которые я вижу на текущий момент:
     1) это изменять статусы уже загруженных в базу документам;
     2) написать свой инклюд файл импорта документов на основе imp-opcr.i.

Здесь мы рассмотрим первый вариант, оставив второй на откуп вам.

Загрузку провалидированных документов в базу АБС будет осуществлять с помощью инклюд-файла imp-opcr.i.

{debug.i "Импорт документов"}
FIND FIRST w-op NO-LOCK NO-ERROR.
IF AVAIL w-op THEN
DO:
  {imp-opcr.i
     &NO-DEL-WOP="FALSE"} 
END.
{debug.i "Импорт документов: выполнено"}

Теперь нам необходимо изменить статус загруженных документов.

{debug.i "Изменение статусов документов"}
FOR EACH w-op:
   FIND FIRST op OF w-op NO-LOCK NO-ERROR.
   IF AVAIL op THEN  
   DO:
      {opent-st.i &status=w-op.op-status}
   END.
END.
{debug.i "Изменение статусов документов:выполнено"}

На этом импорт документов в базу завершен.

 

Вы здесь: Главная ИБС Бисквит СТАНДАРТНЫЕ И УНИВЕРСАЛЬНЫЕ ТРАНЗАКЦИИ СТАНДАРТНЫЕ ТРАНЗАКЦИИ: ИМПОРТ ДОКУМЕНТОВ