Создание новых функций приема в таблицы с использованием макроязыка

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

В программе АМБа очень широко используются всевозможные таблицы. Прием данных в такие таблицы, разноска данных из таблиц в лицевые счета, печать таблиц – все это осуществляется с помощью функций, которые называются функциями приема.

Функции приема широко используются в настроечных таблицах в столбцах: Функция приема, Функция разноски, Функция расчета, Функция предварительной обработки. В формах печати таблиц также интенсивно используются функции приема.

Создание собственной функции приема выполняется в два этапа:

  1. Написание самой функции.

  2. Регистрация написанной функции, что дает возможность вызвать ее из таблицы или формы печати.

Все функции приема имеют единый формат:

Name_Function(&Arg, &pcT, W, A),
    

где Arg – строка параметров, передаваемых в функции; pcT – строка, в которую записывается конечный результат; W – число, означающее максимальную длину результирующей строки pcT; A – число, означающее точность, с которой необходимо записать числовой результат в результирующую строку pcT.

[Важно]

Обратите внимание, что:

  • в результирующую строку pcT можно записывать ровно W символов не больше и не меньше!

  • функция не может возвращать ничего другого кроме pcT. То есть в ней всегда должна присутствовать строчка return pcT;

Саму функцию приема рекомендуется создавать в модуле usfuncrw.s (см. описание модулей в Приложении 2), который предназначен для этих целей. Регистрация собственных функций печати осуществляется в модуле usinit.s. Ничего сложного в процедуре регистрации нет, необходимо в модуле usinit.s в функции User_Initial_FuncRW() добавить строчку следующего вида:

AddFunc("настоящее_имя_функции","имя_функции_для вызова");
    

где настоящее имя функции – имя функции, написанной на макроязыке и имеющей параметры, перечисленные выше; имя функции для вызова – имя, по которому будет осуществляться доступ к функции из таблиц и форм печати.

[Важно]

Следует отметить, что оба имени заключены в кавычки. В имени функции для вызова допускается использовать русские буквы, но не допускаются пробелы!

В качестве примера, напишем функцию для определения величины стажа между двумя датами. Функция может принимать на вход от одного до трех параметров: дата начал, дата конца, формат. Текст функции приведен ниже. Кроме того, текст этой функции можно найти в модуле funcrw.s. (Как уже было сказано ранее, номера строк вводить не надо.)


1   R_STAG2 ( &Arg,  &pcT, W, A)
2   ////////////////////////////////////////////////////////////////
3   // СТАЖ2(парам1,парам2, парам3)
4   // парам1 - псевдоним поля КЧ, где находится дата начала стажа,
5   //          или просто дата в формате известном системе
6   // парам2 - то же что и парам1, только для даты конца
7   //   (при отсутствии этого параметра берется первое число
8   //    текущего месяца)
9   // парам3 - формат, в котором хотим получить стаж
10  //    0 (или отсутствие 3-го параметра) - выдает в формате ??л??м??д.
11  //    1 - выдает в формате ??л??м (количество полных лет и месяцев)
12  //    2 - выдает в формате ??л (количество полных лет)
13  //    Причем буковка 'л' в нужных ситуациях меняется на 'г'.
14  //    Например:  33г и 35л
15  //////////////////////////////////////////////////////////////
16  {
17    var FuncParam =  CreateObject("ParamFuncRW");
18    FuncParam.IInitial(to_string(Arg),",",10);
19    int CountParam=FuncParam.Count();
20  
21    if ( CountParam )
22    {
23      var DateBeg = CreateObject("KDate");
24      var DateEnd = CreateObject("KDate");
25      DateEnd.SetDateII(1,mrasch);
26      DateBeg.SetDateD(GetDateFromFuncRWScript(FuncParam.Get(0)));
27  
28      if ( CountParam>1 )
29      {
30        DateEnd.SetDateD(GetDateFromFuncRWScript(FuncParam.Get(1)));
31      }
32
33      int TypeForm = 0;
34      if ( CountParam>2 ) TypeForm = atoi(to_string(FuncParam.Get(2)));
35  
36      int year=0, month=0, day=0;
37      if ( !DateBeg.IsEqI(INVALID_DATE_VALUE) &&
38      !DateEnd.IsEqI(INVALID_DATE_VALUE) )
39      {
40        int CountM = DateEnd.GetAbs() - DateBeg.GetAbs();
41  
42        if ( CountM>=0 )
43        {
44          if ( CountM==0 )
45          {
46          day = DateEnd.Diff(DateBeg);
47          // защита от дурака - если даты задали не в том порядке
48          if ( day<0 ) day = "-day"
49          }
50          else
51          {
52            var DatePred = CreateObject("KDate");
53            DatePred.SetDateII(DateBeg.GetDay(),DateEnd.GetAbs()-1);
54            day = "DateEnd.Diff(DatePred"
55            if="" ( day="">=DatePred.CountDay() ) day -= DatePred.CountDay();
56            else
57            {  // в этом случае нет одного полного месяца, поэтому
58            // уменьшаем кол-во месяцев на 1
59            CountM--;
60            }
61          }
62          year = CountM/12;
63          month = CountM%12;
64        }
65      }
66  
67      if ( year>99 ) year=month=day=0;
68  
69      int Ost = year%10;
70      char SimvY = (Ost>0 && Ost<5) ? 'г' : 'л';
71
72      char Str[80];
73      Str[0]=0;
74      switch ( TypeForm )
75      {
76        case 1:   // л.м.
77        sprintf(Str,"%2d%c%2dм",year,SimvY,month);
78        break;
79
80        case 2:   // л.
81        sprintf(Str,"%2d%c",year,SimvY);
82        break;
83
84        default:  // л.м.д.
85        sprintf(Str,"%2d%c%2dм%2dд",year,SimvY,month,day);
86        Break;
87      }
88      Text_Text(pcT,Str,W);
89    }
90    return pcT;
91  }
  

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

[Примечание]

Здесь и далее речь идет не о параметрах функции приема, которые передаются в функцию через строку Arg (это те параметры, которые пишутся в функции приема в таблицах или в формах печати в круглых скобках), а не о параметрах Arg, pcT, W и A.

Поэтому необходимо в функции организовать разбор передаваемых в нее параметров, что выполнено в строках 17-19: создается объект ParamFuncRW, который инициализируется строкой переданных параметров Arg. При инициализации мы указали, что параметры отделяются друг от друга запятыми, и их не должно быть больше 10 («с запасом»). Кроме того, в 19 строке вызывается метод созданного объекта, который возвращает фактическое количество параметров, переданных в строке Arg. Далее выполняется анализ количества переданных параметров. Какие-либо действия мы принимаем только в том случае, если строка параметров не пустая (условие в строке 21).

В строках 23-26 создаются и инициализируются два объекта типа Kdate. Причем дата конца периода инициализируется первым числом расчетного месяца, а дата начала периода инициализируется значением, переданным в качестве первого параметра (строка 26). Для этого используется метод Get объекта ParamFuncRW. Заметим, что в качестве параметра методу Get передано значение 0, т.к. нумерация параметров начинается с нуля, а не с единицы.

В строке 28 анализируется: передано больше одного параметра или нет. Если передано больше одного параметра (два, три и т.д.), то дата конца инициализируется значением второго параметра (строка 30).

В строке 33-34, создается переменная, отвечающая за формат результирующей строки. Причем, если в функцию передано три параметра и более (строка 34), значение этой переменной инициализируется значением, стоящим третьим параметром.

[Примечание]

Следует заметить, что в отношении «необязательных» параметров следует всегда придерживаться такой последовательности действий: сначала создается переменная, отвечающая за значение параметра, и ИНИЦИАЛИЗИРУЕТСЯ значением по умолчанию. Затем анализируется количество параметров и, если нужный параметр существует, переменная инициализируется значением этого параметра (как это сделано выше в отношении даты конца периода и формата). Первоначальная инициализация переменной значением по умолчанию очень важна, так как, если ее не сделать и в функцию не будет передан параметр, который допускается не передавать, поведение функции будет непредсказуемо.

По поводу третьего параметра, передаваемого в функцию печати (формат выводимого значения), можно сделать дополнительное замечание. Сейчас этот параметр может передаваться только в виде числа-константы, то есть нельзя передать в качестве этого параметра имя некоторой переменной или имя столбца таблицы. Наша функция в том виде, как она написана, все равно этого не поймет. Чтобы ее улучшить и научить понимать переменные, передаваемые в качестве третьего параметра необходимо изменить строку 34 следующим образом:

if ( CountParam>2 ) TypeForm = atoi(GetVarValue(FuncParam.Get(2)));
  

Т.е. переданный параметр передается для дополнительной обработки в функцию GetVarValue, которая и определит, является ли переданный параметр переменной и, если является, вернет значение этой переменной.

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

В заключение анализа написанной функции, стоит только обратить внимание на строки 88 и 90. Они обеспечивают требования к возвращаемому значению: в строке 88 обеспечивается то, что в результирующей строке будет ровно W символов, ну а в строке 90 возвращается результат работы функции. Заметьте, что если функция не смогла выполнить никакой работы (передано мало параметров, или переданы неверные параметры или возникли какие-либо еще ошибки), то желательно возвращать результирующую строку pcT либо в том виде, в котором вам ее передали, либо записать туда, какое-либо осмысленное сообщение или значение.

Для записи результата в результирующую строку кроме функции Text_Text (она используется для записи строки) можно использовать еще две функции: Long_Text(pcT,Str,W) – для записи целого числа; Double_Text(pcT,Str,A,W) – для записи вещественного числа.

И, в заключение, необходимо сделать несколько замечаний общего вида о функциях приема.