Правильная ссылка на эту страницу
http://az-design.ru/Projects/AzBook/AZMicro/AZMicro38.shtml

Как отобразить это дерево?

       Итак, после всех этих изменений в БД, необходимо заново построить страницу для отображения "Плана Счетов". Так как теперь отображение древовидное, то и компоненты должны использоваться другие.
       Во-первых, удалим все компоненты на странице "План Счетов" (или созданим новую страницу в PageControl).
       Во-вторых, набросаем на форму необходимые элементы, а затем напишем для них соответствующий код.
       Итак, выбираем на PageControl страницу "План Счетов". После чего:
       — из вкладки "Win32" выбираем компонент TToolBar, кладем его на страницу и устанавливаем свойство Align=alTop, а свойство Height=29;
       — из вкладки "Standard" выбираем компонент TPanel, кладем его на страницу и устанавливаем свойство Align=alBottom, а свойство Height=111;
       — из вкладки "Data Control" выбираем компонент TDBMemo, кладем его на Panel1 и устанавливаем свойство Align=alBottom, а свойство Height=90;
       — из вкладки "Data Control" выбираем компонент TDBEdit и дважды кладем его на Panel1 и выравниваем по верху панели.
       — из вкладки "Win32" выбираем компонент TTreeView, кладем его на страницу и устанавливаем свойство Align=alClient;
       — из вкладки "Data Control" выбрираем компонент TDBNavigator и кладем его на ToolBar. Так как перемещатся по таблице не потребуется, то в свойстве VisibleButtons достаточно оставить только кнопки nbEdit, nbPost, nbCancel, nbRefresh.

разработка Плана Счетов
Рис.AzMicro011 — разработка "Плана Счетов"

       Теперь нужно добавить компоненты для управления данными.
       Как и в предыдущих случаях, кладем на форму набор компонентов TTransaction, TQuery, TDataSource с общим именем TVChartAcc и соответствующими префиксами. Этот набор будет использоваться для построения дерева.
       Для редактирования полей будет использоваться второй набор компонентов TTransaction, TQuery, TDataSource, TUpdateSQL с общим именем NodeAccnt и соответствующими префиксами.
       Настройки обоих наборов компонентов необходимо сделать также, как и в предыдущих случаях. В свойстве DBEdit1.DataField необходимо записать AccntNum, в свойстве DBEdit2.DataField необходимо записать AccTitle, а в свойстве DBMemo1.DataField необходимо записать AcDescrp. После чего для компонентов nvNodeAccnt, DBEdit1, DBEdit2, DBMemo1 свойство DataSource необходимо изменить на dsNodeAccnt.
       Осталось отобразить дерево "План счетов". При этом есть две сложности — 1) в таблице несколько дереевьев, и 2) всемто ID используется строковое поле с номером счета.
       Итак, для того чтобы получить имена деревьев и сформировать из них ComboBox воспользуемся добавим в процедуру CreateForm следующие строки:

procedure TForm1.FormCreate(Sender: TObject);
Var
   db : String;
   ps : Integer;
begin
. . . . . .
   cbxTreeSel.Items.Clear;
   qrExeTemp.Close;   qrExeTemp.SQL.Clear;
   qrExeTemp.SQL.Add('Select first 4 AccTitle from ChartAcc');
   qrExeTemp.SQL.Add(' where AccParnt=''.'' and AccOrder<0');
   qrExeTemp.SQL.Add(' order by AccOrder');
   qrExeTemp.Open;  qrExeTemp.First;
   While not qrExeProc.Eof do begin
      cbxTreeSel.Items.Add(qrExeTemp.FieldValues['AccTitle']);
      qrExeTemp.Next;
   end;
   cbxTreeSel.ItemIndex:=0;
. . . . .
end;

       Из таблицы ChartAcc выбираются первые 4 записи, которые в качестве родителя имеют корневую запись — '.'. Так как в таблице могут быть временно неопределенные записи, то необходимо каким то образом определить какие записи действительно являются корневыми. Для этого используется дополнительный признак — поле сортировки должно иметь отрицательное значение. Полученный набор последовательно записывается в cbxTreeSel, после чего его индекс устанавливается в 0. Что и показано на рисунке:

Начальное состояние Плана Счетов
Рис.AzMicro012 — Начальное состояние "Плана Счетов"

       Выбор из списка инициирует первоначальное построение дерева процедурой TreeDataSetOpen, которая привязана к свойству onSelect компонента cbxTreeSel:

procedure TForm1.TreeDatasetOpen();
Var
   RowChild   : Integer;
   sFld  : String;
   ChildNode,NewNode : TTreeNode;
begin
  inherited;
  If qrTVChartAcc.Database.Connected then
     begin
        tvChartAcc.Items.BeginUpdate;
        tvChartAcc.Visible := False;
        tvChartAcc.Items.Clear;
        tvChartAcc.Visible := True;
        If cbxTreeSel.ItemIndex>0 then begin
        trTVChartAcc.Active := True;
        qrTVChartAcc.Close;   qrTVChartAcc.SQL.Clear;
        qrTVChartAcc.SQL.Add('Select * from ChartAcc');
        If cbxTreeSel.ItemIndex=1 then
          qrTVChartAcc.SQL.Add('where AccParnt=''AccBase'' order by AccOrder');
        If cbxTreeSel.ItemIndex=2 then
           qrTVChartAcc.SQL.Add('where AccBalns=''Balans'' order by AccBlOrd');
        If cbxTreeSel.ItemIndex=3 then
           qrTVChartAcc.SQL.Add('where AccProft=''Profit'' order by AccPfOrd');

       В зависимости от состояния cbxTreeSel запрос выбирает записи первого уровня из того или иного дерева.

        qrTVChartAcc.Open;     qrTVChartAcc.First;
        While not qrTVChartAcc.EOF do
          begin
             RowChild := 0;
             sFld := qrTVChartAcc.FieldValues['AccntNum'];
             sFld := acText8(sFld);
             sFld := sFld+' - '+qrTVChartAcc.FieldValues['AccTitle'];

       В отличии от предыдущих случаев ключевое поле является строкой. Разные компоненты по разному обрабатывают текстовые поля. Например, IBO рассматривает ВСЕ строковые поля как VarChar, в то же время IBX рассматривает ВСЕ строковые поля как Char. Для того чтобы исправить эту ситуацию используется простая функция acText8 (см. исходные тексты). Кроме того, на дереве в качестве узла отображается не одно поле, а композиция из двух полей, которая собирается в переменной sFld.

             NewNode := tvChartAcc.Items.Add(tvChartAcc.TopItem,sFld);
             If cbxTreeSel.ItemIndex=1 then 
                RowChild := qrTVChartAcc.FieldValues['AccCount'];
             If cbxTreeSel.ItemIndex=2 then 
                RowChild := qrTVChartAcc.FieldValues['AccBlCnt'];
             If cbxTreeSel.ItemIndex=3 then 
                RowChild := qrTVChartAcc.FieldValues['AccPfCnt'];
             If RowChild>0 then 
            ChildNode := tvChartAcc.Items.AddChild(NewNode,IntToStr(RowChild));

       Если счетчик потомков больше 0, то создается потомок в узле.

             qrTVChartAcc.Next;
          end; // While not qrTVChartAcc.EOF do
          end;
          tvChartAcc.Items.EndUpdate;
          tvChartAcc.Update;
          qrTVChartAcc.Close;      trTVChartAcc.Active := False;
    end; // qrTVGoods.Database.Connected
end;

       После чего все повторяется, пока не будут построены все узлы.
       Для раскрытия узлов используется процедура tvChartAccExpanding, которая привязана к событию onExpanding:

procedure TForm1.tvChartAccExpanding(Sender: TObject; Node: TTreeNode;
                                     var AllowExpansion: Boolean);
Var
   RowChild : Integer;
   sFld : String;
   ChildNode,NewNode : TTreeNode;
begin
  inherited;
     If Node.HasChildren then begin
        Node.DeleteChildren;
        trTVChartAcc.Active := True;
        qrTVChartAcc.Close;   qrTVChartAcc.SQL.Clear;
        sFld := Copy(Node.Text,0,8);

       В данном случае нет необходимости хранить ID узла в каком то дополнительном признаке. Достаточно из текста узла взять первые 8 символов, что и выполняется оператором Copy.

        qrTVChartAcc.SQL.Add('Select * from ChartAcc');
        If cbxTreeSel.ItemIndex=1 then
           qrTVChartAcc.SQL.Add('where AccParnt='''+sFld
                               +''' order by AccOrder');
        If cbxTreeSel.ItemIndex=2 then
           qrTVChartAcc.SQL.Add('where AccBalns='''+sFld
                               +''' order by AccBlOrd');
        If cbxTreeSel.ItemIndex=3 then
           qrTVChartAcc.SQL.Add('where AccProft='''+sFld
                               +''' order by AccPfOrd');
        qrTVChartAcc.Open;     qrTVChartAcc.First;
        While not qrTVChartAcc.EOF do
          begin
             RowChild := 0;
             sFld := qrTVChartAcc.FieldValues['AccntNum'];
             sFld := acText8(sFld);
             sFld := sFld+' - '+qrTVChartAcc.FieldValues['AccTitle'];
             NewNode := tvChartAcc.Items.AddChild(Node,sFld);
             If cbxTreeSel.ItemIndex=1 then
                RowChild := qrTVChartAcc.FieldValues['AccCount'];
             If cbxTreeSel.ItemIndex=2 then
                RowChild := qrTVChartAcc.FieldValues['AccBlCnt'];
             If cbxTreeSel.ItemIndex=3 then
                RowChild := qrTVChartAcc.FieldValues['AccPfCnt'];
             If RowChild>0 then
             ChildNode:=tvChartAcc.Items.AddChild(NewNode,IntToStr(RowChild));
             qrTVChartAcc.Next;
          end; // While not qrTVChartAcc.EOF do
           tvChartAcc.Items.EndUpdate;
           trTVChartAcc.Active := False;
     end;// Node.HasChildren
     tvChartAcc.Update;
end;

       Остальная часть процедуры не отличается от процедуры TreeDatasetOpen.
       Для получения описания счета необходимо сначала настроить компоненты группы NodeAccnt, для чего в процедуру CreateForm необходимо добавить следующий код:

procedure TForm1.FormCreate(Sender: TObject);
Var
   db : String;
   ps : Integer;
begin
. . . . . .
  qrNodeAccnt.Close;  qrNodeAccnt.SQL.Clear;
  qrNodeAccnt.SQL.Add('Select AccntNum,AccTitle,ACDESCRP from ChartAcc');
  qrNodeAccnt.SQL.Add('where AccntNum=:SelAccnt');

       Сначала напишем запрос, который выбирает соответствующие поля для строки, значение поля AccntNum которой равно значению параметра SelAccnt.

  updNodeAccnt.InsertSQL.Clear;
  updNodeAccnt.InsertSQL.Add('Insert into ChartAcc(AccntNum,AccTitle,AcDescrp)');
  updNodeAccnt.InsertSQL.Add(' values(:AccntNum,:AccTitle,:AcDescrp)');
  updNodeAccnt.DeleteSQL.Clear;
  updNodeAccnt.DeleteSQL.Add('Delete from ChartAcc where AccntNum=:OLD_AccntNum');
  updNodeAccnt.ModifySQL.Clear;
  updNodeAccnt.ModifySQL.Add('Update ChartAcc set AccntNum=:AccntNum,');
  updNodeAccnt.ModifySQL.Add(' AccTitle=:AccTitle, AcDescrp=:AcDescrp');
  updNodeAccnt.ModifySQL.Add(' where AccntNum=:OLD_AccntNum');
  updNodeAccnt.RefreshSQL.Clear;
  updNodeAccnt.RefreshSQL.Add('Select  AccntNum,AccTitle,ACDESCRP from ChartAcc');
  updNodeAccnt.RefreshSQL.Add(' where AccntNum=:OLD_AccntNum');
. . . . .
end;

       Затем напишем запросы, обеспечивающие модификацию и обновление таблицы.
       Для выбора строки, которая должна отображаться и изменяться используется событие onChange, по которому изменяется параметр запроса. При этом параметр вычисляется как первые 8 символов ствойства Text текущего узла.

procedure TForm1.tvChartAccChange(Sender: TObject; Node: TTreeNode);
begin
  qrNodeAccnt.Close;
  qrNodeAccnt.Params.ParamValues['SelAccnt'] := Copy(Node.Text,0,8);
  qrNodeAccnt.Open;
end;

результат отображения дерева План Счетов
Рис.AzMicro013 — результат отображения дерева "План Счетов"

 

Архангельский Андрей




<<< Пред. Оглавление
Начало раздела
След. >>>

Дата последнего изменения:
Thursday, 21-Aug-2014 09:10:56 MSK


Постоянный адрес статьи:
http://az-design.ru/Projects/AzBook/AZMicro/AZMicro38.shtml