====== Описание формы ======
За написание и тестирование скриптов (как ДС, так и ВДС) отвечает модуль **TestingScripts.pas** с соответствующим классом **TTestingScripts**
В этом классе для определения вида скрипта отвечает поле **scriptType**. Это поле в дальнейшем отвечает за всё остальное поведение формы.
В зависимости от вида скрипта открывается та или иная страница на pcTypeScript, содержащие в себе нужные сценарии проверки.
Все страницы взаимодействуют с основными контролами класса TAgbMemo - поле ввода mScript и результат его работы mResult.
Форму ни в коем случае нельзя закрывать с сохранением данных, если были внесены изменения в mScript и они не прошли сценарий проверки.
======Отработка написанных скриптов======
При выполнении скриптов ДС-ВДС (как для проверки, так и в режиме работы из заказа) задействуются функции из модуля **UDiscountTools**. Все расчёты в конечном счёте приведут именно к ним.
// выполняет скрипт расчёта скрипта ВДС;
function ExecuteExternalDiscScript(doc_id: int64; script: string; var errScripts: string; AUpTr: TpFIBTransaction; out ExtScriptResInfo: TExtScriptResInfo): Double;
// выполняет скрипт расчёта скрипта ДС.
function ExecuteSimpleDiscScript(AContrID, ATovarID:int64; Control:Integer; ASchemeID: variant; showException: boolean ;
script, Dat: string; var errScripts: string; ARdTr, AUpTr: TpFIBTransaction):double;
Основная разница у них в перечне входных и выходных параметров.
Скрипт ВДС передаёт принимает 1 параметр doc_id (ВнНомер документа заказа), возвращает количество бонусов для начисления и целый блок дополнительной информации, содержащийся в ExtScriptResInfo.
Скрипт ДС передаёт принимает 3 параметра - ID клиента, ID товара и ссылку на контрол из формы заказа. Возвращает размер скидки на конкретную позицию по клиенту.
Общий принцип их работы таков:
1. В модуле **UDiscountTools** Создаётся объект класса TScriptExecutor se, в конструктор передаётся записывающая транзакция;
se := TScriptExecutor.Create(_UpTr);
2. В модуле **ScriptFunctions** Конструктор TScriptExecutor создаёт объект PSScript паскалевского скриптера класса TPSScript из библиотеки uPSComponent;
PSScript:=TPSScript.Create(Nil);
3. В модуле **UDiscountTools** Запускается попытка скомпилировать скрипт se.CompileScript(script, true, errScripts). В случае неудачного выполнения будет возвращен False, а errScripts будет содержать в себе список ошибок;
if not se.CompileScript(script, true, errScripts) then
raise EExternal.Create('Ошибка выполнения скрипта расчёта скидки!');
4. В модуле **ScriptFunctions** При компиляции в объект PSScript передаётся текст скрипта и запускается его компиляция:
PSScript.Script.Text:=Script;
Result:=PSScript.Compile;
5. В модуле **ScriptFunctions** В случае неуспешной компиляции объект PSScript будет иметь в себе заполненные поля PSScript.CompilerMessageCount (количество сообщений) и коллекцию PSScript.CompilerMessages сами сообщения об ошибках), из которых потом и собирается итоговая строка со списком ошибок;
if not Result then
begin
for i:=0 to PSScript.CompilerMessageCount-1 do
begin
if showMSG then
SimpleMessage(PSScript.CompilerMessages[i].MessageToString);
if messages = '' then
messages := PSScript.CompilerMessages[i].MessageToString
else
messages := messages + #13#10 + PSScript.CompilerMessages[i].MessageToString;
end;
end;
6. В модуле **UDiscountTools** определяется наличие заголовка GetDiscount:
ProcNo := se.PSScript.Exec.GetProc('GetDiscount');
if ProcNo = InvalidVal then
errScripts := 'Unknown procedure GetDiscount'
7. В модуле **UDiscountTools** если такой заголовок был найден (ProcNo <> InvalidVal), то пытается выполниться сам скрипт с переданным в него массивом входящих параметров (на примере ДС). Выполнение делается в try..except, а ошибки при выполнении записываются в errScripts :
try
Result := se.PSScript.ExecuteFunction([AContrID,ATovarID,Control],'GetDiscount');
except
on E: EFIBError do
errScripts := E.IBMessage;
on E:Exception do
errScripts := E.Message;
end;
======Какими функциями и процедурами можно пользоваться при написании скриптов======
После создания объекта **PSScript** ему, в конструкторе класса **TScriptExecutor**, присваиваются обработчики для импорта тех или иных функций. Это делается так:
PSScript.OnExecImport:=ClassesPluginExecImport; - здесь регистрируются классы, типы и методы, которые используются в рантайме;
PSScript.OnCompImport:=ClassesPluginCompImport; - здесь регистрируются базовые классы, типы и методы, которые используются при компиляции.
Также для рантайма и компиляции мы дополнительно регистрируем ряд функций и методов, передавая соответствующие адреса и названия, которые планируется использовать в скрипте.
В качестве методов мы сейчас берём работу с базой данных, т.к. класс **TScriptExecutor** содержит в себе транзакцию для работы с ней.
Регистрация методов и функций для рантайма делается так:
exec.RegisterDelphiMethod(Self, @TScriptExecutor.FastSQLVal,'FastSQLVal', cdRegister);
exec.RegisterDelphiFunction(@DaysBetween,'DaysBetween', cdRegister);
Регистрация остальных, кастомных методов и функций для компиляции делается в обработчике **TScriptExecutor.PSScriptCompile:**
Sender.AddMethod(Self, @TScriptExecutor.FastSQLVal,'Function FastSQLVal(Data: string):Variant');
Sender.AddFunction(@DaysBetween,'function DaysBetween(ANow, AThen: TDateTime): Integer');
Все базовые функции и методы, на которые мы ссылаемся при регистрации, должны иметь реализацию в своих соответствующих модулях, доступ к которым должен быть из класса **TScriptExecutor**.
Так, функция **DaysBetween** объявлена в модуле **DateUtils**.
Также стоит обязательно обратить внимание на данную статью:
https://doc.agb.is/special_params_in_scripts
Пройти тест можно по ссылке:
https://forms.gle/EuUzReSkCwNv5PH78