AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX Blogs
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 14.08.2008, 16:59   #1  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Автонумерация полей при обмене значениями с записеподобными структурами
Вначале я хотел назвать заметку "Автонумерация колонок при выводе в Excel", так как подобные примеры - наиболее "на слуху", но по ходу сочинения текста появилось более общее название (хотя и менее ясное с первого взгляда).

Итак, о чем же речь? Наверняка многим, как и мне, приходилось писать код, подобный следующему:

X++:
SysExcelCells cells;
;

cells.item( row,  1).value( ledgerTrans.RecId       );
cells.item( row,  2).value( ledgerTrans.AccountNum  );
cells.item( row,  3).value( ledgerTable.AccountName );
.....................................................
cells.item( row,  8).value( ledgerTrans.Txt         );
cells.item( row,  9).value( ledgerTrans.AmountMST   );
cells.item( row, 10).value( ledgerTrans.Crediting   );
т.е. формировать (или читать) некоторую "запись", последовательно нумеруя ее поля жёсткими литералами (см. выше второй параметр метода item). Наиболее частый случай - нумерация колонок при выводе в Excel (самыми разнообразными способами). Кроме вывода в Excel это может быть, например, заполнение значениями массива (класса Array): array.value(1, ...); array.value(2, ...); и т.д. В качестве примера чтения можно привести обращение к полям записи, выбранной из ODBC-источника: ... = resultSet.getString(1).

В принципе, нам совсем не в тягость пронумеровать такие строки вручную, когда речь идет о 10 строчках (нумерация от 1 до 10). В случае сотни строк - ситуация посложнее, но все равно преодолимая. Если неохота разминать пальцы, набивая значения от 1 до 100, то можно обратиться за помощью к Excel и сгенерировать там строчки кода при помощи формулы, имея в одной колонке номер (ячейка A1: 1), в другой значения для value (B1: ledgerTrans.RecId), а в третьей - собственно формулу (C1: ="cells.item(row," & A1 & ").value(" & B1 & ");").

Неприятности начинаются, когда обнаруживается, что в уже написанный код необходимо добавить еще одну "номерную" строку в начало (середину) или поменять строки местами. Особенно "ужасна" ситуация с началом, когда требуется перебивание всех имеющихся номеров: 1 надо перебить на 2, 2 - на 3, 3 - на 4 и т.д.

И вот в один прекрасный день, копаясь в коде от нашего поставщика решения - компании GMCS, я обнаружил в схожей ситуации вывода в Excel милейшее решение проблемы. Из серии тех, до которых сам никогда не додумаешься просто потому, что никогда не задумаешься, пока не увидишь у другого.

Маленькое лирическое отступление. Мне рассказали историю про человека, который частенько оставался у знакомых в гостях на ночь и неудобно спал на маленьком диване, свернувшись калачиком. Через год он (и хозяева!) случайно узнали, что диван, оказывается, раскладной...

Увидев этот код, я испытал примерно такое же ощущение "через год"
X++:
SysExcelCells cells;
int currNum = 0;

int nextNum()
{
   currNum++;
   return currNum;
}
;

cells.item( row, nextNum() ).value( ledgerTrans.RecId       );
cells.item( row, nextNum() ).value( ledgerTrans.AccountNum  );
cells.item( row, nextNum() ).value( ledgerTable.AccountName );
.............................................................
cells.item( row, nextNum() ).value( ledgerTrans.Txt         );
cells.item( row, nextNum() ).value( ledgerTrans.AmountMST   );
cells.item( row, nextNum() ).value( ledgerTrans.Crediting)  );
Понятно, что при таком подходе можно в любой момент безболезненно переставлять строки по желанию или иной необходимости.

Хотел было здесь остановиться, да вспомнил о еще одной "тягомотине". Это уже точно только для Excel и только для тех ситуаций, когда разработчик предпочитает работать с буквенными обозначениями ячеек. При использовании класса ComExcelDocument_RU решение по аналогии c nextNum может выглядеть как-то вот так:
X++:
static void test_nextCell(Args _args)
{
    ComExcelDocument_RU doc = new ComExcelDocument_RU();
    int currCol = 0;
    int currRow = 1;

    str nextCell()
    {
        currCol++;
        return ComExcelDocument_RU::numToNameCell(currCol, currRow);
    }
    ;

    doc.NewFile();
/*
    doc.InsertValue('A1', 10);
    doc.InsertValue('B1', 20);
    doc.InsertValue('C1', 30);
    doc.InsertValue('D1', 40);
*/
    doc.InsertValue( nextCell(), 10);
    doc.InsertValue( nextCell(), 20);
    doc.InsertValue( nextCell(), 30);
    doc.InsertValue( nextCell(), 40);

    doc.finalize();
}
Хотя, конечно, следует признать, что и без того небыстрый класс здесь будет отвлекаться на довольно затратные дополнительные вычисления nextCell. Но если на минуту предположить, что при перестановке строк надо будет перебивать уже не цифры, а буквы, то поневоле согласишься с этими затратами

P.S. 04.03.09. Иногда бывает нужно нумеровать поля несколько раз внутри метода (например, первый раз при создании структуры, а затем при записи в нее). В этом случае нужно позаботиться о своевременном обнулении счетчика (currNum = 0) при повторном проходе по полям структуры.

Чтобы не выносить обнуление в отдельный оператор, можно несколько усовершенствовать функцию nextNum, добавив необязательный параметр "начать с". В этом случае в первой строке, где используется nextNum, нужно будет явно указать стартовый номер (обычно это 1), а в последующих строках применять nextNum без параметров:
X++:
SysExcelCells cells;
int currNum = 0;

int nextNum(int _beginFrom = 0)
{
    if (prmIsDefault(_beginFrom))
        currNum++;
    else
        currNum = _beginFrom;

    return currNum;
}
;

cells.item( row, nextNum(1)).value( ledgerTrans.RecId       );
cells.item( row, nextNum() ).value( ledgerTrans.AccountNum  );
cells.item( row, nextNum() ).value( ledgerTable.AccountName );
В качестве небольшого бонуса - появившаяся возможность нумеровать строки с пропуском. Например, если надо вначале пройтись по полям с 1 по 3, а затем с 9 по 11, то имеем примерно следующее:
X++:
cells.item( row, nextNum(1))...
cells.item( row, nextNum() )...
cells.item( row, nextNum() )...

cells.item( row, nextNum(9))...
cells.item( row, nextNum() )...
cells.item( row, nextNum() )...

Последний раз редактировалось Gustav; 04.03.2009 в 17:58. Причина: добавление
Старый 08.09.2008, 18:39   #2  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Изготовление связанных тестовых копий форм журналов и форм строк журналов
У нас в компании две развернутые Аксапты - рабочая и тестовая. Как, наверное, у многих, собственные доработки создаются на тестовой инсталляции и после успешной отладки портируются на рабочее приложение.

Иногда возникает необходимость в экспериментальных "необратимых кромсаниях" какого-нибудь объекта, например, формы. "Необратимых" в данном случае означает, что по окончании эксперимента объект не будет восстановлен к первоначальному виду и будет "полностью удален из системы" (т.е. "наигрались и подчистили за собой"). Такие эксперименты, как правило, проводятся на тестовой базе, являя собой этакое "тестирование в квадрате". Впрочем, в случае крайней необходимости их можно аккуратно проводить и на рабочей, и тогда всё сказанное ниже приобретает дополнительную значимость.

Для целей создания подобных объектов в АОТ существует замечательная контекстная команда "Дублировать". После ее выполнения создается копия объекта под названием "CopyOf<НазваниеИсходногоОбъекта>". Я обычно переношу префикс в конец "<НазваниеИсходногоОбъекта>_CopyOf" и объединяю копии объектов в проект (типа "AllCopyOfs", чтобы потом легко было удалять их всех сразу).

Недавно мне пришлось таким "необратимым" образом экспериментировать с формой InventJournalTable ("Складские журналы"). Быстро был создан клон InventJournalTable_CopyOf. Однако нажатие на кнопку "Строки" приводило к открытию формы, конечно же, не "<CтрокиЖурнала>_CopyOf".

В зависимости от типа складского журнала, запись которого активна в данный момент в гриде формы InventJournalTable, по нажатию кнопки "Строки" открывается соответствующая форма. Меня интересовали журналы переноса, для отображения строк которых используется форма InventJournalTransfer (о чем я и прочитал в поле "Название формы" после щелчка по пункту контекстного меню "Настройка").

Дублированием был создан клон и этой формы - InventJournalTransfer_CopyOf. Но наивно было бы полагать, что при нажатии на "Строки" откроется именно она. Конечно, этого не случилось - "Настройка" по-прежнему показывала "InventJournalTransfer" безо всяких "CopyOf".

Представляя, где нужно еще "подкрутить", я бодро "зашагал" в design формы журнала InventJournalTable_CopyOf и нашёл соответствующую кнопку MenuItemButton: lines. Свойство кнопки "MenuItemName" гласило: InventJournalTransLossProfit (MenuItemType = Display). Быстро найдя с помощью панели Sidax указанный пункт меню, я прочитал в свойствах: Class = Form, Object = InventJournalLossProfit (т.е. никак не нужный мне InventJournalTransfer). Цепь замкнулась, моста не было...

Долго дебажить код я не стал, но в процессе понял, что на досуге надо поизучать классы JournalFormTable и InventJournalFormTable. В них срабатывает логика, которая к моменту щелчка по кнопке "Строки" подставляет в свойство "MenuItemName" нужный в данный момент пункт меню, запускающий нужную форму. Сохраненный в форме InventJournalTable дефолтный пункт меню InventJournalTransLossProfit ни на что не влияет (всё получается и с пустым значением свойства MenuItemName) и лишь сбивает с толку.

Как в итоге далее я добился необходимого мне поведения задействованных объектов:
1. Прочитал пункт меню кнопки lines, перекрыв для нее метод clicked (в форме InventJournalTable_CopyOf):
X++:
void clicked()
{;
    box::info(this.menuItemName()); // InventJournalTransTransfer
    super();
}
2. Создал копию выявленного пункта меню: InventJournalTransTransfer_CopyOf.
3. Создал копию формы, которую прочитал в свойствах этого пункта меню: InventJournalTransfer_CopyOf.
4. Прописал копию формы в копию пункта меню.
5. Окончательно придал методу clicked следующий "обманный" вид:
X++:
void clicked()
{
    this.menuItemName('InventJournalTransTransfer_CopyOf');
    super();
}
Теги
excel, rls, полезное, blog, axapta

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
dax-lessons: Generate XML Documentation Files for a project - DAX 2009 Blog bot DAX Blogs 0 08.08.2008 19:06
dax-lessons: Active directory in Axapta Blog bot DAX Blogs 0 27.08.2007 23:00
Kashperuk Ivan: AxPaint - make your DAX look cool :) Blog bot DAX Blogs 0 26.06.2007 21:00
Kashperuk Ivan: (DAX 3.0) SysExportDialog form extension Blog bot DAX Blogs 1 15.05.2007 19:16
Kashperuk Ivan: Two very useful projects for DAX Blog bot DAX Blogs 0 20.04.2007 01:14

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 19:57.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.