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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 14.08.2008, 16:59   #21  
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   #22  
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();
}
Старый 09.09.2008, 11:40   #23  
AlGol is offline
AlGol
Участник
 
277 / 93 (4) ++++
Регистрация: 24.12.2001
Адрес: Тверь.
Цитата:
Сообщение от Gustav Посмотреть сообщение
получение списка полей таблицы
В версии 4.0 получение списка полей таблицы есть в стандартной функциональности.
- В форме с нужной записью, заходим в паспорт записи (правый клик/ паспорт записи).
- нажимаем кнопку Сценарий.

Остается только вставить текст в нужное место.

2Gustav
При вставке получаем следующий код:
X++:
    CustTable.AccountNum = "000001_039";
    CustTable.Name = "ООО """;
    CustTable.Address = "180000, Светлово, ул. Пермяковская, д.21";
    CustTable.Phone = "45254565";
    CustTable.TeleFax = "";
    CustTable.InvoiceAccount = "";
...
    CustTable.MultiDiscPct = 0.00;
    CustTable.EndDiscPct = 0.00;
    CustTable.RContractDimension = NoYes::Yes;
    CustTable.insert();

Последний раз редактировалось AlGol; 22.09.2008 в 11:40.
За это сообщение автора поблагодарили: Lemming (1), Gustav (3).
Старый 06.10.2008, 17:01   #24  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Визуальная имитация раннего связывания COM-объектов
Известно, что с COM-объектами Аксапта работает через позднее связывание. Это означает, что при портировании в Х++, например, кода VBA из Excel, нам придется расписать отдельными операторами X++ все точки, разделяющие объекты в операторах VBA.

Допустим, у нас есть такой код VBA, созданный макрорекордером и нами слегка подправленный, который в Excel записывает значение 123 в ячейку C2 активного листа активной книги, затем делает шрифт этой ячейки жирным ("болдит") и, наконец, заливает ячейку светложелтым цветом:

Код:
 
Sub Macro1()
    Range("C2").FormulaR1C1 = "123"
    Range("C2").Font.Bold = True
    Range("C2").Interior.ColorIndex = 36 'Light Yellow  
End Sub
Чтобы воспроизвести его в Аксапте, нужно будет (помимо полного квалифицирования объекта Range от объекта Application) расписать все точки между объектами и методами. Получится примерно так:
X++:
{
    COM application;
    COM workbook = SysExcelApplication::construct().workbooks().add().comObject();
    COM range;
    COM font;
    COM interior;
    ;

    application = workbook.Parent();
    application.Visible(true);

    range = application.Range('C2');

    range.FormulaR1C1(123);

    font = range.Font();
    font.Bold(true);

    interior = range.Interior();
    interior.ColorIndex(36);
}
Нам приходится выделять специальные переменные для промежуточных объектов - font и interior. Хотя наша цель лишь заболдить и закрасить ячейку, и больше эти объекты нам не понадобятся.

Вместо нескольких таких переменных, истинный объектный смысл каждой из которых нас в данном случае мало интересует, можно использовать одну фиктивную. Обычно я называю ее comTemp - для акцентирования ее "временности" и "промежуточности". С такой "болван"-переменной можно написать операции следующим образом:
X++:
    COM comTemp;
    ..........

    comTemp = range.Font();
    comTemp . Bold(true);

    comTemp = range.Interior();
    comTemp . ColorIndex(36);
Часто в случае только двух точек можно исхитриться записать их в одной строке вообще без промежуточной переменной:
X++:
    COM::createFromObject( range.Font()).Bold( true );
    COM::createFromObject( range.Interior()).ColorIndex(36);
Если не обращать внимания на устрашающие начала операторов с вызова статического метода COM::createFromObject, то получается почти как на VBA. Но, к сожалению, таким двоеточием всё и ограничивается. Использовать более двух точек не получится даже вложенными друг в друга вызовами COM::createFromObject или COM::createFromVariant (последний иногда бывает нужен для выделения COM-объекта из свойства Item, например, при обращении к одной ячейке листа через семейство Cells). Попытка сконструировать такое вложение вызовет ошибку компиляции.

Таким образом, для реализации следующего "многоточечного" оператора VBA:
Код:
 
Application.ActiveWorkbook.Worksheets.Item(1).Cells.Item(2,3).Font.Bold = True
в X++ нам потребуются примерно такие строчки:
X++:
    comTemp = application.ActiveWorkbook();
    comTemp = comTemp.Worksheets();
    comTemp = comTemp.Item(1);
    comTemp = comTemp.Cells();
    comTemp = COM::createFromVariant(comTemp.Item(2,3));
    comTemp = comTemp.Font();
    comTemp.Bold(true);
И чисто визуально это иногда удручает.

В целях "борьбы" с описанным явлением я наваял совсем небольшой метод, с использованием функции runbuf, который позволяет существенно сократить количество строк код и придать вызову внешнюю похожесть на оператор VBA:
X++:
{
    COM application;
    COM workbook = SysExcelApplication::construct().workbooks().add().comObject();
    COM range;
    COM comTemp;
    ;

    application = workbook.Parent();
    application.Visible(true);

    range = application.Range('C2');

    range.FormulaR1C1(123);

    comTemp = comEarlyBindingImitation( application,
    'ActiveWorkbook()','Worksheets()','Item(1)','Cells()','~Item(2,3)','Font()');
    comTemp.Bold(true);
}
Текст статического метода comEarlyBindingImitation - для помещения в Global или аналогичный по смыслу класс:
X++:
static COM comEarlyBindingImitation( COM _com0,
                                     str _com1 = '',
                                     str _com2 = '',
                                     str _com3 = '',
                                     str _com4 = '',
                                     str _com5 = '',
                                     str _com6 = '',
                                     str _com7 = '',
                                     str _com8 = '')
{
    str myjob;

    str getSourcePart(str _com = '')
    {
        if (_com)
            if (subStr(_com,1,1) != '~')
                return strFmt('c=c.%1; ', _com);
            else
                return strFmt('c=COM::createFromVariant(c.%1); ', 
                       subStr(_com,2,strLen(_com)-1));
        else
            return '';
    }
;
    myjob = 'COM comEaBIm(COM _com0) {COM c;; c = _com0; ';
    myjob += getSourcePart(_com1);
    myjob += getSourcePart(_com2);
    myjob += getSourcePart(_com3);
    myjob += getSourcePart(_com4);
    myjob += getSourcePart(_com5);
    myjob += getSourcePart(_com6);
    myjob += getSourcePart(_com7);
    myjob += getSourcePart(_com8);
    myjob += 'return c; }';

    return runbuf(myjob, _com0);
}
Не обошлось без минимального "встроенного языка" метода Тильда в строке ~Item(2,3) означает, что к Item(2,3) надо применить COM::createFromVariant. Знаю об этом из личного опыта по работе с объектами Range('...').Item(i, j) и Cells().Item(i, j).

Интересно, что терминальные свойства (типа Bold или ColorIndex) можно тоже включить в это "раннее связывание" и тогда вообще обойтись одним оператором для одной операции. Следующие два оператора работают как надо:
X++:
    comEarlyBindingImitation(application,
        'Range("C2")','Font()','Bold(true)');

    comEarlyBindingImitation(application,
        'Range("C2")','Interior()','ColorIndex(36)');
За это сообщение автора поблагодарили: kashperuk (3), aidsua (1).
Старый 06.10.2008, 17:24   #25  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
интересно. Но все равно некрасиво.
Если бы я делал что-то подобное, я бы делал com-wrapper.

То есть у меня был бы класс, назывался Excel, к примеру.
Внутри класса при инициализации создавался бы объект Excel (COM), который бы возвращался из большинства методов. Поэтому код можно было б записать аналогично VBA (только через вызовы методов):

Excel.ActiveWorkbook().Worksheets().Item(1).Cells().Item(2,3).Font().Bold(true);

Может, как идею, попробуешь это реализовать? Хотя бы базовые методы для начала?
Старый 06.10.2008, 17:29   #26  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,158 / 1286 (47) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Тем более есть пример в стандартном приложении, это классы: SysExcel*
Старый 06.10.2008, 17:49   #27  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
Цитата:
Сообщение от Raven Melancholic Посмотреть сообщение
Тем более есть пример в стандартном приложении, это классы: SysExcel*
Да, именно с них я и спер идею
Меня в этих классах просто всегда смущало их большое кол-во.
Поэтому и предложил все это в один класс запихнуть
Старый 06.10.2008, 18:15   #28  
belugin is offline
belugin
Участник
Аватар для belugin
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,622 / 2922 (107) +++++++++
Регистрация: 16.01.2004
Записей в блоге: 5
много классов это хорошо, а не плохо. Плохо, когда range начинает обладать методами документа
Старый 06.10.2008, 18:26   #29  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
Цитата:
Сообщение от belugin Посмотреть сообщение
много классов это хорошо, а не плохо. Плохо, когда range начинает обладать методами документа
Идею с запихиванием всего в один класс, к слову, спер я у тебя, Максим
У тебя был проектик такой - QueryBuilder какой-то, который схожую логику представлял
Старый 06.10.2008, 19:05   #30  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от kashperuk Посмотреть сообщение
Поэтому код можно было б записать аналогично VBA (только через вызовы методов):

Excel.ActiveWorkbook().Worksheets().Item(1).Cells().Item(2,3).Font().Bold(true);

Может, как идею, попробуешь это реализовать? Хотя бы базовые методы для начала?
Цитата:
Сообщение от Raven Melancholic Посмотреть сообщение
Тем более есть пример в стандартном приложении, это классы: SysExcel*
Не, не уговаривайте, не буду этим заниматься
Хотя бы вот по таким причинам:

1. Конкретно Excel я взял только для примера, поскольку именно его COM-модель довольно хорошо знакома многим разработчикам и в нем можно соорудить впечатляющий многоточечный вызов. Но есть еще масса других полезных приложений: Access, Word, Outlook и др.

2. Конкретно для Excel это уже действительно есть и именно SysExcel*. При желании можно по мере востребования добавлять недостающие методы прямо в эти классы.

3. А как узнать, что есть еще что-то, чего нам не хватает, но что хотелось бы чтоб было? Правильно, заглянуть в сам Excel.

4. А если мы все равно туда заглядываем, так не проще ли тогда обработку данных создавать и отлаживать в самом Excel на VBA, а затем переводить на X++? "За" - макрорекордер (!), мощнейший хелп и IntelliSence, который в Аксапте мы по-любому не переплюнем - в Аксапте из списка будут выпадать лишь методы, освоенные нами или нашими сподвижниками к данному моменту развития, а в Excel - все имеющиеся, среди которых можно обнаружить для себя что-то новенькое и интересненькое.

5. Честно говоря, мне самому обертки не очень нужны, мне вполне хватает чистого COM. Соответственно, не очень интересно заниматься и созданием таких оберток.
Старый 06.10.2008, 22:08   #31  
belugin is offline
belugin
Участник
Аватар для belugin
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,622 / 2922 (107) +++++++++
Регистрация: 16.01.2004
Записей в блоге: 5
Цитата:
Сообщение от kashperuk Посмотреть сообщение
Идею с запихиванием всего в один класс, к слову, спер я у тебя, Максим
У тебя был проектик такой - QueryBuilder какой-то, который схожую логику представлял
Там немножко по другому - класс представаляет Expression Builder - и там два класса, насколько помню Он предназначен для создания обхектов, а не для структурированного доступа к ним.
Старый 13.11.2008, 13:35   #32  
AndyD is offline
AndyD
Участник
КОРУС Консалтинг
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
 
2,560 / 2476 (88) +++++++++
Регистрация: 20.08.2005
Цитата:
Сообщение от Gustav Посмотреть сообщение
Так абсолютно не допёр смысл команды "Выделить область" (ALT+A). Что она делает-то и как увидеть ее эффект?
Только сейчас обратил внимание

Если еще не поздно. Если находишься в режиме блочного выделение, то с помощью ALT+A возвращаешься к нормальному вылению
__________________
Axapta v.3.0 sp5 kr2
За это сообщение автора поблагодарили: Gustav (3).
Старый 13.11.2008, 18:59   #33  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Да, точно! Итак, если щелкнуть мышкой в одном месте, потом с Shift в другом, то получится обычное выделение. Следующее нажатие ALT+O превратит его в блочное. Следующее нажатие ALT+A вернет к обычному. И далее можно нажимать попеременно ALT+O / ALT+A, до конца рабочего дня, раздумывая, какое же выделение тебе больше по душе

Но если подряд дважды нажать ALT+O или дважды ALT+A, то "карета превратится в тыкву" и всякое выделение пропадет.
Старый 13.11.2008, 22:12   #34  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,273 / 3466 (122) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
Цитата:
Сообщение от kashperuk Посмотреть сообщение
Excel.ActiveWorkbook().Worksheets().Item(1).Cells().Item(2,3).Font().Bold(true);
Может, как идею, попробуешь это реализовать? Хотя бы базовые методы для начала?
Эта идея хорошо реализуется через интерфейсы (создаем класс и пишем в classDeclaration: interface Workbook). Только все эти объекты создавать надо - а так - было бы конечно удобно...
__________________
Возможно сделать все. Вопрос времени
Старый 04.12.2008, 16:02   #35  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Критерии запроса: ловушка перечисления через запятую
Я был не очень силен при работе с отрицаниями (!...) в критериях запроса, так как до недавнего времени их практически не использовал. И хотя мне казалось, что там не должно быть ничего сложного, при первой же близкой встрече с ними я попал в ловушку.

Я представлял себе, чисто визуально, бубня что-то школьное типа "от перестановки мест слагаемых...", что запятая есть разделитель списка в строке критериев и далее, как ты их не перечисли, результат будет один и тот же. Поэтому для таблицы, содержащей числовое поле (ЧП) и 100 строк со значением этого ЧП от 1 до 100, я написал такой критерий:
PHP код:
!15,!19,11..20 
и ожидал получить в выборке 8 чисел от 11 до 20 без 15 и 19. Каково же было мое удивление, когда в выборке явились все 100 строк таблицы!

Желаемого результата удалось достичь после перестановки компонентов критерия:
PHP код:
11..20,!15,!19 
Объяснения наблюдаемого явления нашлись в "Библиотеке" у mazzy в замечательной статье "Поиск данных" ( http://axapta.mazzy.ru/lib/search/ ). Там есть такая скромная фраза: «Я не обновлял эту статью, поскольку в Microsoft Axapta 3.0 написан хороший хелп по поиску. Нажмите F1 когда указываете критерии в запросе, получите описание. Там все хорошо описано.». Однако, в «минуту жизни трудную» помогла именно статья, а не хелп. Точнее – дополнение к статье Андрея Бобкова:
Цитата:
«3. , (запятая) это символ, позволяющий объединить несколько критериев. Причем, если после запятой идет ! (восклицательный знак - отрицание), то запятая интерепретируется как логическое "И" (по смыслу получается "кроме"), а если нет отрицания - то как логическое "ИЛИ".»
Ознакомившись с этим важным знанием, я расписал свои критерии а-ля SQL и всё достаточно наглядно прояснилось:
PHP код:
!15,!19,11..20 --> (NOT (ЧП=15)) AND (NOT (ЧП=19)) OR (ЧП BETWEEN 11 AND 20)

11..20,!15,!19 --> (ЧП BETWEEN 11 AND 20) AND (NOT (ЧП=15)) AND (NOT (ЧП=19)) 
Здесь помним, что в Аксапте операции AND и OR имеют равные приоритеты (Логические операции в Х++) и выполняются последовательно слева направо. Хотя в данном случае это, возможно, лишнее замечание, так как в первом условии AND и так стоит левее OR (на самом внешнем скобочном уровне).

В хелпе же всё гораздо туманнее. Про запятую написано: "...соответствует нескольким критериям. Введите все критерии, разделив их запятыми." (я и вводил так!). И далее: "Например, "A..D, !C" находит "A", "B" и "D". Справедливости ради надо отметить, что, тем не менее, пример A..D,!C удачен (возможно, сам того не ведая ), так как сначала указан диапазон, а затем исключение.

Возможно, так и надо сформулировать правило для конечного пользователя: если для поля указывается несколько критериев через запятую, то критерии отрицания (исключения) следует указывать в конце строки после всех "неотрицательных" критериев.

Ну, и на сладкое – курьез из ДевГайда (по меню хелп-файла: Creating Queries => Query Basic => Organization of queries; или из MSDN: http://msdn.microsoft.com/en-us/library/bb278121.aspx) с критерием, нашему новоиспеченому правилу не соответствующим:

Цитата:
Ranges

A number of ranges can be defined for each data source. Each range is defined through the Value property. The Value property is a string that describes conditions to be met by the records in the data source. For example, if restrictions are required on which AccountNumbers the query should retrieve, the range defined would be:

"..5000, 5012 .. 5014, 5500, !6500, 6000.."

This line tell the system to retrieve:

* records with AccountNumbers less than or equal to 5000

* records in the range from 5012 to 5014, the record with the AccountNumber equal to 5500,

* records with AccountNumbers above 6000, except for the record with the AccountNumber equal to 6500
Утверждение «except for the record with the AccountNumber equal to 6500» ложно и значение 6500 присутствует в выборке (по уже выясненным выше причинам).


P.S. Интересно, что данный топик из DevGuide перед публикацией на msdn явно подвергался правке: например, заметно переработана таблица спецсимволов, используемых при построении критериев, а перед таблицей - фраза, которой очень не хватает в хелпе и которую я познавал по частям в разное время и из разных источников:
Цитата:
Range expressions on different fields are combined by using the AND expression.
Range expressions on the same field are combined by using the OR expression.
Критерий же примера так пока и не исправлен: ..5000, 5012..5014, 5500, !6500, 6000..


P.S. 14.04.2010. Имеется важная дополнительная информация в теме: Отличия операторов ! и != в фильтрах.

Последний раз редактировалось Gustav; 14.04.2010 в 17:04.
За это сообщение автора поблагодарили: Dron AKA andy (2), Berty Wooster (1), donMigel (1), Logger (4), alex55 (1).
Старый 29.12.2008, 16:50   #36  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Excel: шаблон XLT vs псевдошаблон XLS
История берет свое начало здесь: Создание нескольких экземпляров Excel. Там же указаны некоторые практические шаги по задачам, упоминаемым здесь.

Итак, в очередной раз столкнулся с этой "миной": разработчики (из весьма уважаемой компании, вроде пора бы уж... ан, увы!) использовали файл с расширением XLS (т.е. обычный файл Excel) в качестве шаблона для одного из наших отчетов. По закону подлости "заряд" сработал в напряженную пору конца года, когда этот отчет понадобился многим пользователям и повалились ошибки из-за монопольного захвата шаблонного файла.

Не препарируя код Аксапты, администратор настроил в Excel общий доступ для файла. Захваты прекратились, но появилось ненужное стандартное диалоговое окно с запросом имени сохраняемого файла. При этом по умолчанию имя предлагается с "единичкой" на конце: <ИсходноеИмяФайла>1.xls - т.е. как и в случае создания нового файла от шаблона XLT, за одним исключением - при порождении от XLT не требуется сиюминутного ввода имени нового файла.

Интересно, что сохранять файл в ответ на ненужное окно совсем не обязательно, т.е. можно нажать Cancel - при этом в памяти остается несохраненный файл с именем по умолчанию. Не знаю, считать эту "фичу" Excel "багой" или нет, но, по крайней мере, знать о такой особенности поведения надо. Пользователя же это лишнее ненужное окно, разумеется, раздражает вне зависимости от степени его осведомленности в этих тонкостях. Для наглядной демонстрации того, о чем идет речь, можно выполнить в окне отладки Excel такую команду: Application.Workbooks.Add("<полное имя файла, которому дан общий доступ>.XLS") и всё увидеть своими глазами.


Напрашивающиеся возможные сценарии использования "шаблона" XLS в подобных ситуациях выглядят следующим образом:

1. Невидимое для пользователя открытие шаблонного XLS-файла (командой Workbooks.Open(<имя файла>.xls)), программное копирование из него шаблонного листа в новый файл, закрытие шаблонного XLS-файла, дальнейшая работа с новым файлом. Несмотря на быстрые автоматические операции, все равно возможны конфликты пользователей в борьбе за файл (без общего доступа).

2. Открытие XLS-файла без общего доступа как шаблона (командой Workbooks.Add(<имя файла>.xls)), сохранение под новым именем. В промежутке между открытием и сохранением другие пользователи не могут воспользоваться файлом. Практика показала, что конфликты успевают возникнуть даже при быстром программном сохранении файла под именем, ранее запрошенным у пользователя (как дополнительное неудобство такого программного варианта - обязательность сохранения файла на диске).

3. Открытие XLS-файла с общим доступом как шаблона (командой Workbooks.Add(<имя файла>.xls)), борьба с ненужным окном, сохранение под новым именем. Тем не менее, постоянный свободный доступ других пользователей к файлу.


Эти три сценария приведены для того, чтобы обрисовать примерный алгоритм борьбы с проблемами, возникающими из-за использования файла XLS в качестве шаблона:

* Если вы настроили общий доступ к файлу и проблемы прекратились (пользователи не конфликтуют и лишнее окно не появляется), то, скорее всего, используется сценарий 1 и ваши проблемы на этом закончились (в код лезть не придется).

* Если же после настройки общего доступа появилось лишнее окно, то вы, вероятно, перешли от сценария 2 к сценарию 3. Вам следует при помощи Excel переделать файл XLS в XLT (не просто изменив одну букву в расширении, а сохранить файл как шаблон) и внести необходимые изменения в код X++ (здесь уже просто заменив одно расширение файла на другое).

Наверное, возможны более изощренные сценарии, когда эти правила не спасут или спасут не до конца, но я затрудняюсь вообразить подобные сценарии.


Всего этого можно избежать, если выработать в себе самом и распространять среди других привычку с самого начала работать с нормальными шаблонами - файлами с расширением XLT.

Ну, или работать в своей Аксапте одному в одной сессии - тоже способ избежать блокировок со стороны других пользователей. С Наступающим!
Старый 20.05.2009, 13:23   #37  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Проблемы при создании наследников класса RunBaseBatch
Столкнулся со следующими проблемами при создании наследника RunBaseBatch дублированием (копированием) из другого класса-наследника (работаю с Ax 3.0 SP4):

1) невозможность отобразить вкладку "Пакет" - бездействие метода canGoBatch;
2) невозможность откомпилировать новый класс - ошибка в методе main с сообщением "Нельзя создать объект, поскольку абстрактный метод RunBase.pack() не реализован."

По первой проблеме нашёл рекомендацию здесь: Замена стандартной формы Dialog. Не выводится вкладка Пакет. . Действительно, заработало после того, как создал класс не просто дублированием другого класса, а создал новый класс вручную и перетащил по очереди методы мышкой из исходного класса.

Вторая ситуация описана в теме: Ошибка в "Тренинге по Средствам Разработки" , но явный метод борьбы не указан. Поэтому изложу свой.

Как и из-за чего именно возникает проблема 2 по ходу дублирования (копирования) не заметил, но научился вопроизводить так:

* Контекстной командой Дублировать создадим копию класса Tutorial_RunBaseBatch - CopyOfTutorial_RunBaseBatch.
* Изменим метод main, подогнав его под новый класс:
X++:
static void main(Args args)
{
    CopyOfTutorial_RunbaseBatch    tutorial_RunBase;
;
    tutorial_RunBase = new CopyOfTutorial_RunbaseBatch();

    if (tutorial_RunBase.prompt())
        tutorial_RunBase.run();
}
Всё хорошо, всё компилируется и выполняется.

* Переименуем метод pack, например, в mypack. Компиляция теперь проходит неудачно и указывает на метод main c выдачей упомянутого выше сообщения.
* Возвращаем методу pack его прежнее имя pack. Компиляция в ответ не становится удачной и информирует о той же ошибке.

И вот то, что помогло: комментируем в main строку c new, компиляция - ОК, далее раскомментируем строку и опять компилируем - ОК.

А самый первый раз наугад поборолся с проблемой 2 так: удалил метод main, скомпилировал класс без него и создал метод заново вручную.
Старый 20.05.2009, 13:34   #38  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1234 (44) ++++++++
Регистрация: 11.04.2008
Попробуйте после дублицирования сделать 2 шага
1. Меню: Средства разработки->Объекты приложения->Обновить AOD
2. Компиляция Вашего класса.

Должно помочь.
За это сообщение автора поблагодарили: Gustav (4).
Старый 20.05.2009, 14:36   #39  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от DSPIC Посмотреть сообщение
Попробуйте после дублицирования сделать 2 шага
1. Меню: Средства разработки->Объекты приложения->Обновить AOD
2. Компиляция Вашего класса.

Должно помочь.
Да, спасибо, вроде помогает. Я Вам скажу больше - помогает даже просто одна контекстная команда Компилировать, выполненная на классе в дереве АОТ (!), а не кнопкой F7 в окне редактора, как я обычно делаю. Однако, после выполнения контекстной Компилировать строка с new все равно остается подчеркнутой красной волнистой линией, а main в АОТ индицируется красной точкой. И это мощно сбивает с толку Хотя достаточно теперь выполнить ту же F7 в методе (или еще раз Компилировать в дереве) и таки наступает счастье! Да уж...
Старый 20.05.2009, 15:34   #40  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1234 (44) ++++++++
Регистрация: 11.04.2008
См. также:

Проблемы:
1. DAX40 #if.never в LedgerBondServer_RU (при применении макросов)
2. DAX40 #if.never в LedgerBondServer_RU (при смене родителя класса)
3. Сюда же можно добавить "вашу" проблему. (при дублицировании... наследника?)

Общее решение:
1. Меню: Средства разработки->Объекты приложения->Обновить AOD (Dictionary::aodFlush())
2. Компиляция Вашего класса.

Перекур
Рассуждения на тему: X++ компилируемый или интерпретируемый язык?

Вывод
При странном поведении "компиляции" объектов X++ помогает это общее решение. Т.е. его нужно пробовать в первую очередь. А всё остальное: F7, локальная компиляция, переиндексация имеют условное воздействие
За это сообщение автора поблагодарили: Gustav (2).
Теги
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, время: 01:02.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.