====== Описание формы ====== За написание и тестирование скриптов (как ДС, так и ВДС) отвечает модуль **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