Добрый день! В рамках исследования параметров URL, через которые можно задать те или иные команды системе были найдены следующие интересные моменты:
1. Встроенные команды.
https://community.dynamics.com/ax/b/...url-parameters
В том числе, наиболее интересные:
prt=[partitionID] - указание раздела, в котором будет проводиться работа
cmp=[legal entity] - указание компании, в которой будет проводиться работа
lng=[LanguageId] - указание языка интерфейса. Очень удобно, когда хочется посмотреть как одна и та же надпись выглядит как по-русски, так и по-английски
mi=[menuItem] - указание пункта меню, который требуется вызвать
f=[formName] - указание названия формы, которую нужно открыть напрямую, не через пункт меню
q=[queryString] - запрос, который передается на форму и позволяет отфильтровать данные формы
2. Запрос система может генерить сама (см
https://axology.wordpress.com/2016/1...or-operations/). Для этого нужно в параметрах пользователя включить параметр "Автоматическое обновление параметров запроса"
и воспользоваться расширенным фильтром на форме
Запрос зашифрован, однако если его расшифровать - то там будет строка вида Параметр1=Значение1&Параметр2=Значение2, где параметры - это поля фильтра запроса (по сути - предложение WHERE в SQL-запросе). Однако D365 принимает только зашифрованную строку, так что смысла в дешифровке особой нет. Возможно, есть параметры, при которых шифрование отключается - но я их пока не видел (в АХ 2012 и ранее на портале такой параметр был)
3. Каждый запускаемый объект имеет право самостоятельно определять дополнительные параметры URL, которые он готов принять. Пример - см класс SysTableBrowser и параметр TableName:
4. Есть официальная статья
Create and use deep links, в которой приводится пример, как сгенерить URL-адрес, открывающий заданный пункт меню с заданным фильтром (Query)
X++:
// gets the generator instance
var generator = new Microsoft.Dynamics.AX.Framework.Utilities.UrlHelper.UrlGenerator();
var currentHost = new System.Uri(UrlUtility::getUrl());
generator.HostUrl = currentHost.GetLeftPart(System.UriPartial::Authority);
generator.Company = curext();
generator.MenuItemName = <menu item name>;
generator.Partition = getCurrentPartition();
// repeat this segment for each datasource to filter
var requestQueryParameterCollection = generator.RequestQueryParameterCollection;
requestQueryParameterCollection.AddRequestQueryParameter(
<datasource name>,
<field1>, <value1>,
<field2>, <value2>,
<field3>, <value3>,
<field4>, <value4>,
<field5>, <value5>
);
System.Uri fullURI = generator.GenerateFullUrl();
// to get the encoded URI, use the following code
fullURI.AbsoluteUri
Соответственно, на базе этого кода можно написать свой, который будет фильтровать по RecId и т.о. отображать текущие записи. Тут правда есть нюанс - в стародавние времена было модно сложные запросы вида %1.%2 == %3.%4 || %5.%6 == %7.%8 делать как Range по полю RecId, как по наиболее редко используемому полю в запросах. В этом случае фильтрация по RecID может работать некорректно. Поэтому я немного видоизменил код и попробовал фильтроваться по первичному ключу:
X++:
public str generateFullURL()
{
str ret;
FormRun formRun = element.args().caller();
FormDataSource formDS = formRun.dataSource();
if (formRun && formRun.isRootNavigable())
{
System.Uri host = SessionContext::Get_Current().Get_RequestUrl();
UrlHelper.UrlGenerator generator = new UrlHelper.UrlGenerator();
generator.MenuItemName = formRun.args().menuItemName();
generator.MenuItemType = formRun.args().menuItemType();
generator.HostUrl = host.GetLeftPart(System.UriPartial::Path);
generator.Company = curExt();
generator.EncryptRequestQuery = true;
Microsoft.Dynamics.AX.Framework.Utilities.UrlHelper.RequestQueryParameterCollection requestQueryParameterCollection = generator.RequestQueryParameterCollection;
DictTable dictTable = new DictTable(formDS.cursor().TableId);
FieldId primaryKey = dictTable.primaryKeyField();
if (!primaryKey)
{
primaryKey = fieldnum(Common, RecId);
}
requestQueryParameterCollection.AddRequestQueryParameter(formDS.name(), fieldId2Name(dictTable.id(), primaryKey), strfmt("%1", formDS.cursor().(primaryKey)));
ret = generator.GenerateFullUrl().AbsoluteUri;
}
return ret;
}
Осталась последняя деталь - этот код надо вывести на форму, которая открывается при нажатии на кнопку "Получить ссылку" на закладке Параметры
Здесь вызывается пункт меню FormRunGetLinkAction, который вызывает одноименную форму.
Теперь есть 2 варианта, как вставить наш код в эту форму (без оверлеинга):
А. Добавить Post-обработчик на метод run формы и в контрол Link записать наш текст. Это легкий способ, но он перезатирает стандартную функциональность
Б. Добавить Post-обработчик на метод run формы и добавить в рантайме динамически новый контрол.
Расширение (Extension) к самой форме создать не получится - установленные паттерны на ней не позволяют без ошибок компиляции добавить статический контрол. А менять паттерны Extension не разрешает.
Пойдем вторым путем (вариант Б). Не забудем вставить проверку существования контрола, на случай, если кнопку "Получить ссылку" попробуют нажать при уже открытой форме
X++:
public void postRun()
{
this.showFullLink();
}
public void showFullLink()
{
if (element.args() && element.args().caller() && element.args().callerType() == UtilElementType::Form)
{
FormStringControl ctrlFullLink = this.ctrlFullLink();
if (!ctrlFullLink)
{
ctrlFullLink = element.design().addControl(FormControlType::String, ctrlFullLinkName);
}
ctrlFullLink.allowEdit(false);
ctrlFullLink.displayLengthMode(this.ctrlLink().displayLengthMode());
ctrlFullLink.displayLength(this.ctrlLink().displayLength());
ctrlFullLink.label("@SYS22569");
ctrlFullLink.text(this.generateFullURL());
}
}
Теперь осталось самая малость - оформить все в полноценный класс
X++:
using Microsoft.Dynamics.AX.Framework.Utilities;
using Microsoft.Dynamics.@Client.ServerForm.Contexts;
class FormRunGetLinkActionHandler
{
FormRun element;
private const str ctrlFullLinkName = "FullLink";
public FormRun formRun (FormRun _formRun = element)
{
element = _formRun;
return element;
}
public static FormRunGetLinkActionHandler construct(FormRun _formRun)
{
FormRunGetLinkActionHandler handler = new FormRunGetLinkActionHandler();
handler.formRun(_formRun);
return handler;
}
public str generateFullURL()
{
str ret;
FormRun formRun = element.args().caller();
FormDataSource formDS = formRun.dataSource();
if (formRun && formRun.isRootNavigable())
{
System.Uri host = SessionContext::Get_Current().Get_RequestUrl();
UrlHelper.UrlGenerator generator = new UrlHelper.UrlGenerator();
generator.MenuItemName = formRun.args().menuItemName();
generator.MenuItemType = formRun.args().menuItemType();
generator.HostUrl = host.GetLeftPart(System.UriPartial::Path);
generator.Company = curExt();
generator.EncryptRequestQuery = true;
Microsoft.Dynamics.AX.Framework.Utilities.UrlHelper.RequestQueryParameterCollection requestQueryParameterCollection = generator.RequestQueryParameterCollection;
DictTable dictTable = new DictTable(formDS.cursor().TableId);
FieldId primaryKey = dictTable.primaryKeyField();
if (!primaryKey)
{
primaryKey = fieldnum(Common, RecId);
}
requestQueryParameterCollection.AddRequestQueryParameter(formDS.name(), fieldId2Name(dictTable.id(), primaryKey), strfmt("%1", formDS.cursor().(primaryKey)));
ret = generator.GenerateFullUrl().AbsoluteUri;
}
return ret;
}
public void postRun()
{
this.showFullLink();
}
public void showFullLink()
{
if (element.args() && element.args().caller() && element.args().callerType() == UtilElementType::Form)
{
FormStringControl ctrlFullLink = this.ctrlFullLink();
if (!ctrlFullLink)
{
ctrlFullLink = element.design().addControl(FormControlType::String, ctrlFullLinkName);
}
ctrlFullLink.allowEdit(false);
ctrlFullLink.displayLengthMode(this.ctrlLink().displayLengthMode());
ctrlFullLink.displayLength(this.ctrlLink().displayLength());
ctrlFullLink.label("@SYS22569");
ctrlFullLink.text(this.generateFullURL());
}
}
public FormStringControl ctrlLink()
{
return element.design().controlName(formControlStr(FormRunGetLinkAction, Link));
}
public FormStringControl ctrlFullLink()
{
return element.design().controlName(ctrlFullLinkName);
}
[PostHandlerFor(formStr(FormRunGetLinkAction), formMethodStr(FormRunGetLinkAction, run))]
public static void FormRunGetLinkAction_Post_run(XppPrePostArgs _args)
{
FormRunGetLinkActionHandler handler = FormRunGetLinkActionHandler::construct(_args.getThis());
handler.postRun();
}
}
На выходе мы получаем:
По нижней ссылке можно открыть одну отфильтрованную запись.
Ну и можно приложить модель (я все делал в отдельной модели) - файл axmodel (выгружался из PU10), выгруженный проект (axpp-файл) и решение (solution) в студии, чтобы пример было удобно открыть из студии
SysGetFullShareLink-VSUH.axmodel.rar
SysGetFullShareLink.axpp
SysGetFullShareLink_Project.rar