Итак, вы разработчик. Ваша задача - заставить работать в веб-доступе DIRECTUM то, что уже функционирует в десктоп-клиенте. Воспользуйтесь планом ниже:
Шаг 0. Успокойтесь и настройтесь на разработку.
Шаг 1. Проанализируйте существующую прикладную разработку.
В первую очередь при рефакторинге ПЧ под веб необходимо определить список мест, в которых будут происходить ошибки. К ним относится:
Шаг 2. Изолировать отображение окон, сгруппировать общие вычисления в функции или методы.
Шаг 3. Отображение окон или форм объектов реализовать в веб-модуле. И организовать правильный вызов прикладного кода. Для этого используйте следующие возможности:
Шаг 4. Измените разработку прикладной части и добавьте обработчики в веб-модули, ориентируясь на результаты третьего шага.
Рассмотрим применение этих шагов на практике.
При прекращении поручения необходимо запросить причину прекращения. Для этого реализован прикладной диалог, который вызывается на действии.
Dialog = Dialogs.AssigmentAbort.CreateNew
if not IsNOMADRuntimeContext() and not IsWebRuntimeContext()
RecordID = Object.SYSREQ_ID
Dialog.AbortType = 'E'
Dialog.ShortString = RecordID
RecordOpened = Object.RecordOpened
else
RecordOpened = FALSE
endif
ShowDialog(Dialog)
Reason = Dialog.Requisites('LongString').Value
RecordID = Dialog.Requisites('ShortString').Value
ResultDialog = Dialog.Result
if ResultDialog <> mrCancel
// Прекратить задачи по текущему поручению и всем его подчиненным, послать уведомления о прекращении работ,
// изменить статус поручений и снять их с контроля
if IsWebRuntimeContext()
Object.Params.Add('AssignmentIsAborted'; TRUE)
endif
EndWorkForAssignment(RecordID; Reason; 'E')
if not IsNOMADRuntimeContext() and not IsWebRuntimeContext()
if RecordOpened
Actions = Object.Form.Actions
// Установить доступность кнопок напомнить о сроке, скорректировать, прекратить, принять, на доработку, запросить отчет
ActionNameArray = "NotifyAboutDeadline|CorrectAssignment|StopWork|Assept|Revision|RequestState"
foreach ActionName in CSubString(ActionNameArray; '|')
Action = Actions.FindAction(ActionName)
if not VarIsNull(Action)
Action.Enabled = FALSE
endif
endforeach
endif
endif
endif
Логика прекращения поручения достаточно сложная, поэтому дублирование ее еще и в веб-модуле нецелесообразно.
Функция в веб показывает диалог, а затем запускает само действие в ПЧ с уже заполненными параметрами. Запуск действия (а не сценария) позволяет также обновить карточку Поручения автоматически.
// Прервать исполнение поручения
RecordOffice.abortAssignment = function () {
var dialogName = 'AssigmentAbort';
var actionName = 'StopWork';
WA.FC.dialogs.getObjectByName(dialogName, MODEL.FULL).done(function (dialog) {
dialog.show();
dialog.setTitle(L('ASSIGNMENT_DIALOG_ABORT'));
dialog.Init.done(function () {
dialog.form.requisites['AbortType'].setValue('1');
dialog.form.requisites['ShortString'].setValue(WA.CR.ID);
});
dialog.bind(WA.CMP.dialogs.FormDialog.EVENT.AFTER_ACTION, function (dialogActionName, result) {
// Нестандартная кнопка
if (dialogActionName === 'OK') {
WA.SRV.call('/Action.asmx/Execute', {
Link: (new WA.Link()).toLinkModel(), ActionName: actionName,
SerializedForm: dialog.form.serialize({
onlyValue: true,
returnRowNumber: true,
withoutTables: true
}),
CommandNumber: 0, CommandType: 0, CommandResult: {}
})
.done(function (response) {
var responseAsObject = $.parseJSON(response);
// Выполнить комманды
var cmds = responseAsObject.Commands;
WA.CR._performCommands(cmds);
// Применить изменения к форме
var changes = responseAsObject.Changes;
WA.CR.form.applyServerSideChanges(changes);
_disableAssignmentButtons();
WA.CR.inlineHint.showInfo(L('ASSIGNMENT_TASK_WAS_ABORTED'));
FreeRecord(WA.CR.ReferenceCode, WA.CR.ID);
});
}
});
});
};
Часто, чтобы пользователю было удобнее, требуется предзаполнить реквизиты диалога, а после успешного запроса всех параметров выполнить какую-либо логику.
Удобнее всего будет реализовать всю логику на стороне ПЧ, а в веб-доступе только отобразить диалог.
Рассмотрим пример: в Исходящих РКК есть кнопка "Контролировать возврат", оформлена в действии "Контроль возврата" (CheckReturn), для запроса данных и формирования задачи реализован прикладной диалог "Контроль возврата документа" (DISICheckReturn).
Действие построено следующим образом:
Dialog = Dialogs.DISICheckReturn.CreateNew()
Dialog.Int = Object.SYSREQ_ID
ShowDialog(Dialog)
if Dialog.Result = mrOk
// Задача на контроль возврата успешно отправлена.
ShowMessage(LoadString('DIRE6F944C2_21AF_4002_8F4A_C2C0498F8C52'; 'RM'))
endif
Как можно заметить, вся основная логика расположена в самом диалоге. Разберём по порядку.
1. Предзаполнение реквизитов диалога значениями по-умолчанию можно вынести на событие "Создание" диалога.
// Заполнить поля диалога
Object.Date = ServiceFactory.GetRelativeDate(Today(); 21; dotDays)
Object.User = Tasks.CurrentUser.Code
2. Чаще требуется предзаполнять реквизиты в зависимости от контекста. Выше, мы указали ИД РКК в реквизите диалога Int. На событии Вычисление реквизита Int вычислим пользователя от РКК
// Заполнить поля диалога
Employee = GetRequisiteValueAsString('РКК'; ''; 'Employee';;; Object.Int)
// Найти пользователя по работнику
if Employee <<>> ''
Object.User = GetRequisiteValueAsString('РАБ'; Employee; 'Пользователь')
endif
3. На событии Закрытие Возможность проверим валидность данных
if DateDiff('D'; Today(); Object.Date) < 0
// Указанная дата меньше текущей.
Raise(CreateException('EDIRInvalidDate'; LoadString('DIRE3CB79BB_B019_4B0F_8F2F_86B0BE78E90A'; 'MM'); ecWarning))
endif
4. И в завершение, на событии Закрытие запустим серверное событие, которое сформирует задачу по контролю возврата.
// Стартовать задачу на котроль возврата
if Object.Result = mrOk
RRCID = Object.Int
DocIDs = Object.LongString
if Assigned(RRCID) or Assigned(DocIDs)
// Сформировать список общих параметров
ParamStringList : IStringList = CreateStringList()
ParamStringList.Delimiter = CONST_ELEMENT_DELIMITER
ParamStringList.Add('UserName' & CONST_VALUE_DELIMITER & CurrentUserName())
ParamStringList.Add('CheckReturnUserID' & CONST_VALUE_DELIMITER & Object.Requisites("User").ValueID)
ParamStringList.Add('CheckReturnDeadline' & CONST_VALUE_DELIMITER & Object.Requisites("Date").AsString)
ParamStringList.Add('AccompanyDocsIDs' & CONST_VALUE_DELIMITER & Object.Requisites("LongString2").AsString)
// Запустить серверное событие для отправки задачи на контроль возврата зарегистрированного документа
ServerEventScript = ServerEvents.GetObjectByName('DISICheckReturn')
Params = ServerEventScript.Params
Params.ValueByName('TaskID').Value = Object.Int3
Params.ValueByName('RRCID').Value = RRCID
Params.ValueByName('DocIDs').Value = DocIDs
Params.ValueByName('StringParamList').Value = ParamStringList.DelimitedText
ServerEventScript.Start
endif
endif
В веб-модуле создаём функцию, которая будет отображать диалог по кнопке в РКК.
/**
* Контролировать возврат документа
* @method RecordOffice.checkReturn
**/
RecordOffice.checkReturn = function () {
// Заполнить реквизиты диалога и отправить задачу на контроль
WA.SRV.call("/RecordOffice.asmx/GetCheckReturnDialog", { ID: WA.CR.ID }).done(function (res) {
WA.FC.dialogs.getDialogByInstanceID(res).done(function (dialog) {
dialog.show();
dialog.bind(WA.CMP.dialogs.FormDialog.EVENT.AFTER_ACTION, function (dialogActionName, result) {
if (dialogActionName === DIALOG_ACTION.OK) {
ShowInfo(L("CHECK_RETURN_TASK_SEND"));
};
});
});
});
}
Для заполнения реквизита диалога "РКК" создаём веб-метод
''' <summary>
''' Подготавливает диалог для контроля возврата по документу.
''' </summary>
''' <param name="ID">ИД РКК</param>
''' <returns>ИД диалога</returns>
<WebMethod>
Public Function GetCheckReturnDialog(ByVal ID As Integer) As WebServiceResponse(Of String)
Dim Dialog As API.Dialog = Nothing
Dialog = WebSession.Context.Dialogs.GetDialogByName(Constants.CHECK_RETURN_DIALOG_NAME)
Dialog.Requisites("Int").Value = ID
Return WebServiceResponse(Of String).OK(Dialog.InstanceID)
End Function
Как видим, в веб-модуле потребовалось минимальное дублирование функционала, вся основная логика подхватывается с событий диалога автоматически.
Необходимо учитывать это при разработке и максимально переносить вычисления на события диалога.
Функция ShowDialog() реализована для запроса данных для формирования отчета.
Работает с iDialog следующим образом: для десктопа отображает диалог, для веб-доступа заполняет реквизиты диалога одноименными параметрами родительского объекта (Object.Params).
Пример вызова в ПЧ:
// Создать и показать диалог запроса параметров
Dialog = Dialogs.AssignmentExecutionDeadlineControl.CreateNew
if not VarIsNull(Object.Params.FindItem('Cut'))
if Object.Params.ValueByName('Cut') == 'Meeting'
Cut = MeetingStr
else
if Object.Params.ValueByName('Cut') == 'Claim'
Cut = ClaimStr
else
if Object.Params.ValueByName('Cut') == 'RRC'
Cut = RRCStr
endif
endif
endif
Dialog.AssignmentCut = Cut
endif
ShowDialog(Dialog)
if Dialog.Result = mrOk or IsWebRuntimeContext()
…
В вебе требуется минимальная доработка: вставить в xml-ку строку с указанием, что параметры отчета будут браться из диалога, и имени диалога:
<Report name="AssignmentExecutionDeadlineControl" getparams="fromdialog" dialogname="AssignmentExecutionDeadlineControl" />
Сценарии используются в случае, если нет возможности использовать метод: работа идет с несколькими объектами, или этот объект не имеет методов. Например, для изменения документа из карточки задания используется следующий фрагмент js кода:
// Вызвать сценарий для смены ВЭД и ТКЭД документа
WA.FC.scripts('WADISITransformDoc').execute({ DocID: docID, DocKind: docKind, DocType: docType, Text: text, InvoiceType: invoiceType })
.success(function (Doc) {
// Открыть карточку документа
var docUrl = docLink.toURL();
docUrl.setParam('mode', 'edit')
var docWin = docUrl.open();
// Ждать, пока не закроют
var timer = setInterval(function () {
if (docWin.closed) {
clearInterval(timer);
if (dialog)
dialog.close();
};
}, 1000);
});
Вместо сценария предпочтительно использовать методы объектов. Потому что это проще и менее трудоемко. Как минимум, не потребуется выдавать права на новые сценарии.
Типовые случаи применения методов:
1. Если в действии происходят сложные вычисления, результатом которых являются открытие объектов или вывод сообщения.
2. Если в действии вначале запрашиваются параметры у пользователя, после чего происходят сложные манипуляции с этими данными.
Используйте для простоты разработки общие функции для открытия окон/объектов
Например,
/**
* Функция для отправки договорного документа на согласование
* @method ContractsManagement.sendContractForApproval
*/
ContractsManagement.sendContractForApproval = function () {
WA.CR.executeEntityMethod("OnExecute_Задача")
.done(function (TaskIDStdRoute) {
if (TaskIDStdRoute[0]) {
BaseWebAccess.openTaskInNewTab(TaskIDStdRoute[0]);
}
else {
SendAsAttachment({
id: WA.CR.ID,
kind: WA.CR.getKind()
}, TaskIDStdRoute[1], '');
};
})
.fail(function (message) {
// Если константа CMAgreementStandardRoute не означена, то выводить исключение
WA.CR.inlineHint.showError(message);
});
};
В самом методе нужно реализовать создание записи. А вот открытие карточки записи должно отличаться для веб и десктоп-клиента: в контексте веб-доступа надо вернуть созданную запись как результат, для того, чтобы потом отобразить запись на уровне веб-модуля:
// Запретить изменять не ключевым участникам
if not MMCurrentUserIsMeetingKeyFigure(Object; TRUE) and Object.Environment.IndexOfName('From_Agent') = -1
// Неключевым участникам запрещено изменять совещание. Ключевыми участниками являются инициатор, секретарь, председатель и их замещающие.
Message = LoadString('DIR52F15441_539B_41B9_8AFB_BCBC9FDFD475'; 'MM')
Raise(CreateException('EDIRInvalidUserAction'; Message; ecWarning))
endif
MeetingRef = References.СВЩ.GetComponent
MeetingRef.ViewName = 'Главное'
AddWhere = MeetingRef.AddWhere("1 = 2")
MeetingRef.Open
MeetingRef.Append
MeetingRef.Requisites("MTemplate").Value = Object.SYSREQ_CODE
if not IsWebRuntimeContext()
MeetingRef.Form.ShowModal
MeetingRef.Close
MeetingRef.DelWhere(AddWhere)
else
MeetingRef.DelWhere(AddWhere)
Result = MeetingRef
MeetingRef = nil
endif
В веб-модуле вычисления будут выглядеть так:
/**
* Функция для создания совещания по серии
* @method Meetings.CreateSeriaMeeting
*/
Meetings.CreateSeriaMeeting = function () {
WA.CR.executeEntityMethod("OnExecute_MeetingCreate")
.done(function (res) {
var rrctLink = new WA.Link(res.id, OBJECT_TYPE.REFERENCE_RECORD, 'СВЩ');
var rrcUrl = rrctLink.toURL();
rrcUrl.setParam("refview", "Главное");
rrcWin = rrcUrl.open();
});
};
В случае, если перед вычислениями нужно запросить данные у пользователя, используем комбинированный вариант: диалог+метод. Вычисления в веб-модуле будут выглядеть так:
/**
* Отменить совещение
* @method Meetings.cancelMeeting
*/
Meetings.cancelMeeting = function () {
WA.FC.dialogs.getObjectByName('MMMeetingCancel', MODEL.FULL).done(function (dialog) {
dialog.show();
dialog.bind(WA.CMP.dialogs.FormDialog.EVENT.AFTER_ACTION, function (dialogActionName, result) {
if (dialogActionName === DIALOG_ACTION.OK) {
WA.CR.executeEntityMethod("OnExecute_MeetingCancel", dialog.form.requisites['LongString'].getValue());
}
});
});
};
Для того, чтобы это заработало, нужно:
1. Реализовать диалог для запроса данных у пользователя. Возможно, к моменту разработки он уже будет.
2. В метод добавить параметр CancelReason. И, разумеется, сделать метод видимым.
3. В текст метода добавить вычисления.
// Запретить изменять не ключевым участникам
if not MMCurrentUserIsMeetingKeyFigure(Object)
// Неключевым участникам запрещено изменять совещание. Ключевыми участниками являются инициатор, секретарь, председатель и их замещающие.
Message = LoadString('DIR9379F646_5AF4_489F_8A05_D43D69C53CA9'; 'MM')
Raise(CreateException('EDIRInvalidUserAction'; Message; ecWarning))
endif
Object.Environment.SetVar('CANCEL_RECORD'; TRUE)
Reason = MeetingCancelInitiate(Object;; CancelReason)
Object.СостСовещ = 'Прекращено'
if Reason <<>> ''
Object.Room = ''
Object.Дополнение = ''
endif
Object.Events.DisableAll
Object.Rules.DisableAll
Object.Save
Object.Events.EnableAll
Object.Rules.EnableAll
// Установить доступность кнопок для задач
Object.OnExecute_SetButtonsVisibility
if Object.Environment.IndexOfName('CANCEL_RECORD') > -1
Object.Environment.PopVar('CANCEL_RECORD')
endif
Указанные выше примеры не постулируют разработку веб-модулей, а лишь демонстрируют варианты реализации. У вас реализация может отличаться. Это нормально.
Команда стандартного веб-доступа всеми силами стараются упростить разработку веб-модулей и обеспечить поддержку прикладного кода в веб-клиенте. Дело это длительное и трудоемкое.
Если у вас есть пожелания к модификации веб-доступа или веб-модулей, оформите обращение в DIRECTUM.
Авторизуйтесь, чтобы написать комментарий