Если уж создан постоянный список полей, можно
установить параметры их отображения на клиентской части, в частности, DisplayLabel, DisplayWidth и Visible, а у провайдера - флаги poIncFieldProps.
При этом на клиентской части можно не заботиться о списке полей – значения, полученные с сервера приложений, переопределяют заданные на клиенте в любом
случае. Заодно у провайдера надо установить опцию poMultiRecordUpdates, чтобы
на клиентской части можно было изменять сразу несколько записей в справочнике
до отправки изменений на сервер.
Поле CLIENT_ID в справочнике поставщиков и получателей
является первичным ключем, а стало быть, в нем должны содержаться уникальные
значения. Для получения уникальных значений удобно использовать
автоинкрементальные поля (autoincrement field). В IB собственно
автоинкрементных полей нет, нарастающие значения получают от генератора с
помощью функции Gen_ID, и как правило, присваивают это значение полю в
триггере. Мне нравится ситуация, когда новое уникальное значение появляется на
клиентской части сразу после добавления записи. Поэтому вместо присвоения
значения, полученного от генератора, в триггере, используется хранимая
процедура, результатом работы которой и является это значение. Для этого в
удаленном модуле данных расположен компонент spNewID: TIBStoredProc, присоединенный к компоненту транзакции ibtDefault, который предоставляет доступ
к хранимой процедуре на сервере БД. Процедура описана в базе данных следующим
образом:
create
procedure CLIENT_ID
returns
(ID integer)
as
begin
ID = Gen_ID(CLIENT_ID_GEN,1);
end
Как видно, процедура просто выдает следующее значение
генератора. Это гарантирует, что при последовательных запросах к ней это
значение повторяться не будет. Получение значения на клиентской части
обеспечивается методом сервера, об этом немного ниже.
Вторая хранимая процедура, spClientFullName, присоединена к компоненту транзакции ibtClient и предназначена для выдачи имени
и телефона поставщика или получателя в виде единой строки «Имя (телефон)», возвращаемой процедурой сервера БД CLIENT_FULL_NAME. Эта строка также
передается на клиентскую часть через метод сервера.
Группа компонентов ibtDocList, ibqDocList, dspDocList
и ibqDelDoc предназначена для работы со списком документов. У IbtDocList, компонента транзакции, установлен режим read committed, а в компоненте
ibqDocList содержится SQL-запрос «select * from List_doc(:FromDate, :ToDate)».
Весь список документов сразу выводить довольно бессмысленно, их может быть
много. Поэтому запрос выбирает список документов, даты которых лежат в
промежутке от FromDate до ToDate. Провайдер dspDocList выдает этот список
клиентской части.
Дополнительный компонент, ibqDelDoc, как, думаю, видно
из его названия, предназначен для удаления документа, в его свойстве SQL стоит
запрос «delete from DOC_TITLE where DOC_ID = :DOC_ID». Несмотря на то, что для
создания и изменения документа планируется использовать отдельный модуль, rdmDoc, для удаления документа вовсе необязательно его открывать, и с точки
зрения интерфейса пользователя удобно делать это прямо из списка документов. На
первый взгляд, использование отдельного запроса для удаления кажется излишним, для этого обычно достаточно объявить в обработчике dspDocList.OnGetTableName
имя таблицы (DOC_TITLE), и удаление будет автоматически обеспечено. Однако в
постановке задачи стоит условие, что открытый в одной клиентской части документ
должен быть недоступен для изменения (а значит, и удаления) из других
клиентских частей. Поэтому приходится делать это в обработчике события
dspDocList.OnBeforeUpdateRecord следующим образом: