Кроме создания собственных алгоритмов расчета, не менее полезной возможностью является создание собственных функций приема в таблицы.
В программе АМБа очень широко используются всевозможные таблицы. Прием данных в такие таблицы, разноска данных из таблиц в лицевые счета, печать таблиц – все это осуществляется с помощью функций, которые называются функциями приема.
Функции приема широко используются в настроечных таблицах в столбцах: Функция приема, Функция разноски, Функция расчета, Функция предварительной обработки. В формах печати таблиц также интенсивно используются функции приема.
Создание собственной функции приема выполняется в два этапа:
Написание самой функции.
Регистрация написанной функции, что дает возможность вызвать ее из таблицы или формы печати.
Все функции приема имеют единый формат:
Name_Function(&Arg, &pcT, W, A),
где
Arg
– строка параметров,
передаваемых в функции;
pcT
– строка,
в которую записывается конечный результат;
W
– число, означающее максимальную длину результирующей строки
pcT
; A
– число, означающее
точность, с которой необходимо записать числовой результат в результирующую
строку 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 }
Разберем работу этой функции по порядку. Как уже было сказано ранее, данная функция приема может получать от одного до трех параметров.
Здесь и далее речь идет не о параметрах функции приема, которые
передаются в функцию через строку |
Поэтому необходимо в функции организовать разбор передаваемых в нее
параметров, что выполнено в строках 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)
– для записи
вещественного числа.
И, в заключение, необходимо сделать несколько замечаний общего вида о функциях приема.
Возможности применения вашей функции приема зависит от назначения функции. Если ваша функция производит запись каких-либо значений в лицевой счет, то единственное место для ее применения – использовать ее в столбце Функция разноски в настроечной таблице. Во всех остальных местах: столбец Функция приема, столбец Функция расчета, форма печати, ваша функция не сможет отработать так, как вы задумали, если не принять дополнительных мер (а именно, обеспечить запись лицевого счета). Аналогично, нет смысла в использовании функции, которая читает что-либо из лицевого счета в столбце Функция расчета, т. к. в этот момент ни один лицевой счет не прочитан.
В столбце Функция приема настроечной таблицы
возможно использование не только функций приема, там можно написать
небольшой алгоритм непосредственно на макроязыке. Значение в столбце
Функция приема интерпретируется как алгоритм на
макроязыке, если в нем находится подстрока return
. Это
необходимо в ситуациях, когда готовых функций недостаточно для
правильной обработки принимаемых значений. Например, можно написать
такое выражение:
char val[81]; sprintf(va,”%05d”,atoi(C_FIO)); return val;
В этом примере предполагается, что есть столбец с именем C_FIO (обычно это столбец с табельным номером). В этом случае в столбец, для которого будет введено написанное выше выражение, будет вписан табельный номер, в котором будет не меньше пяти цифр. Если же цифр в табельном номере было меньше, то недостающие позиции займут лидирующие нули: 1 – 00001, 2 – 00002, 13 – 00013.
В столбце Функция расчета настроечной таблицы можно использовать функции приема только в том случае, если в первой позиции значения из этого столбца стоит символ #. При отсутствии этого символа в первой позиции содержимое столбца трактуется как арифметическое выражение.