Показать сообщение отдельно
Старый 27.12.2017, 22:02   #1  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,275 / 3476 (122) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
D365 FO Ссылка на конкретную запись. Параметры URL
Добрый день! В рамках исследования параметров 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/). Для этого нужно в параметрах пользователя включить параметр "Автоматическое обновление параметров запроса"
Название: SNAG_Program-0018.png
Просмотров: 1538

Размер: 8.5 Кб
и воспользоваться расширенным фильтром на форме
Нажмите на изображение для увеличения
Название: SNAG_Program-0019.png
Просмотров: 520
Размер:	49.5 Кб
ID:	11798
Запрос зашифрован, однако если его расшифровать - то там будет строка вида Параметр1=Значение1&Параметр2=Значение2, где параметры - это поля фильтра запроса (по сути - предложение WHERE в SQL-запросе). Однако D365 принимает только зашифрованную строку, так что смысла в дешифровке особой нет. Возможно, есть параметры, при которых шифрование отключается - но я их пока не видел (в АХ 2012 и ранее на портале такой параметр был)

3. Каждый запускаемый объект имеет право самостоятельно определять дополнительные параметры URL, которые он готов принять. Пример - см класс SysTableBrowser и параметр TableName:
Нажмите на изображение для увеличения
Название: SNAG_Program-0020.png
Просмотров: 470
Размер:	27.3 Кб
ID:	11799

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;
    }
Осталась последняя деталь - этот код надо вывести на форму, которая открывается при нажатии на кнопку "Получить ссылку" на закладке Параметры
Нажмите на изображение для увеличения
Название: SNAG_Program-0021.png
Просмотров: 485
Размер:	19.7 Кб
ID:	11800

Здесь вызывается пункт меню 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();
    }

}
На выходе мы получаем:
Нажмите на изображение для увеличения
Название: SNAG_Program-0022.png
Просмотров: 566
Размер:	109.0 Кб
ID:	11805
По нижней ссылке можно открыть одну отфильтрованную запись.

Ну и можно приложить модель (я все делал в отдельной модели) - файл axmodel (выгружался из PU10), выгруженный проект (axpp-файл) и решение (solution) в студии, чтобы пример было удобно открыть из студии
SysGetFullShareLink-VSUH.axmodel.rar
SysGetFullShareLink.axpp
SysGetFullShareLink_Project.rar
__________________
Возможно сделать все. Вопрос времени

Последний раз редактировалось sukhanchik; 28.12.2017 в 13:49.
За это сообщение автора поблагодарили: mazzy (10), S.Kuskov (2), belugin (10), Logger (10), raz (10), Jorj (1), Ivanhoe (10), trud (7), gl00mie (10).