Расширение проекта F-16 интерфейс к таблицам BDE(Paradox, dBASE и прочие)

Давно хотелось как-то формализовать генерацию редактора таблиц базы данных. Но дело упиралось в описание таблиц и взаимосвязей. Идея использовать XML заимствована из MODX(это CMS такая). Там все таблицы и взаимосвязи описываются через XML. Первоначально был разработан XML-шаблонизатор таблиц Firebird для программы "Склад-6(ЕГАИС)". Получилось очень неплохо. Позже шаблонизатор был переработан в динамическую библиотеку(DDL) для применения в Инфобухгалтере (таблицы Paradox и dBASE). Так появился F-16. F-16M - дальнейшее развитие шаблонизатора для таблиц BDE.

Данный модуль резко повысил скорость разработки новых деклараций и прочих расширений в Инфобухгалтере. Как бонус можно использовать автономно для справочников телефонов, напоминалок и прочих целей. Что я и сделал.

На днях клиент попросил меня для модуля ТСЖ сделать более удобным заполнение счетчиков горячей воды. Общим списком, а не заходить в каждую квартиру. Естественно, сразу решил делать в F-16M. Для этого надо просто написать XML-файл.
<?xml version="1.0" encoding="UTF-8"?>
<File id="main">
   <object del="No" h="800" inst="No" last="0" mask="1" max="1" name="SCH" return="C:\IBS\F16M_TSG" table="SCHETS.DB" title="Проживающие" w="800">
      <id autoinc="Yes" key="ID" />
      <sql text="select * from "c:\ibs\F16M_TSG\sch.sql"" />
      <field caption="Код ИБ" edit="No" key="COD_IB" size="8" width="120" />
      <field bold="1" caption="Сч.1" color="clBlue" mask="1" edit="Yes" key="V1" size="8" width="70">
         <title bold="1" color="clOlive" />
      </field>
      <field bold="1" caption="Сч.2" color="clGreen" edit="Yes" key="V2" size="8" width="70">
         <title bold="1" color="clOlive" />
      </field>
      <field caption="Год" edit="No" key="Y" size="8" width="60" />
      <field caption="Месяц" edit="No" key="M" size="8" width="60" />
      <field caption="Номер счетчика" edit="No" key="N" size="8" width="90" />
      <field caption="Дата" edit="No" key="D" size="8" width="70" />
   </object>
</File>
   
Кстати, очень удобно такие файлы создавать в SLIM.

Запускаем F-16M, передав ему в параметрах XML и получаем:

F-16M

Синий цвет колонки указывает на режим маски. Т.е. можно выделить любую колонку и фильтровать записи, набрав маску. Если нажать F3, то переходим в режим редактирования. При этом фильтр остается, что очень удобно. SQL-запрос может быть "не живым", т.е. содержать сортировки, присоединения. Т.е. каким угодно. Обновление записей производится по ID. Причем обновляется не вся запись, а только измененные поля. Таким образом если кто-то меняет цену, а другой тут-же подправляет наименование, в итоге они не перезатрут друг-друга. В данном случае запрос идет через файл на диске. Это сделано для того, чтобы можно было менять запрос. Или выводить показания за конкретный месяц, или за весь год. В Инфобухгалтере запрашивается год и месяц(если 0 - то весь год), создается запрос и вызывается F-16M.


F-16M

Здесь выведены показания за год и отфильтрованы по одной квартире.

Аппетит приходит во время еды... Въехав в тему, клиент попросил сделать аналогично для работы с квартирами. Отличие F-16M от F-16 в том, что F-16M может обрабатывать подчиненные таблицы. Что и пригодилось в данном случае. Т.к. к квартирам "привязаны" таблица помещений и таблица проживающих.

<?xml version="1.0" encoding="UTF-8"?>
<File id="main">
   <object name="KV" table="KV.DB" w="800" h="800" del="No" ins="No" title="Проживающие" mask="1" return="C:\IBS\F16M_TSG" last="0" max="1">
   <id key="COD_IB" autoinc="No"/>
        <sql text="select T.* FROM 'KV.DB' T
WHERE NOT(T.COD_IB IS NULL)
 ORDER BY T.COD_IB"/> 
        <field key="COD_IB" caption="Код ИБ" width="120" edit="No" size="8"/>
        <field key="S" caption="Площадь" width="80" edit="Yes" bold="1" mask="1" size="10"/>
        <field key="N_DOG" caption="N договорра" width="80" edit="Yes"/>
        <field key="D_DOG" caption="Д. договора" width="80" edit="Yes"/>
        <field key="S_VSPOM" caption="S вспом" width="80" edit="Yes" bold="1" size="10" color="clGreen"/>
        <field key="L_SCET" caption="Лиц. счет" width="80" edit="Yes" size="8"/>
        <field key="ETAG" caption="Этаж" width="80" edit="Yes" size="8"/>
        <field key="PRIV" caption="Приватиз." width="80" edit="Yes" size="8"/>
        <field key="SCHET" caption="Счетчики" width="80" edit="Yes" size="8" lookup="2">
                   <look2>
                		<each f="" v="" type="i">
	                        <i>ДА
        	                <i>НЕТ	                  
                		</each> 
                   </look2>
        </field>
   </object>
   <object name="FAMILY" table="FAMILY.DB" w="800" h="30" del="No" ins="No" title="Проживающие" mask="1" return="C:\IBS\F16M_TSG" last="0" max="1" master="KV">
   <id key="ID" autoinc="No"/>
        <sql text="select T.* FROM 'FAMILY.DB' T
WHERE T.COD_IB=:COD_IB"/> 
        <field_ key="COD_IB" caption="Код ИБ" width="100" edit="No" size="10"/>
        <field key="FIO" caption="ФИО" width="200" edit="Yes" bold="1" mask="1" size="10"/>
        <field key="DOC" caption="Документ" width="80" edit="Yes"  size="10"/>
        <field key="KEM_VYDAN" caption="Кем выдан" width="80" edit="Yes"  size="10"/>
        <field key="B_DATE" caption="День рождения" width="80" edit="Yes" bold="1" size="10" color="clGreen"/>
        <field key="DATE_PROP" caption="Д. прописки" width="80" edit="Yes" bold="1" size="8"/>
        <field key="OTV_L" caption="Главный" width="80" edit="Yes" bold="1" size="8"/>
        <field key="R_OTN" caption="Родственные отношения" width="80" edit="Yes" bold="1" size="8"/>
        <field key="VR_OTS" caption="Врем. отсутств." width="80" edit="Yes" bold="1" size="8"/>
        <field key="DATE_OUT" caption="Дата выписки" width="80" edit="Yes" bold="1" size="8"/>
   </object>
   <object name="S_VSPOM" table="S_VSPOM.DB" w="800" h="20" del="No" ins="No" title="Вспом. площадь" mask="1" return="C:\IBS\F16M_TSG" last="1" max="1" master="KV">
   <id key="ID" autoinc="No"/>
        <sql text="select T.* FROM 'S_VSPOM.DB' T
WHERE T.COD_IB=:COD_IB"/> 
        <field_ key="COD_IB" caption="Код ИБ" width="100" edit="No" size="10"/>
        <field key="NAME" caption="Наименование" width="200" edit="Yes" bold="1" mask="1" size="10"/>
        <field key="S" caption="Площадь" width="80" edit="Yes"  size="10"/>
   </object>   
</File>


   
F-16M

Ну круто, что и говорить).

Параметры

  • object - объект для отображения. Не имя таблицы. Может быть несколько object с запросом к одной и той же физической таблице
    • table - имя физ. таблицы. Если не задан SQL, создается простой запрос "SELECT T.* FROM 'table' T"
    • w,h - размеры таблицы в пикселях. Если object подчиненный, h - высота в процентах.
    • del,inst,edit - Yes или No. Позволяет ограничить удаление, вставку, редактирование как всей таблицы, так и отдельных полей(столбцов).
    • title - Заголовок окна.
    • mask - при загрузке перейти в режим поиска по маске. Номер колонки по аттрибуту "mask" поля(field).номер колонки, которая служит для поиска по маске при запуске.
    • return - файл возврата(все поля и значения выбранной строки)
    • last - 1-при открытии на последнюю запись, 0 - в начало
    • max - 1-окно раскрыто полностью, 0-нет
    • master - object-мастер для текущей подчиненной таблицы
  • id - ИД таблицы
    • key - имя поля ИД(напр."COD_IB")
    • autoinc - Yes(No) важно для вставки строки
  • sql - запрос к базе
    • text - текст запроса
  • field - описание поля
    • key - физ. название поля таблицы
    • caption - Заголовок колонки таблицы
    • width - ширина колонки
    • edit - Yes(No) признак возможности редактирования поля
    • size - размер шрифта
    • mask - 1 поле поиска по-умолчанию
    • color - цвет шрифта(clRed clGreen clBlue clNavy clOlive)
    • lookup - признак вида выбора(при редактировании поля - выбор из списка или другой таблицы)
      • lookup=1 - просто подчиненная таблица без выбора значения
        • напр.: <look1 name="girls" title_field="prochee"/>
        • при этом у подч. объекта должен быть параметризованный SQL(...where 'id_M'=:id....), если у главной таблицы ключ - 'id'
      • lookup=2 - вызов с выбором значения из таблиц и списков для конкретного поля
        • <each f="" v="" type="i"> - признак выбора из списка, при пустых 'f' и 'v' - срабатывает в любом случае.
          • если f="name" v="Надя" - то выбор запустится только если в этой строке поле "name"="Надя". Это позволяет на одну колонку назначать сколь-угодно lookup, что очень удобно.
              <field key="ZNACH" caption="Значение" width="300" edit="Yes" lookup="2" focus="1">
                       <look2>
                          <each f="COD" v="Период" type="table">   - если поле 'COD'="Период", то выбираем из из объекта  CODS_PERIOD значение его поля 'COD'
                             <object name="CODS_PERIOD" f="COD"/>
                          </each>
                          <each f="COD" v="НомКорр" type="i"> - а если 'COD'="НомКорр", то выбираем из списка
                             <i>000</i>
                             <i>001</i>
                             <i>002</i>
                             <i>003</i>
                             <i>004</i>
                          </each>
                       </look2> 
                </field>
                                                      
      • lookup-3 - "истинный" lookup. Например при редактировании поля("Ед.изм.") при выборе из таблицы(запроса) Единиц измерения, выбираем "метр кв.", а на самом деле заполняется поле 'id_ed' значением '456'. Но мы видим, что стало "метр кв.". Наименование отображается в "мнимом поле" начинающемся с 'XXX'("XXXNAME_ED "). При назначении lookup-3- необходимо указать lf(....lookup="3" lf="PARENT"....);
        ...........................
        <object name="RAZ_M" table="RAZDELS" w="800" h="600" del="Yes" ins="Yes" title="Разделы" mask="1">
          <sql text="select T.id, T.name, T.parent, V.name as XXX from razdels T left outer join razdels V on T.PARENT=V.id where 1=1" /> 
          <id key="ID"/> 
          <field key="NAME" caption="Наименование" width="200" dbtype="text" edit="Yes" mask="1" /> 
        - <field key="XXX" caption="Родитель" width="200" dbtype="text" edit="Yes" lookup="3" lf="PARENT">
              <look3 name="R_S" table="RAZDELS" lf="ID" f="NAME" /> 
          </field>
        </object>
        <object name="R_S" table="RAZDELS" w="300" h="30" del="No" ins="No" title="Разделы-родители" mask="1">
          <field key="NAME" caption="Наименование" width="200" dbtype="text" edit="No" mask="1" /> 
        </object>
        ...........................
        
      • lf - поле вызываемой таблицы, значение которого скопируется в поле "PARENT" вызывающей таблицы
      • f - поле вызываемой таблицы, значение которого скопируется в "мнимое поле"
      • Бывает, что при выборе записи нужно заполнить сразу несколько полей в вызывающей таблице.
        ...........................                                
         <look3 name="R_S" table="RAZDELS" lf="ID" f="NAME"
             <to_other lf="..." ff="..." value="..."/>
             <to_other lf="..." ff="..." value="..."/>
             <to_other lf="..." ff="..." value="..."/>
             .................................................
         </look3> 
        ...........................                            
                                    
        в поле 'lf' запишется значение поля 'ff'. Но если 'value' не пустое, то в 'lf' запишется значение аттрибута 'value'. Иногда надо и так.
  • format - форматирование вывода
  • edit_format - форматирование при редактировании
    <field key="PRICEREST" caption="Цена исходного остатка" width="120"
                edit="Yes" bold="1" format=",##0.00" edit_format="#.00" /> 
    <field key="COUNTREST" caption="Исходный остаток" width="120"
                edit="Yes" bold="1" format=",##0.00" edit_format="#.00" /> 
                
  • 'XXX...' - 'XXX' в начале имени поля позволяет игнорировать это поле при сохранении. Это позволяет работать с присоединенными(left outer join, inner join) таблицами и в то же время редактировать основную таблицу.