«Краткое оглавление КРАТКОЕ ОГЛАВЛЕНИЕ 1 ПОДРОБНОЕ ОГЛАВЛЕНИЕ 4 ВВЕДЕНИЕ 17 Для кого предназначено настоящее руководство 17 История создания платформы 17 Платформа с открытым кодом 18 Обзор архитектуры 20 УСТАНОВКА И ...»
-- [ Страница 8 ] --
Аналогично настроим права доступа для документа «04. Счета-фактуры (оптовая торговля)».
Ограничение доступа для группы Менеджеры Займемся теперь менеджерами. Задачу разграничения прав доступа для этой группы разобьем на следующие подзадачи:
• Запретить добавление и удаление счетов-фактур за пределами текущего месяца;
• Запретить добавление, изменение и удаление накладных за пределами текущего месяца;
• Запретить просмотр накладных за пределами текущего месяца.
Первые два пункта из нашего списка можно решить, наложив ограничения в тригерах на таблице GD_DOCUMENT.
Прежде чем перейти к созданию тригеров определим идентификаторы типов документов и групп пользователей. Для получения идентификаторов типов перейдем в Исследователь и в разделе «Сервис»
выберем команду «Типовые документы». В древовидном списке, в левой части окна найдем раздел «Оптово-розничный склад». В правой части, установим курсор на позицию «04. Счета-фактуры (оптовая торговля)» как показано на следующем рисунке.
Рис. 100 Типы документов.
Нажмем правую кнопку мыши и выберем команду «Свойства…». Выпишем значение идентификатора записи. Аналогичную операцию проделаем для документа «03. Отпуск товара на сторону (оптовая торговля)». У нас получились следующие значения:
• Счет-фактура — 147043282;
• Накладная — 147043395.
Обратите внимание, что значения идентификаторов на каждой базе данных будут отличаться от приведенных выше! Поэтому, выполняя наш пример на своей базе данных вы получите другие значения.
Неизменными от базы к базе будут РУИДы этих записей, но об их использовании мы поговорим отдельно.
Для получения идентификаторов групп пользователей перейдем в раздел «Исследователь»—«Сервис»— «Администратор» и выберем команду «Группы пользователей». Так же, как и для документов, воспользуемся командой «Свойства…» из контекстно-зависимого меню. Наши значения идентификаторов:
• Руководство — 7;
Как и в случае с идентификаторами типов документов, значения полученные на вашем экземпляре базы данных скорее всего будут отличаться от полученных нами.
Собственно, использовать непосредственно идентификаторы групп в тригерах мы не сможем. Нам нужна битовая маска, которую мы вычислим следующим образом:
27-1+29-1=26+28=64+256= В рассматриваемой нами задаче необходимо запретить менеджерам и представителям группы руководство вставку и удаление счетов-фактур и накладных за пределами текущего месяца. Кроме этого, руководству запрещается изменение счетов и накладных за пределами текущего месяца, а менеджерам запрещается изменение только накладных, но разрешается изменение счетов.
Создадим три тригера на таблице GD_DOCUMENT, которые будут вызываться, соответственно, перед вставкой, изменением и удалением записи. Если дата документа выходит за пределы текущего месяца и документ является счетом-фактурой или накладной и пользователь входит хотя бы в одну из «запрещенных» групп, то сгенерируем исключение (EXCEPTION). Обратите внимание, что мы не будем создавать своего объекта исключения, а воспользуемся GD_E_BLOCK — объектом присуствующим в эталонной конфигурации базы данных.
Для создания тригера необходимо открыть Исследователь системы. Далее последовательно откроем раздел «Сервис» и, находящийся в нем раздел «Атрибуты». Вызовем команду «Таблицы». На экране откроется список таблиц базы данных. Найдем в нем GD_DOCUMENT и откроем данную таблицу на редактирование в диалоговом окне. Перейдем на вкладку «Триггеры». В верхней части вкладки установим курор на позицию «Before Insert» и нажмем кнопку «Добавить».
Рис. 101 Добавление триггера.
На экране откроется окно создания нового триггера.
Рис. 102 Создание тригера.
Введем текст триггера и нажмем «Ок». Вышеуказанные операции повторим последовательно для создания триггеров перед изменением и перед удалением записи. Ниже приводятся исходные коды всех трех триггеров. Будьте внимательны! Они не идентичны. В триггере перед обновлением записи мы проверяем пользователя только на вхождению в группу Руководство (битовая маска 256), а в триггере перед удалением мы используем мета-переменную OLD, а не NEW.
Триггер Before Insert
DECLARE VARIABLE D DATE;
DECLARE VARIABLE G INTEGER;
BEGIN D = CAST(’01.’ || EXTRACT(MONTH FROM CURRENT_DATE) || ‘.’ ||
EXTRACT(YEAR FROM CURRENT_DATE) AS DATE);
IF (NEW.documentdate < :D) THEN BEGIN IF (NEW.documenttypekey IN (147043282, 147043395)) THEN SELECT FIRST 1 ingroup FROM gd_user WHERE ibname = CURRENT_USER IF (BIN_AND(:G, 320) 0) THEN EXCEPTION gd_e_block;
END Триггер Before Update
DECLARE VARIABLE D DATE;
DECLARE VARIABLE G INTEGER;
BEGIN D = CAST(’01.’ || EXTRACT(MONTH FROM CURRENT_DATE) || ‘.’ ||
EXTRACT(YEAR FROM CURRENT_DATE) AS DATE);
IF (NEW.documentdate < :D) THEN BEGIN IF (NEW.documenttypekey IN (147043282, 147043395)) THEN SELECT FIRST 1 ingroup FROM gd_user WHERE ibname = CURRENT_USER IF (BIN_AND(:G, 64) 0) THEN EXCEPTION gd_e_block;
IF ((BIN_AND(:G, 256) 0) AND (NEW.documenttypekey = 147043395)) THEN EXCEPTION gd_e_block;
END Триггер Before Delete
DECLARE VARIABLE D DATE;
DECLARE VARIABLE G INTEGER;
BEGIN D = CAST(’01.’ || EXTRACT(MONTH FROM CURRENT_DATE) || ‘.’ ||
EXTRACT(YEAR FROM CURRENT_DATE) AS DATE);
IF (OLD.documentdate < :D) THEN BEGIN IF (OLD.documenttypekey IN (147043282, 147043395)) THEN SELECT FIRST 1 ingroup FROM gd_user WHERE ibname = CURRENT_USER IF (BIN_AND(:G, 320) 0) THEN EXCEPTION gd_e_block;
END Создание триггеров в базе данных произойдет только после того, как будет закрыто по кнопке «Ок» окно редактирования таблицы. При этом на экран будет выведено окно с ходом выполнения соответствующих SQL команд.
Осталось только скрыть от менеджеров накладные за пределами текущего месяца. Для этого, при формировании запроса для извлечения из базы данных списка накладных мы будем проверять входит ли текущий пользователь в группу «Менеджеры» и если да, то добавлять в часть WHERE запроса условие для отбора записей только после первого числа текущего месяца.
Реализуем вышеописанный алгоритм:
1. Откроем форму просмотра накладных;
2. Переведем ее в режим дизайнера нажав одновременно Ctrl-Alt-E на клавиатуре;
3. Найдем на форме компонент gdcInvDocument и выделим его;
4. В Инспекторе объектов перейдем на вкладку События;
5. Найдем в списке событие OnGetWhereClause и создадим обработчик для него;
6. В сгенерированный системой код обработчика добавим свои строки, как показано на рисунке Рис. 103 Определение обработчика события.
Полный текст обработчика события приведен ниже. Добавленый нами код достаточно тривиален. Мы проверяем не установлен ли в битовом наборе групп в которые входит текущий пользователь бит, соответствующий группе «Менеджеры», если установлен, то добавляем к условиям секции WHERE ограничение на дату документа. При этом, если секция WHERE уже содержала некоторые условия, то присоединим новое условие через логический оператор AND.
option explicit sub gdcInvDocumentOnGetWhereClause(ByVal Sender, ByRef Clause) '*** Данный код необходим для вызова встроенного обработчика *** '*** В случае его удаления возможно нарушение работы системы *** Dim ParamArr(1) Set ParamArr(0) = Sender ParamArr(1) = Clause call Inherited(Sender, "OnGetWhereClause", ParamArr) Clause.Value = ParamArr(1) '*** конец кода поддержки встроенного обработчика *** if (IBLogin.InGroup and 2^(9 - 1)) 0 then Clause = Clause & "z.documentdate >= '01." &_ Month(Date) & "." & Year(Date) & "'" end if end sub Тестирование Осталось протестировать, созданный нами код. Для этого следует по-очередно войти в Гедымин под учетными записями Руководителя, Бухгалтера и Менеджера и попробовать создать, изменить и удалить документ как в рамках текущего месяца и за более ранние даты. Если все было сделано правильно, то при попытке выполнения недозволенной операции на экран должно быть выдано сообщение о возникновении исключения.
Третье приложение Цель главы открыть перед разработчиком все аспекты системы Гедымин. Поэтому возьмемся за написание склада.
Постановка задачи Разработка настройки автоматизации для документооборота оптово-розничной торговли. В самом начале нам необходимо определить объекты учеты, необходимые документы, используемые атрибуты. Основа документооборота оптово-розничной торговли составляют складские документы (документы осуществляющие перемещение и учет товаров), основные объекты учета – товары и контрагенты.
Атрибуты необходимы для учета товара Фиксированные атрибуты (справочник ТМЦ) Наименование ТМЦ Шифр ТМЦ Штрих код Ед. изм.
Наценочная группа Вес товара Ед. изм. Отпуска Изображение Изменяемые атрибуты (карточка ТМЦ) Счет Ссылка на позицию прихода ставка НДС прихода Позиция счета НДС прихода руб.
Сертификат(старые) Контрактная цена(вал) Контрактная цена(руб) Цена учета руб Цена покупки(вал) Цена покупная(экв.) Цена покупная Цена(вал) Цена отпуска(экв.) Цена отпуска со всеми налогами Цена стеклопосуды Цена руб.
Цена НДС(вал) Цена НДС руб.
Цена налога на топливо входящий Оптовая цена(вал) Оптовая цена(экв.) Оптовая цена руб Цена поставщика Розничная цена(экв.) Розничная цена Цена налога с продаж входящий Цена с торговой надбавкой Цена с НДС и торг.надбавкой Цена отпуска с НДС(вал) Цена отпуска с НДС Валюта прихода Там.сбор.вал.часть(вал) Там.сбор вал.часть(руб) Там.сбор руб.часть(руб) Таможенные пошлины(вал) Таможенные пошлины(руб) Таможенный НДС(руб) Таможенное разрешение Дата поступления Срок годности Артикул Удостоверение ГТР Фиксированная цена Ссылка на позицию расхода Валюта расхода Условия поставки Оптовая надбавка Курс на дату расхода Страна происхождения Накладные расходы рублевые Накладные расходы(вал) Накладные расходы(руб) Упаковка Надбавка поставщика Надбавка отпуска Производитель Поставщик Качество Кол-во в упаковке Ссылка на заявку Курс на дату прихода Ставка НДС отпуска Акцизные марки Статистическая декларация Ставка налога с продаж Торговая надбавка Процент торг.надб.от покуп.цены Торг.надб.от отповой цены Трансп.расходы(вал) Трансп.расходы(руб) Транс.расх. по терр.РБ Сертификат Перечисление документов и форм, которые нам понадобятся Складские документы 01. Накладная на получение товара По этому документу на подразделении нашей организации регистрируется товар в количестве и его цене.
В документе производится расчет оптовой и розничной цены. По данному документу возникает задолженность нашей организации перед поставщиком товара.
01.Накладная на получение импортного товара Аналогично предыдущему документу с добавлением валютных цен, с возможностью указания таможенных расходов и алгоритмом распределения этих расходов по позициям накладной с включением их в стоимость товара.
02. Внутреннее перемещение товаров По данному документу производится перемещение ТМЦ с одного подразделения нашего предприятия на другое, при этом ни какие другие атрибуты за исключением оптовой цены и балансового счета не изменяются 03. Отпуск товара на сторону Оптовая торговля в рублях По данному документу производится отпуск товара сторонней организации, при этом уменьшается количество товара на нашем подразделении и возникает задолженность организации перед нами. В данном документе должна быть возможность автоматического заполнения цены отпуска на основании сформированных цен в приходе или цен по выбранному прайсу, при этом пользователь должен иметь возможность изменять цену самостоятельно.
Розничная торговля в рублях Аналогично предыдущему документу, но с фиксорованным алгоритмом формирования цены. Т.к. отпуск идет из розничной торговли, значит цена должна быть использована розничная с соответствующими расчетами налогов и округлениями.
Оптовая торговля на экспорт Аналогично отпуску товара в оптовой торговли, но с валютными ценами, возникновением валютной задолженности, с возможностью отпуска без начисления НДС.
04. Счета-фактуры валютные(оптовая торговля) 04. Счета-фактуры из розничной торговли 04. Счета-фактуры (оптовая торговля) 05. Возврат товара от покупателей 06. Выдача в торговые подразделения 06а. Возврат с торговых подразделений 07. Возврат товаров поставщику 08. Списание товаров 09. Переоценка товаров 10. Инвентаризация товаров 11. Реализация товаров в розницу Документы пользователя Доверенность Договор Изменение надбавок Прайс-лист Прейскурант фиксированных цен Оптовый прайс-лист Справочники Автомобили Марки автомобилей Сертификаты Таможенное разрешение Типы складов Группы надбавок Удостоверение ГТР Условия поставки Условия оплаты Форма оплаты Цели приобретения Справочник качества продукции Статистическая декларация Типы транспортировки Типы формирования цен Проектирование базы данных (проектирование документов);
Проектирование пользовательского интерфейса;
Кодирование;
Как осуществляется отладка кода в системе Гедымин;
Запуск приложения:
Ввод остатков;
Повседневная эксплуатация;
Получение выходных данных;
Сопряжение с внешними программами Вряд ли сегодня найдется предприятие, применяющее для автоматизации только один программный продукт. В реальной практике можно встретить такие случаи, когда бухгалтерия использует одну программу, отдел сбыта — другую, а руководство использует Microsoft Excel для анализа и обработки управленческой информации. Современная система должна как предоставлять доступ к своим данным и объектам внешним программам, так и уметь воспользоваться функциями внешних программ, извлекать данные из внешних источников.
В настоящей главе мы на конкретных примерах рассмотрим как с помощью Гедымина можно импортировать данные из текстовых файлов, работать с данными в формате XML, подключаться к COM серверам, а также к внешним базам данных с использованием технологии ADO.
Импорт данных из текстовых файлов Хотя мы живем в эпоху XML, DCOM, ADO и прочих технологий, призванных максимально упростить передачу данных от одного приложения к другому, тем не менее, еще находятся программы, единственный способ взаимодействия с которыми — чтение или запись данных в текстовые файлы.
Пример такой программы — электронная платежная система банк-клиент, широко распространенная в Беларуси. Клиентская часть системы соединяется с сервером в банке по модему и принимает информацию о движении денег по счетам, курсам валют и т.п. Информация записывается в текстовые файлы, имеющие определенную структуру.
Пусть перед нами стоит задача извлечь полученную информацию из текстового файла и поместить ее в базу данных. Решить задачу можно, что называется, «в лоб», написав макрос, который будет считывать текст из файла, анализировать его, извлекать данные и помещать их в базу. Однако, такой подход не самый удачный. Во-первых, банки любят периодически изменять формат выходного файла, в результате чего придется частично или полностью переписывать созданный макрос. Во-вторых, у каждого банка может быть свой формат данных. В-третьих, написать корректный парсер текста, так чтобы он аккуратно обрабатывал все возможные ошибки в данных, несоответствие формата файла и т.п. не так уж и просто.
Решение данной задачи потребует как высокой квалификации разработчика, так и большого количества времени. К счастью, в Гедымин уже встроен инструментарий для автоматизации процедур импорта данных из текстовых файлов.
Работает механизм импорта следующим образом:
1. существует глобальный объект Converter, который позволяет считать с диска текстовый файл, содержащий информацию, разобрать его и поместить данные в специальную внутреннюю структуру, откуда, впоследствии, они могут быть записаны в базу или обработаны произвольным образом в макросе;
2. глобальному объекту Converter передается шаблон (своего рода инструкция на специальном языке), который определяет структуру текстового файла с данными;
3. для разбора текста и извлечения данных вызывается метод StartConvert, в который передается имя текстового файла. Данный метод разбирает текст, извлекает из него данные и помещает их во внутреннюю структуру;
4. после окончания разбора, данные из внутренней структуры перекачиваются в базу с помощью бизнес-объектов или компонент доступа к базе данных таких как: TIBSQL, TIBDataSet.
Схематически весь процесс можно изобразить следующим образом:
Рис. 104 Процесс импорта данных из текстового файла.
Рассмотрим компоненты процесса более подробно:
Шаблон Шаблон — это последовательность инструкций на специальном языке, которая описывает структуру текстового файла с данными. Предполагается, что файл с данными организован следующим образом:
Начало данных помечено определенной последовательностью символов или метасимволом30. До начала данных может располагаться произвольный текст;
2. Данные можно структурировать (разбить) на области (Areas). Областей может быть одна или 3. Начало области задается последовательностью символов или метасимволом.
4. Каждая область имеет свое имя.
5. Внутри области может располагаться произвольное количество полей и таблиц.
6. Каждое поле имеет свое имя. Поле задается путем:
a. указания его начала и указанием длины поля в символах, b. или указанием начала и указанием множества символов, которые может содержать c. или указанием начала и указанием последовательности символов, которая завершает 7. Началом поля считается первый левый символ, если указана ориентация поля слева-направо или крайний правый символ, если указана ориентация справа-налево. Если ориентация поля не указана непосредственно, то, по-умолчанию, принимается ориентация слева направо.
8. Начало поля задается:
a. Либо указанием координаты первого символа поля по горизонтали относительно начала строки и по вертикали относительно текущей строки, 9. Маркер — это последовательность символов или метасимвол. Маркер отыскивается в исходном тексте и курсор устанавливается на первый символ маркера. Дополнительно, при определении a. Что необходимо пропустить следующие за маркером N символов. В этом случае курсор Поддерживаются следующие метасимволы: BOF — начало файла, EOF — конец файла, BOL — начало строки, EOL — конец строки, EL — пустая строка.
При разборе текстовых данных, сама указанная последовательность символов, завершающая поле в это поле не включается.
b. Что необходимо пропускать следующие символы, пока они соответствуют заданному c. Что необходимо пропускать следующие за меркером символы, пока не встретится символ, входящий в заданное множество символов32.
10. Каждая таблица имеет свое имя. Начало таблицы может задаваться определенной последовательностью символов или метасимволом.
11. Таблица содержит одинаковые по своей структуре записи (ноль, одну или несколько). Структура записи определяется перечислением полей33.
12. Окончание таблицы может задаваться определенной последовательностью символов или 13. Окончание области может задаваться определенной последовательностью символов или 14. Окончание данных может задаваться определенной последовательностью символов или 15. Если окончание таблицы или области не задано явно, то текущая таблица или область считаются законченными, если за ними начинается следуюшая таблица или область.
16. Если окончание данных не задано явно с помощью последовательности символов или метасимволом, то конец файла считается концом данных.
Схематически, внутреннюю организацию файла с данными можно представить следующим образом:
Рис. 105 Внутренняя организация текстового файла с данными.
Параметры обработки текста Кроме описания структуры текстового файла с данными шаблон может содержать следующие параметры, определяющие как будет идти обработка текста:
• CodePage — Допустимые значения Oem или ANSI. Определяет кодовую страницу, в которой представлен текст в файле. Пример: CodePage: Oem. Кодировка OEM применяется для файлов созданных в DOS, ANSI — во Windows.
• TrimRight — Допустимые значения Yes или No. Определяет будут ли удаляться концевые пробелы из строк файла. Пример: TrimRight: Yes.
• TrimLeft — Допустимые значения Yes или No. Определяет будут ли удаляться начальные пробелы из строк файла. Пример: TrimLeft: No.
Множество символов задается строкой. Например, множество цифровых символов можно задать строкой “0123456789”. Порядок символов в строке значения не имеет.
Одна запись таблицы может занимать произвольное количество строк в исходном текстовом файле.
• CaseSensitive — Допустимые значения Yes или No. Определяет будет ли учитываться регистр символов при распозновании меток начала области, таблицы, маркеров и т.п. Пример:
CaseSensitive: Yes.
Каждый параметр должен располагаться на отдельной строке в файле шаблона.
Комментарии в тексте шаблона В тексте шаблона допускается использование коментариев. Символ коментария — двойная косая черта — “//”. Как и в Delphi, символ коментария помечает весь текст, следующий за ним, до конца строки как, собственно, коментарий.
Пример:
// Пример комментария.
Формальная спецификация шаблона Ниже приводится формальная спецификация шаблона:
//Учитываем кодовую страницу в файле данных, по умолчанию OEM CodePage: {ANSI| OEM} //Указывает на то, удалять концевые пробелы или нет, по умолчанию Yes TrimRight: {Yes|No} // Указывает на то, удалять начальные пробелы или нет, по умолчанию No TrimLeft: {Yes|No} //Учитывать или не учитывать регистр при поиске маркеров, по умолчанию Yes CaseSensitive: {Yes|No} // Системные символы // BOF -- пачатак файлу // EOF -- канец файлу // EOL -- канец радка // BOL -- пачатак радка // EL -- пусты радок System_Symbol = { BOF | EOF | EOL | BOL | EL } Literal_String = "any chars"
ENDMARKER
ENDFIELD
ENDUSERFIELD
ENDLABEL
Пояснения по формальной спецификации шаблона Данные BEGINFILE : Задает последовательность символов, по которой в файле будет [[][]...] Файл содержит произвольное количество областей.
ENDFILE : Задает последовательность символов, по которой в файле будет Область BEGINAREA : Задает последовательность символов, по которой в файле будет Name: Наименование области. Обязательный параметр.
[[][]...] Область содержит произвольное количество полей и таблиц.
ENDAREA : Задает последовательность символов, по которой в файле будет Маркер TEXT: Необязательный параметр. Текст для поиска в файле. Указатель WHOLEWORD: {Yes|No}] Необязательный параметр, используется в паре с параметром TEXT.
SKIPCHARS: {EL|Literal_String} Пропустить указанные символы. Параметр не обязательный. Если SKIPUNTILCHARS: Необязательный параметр. Указатель перемещается, пока не SKIPNEXTCHARS: int N Необязательный параметр. Пропустить указанное количество Поле NAME: Обязательный параметр. Имя поля.
SIZE: int Size Обязательный параметр. Длина поля. При считывании поля из MARKER: Необязательный параметр. Позволяет указать маркер, относительно POSX: int X Смещение данных поля в файле по оси Х, относительно текущего POSY: int Y Смещение данных поля в файле по оси У, относительно текущего NODATA: int Необязательный параметр. Указывает на сколько позиций по TERMINATOR: Необязательный параметр. Последовательность символов, которая TRIMCHARS: Необязательный параметр. Задает множество символов, которые LEGALCHARS: Необязательный параметр. Задает множество символов, которые ALIGNMENT: Выравнивание поля в файле с данными. Слева-направо или справаналево. По-умолчанию параметр имеет значение: слева-направо.
HEIGHT: int Height Необязательный параметр. Высота поля. При использовании полей с Пользовательское поле BEGINUSERFIELD Начало определния пользовательского поля.
NAME: Обязательный параметр. Наименование поля.
ENDUSERFIELD Конец определения пользовательского поля.
Таблица BEGINTABLE : Начало таблицы в данных помечено определенной NAME: Наименование таблицы. Параметр обязательный.
[] Необязательный маркер, для перемещения курсора на начало [NECESSARILY: int Необязательный параметр. Содержит номер обязательного столбца ColumnIndex] в первой строке записи. Т.е. в данном столбце в первой строке [] Метка записи. Параметр необязательный. Используется в таблицах, [ENDRECORD: ] Последовательность символом, которой помечается конец записи.
[[Field][Field]...] Определение полей, составляющих запись таблицы.
ENDTABLE : Конец таблицы должен быть помечен определенной Метка BEGINLABEL Начало определения метки записи.
POSX: int X Позиция в строке, с которой начинается запись.
MASK: Последовательность символов, определяющая начало записи.
Глобальный объект Converter Глобальный объект Converter предназначен для извлечения данных из текстовых файлов, имеющих определенную структуру.
Методы • procedure LoadFromTempFile(ATempName: String) Загружает шаблон из текстового файла, заданного именем ATempName.
• procedure LoadTemplateFromStream(S: TStream) Загружает шаблон из потока, переданного параметром S.
• procedure StartConvert(ATextName: String) Разбирает переданный текстовый файл (конвертирует его данные во внутреннюю структуру).
Имя файла передается через параметр ATextName. Перед вызовом процедуры в объект с помощью методов LoadFromTempFile или LoadTemplateFromStream должен быть загружен Свойства Объект, содержащий данные, извлеченные из текстового файла. Подробное описание см. ниже.
Объект Database Объект Database представляет собой внутреннюю структуру, куда помещаются данные из влеченные из текстового файла после его разбора в соответствии с шаблоном. Объект содержит всего два свойства:
массив областей и количество областей в массиве.
Свойства • Areas(Index: Integer) Массив объектов, областей с данными.
• AreasCount: Integer Количество объектов в массиве Areas.
Объект Area Свойства • AreaFields: TClientDataSet Поля области. Хранятся в наборе данных, который содержит только одну запись. Обращение к каждому полю возможно по имени, как к обычному полю датасета:
• Name: String Наименование области. Задается в шаблоне.
• TableCount: Integer Количество таблиц в области.
• Tables(Index: Integer) Массив таблиц, содержащихся в области. См. объект Table ниже.
Объект Table Свойства • Name: String Наименование таблицы. Задается в шаблоне.
• Records: TClientDataset Записи, находящиеся в таблице. Представлены ввиде обычного TClientDataset.
Пример импорта Поясним сказанное на конкретном примере. Пусть на некотором предприятии установлена система банкклиент, которая ежедневно принимает выписки по рассчетному счету. Стоит задача импортировать эти выписки из текстового файла и записывать их в базу данных.
Образец выписки Ниже приводится образец файла с выпиской, формируемого системой банк-клиент:
BEG ОТ : КОМУ : ДАТА : ИСХ : ТИП : 199 /9/ОТВЕТ НА ЗАПРОС :20: 10/10/91 11: :79: /9/ОТВЕТ НА КЛИЕНТСКИЙ ЗАПРОС Лицевой по счету N 3012-006540016 за 10/10/ ООО "ПРОЕКТ" Исходящий остаток за 10/10/97 13281645. ------------------------------Состояние 99812 / 3012005640016 (K1) на 10/10/ Остаток по 99812 за 10/10/97 11386100. Состояние 99814 / 3012005640016 (K2) на 10/10/ Остаток по 99814/3012005640016(K2) за 10/10/91 2792717300. ------------------------------Код банка 153001369 Время-11:
Г.МИНСК ЦА БЕЛПРОМСТРОЙБАНКА
------------------------------------------------------------END Шаблон выписки Построим шаблон, для извлечения данных из выписки:
// все концевые пробелы в каждой строке выписки следует удалить TrimRight: Yes // начальные пробелы не трогаем, оставляем как есть TrimLeft:No // выписка формируется и сохраняется в файле в DOS кодировке CodePage: Oem // поиск маркеров в тексте выписки будет производиться с учетом // регистра CaseSensitive:Yes // Данные в файле начинаются после слова BEG BeginFile: "BEG" // файл содержит выписки // каждая выписка начинается со слова "Лицевой" BeginArea: "Лицевой" Name: "Account Area" // выписка содержит два поля и таблицы, содержащие // записи о движении средств по счету и список документов // помещенных на картотеку // поле - расчетный счет, по которому сформирована выписка BeginField // расчетный счет располагается в тексте выписки // за символом N и следующими за этим символом // расчетный счет имеет длину 15 символов // непосредственно за расчетным счетом располагается пробел Text: "N" SkipChars: " " EndMarker Name: "Account" Terminator: " " Size: EndField // поле - дата на которую сформирована выписка BeginField // дата располагается после предлога "за" и следующими // за ним пробелами // поле Дата может содержать цифры и знаки "." и "/", // как разделители частей даты // длина поля 12 символов BeginMarker Text: "за" SkipChars: " " EndMarker Name: "Date" LegalChars: "0123456789./" Size: EndField // таблица, содержащая записи о движении средств по // счету, начинается со строки "Входящий остаток" BeginTable: "Входящий остаток" Name: "StatementLines" // после подстроки, определяющей начало таблицы, // пропустим все символы до конца строки BeginMarker SkipUntilChars: EL EndMarker // поле - тип операции BeginField // поле занимает первые два символа строки BeginMarker EndMarker Name: "OpType" Alignment: Right EndField // поле - код банка BeginField // поле начинается с 4-го символа от начала строки // и имеет длину 3 символа // обратите внимание, что так как в описании данного поля // нет никакой информации о перемещении курсора (маркере) // то положение курсора отсчитывается от маркера, // установленного предыдущим полем. т.е. от начала строки Name: "BankCode" EndField // поле - номер счета BeginField // поле начинается с 8 символа о начала строки // обратите внимание, как формируются данные этого поля:
// сначала считываются все символы, пока они входят // во множество "0123456789-", потом из считанных // данных убираются символы, входящие во множество "-" // (в данном случае - это один символ) Name: "Account" PosX: LegalChars: "0123456789-" TrimChars: "-" Size: EndField // поле - номер документа BeginField // обратите внимание, что поле выровнено по правому краю // соответственно и мы указываем выравнивание вправо и, // в этом случае, PosX задает позицию крайнего правого // символа. В номер мы включим все символы, начиная от // 28 позиции, двигаясь влево, пока не встретится символ // пробела Name: "DocNumber" PosX: Terminator: " " Alignment: Right Size: EndField // сума по дебету BeginField // поле имеет выравнивание вправо. крайний правый символ // занимает позицию 53. длина поля 15 символов.
// число может быть записано с разделителями тысяч - пробелами, // поэтому мы указываем терминатор считываемых данных не один // символ, а строку из двух пробелов. после считывания данных // удалим из них все пробелы (т.е. все разделители тысяч), // чтобы можно было легко преобразовать строку в число.
// обратите внимание на использование параметра nodata, равного // 8 символам. дело в том, что колонки Дебет и Кредит могут // пересекаться, если сумма по кредиту слишком большая.
// т.е. число по Кредиту может залезть на 53-ю, 52-ю, 51-ю и т.д.
// позиции. Для того, чтобы отличить, где данные принадлежат // полю Дебет, а где - Кредит, мы указываем, что после поля // Дебет должно идти хотябы 8 пробелов или строка должна // заканчиваться.
Name: "Debet" PosX: Alignment: Right TrimChars: " " Terminator: " " Size: Nodata: EndField // поле - сумма по Кредиту BeginField Name: "Credit" PosX: Alignment: Right TrimChars: " " Terminator: " " Size: EndField // таблица заканчивается пустой строкой EndTable: EL // после таблицы с информацией о движении средств по счету // может находиться таблица со списком документов, помещенных // на картотеку К1. таблица (если она есть) начинается // со строки "(K1) на" BeginTable: "(K1) на" Name: "K1" // пропускаем пустые строки, которые могут идти после начала // таблицы и строку заголовок таблицы.
BeginMarker SkipUntilChars: "Сумма" EndMarker // поле - дата документа. Длина 10 символов, начиная с первого // символа строки.
BeginField Name: "Term" Size: Alignment: Left EndField // поле - код МФО. 5 символов вправо от тринадцатой позиции // от начала строки.
BeginField Name: "MFO" PosX: Alignment: Right EndField // поле - номер счета BeginField Name: "Account" PosX: LegalChars: "0123456789-" Terminator: " " Size: Alignment: Right EndField // поле - номер документа BeginField Name: "DocNumber" PosX: Terminator: " " Alignment: Right EndField // поле - сумма BeginField Name: "Summa" PosX: Alignment: Right TrimChars: " " Terminator: " " Size: EndField // таблица заканчивается пустой строкой EndTable: EL // таблица с картотекой К2 начинается со строки "(K2) на" BeginTable: "(K2) на" Name: "K2" // пропускаем заголовок таблицы BeginMarker SkipUntilChars: "Сумма + пеня" EndMarker // поле - дата документа BeginField Name: "Term" Size: Alignment: Left EndField // поле - код МФО BeginField Name: "MFO" PosX: Alignment: Right EndField // поле - номер счета BeginField Name: "Account" PosX: LegalChars: "0123456789-" Terminator: " " Size: Alignment: Right EndField // поле - номер документа BeginField Name: "DocNumber" PosX: Terminator: " " Alignment: Right EndField // поле - Np BeginField Name: "Npl" PosX: Terminator: " " // таблица заканчивается пустой строкой EndTable: EL // укажем строку, которой помечено окончание области EndArea: "--------------------" // данные в файле оканчиваются строкой "END" EndFile: "END" Проверка шаблона Проверим, насколько правильно сформирован шаблон для распознавания файла выписки. Пусть сама выписка сохранена в файле vyp.txt, а шаблон — в файле vyp.tem. Откроем Гедымин, в главном окне выберем пункт меню Сервис и в нем команду «Просмотр шаблона документов…». В появившемся окне введем полное имя файла с шаблоном и полное имя файла с выпиской и нажмем кнопку «Считать». Если шаблон не содержит ошибок и файл с выпиской содержит корректные данные, соответствующие шаблону, то текстовые данные будут считаны, разобраны и отображены в окне:
Рис. 106 Окно "Считывание документа по шаблону".
Используя данное окно можно просмотреть список областей, распознанных в указанном файле. Для каждой области можно ознакомиться со списком переменных и списком, содержащихся в ней таблиц.
Если шаблон содержит ошибки, то при нажатии на кнопку «Считать» на экран будет выведено соответствующее сообщение с пояснением ошибки и номером неверной строки.
Макрос импорта После того, как составлен шаблон для разбора файла с выпиской, необходимо написать макрос, который будет извлеченные из текстового файла данные помещать в базу. Полнофункциональные макросы для импорта выписок в форматах различных банков входят в состав настройки «Банк и касса». При желании, вы можете ознакомиться с ними. Поскольку цель данной главы скорее состоит в том, чтобы предоставить общую информацию о принципах работы механизма импорта, нежели в том, чтобы вдаваться в конкретные подробности мы приведем очень простой пример. Создадим макрос, который будет загружать данные из текстового файла с выпиской, суммировать обороты по дебету и кредиту и выводить их на экран.
option explicit sub ImportVyp ' загружаем в конвертер шаблон из файла ' обратите внимание! в вашем случае путь и имя файла ' могут быть другими.
Converter.LoadFromTempFile("d:\temp\vyp.tem") ' разбираем файл с выпиской Converter.StartConvert("d:\temp\vyp.txt") dim I, D, C, K, DB set DB = Converter.Database ' пробегаемся по всем областям в файле for I = 0 to DB.AreasCount – ' в каждой области просматриваем все таблицы ' нас интересуют только те, которые называются “StatementLines” for K = 0 to DB.Areas(I).TableCount - if DB.Areas(I).Tables(K).Name = "StatementLines" then set T = DB.Areas(I).Tables(K).Records ' пробегаем от первой до последней запис и суммируем значения ' обратите внимание, что мы не обрабатываем незаполненные поля ' и как мы подготавливаем строковое значение (а при импорте ' из текстового файла все значения строковые) для конвертации ' в число. Поскольку в файле всегда используется разделитель ' целой и дробной части числа точка, мы заменяем точку на ' разделитель, установленный в нашей системе.
' если бы мы не сделали этого и на нашем компьютере ' была бы установлена запятая, то при выполнении данного if T.FieldByName("Debet").AsString > "" then D = D + CDbl(Replace(T.FieldByName("Debet").AsString, ".",_ Application.DecimalSeparatorSys)) if T.FieldByName("Credit").AsString > "" then C = C + CDbl(Replace(T.FieldByName("Credit").AsString, ".",_ Application.DecimalSeparatorSys)) next MsgBox "Оборот по дебету: " & D & vbCrLf &_ "Оборот по кредиту: " & C, vbOkOnly, "Импорт выписки" end sub Результат выполнения макроса:
Рис. 107 Результат выполнения макроса.
Отправка электронной почты Set iMsg = CreateObject("CDO.Message") Set iConf = CreateObject("CDO.Configuration") set Fields = iConf.Fields Fields("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "mail.tut.by" ' имя SMTP сервера Fields("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 2525 ' порт сервера для отправки сообщений Fields("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2 ' cdoSendUsingPort ' CdoSendUsing enum value = Fields("http://schemas.microsoft.com/cdo/configuration/smtpaccountname") = "KLN" Fields("http://schemas.microsoft.com/cdo/configuration/sendemailaddress") = """KLN"" " Fields("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1 'cdoBasic Fields("http://schemas.microsoft.com/cdo/configuration/sendusername") = "KLN" Fields("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "password" Fields.Update ' Using the CDO instance With iMsg.Configuration = iConf ' Set who the email is going to.To = "[email protected]" ' Set the subject.Subject = "Test" ' Set the Text of the message - I could have used the HTMLBody property to send.TextBody = "THIS is the test!".AddAttachment "C:\ORDER.XLS" On Error Resume Next End With set iMsg = Nothing set iConf = Nothing Импорт данных с веб сайта Часто возникает необходимость в извлечении информации с определенного веб сайта и помещении ее в базу данных. Рассмотрим пример организации импорта актуальных курсов валют с сайта Национального Банка Республики Беларусь.
Информация о курсах доступна по адресу34:
http://www.nbrb.by/statistics/rates/RatesDaily.asp?fromDate=YYYY-MM-DD, где YYYY, MM и DD — это год, месяц и день, на который будут отображены курсы валют.
Нам придется извлекать данные вручную, непосредственно из кода HTML.
Ниже приводится текст макроса снабженный подробными комментариями.
Макрос импорта курсов валют option explicit ' системные требования: Windows XP SP1, Windows 2000 SP ' Windows 2003 Server ' Процедура Curr_LoadRates загружает курсы валют с сайта НБ РБ.
' Список валют, для которых будут запрашиваться курсы задается ' в словаре CurrCode.
sub Curr_LoadRates Dim Creator Set Creator = New TCreator ' словарь CurrCode нужен нам для двух целей:
' во-первых, в нем задается список валют, курсы ' которых мы будем загружать с сайта ' во-вторых, в нем мы устанавливаем соответствие ' между кодом (буквенной аббревиатурой) валюты ' используемой на сайте (Key) и кодом валюты ' используемым в нашей базе данных (Item). Очевидно, URL действовал на момент написания документации.
' что они могут как совпадать, так и различаться ' например, доллар США на сайте может обозначаться ' как USD, а у нас в базе он будет проходить как ' USD NBRB, что означает курс доллара США, установленный ' Национальным банком Республики Беларусь.
Dim CurrCode Set CurrCode = CreateObject("Scripting.Dictionary") CurrCode.Add "USD", "USD" CurrCode.Add "EUR", "EUR" CurrCode.Add "RUB", "RUB" CurrCode.Add "PLN", "PLN" CurrCode.Add "UAH", "UAH" CurrCode.Add "LTL", "LTL" CurrCode.Add "LVL", "LVL" CurrCode.Add "GBP", "GBP" ' идентификатор записи валюты, относительно которой ' задаются курсы валют на сайте ' в данном случае -- это наш родной белорусский рубль Dim BaseCurrID BaseCurrID = gdcBaseManager.GetIDByRUIDString("200010_17") ' даты: с какой и по какую загружать курсы валют с сайта ' по какую -- возьмем текущую системную дату ' с какой -- определим следующим образом: будем искать в нашей ' базе для каждой из заданных валют самую последнюю ' дату курса. из всех найденных дат возьмем наиболее раньнюю.
Dim FromDate, ToDate ToDate = Date FromDate = ToDate Dim q, qFind, Tr Set Tr = Creator.GetObject(Application, "TIBTransaction", "") Set q = Creator.GetObject(Application, "TIBSQL", "") Set qFind = Creator.GetObject(Application, "TIBSQL", "") Set Tr.DefaultDatabase = gdcBaseManager.Database Set q.Transaction = Tr Set qFind.Transaction = Tr Tr.StartTransaction ' уберем коды валют которых нет нашей базе или ' которые встречаются более одного раза q.SQL.Text =_ "SELECT COUNT(c.code) " &_ "FROM gd_curr c " &_ "WHERE c.code = :C " Dim I For Each I in CurrCode q.ParamByName("C").AsString = CurrCode.Item(I) q.ExecQuery if q.Fields(0).AsInteger 1 then _ CurrCode.Remove(I) q.Close Next q.SQL.Text =_ "SELECT MAX(r.fordate) " &_ "FROM gd_currrate r JOIN gd_curr c ON r.fromcurr = c.id " &_ "WHERE c.code = :FC and r.tocurr = :TC" q.ParamByName("TC").AsInteger = BaseCurrID For Each I in CurrCode q.ParamByName("FC").AsString = CurrCode.Item(I) q.ExecQuery if Int(q.Fields(0).AsDateTime) < FromDate then _ FromDate = Int(q.Fields(0).AsDateTime) q.Close Next ' проверим, может в базе уже есть все курсы if ToDate < FromDate then _ exit sub ' если интервал слишком большой -- ограничим его if (ToDate - FromDate) > (365 * 1) then _ FromDate = ToDate - 365 * ' подготовим запрос для вставки курса валюты q.SQL.Text = _ "INSERT INTO gd_currrate (fromcurr, tocurr, fordate, coeff) " &_ "SELECT ID, :TC, :FD, :R FROM gd_curr WHERE code = :FC " q.ParamByName("TC").AsInteger = BaseCurrID ' подготовим запрос для поиска курса валюты на указанную дату qFind.SQL.Text =_ "SELECT * " &_ "FROM gd_currrate r JOIN gd_curr c ON r.fromcurr = c.id " &_ "WHERE c.code = :FC and r.tocurr = :TC AND r.fordate = :FD" qFind.ParamByName("TC").AsInteger = BaseCurrID Dim strResult Dim WinHttpReq Dim strURL ' если объект не удается создать (например, неподходящая ' версия Windows), то просто завершаем выполнение On Error Resume Next Set WinHttpReq = CreateObject("WinHttp.WinHttpRequest.5.1") if Err.Number 0 then _ Exit Sub On Error GoTo Dim D, K, J, E, SS, Mo, Da For D = FromDate to ToDate Step qFind.Close qFind.ParamByName("FD").AsDateTime = D q.ParamByName("FD").AsDateTime = D if Month(D) < 10 then Mo = "0" & Month(D) else Mo = Month(D) end if if Day(D) < 10 then Da = "0" & Day(D) else Da = Day(D) strURL = "http://www.nbrb.by/statistics/rates/RatesDaily.asp?fromDate=" 'strUrl = "http://localhost/nbrb/ratesdaily.asp.htm" ' в случае если сайт не доступен -- просто ' заверши процедуру On Error Resume Next WinHttpReq.Open "GET", strURL, false WinHttpReq.Send strResult = WinHttpReq.ResponseText if Err.Number 0 then _ For Each I in CurrCode ' для каждой валюты проверим: нет ли в базе курса на ' указанную дату. если есть, то пропускаем эту валюту qFind.ParamByName("FC").AsString = CurrCode.Item(I) qFind.ExecQuery
Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.