На главную   На главную   Форумы Новости Документация Скачать Купить  
Регистрация  
Система Allegro
Oб Allegro Характеристики Пример конфигурации Документация База ошибок Развитие
Версия для печати К списку книг Вернуться к оглавлению Предыдущий параграф Следующий параграф
Поиск по книге

Глава 5. СОЗДАЕМ ОКОННЫЙ ИНТЕРФЕЙС «ПОСТУПЛЕНИЯ НА СКЛАД»

Дорабатываем SQL-запросы позиций и шапки документа

Запрос, записанный нами в свойстве SelectSQL компонента qryDetails извлекает данные только из таблицы STOCK_IN_ITEM. В этой таблице вместо наименований товара хранятся их внутренние идентификаторы ID в поле GOODS Для того чтобы видеть не внутренние номера, а наименования товаров, нам нужен запрос, объединяющий таблицу STOCK_IN_ITEM с таблицей наименований объектов OBJECT_NAMES. К тому же мы хотим , чтобы записи отображались в определенном порядке. Поэтому изменим у компонента qryDetails свойство SelectSQL, вписав в него такой SQL-запрос:

select
  SI.ID,
  SI.N,
  SI.GOODS,
  O.SHORT_NAME ITEM_NAME,
  SI.QUANTITY,
  SI.PRICE_L,
  SI.PRICE_L_WO_VAT,
  SI.PRICE_R,
  SI.PRICE_R_WO_VAT,
  SI.AMOUNT_L,
  SI.AMOUNT_R,
  SI.AMOUNT_S
from
  STOCK_IN_ITEM SI,
  OBJECT_NAMES O
where
  SI.GOODS = O.OBJECT_ID
order by SI.N

В данном случае выборка происходит из 2 таблиц, каждой из них присвоено по псевдониму (SI и O), что сокращает и делает более читабельным тест запроса. Таблицы связаны условием:

SI.GOODS = O.OBJECT_ID

Результирующий набор упорядочен по полю N фразой:

order by SI.N

Активизируем запрос, установив свойство Active = True. Теперь мы видим наименование товара в наборе данных. Хорошо бы еще присвоить русские названия колонкам этого набора и скрыть некоторые внутренние и ненужные будущему пользователю колонки, например, ID и N. Компонент класса TIBDataSet работает с набором данных при помощи так называемых компонентов-полей, потомков класса TField библиотеки визуальных компонентов VCL Delphi. Можно создать « постоянные» (persistent) поля вручную на стадии разработки интерфейса и придать им нужные свойства. Если «постоянные » поля не были созданы, то компонент TIBDataSet создает вместо них «временные» поля сразу после того, как будет активизирован запрос SELECT. То, что мы видим в сетке сейчас - это «временные» поля , созданные автоматически. Их заголовки нас явно не устраивают .

Давайте создадим список полей вручную.

Для этого нужно дважды щелкнуть мышью на компоненте qryDetail. Появится редактор полей, пока пустой, так как « постоянных» полей у компонента еще нет. Вызовем правой кнопкой мыши контекстное меню и выберем пункт Добавить поля… Нам будет предложено выбрать какие-то поля из списка всех потенциальных полей, которые присуствуют в этом наборе данных , причем по умолчанию предлагается выбрать их все. Так и поступим, нажав OK.



«Постоянные» поля теперь созданы в списке и видны в редакторе полей:



Выбирая какие-то из них в редакторе полей, мы можем присвоить значения их свойствам в Инспекторе объектов. Параллельно мы можем следить за тем, как изменяется внешний вид нашей сетки.

У полей ID, N, GOODS, PRICE_ R, PRICE_R_WO_VAT и AMOUNT_R установим значение свойства

Visible = False

Это отключит отображение соответствующих полей в сетке.

Изменим заголовки некоторых полей, присваивая свойству DisplayLabel новое значение :


ITEM_NAME Наименование
QUANTITY Кол-во
PRICE_L Цена
PRICE_L_WO_VAT Цена б/НДС
AMOUNT_L Сумма
AMOUNT_S Сумма, USD

Изменим ширину отображения некоторых полей, изменяя значение свойства DisplayWidth :


ITEM_NAME 45
QUANTITY 5
PRICE_L 10
PRICE_L_WO_VAT 10
AMOUNT_L 12
AMOUNT_S 12

Изменим выравнивание текста в полях QUANTITY, PRICE_L , PRICE_L_WO_VAT, установив для них всех:

Alignment = taCenter

Закроем редактор полей. Сохраним и запустим проект (F9 ). Сейчас наш интерфейс выглядит так:



Пока что данные в сетке не редактируются, так как не созданы запросы UPDATE и DELETE.

Займемся пока текстом запроса шапки документа. У компонента qryMaster в свойство SelectSQL впишем такой текст:

select
  SI.ID,
  SI.DIR_ID,
  SI.DOC_DATE,
  SI.DOC_KIND,
  SI.DOC_NO,
  SI.ENTRY_DATE,
  SI.HAS_ENTRY,
  SI.STOCK_ACC,
  A1.NAME STOCK_ACC_NAME,
  SI.LAYER_ID,
  L.SHORT_NAME LAYER_NAME,
  SI.VAT_RATE,
  SI.ACC_ID,
  A2.NAME CREDIT_ACC_NAME,
  SI.CONTRAGENT,
  O.SHORT_NAME CONTRAGENT_NAME,
  SI.CALC_MODE,
  SI.EXCH_RATE_L,
  SI.EXCH_RATE_S,
  SI.TOTAL_L,
  SI.TOTAL_R,
  SI.TOTAL_S
from
  STOCK_IN SI,
  OBJECT_NAMES O,
  LAYER L,
  ACC A1,
  ACC A2
where
  SI.CONTRAGENT = O.OBJECT_ID and
  SI.LAYER_ID = L.LAYER_ID and
  SI.STOCK_ACC = A1.ACC_ID and
  SI.ACC_ID = A2.ACC_ID

Здесь мы объединяем пять таблиц: главную таблицу документа « Поступление на склад» STOCK_IN, таблицу наименований объектов OBJECT_NAMES, таблицу слоев LAYER и дважды таблицу счетов ACC под разными псевдонимами (ACC1 и ACC2 ). Создадим «постоянные» поля дважды щелкнув на компоненте qryMaster и добавив все возможные поля в список.

Добавим на верхнюю панель формы надписи с названиями полей и компоненты для отображения данных шапки. Для этого используем компоненты Label с палитры Standard и DBText с палитры DataControls.



Впишем свойства Caption компонентов типа TLabel названия полей самых важных полей шапки документа:



Выберем все компоненты DBText, держа нажатой клавишу Shift и щелкая по ним мышью. В Инспекторе объектов назначим им всем одновременно свойства:

AutoSize = True

DataSource = dsrMaster

Font.Style = [fsBold]

Свойства Name всех этих компонентов изменять не будем. А свойствам DataField назначим имена полей запроса:


DBText1 STOCK_ACC_NAME
DBText2 ENTRY_DATE
DBText3 TOTAL_L
DBText4 LAYER_NAME
DBText5 CONTRAGENT_NAME
DBText6 CREDIT_ACC_NAME
DBText7 DOC_DATE
DBText8 DOC_NO

Активизируем запрос qryMaster.



Итак, запросы SELECT для шапки и для позиций у нас почти готовы. Единственное, чего в них недостает , так это возможности запрашивать информацию а каком-то конкретном документе, с конкретным ID. Если бы документов было много, то мы сейчас видели бы их все позиции одновременно в нашей сетке dbgDetail. Мы не хотели с самого начала отягощать и без того сложную для многих читателей тему создания SQL-запросов всеми подробностями, и потому вносим все необходимые уточнения постепенно по ходу изложения.

Итак, если у нас имеется запрос вида:

select * from stock_in_item

То он возвратит нам все строки таблицы stock_in _item. Для того чтобы получить только те строки , которые относятся к документу с ID = 1050, мы могли бы написать такой запрос:

select * from stock_in_item
where ID = 1050

Однако такой подход требует каждый раз менять текст запроса, если мы хотим запросить тот или иной документ. Поэтому существует более гибкий механизм – параметризованные запросы. Для того чтобы написать параметризованный запрос в компонент типа TIBDataSet, мы должны включить в его текст переменную, предваряемую двоеточием, и называемую параметром, например:

select * from stock_in_item
where ID = :A

Прежде чем открыть запрос, мы должны будем сообщить компоненту значение параметра А. Делается это обычно во время выполнения программы (run-time) с помощью вызова метода ParamByName.

Добавим в каждый наш SQL-запрос параметр, с помощью которого будем передавать ID документа. Для этого в секцию WHERE к имеющимся условиям нужно добавить условие:

SI.ID = :ID

Это условие ограничит набор только строками, в которых значение поля ID равно значению параметра :ID.

Итак, окончательно текст запроса в свойстве qryMaster.SelectSQL выглядит так:

select
  SI.ID,
  SI.DIR_ID,
  SI.DOC_DATE,
  SI.DOC_KIND,
  SI.DOC_NO,
  SI.ENTRY_DATE,
  SI.HAS_ENTRY,
  SI.STOCK_ACC,
  A1.NAME STOCK_ACC_NAME,
  SI.LAYER_ID,
  L.SHORT_NAME LAYER_NAME,
  SI.VAT_RATE,
  SI.ACC_ID,
  A2.NAME CREDIT_ACC_NAME,
  SI.CONTRAGENT,
  O.SHORT_NAME CONTRAGENT_NAME,
  SI.CALC_MODE,
  SI.EXCH_RATE_L,
  SI.EXCH_RATE_S,
  SI.TOTAL_L,
  SI.TOTAL_R,
  SI.TOTAL_S
from
  STOCK_IN SI,
  OBJECT_NAMES O,
  LAYER L,
  ACC A1,
  ACC A2
where
  SI.CONTRAGENT = O.OBJECT_ID and
  SI.LAYER_ID = L.LAYER_ID and
  SI.STOCK_ACC = A1.ACC_ID and
  SI.ACC_ID = A2.ACC_ID and
  SI.ID = :ID

Аналогично добавим такое же условие в компонент запроса позиций. Окончательный текст в свойстве qryDetail.SelectSQL должен выглядеть так :

select
  SI.ID,
  SI.N,
  SI.GOODS,
  O.SHORT_NAME ITEM_NAME,
  SI.QUANTITY,
  SI.PRICE_L,
  SI.PRICE_L_WO_VAT,
  SI.PRICE_R,
  SI.PRICE_R_WO_VAT,
  SI.AMOUNT_L,
  SI.AMOUNT_R,
  SI.AMOUNT_S
from
  STOCK_IN_ITEM SI,
  OBJECT_NAMES O
where
  SI.GOODS = O.OBJECT_ID and
  SI.ID = :ID
order by SI.N

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

Теперь займемся передачей параметра ID. Выберем форму StockInForm, откроем закладку «События» в Инспекторе объектов и дважды щелкнем на событии OnCreate. В код программы вставится обработчик события. Впишем в него следующий текст:

procedure TStockInForm.FormCreate(Sender: TObject);
begin
  qryMaster.ParamByName('ID').AsInteger := RunContext.Documents[0].doc_id;
  qryMaster.Open;
  qryDetail.ParamByName('ID').AsInteger := RunContext.Documents[0].doc_id;
  qryDetail.Open; //открываем запрос позиций
end;


Смысл этого текста мы поясним позже, а пока назначим переменные контекста с помощью пункта меню Запуск/Переменные контекста . В появившемся диалоге установим значения первых двух величин, с помощью кнопочек с многоточием. Выберем в предложенных диалогах тип документа «Поступление на склад» и тот документ , что у нас имеется.



Установим свойство обоих запросов Active = False.

Запустим проект, нажав F9.

Итак, у нас все работает. Выйдем из дизайнера и попробуем запустить проект из «Проводника по документам», щелкнув дважды на названии документа. А теперь, используя пункт контекстного меню «Создать» «Проводника», попробуем создать «Поступление на склад»:

Появится еще одно окно «Поступление на склад», но с пустым набором.

При вызове каждого экземпляра интерфейса документа на экран, программа создает специальный компонент RunContext, в котором помещает информацию о том, какой тип документа doc_tye_id вызван на экран и каков его doc_id. Собственно, мы этой информацией об doc_id и воспользовались, когда передавали значение параметра ID в запросы.

При создании нового документа программа Allegro присваивает doc_id отрицательное значение:



RunContext.Documents[0].doc_id = -1

После того, как новый документ будет реально создан в базе данных, мы обязаны присвоить свойству RunContext.Documents [0].doc_id значение ID созданного документа . Это нужно для того, чтобы программа могла различать в многооконном интерфейсе открытые документы и не вызвала один и тот же документ дважды (в двух разных окнах). В принципе один проект оконного интерфейса может обслуживать одновременно несколько документов. Например, заказ и связанные с ним размещения и поставки. Для этого объект RunContext имеет свойство- массив RunContext.Documents. В простейшем случае используется один элемент массива с индексом 0. Подразумевается, что при вызове проекта оконного интерфейса всегда задействуется хотя бы один какой -то конкретный документ.



Пример создания склада в Allegro Наверх