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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 04.05.2010, 17:23   #1  
Roman N. Krivov is offline
Roman N. Krivov
Участник
 
25 / 11 (1) +
Регистрация: 04.05.2010
Адрес: Мир, Россия, Московская область
Вывод в Excel через Array
Доброго времени суток.

Задача: Создать отчет в Excel с табличной частью из шаблона
Решение: Есть в отчете табличная область, например, A19:CG35 (N столбцов и M строк). Я заполняю массив Array N x M раз.
X++:
protected boolean fillData()
{
    . . .
    ;
    reportRows = 0;

    . . .

        while select returnLine
            where returnLine.JournalId                  == returnTable.JournalId
        {
             inventTable = InventTable::find(returnLine.ItemId);
             custInvoiceJour = CustInvoiceJour::findFrominvoiceId(returnLine.InvoiceId);
             custInvoiceTrans = CustInvoiceTrans::findRecId(returnLine.CustInvoiceTransRecId);

            tableRows.value(tableRows.lastIndex() + 1, strfmt("%1", returnLine.ItemId));
            tableRows.value(tableRows.lastIndex() + 1, strfmt("%1", inventTable.ItemName));
            tableRows.value(tableRows.lastIndex() + 1, strfmt("%1", inventTable.ItemArticle));
            tableRows.value(tableRows.lastIndex() + 1, strfmt("%1", custInvoiceJour.InvoiceExternalId));
            tableRows.value(tableRows.lastIndex() + 1, strfmt("%1", custInvoiceJour.InvoiceDate));

            tableRows.value(tableRows.lastIndex() + 1, strfmt("%1", custInvoiceTrans.Qty));
            tableRows.value(tableRows.lastIndex() + 1, strfmt("%1", custInvoiceTrans.SalesPrice));
            tableRows.value(tableRows.lastIndex() + 1, strfmt("%1", custInvoiceTrans.amountDiscAmountInclTax()));
            tableRows.value(tableRows.lastIndex() + 1, "");
            tableRows.value(tableRows.lastIndex() + 1, "");
            tableRows.value(tableRows.lastIndex() + 1, strfmt("%1", returnLine.QtyDocument));
            tableRows.value(tableRows.lastIndex() + 1, strfmt("%1", returnLine.SummDoc));
            tableRows.value(tableRows.lastIndex() + 1, "");
            tableRows.value(tableRows.lastIndex() + 1, "");
            tableRows.value(tableRows.lastIndex() + 1, "");
            reportRows++;
        }

        return (reportRows > 0) ? true : false;
    }
    return false;
}
Далее вывожу данные массив в указанную область:
X++:
protected void fillTable()
{
    Bookmark    bookmark;
    ;

    bookmark = strfmt("%1%2:%3%4",
                    #FirstTableCol,
                    this.firstTableRow(),
                    #LastTableCol,
                    this.firstTableRow() + this.numOfRows() - 1);

    this.insertValue(bookmark, ExcelReportFromTemplate::array2variant(tableRows), 1);

}
В результате у меня первая строка дублируется M раз. ЧЯДНТ? Массив сформирован правильно. В нём строки не дублируются.

Microsoft Business Solution Axapta 3.0 Build #1951.3730/514-193 SP3/OP023071
Старый 04.05.2010, 18:14   #2  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Код статики ExcelReportFromTemplate::array2variant - в студию!

Хотя я более, чем уверен, что это из-за того, что нельзя передавать двумерные вариантные массивы. Это ограничение Аксапты. Вероятно, придётся в методе fillTable вводить цикл и вставлять букмарками, адреса которых лежат в пределах одной строки Excel, а не сразу всем прямоугольным диапазоном как сейчас.
Старый 04.05.2010, 23:26   #3  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Помнится, вот здесь был пример импорта из двумерного массива (SafeArray), возможно, по аналогии можно реализовать и экспорт, хотя мне лично больше нравится экспорт в Excel табличных частей отчетов через ADO
Старый 05.05.2010, 08:43   #4  
Roman N. Krivov is offline
Roman N. Krivov
Участник
 
25 / 11 (1) +
Регистрация: 04.05.2010
Адрес: Мир, Россия, Московская область
Ты вчерась просил ковер? Ну так я его припер!
ExcelReportFromTemplate::array2variant
X++:
static ComVariant array2variant(Array _array)
{
    Integer    idx;
    array arr = new array (types::class);
    ;

    if (_array)
    {
        for (idx=1; idx <= _array.lastIndex(); idx++)
        {
            arr.value(idx, ExcelReportFromTemplate::createVariantFromValue(_array.value(idx)));
        }
        return ComVariant::createFromArray(arr);
    }

    return ComVariant::createNoValue();
}
ExcelReportFromTemplate::createVariantFromValue
X++:
private static ComVariant createVariantFromValue(anytype _value)
{
    str     value;
    ;
    switch (typeof(_value))
    {
        case Types::String      :
        case Types::RString     :
        case Types::VarString   :
            return ComVariant::createFromStr(_value);

        case Types::Enum        :
            return ComVariant::createFromStr(enum2Value(_value));

        case Types::Integer     :
            return ComVariant::createFromInt(_value);

        case Types::Real        :
            return ComVariant::createFromReal(_value);

        case Types::Date        :
        case Types::DateTime    :
            return ComVariant::createFromDate(_value);

        case Types::Real     :
            return ComVariant::createFromReal(_value);

    }
    return ComVariant::createNoValue();
}

Последний раз редактировалось Roman N. Krivov; 05.05.2010 в 08:46.
Старый 05.05.2010, 08:45   #5  
Roman N. Krivov is offline
Roman N. Krivov
Участник
 
25 / 11 (1) +
Регистрация: 04.05.2010
Адрес: Мир, Россия, Московская область
Цитата:
Сообщение от gl00mie Посмотреть сообщение
Помнится, вот здесь был пример импорта из двумерного массива (SafeArray), возможно, по аналогии можно реализовать и экспорт, хотя мне лично больше нравится экспорт в Excel табличных частей отчетов через ADO
Конечно, вариант ADO хорош, но на небольших данных массив лучше. А данный вариант точно работает, так как видел несколько отчетов по такому же принципу. Вот только пример не сохранился
Старый 05.05.2010, 08:49   #6  
Roman N. Krivov is offline
Roman N. Krivov
Участник
 
25 / 11 (1) +
Регистрация: 04.05.2010
Адрес: Мир, Россия, Московская область
Есть еще вариант сохранить Excel'евский шаблон как XML файл, его редактировать и открывать как уже готовый. Вот только как выделить оттуда табличную часть и добавить нужное количество строк с разбивкой по страницам - данная задача без конкретного решения.
Старый 05.05.2010, 10:36   #7  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,652 / 1158 (42) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Цитата:
Сообщение от Roman N. Krivov Посмотреть сообщение
Конечно, вариант ADO хорош, но на небольших данных массив лучше.
Для небольших данных лучше прямая вставка в каждую ячейку. Поштучно.

Вариант с ADO - это, по сути, тот же массив, но выполненный другими средствами. Просто вместо массива заполняется RecordSet. Разный объект-получатель. Поэтому совершенно без разницы, что заполнять

Если Вы не в курсе, как это делается, то в среде Axapta создается объект RecordSet и наполняется, а потом заполненный RecordSet просто передается в Excel. Технология совершенно такая же, как и при использовании массивов, но нет проблем с неадекватностью форматов хранения.

Цитата:
Сообщение от Roman N. Krivov Посмотреть сообщение
А данный вариант точно работает, так как видел несколько отчетов по такому же принципу. Вот только пример не сохранился
Скорее всего, там использовался одномерный массив. В примере по ссылке четко об этом говорится. Да и заполнение Excel на это явно указывает. Я сильно сомневаюсь, что получится адекватно сформировать структуру двумерного массива Excel.

Если уж Вы перебираете варианты, то есть еще вариант вставки через буфер обмена. Его основной недостаток - слабая управляемость этого самого буфера обмена и возможность его разрушения/изменения в процессе вставки.
За это сообщение автора поблагодарили: Gustav (2).
Старый 05.05.2010, 10:39   #8  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Roman N. Krivov, спасибо за демонстрацию кода. Примерно это я и ожидал увидеть.
Цитата:
Сообщение от Roman N. Krivov Посмотреть сообщение
Конечно, вариант ADO хорош, но на небольших данных массив лучше.
Чем лучше? Вариант ADO, например, нормально разбирается с типами данных столбцов, а Вы у себя всё в текст конвертите. Уже немножко скованно. По скорости лучше? Но какое расхождение можно поймать при вставке всего 17 строк?
Цитата:
Сообщение от Roman N. Krivov Посмотреть сообщение
А данный вариант точно работает, так как видел несколько отчетов по такому же принципу. Вот только пример не сохранился
Уверены, что там был не построчный вывод?
Цитата:
Сообщение от Roman N. Krivov Посмотреть сообщение
Есть еще вариант сохранить Excel'евский шаблон как XML файл, его редактировать и открывать как уже готовый.
XLT, наверное, имеете в виду - eXceL Template.
Цитата:
Сообщение от Roman N. Krivov Посмотреть сообщение
Вот только как выделить оттуда табличную часть и добавить нужное количество строк с разбивкой по страницам
Заготовьте в шаблоне табличную часть с 2-мя строками в диапазоне A19:CG20. Поименуйте этот диапазон в шаблоне, например, как "TablePart" и далее в своем коде обращайтесь к нему через этот "букмарк", т.е. Range("TablePart") вместо Range("A19:CG20"). Далее вставляйте перед второй строкой нужное кол-во строк. Допустим, надо вам всего 17 строк. 2 уже есть, значит надо вставить еще 15 перед исходной 2-й. После их вставки ваш Range("TablePart") автоматически будет ссылаться уже на Range("A19:CG35").
Старый 05.05.2010, 11:13   #9  
Roman N. Krivov is offline
Roman N. Krivov
Участник
 
25 / 11 (1) +
Регистрация: 04.05.2010
Адрес: Мир, Россия, Московская область
Цитата:
Сообщение от Владимир Максимов Посмотреть сообщение
Скорее всего, там использовался одномерный массив. В примере по ссылке четко об этом говорится. Да и заполнение Excel на это явно указывает. Я сильно сомневаюсь, что получится адекватно сформировать структуру двумерного массива Excel.
Формируется и вставляется именно одномерный массив размер N x M элементов

Цитата:
Сообщение от Gustav Посмотреть сообщение
Уверены, что там был не построчный вывод?
Уверен. Там одномерный массив вставлялся целиком.

Цитата:
Сообщение от Gustav Посмотреть сообщение
XLT, наверное, имеете в виду - eXceL Template.
Нет, именно XML (Таблица XML)

Цитата:
Сообщение от Gustav Посмотреть сообщение
Вариант ADO, например, нормально разбирается с типами данных столбцов
Вот как раз с данной проблемой и столкнулся. У меня число преобразуется в дату. В качестве типа поля в ADORecordSet использую #adDouble
X++:
#define.adDouble            (  5)
Но при вставки в Excel в ячейке получается дата, хотя и в формате ячейки указано формат "Число"

Последний раз редактировалось Roman N. Krivov; 05.05.2010 в 11:15.
Старый 05.05.2010, 12:30   #10  
AndyD is offline
AndyD
Участник
КОРУС Консалтинг
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
 
2,560 / 2476 (88) +++++++++
Регистрация: 20.08.2005
Цитата:
Сообщение от gl00mie Посмотреть сообщение
Помнится, вот здесь был пример импорта из двумерного массива (SafeArray), возможно, по аналогии можно реализовать и экспорт, хотя мне лично больше нравится экспорт в Excel табличных частей отчетов через ADO
Там выше есть и пример экспорта, правда, с помощью COM-объекта
__________________
Axapta v.3.0 sp5 kr2
Старый 05.05.2010, 15:00   #11  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Цитата:
Сообщение от Roman N. Krivov Посмотреть сообщение
Есть в отчете табличная область, например, A19:CG35 (N столбцов и M строк). Я заполняю массив Array N x M раз.
Цитата:
Сообщение от Roman N. Krivov Посмотреть сообщение
Формируется и вставляется именно одномерный массив размер N x M элементов
Я, кажется, понял, в чем подвох - вы, видимо, заложились на то, что Excel каким-то образом сам "завернет" ваш одномерный массив по достижении границы выделенной области и продолжит заполнять эту область со следующей строки (к слову, непонятно, почему он должен заполнять ее по строкам, а не, к примеру, по столбцам). Так вот, по-моему, Excel так делать не умеет - поэтому он и дублирует данные первых N элементов массива во всех строках. Так что придется вам либо заполнять данные отдельными строками/столбцами, работая с одномерным массивом, либо передавать Excel'ю двумерный массив.
Старый 05.05.2010, 15:50   #12  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
небольшой оффтопик
Цитата:
Сообщение от gl00mie Посмотреть сообщение
(к слову, непонятно, почему он должен заполнять ее по строкам, а не, к примеру, по столбцам)
Excel в душЕ "по строкам" любит больше
Я как раз тут намедни демонстрировал одномерную индексацию ячеек внутри диапазона: Excel диапазон ячеек
Старый 06.05.2010, 08:52   #13  
Roman N. Krivov is offline
Roman N. Krivov
Участник
 
25 / 11 (1) +
Регистрация: 04.05.2010
Адрес: Мир, Россия, Московская область
Цитата:
Сообщение от Gustav Посмотреть сообщение
Excel в душЕ "по строкам" любит больше
Я как раз тут намедни демонстрировал одномерную индексацию ячеек внутри диапазона: Excel диапазон ячеек
Вот именно, исходя из твоего описания нумерации ячеек Range(...).Cells.Item(...)
Код:
(1) (2) (3)
(4) (5) (6)
(7) (8) (9)
и должны заполняться ячейки из одномерного массива в 9 элементов. Т.е.
Код:
for (idx = 1; idx <= 9; idx++) {
    Range("B2:D4").Cells.Item(idx) = ItemsArray[idx];
}
А на деле получается, что все строки данной матрицы равны первой, т.е.:
Код:
Range("B2:D4").Cells.Item(1) = ItemsArray[1];
Range("B2:D4").Cells.Item(2) = ItemsArray[2];
Range("B2:D4").Cells.Item(3) = ItemsArray[3];
Range("B2:D4").Cells.Item(4) = ItemsArray[1];
Range("B2:D4").Cells.Item(5) = ItemsArray[2];
Range("B2:D4").Cells.Item(6) = ItemsArray[3];
Range("B2:D4").Cells.Item(7) = ItemsArray[1];
Range("B2:D4").Cells.Item(8) = ItemsArray[2];
Range("B2:D4").Cells.Item(9) = ItemsArray[3];
Вопрос: почему?

Последний раз редактировалось Roman N. Krivov; 06.05.2010 в 08:56.
Старый 06.05.2010, 11:09   #14  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,652 / 1158 (42) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Цитата:
Сообщение от Roman N. Krivov Посмотреть сообщение
Вот как раз с данной проблемой и столкнулся. У меня число преобразуется в дату. В качестве типа поля в ADORecordSet использую #adDouble
X++:
#define.adDouble            (  5)
Но при вставки в Excel в ячейке получается дата, хотя и в формате ячейки указано формат "Число"
Такой тестовый пример нормально отображает числа и даты?

X++:
static void ADORecordSet2Excel(Args _args)
{
    Com comRecordSet;
    Com comFields;
    Com comField;

    ComExcelDocument_RU excel;
    Com                 comRange;
    ;

    comRecordSet = new COM('ADODB.Recordset');
    // формируем структуру Recordset
    comFields = comRecordSet.Fields();
    comFields.Append("FieldStr",    COMVariantType::VT_BSTR);
    comFields.Append("FieldReal",   COMVariantType::VT_R8);
    comFields.Append("FieldDate" ,  COMVariantType::VT_DATE);

    // Открываем RecordSet для заполнения
    comRecordSet.open();

    // Формируем первую строку
    comRecordSet.AddNew();
    comField = comFields.Item("FieldStr");
    comField.value(comVariant::createFromStr("Первая строка"));

    comField = comFields.Item("FieldReal");
    comField.value(comVariant::createFromReal(123.456));

    comField = comFields.Item("FieldDate");
    comField.value(comVariant::createFromDate(systemDateGet()));

    // Формируем вторую строку
    comRecordSet.AddNew();
    comField = comFields.Item("FieldStr");
    comField.value(comVariant::createFromStr("Вторая строка"));

    comField = comFields.Item("FieldReal");
    comField.value(comVariant::createFromReal(789.01));

    comField = comFields.Item("FieldDate");
    comField.value(comVariant::createFromDate(systemDateGet()+10));

    // Открываем Excel с пустым листом и сразу делаем его видимым
    excel = new ComExcelDocument_RU();
    excel.newFile("",true);

    // Определяем ячейку, откуда будет осуществляться вывод
    comRange = excel.findRange("A1");

    // Выводим данные
    comRange.CopyFromRecordset(comRecordSet);
}
Старый 06.05.2010, 11:19   #15  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от Roman N. Krivov Посмотреть сообщение
А на деле получается, что все строки данной матрицы равны первой, т.е.:
Вопрос: почему?
Потому что такая особенность этого дела - желает Excel элементы Array в строку записывать и всё тут.
Вот маленький джобик, по-моему, достаточно наглядный:
X++:
    COM     rng = sysExcelApplication::construct().workbooks().add().worksheets().itemFromNum(1).cells().range('A1:C3').comObject();
    Array   arr = new Array(Types::String);
    ;
    arr.value(1, '1'); arr.value(2, '2'); arr.value(3, '3');
    arr.value(4, '4'); arr.value(5, '5'); arr.value(6, '6');
    arr.value(7, '7'); arr.value(8, '8'); arr.value(9, '9');

    rng.value2(arr); // вставка в диапазон A1:C3 - размер 3 х 3

    rng = rng.Offset(4,0);
    rng = rng.Resize(1,9);
    rng.value2(arr); // вставка в диапазон A5:I5 - размер 1 х 9 (одна строка)

    rng = rng.Offset(3,0);
    rng = rng.Resize(1,12);
    rng.value2(arr); // вставка в диапазон A9:L9 - размер 1 х 12 (одна строка)

    COM::createFromObject(rng.Application()).Visible(true);
Старый 06.05.2010, 12:02   #16  
Roman N. Krivov is offline
Roman N. Krivov
Участник
 
25 / 11 (1) +
Регистрация: 04.05.2010
Адрес: Мир, Россия, Московская область
Цитата:
Сообщение от Gustav Посмотреть сообщение
Потому что такая особенность этого дела - желает Excel элементы Array в строку записывать и всё тут.
Вот маленький джобик, по-моему, достаточно наглядный:
Джобик хорош.
А вот результат его работы: (см. аттач)

Решил заполнять отчёт через AdoRecordSet.
Миниатюры
Нажмите на изображение для увеличения
Название: Result.PNG
Просмотров: 476
Размер:	30.5 Кб
ID:	5805  

Последний раз редактировалось Roman N. Krivov; 06.05.2010 в 13:07.
Теги
ado, array, excel, recordset

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Вспомогательный класс для импорта из Excel через ADO gl00mie DAX: База знаний и проекты 80 10.04.2017 10:55
Вывод всех меток из заданного уровня на заданных языках в Excel wojzeh DAX: Программирование 0 19.03.2010 23:45
Работа с Excel через COM и ошибка 0x800A03EC (Range.AutoFilter) gl00mie DAX: Программирование 15 30.03.2007 18:37
Вывод в Excel в формате XML и ширина колонок gl00mie DAX: Программирование 1 30.10.2006 10:45
Вывод в Excel в определнный Worksheet... soin DAX: Программирование 1 22.10.2004 13:53

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

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

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