|
12.03.2009, 10:50 | #1 |
Участник
|
Строка в Excel
Всем доброго времени суток! Третий день бьюсь над проблемой экспорта большого количества символов в Excel. Суть проблемы следующая. При формировании отчетов, берется набор серийных номеров (с помощью метода) с группировкой по номенклатуре (серийные номера выводятся в строку через запятую). Длина одного серийного номера-13 символов. При попытке экспорта в Excel информация выводится корректно, но до достижения количества символов строки определенной длины (например, если продано 100 экземпляров одной номенклатуры, по сути должно быть 1300 символов серийных номеров+пробелы+запятые...). Читал топик "Поговорим об ADO". Попытался в коде
X++: int adoTypeToExcel(str _type) { switch (_type) { // используются значения констант перечисления DateTypeEnum из топика TypeProperty (ADO) // (см. справку по ADO в файле ADO210.CHM - можно поискать на своем компе) case 'num' : return 5; // adDouble case 'str' : return 8; // adBSTR case 'date': return 133; // adDBDate case 'memo': return 203; // adLongVarChar } return 203; } прописать данный набор данных.Если указать набор серийных номеров как str - оставляет строки (более 1300 симв) пустыми. Если указать, как мемо выдает ошибку Метод 'Append' в COM-объекте класса 'Fields' возвратил код ошибки 0x800A0BB9 (<неизвестно>), который означает: Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another. Есть мнения? Заранее благодарен! |
|
12.03.2009, 11:17 | #2 |
Moderator
|
А поле рекордсета какой тип имеет? 8 ?
|
|
12.03.2009, 11:19 | #3 |
Участник
|
|
|
12.03.2009, 11:22 | #4 |
Moderator
|
а 8 пробовали?
|
|
12.03.2009, 11:28 | #5 |
Участник
|
Вот полностью код, думаю, что Class Declaration не интересует. Строка под названием "Серийные номера". Если описать ее как str - некоторые строки остаются пустыми, если как мемо, вылетает ошибка, указанная выше (Кстати AX 3.0, SP3, SQL 2005).
X++: void Exec(str Path,SnQueryTableTmp SnQ) { ; xlApp = new COM('Excel.Application'); xlApp.Visible(true); wbks = xlApp.Workbooks(); wbk = wbks.Add(); wkss = wbk.Worksheets(); wks = wkss.Add(); wks = wkss.Item(1); //wks2 = wkss.Item(2); //wks3 = wkss.Item(3); wks.Name('Итог'); //wks2.Name('Суммы по дилерам'); //wks3.Name('Склад'); rng = wks.Range('A1'); //rng2= wks2.Range('A1'); //rng3= wks3.Range('A1'); //==================================Первая вкладка======================= rstAxa = new COM('ADODB.Recordset'); flds = rstAxa.Fields(); flds.Append('Дата закрытия' ,this.adoTypeToExcel('date')); flds.Append('Отгрузочная накладная' ,this.adoTypeToExcel('str')); flds.Append('Заказ' ,this.adoTypeToExcel('str')); flds.Append('Безналичная накладная' ,this.adoTypeToExcel('str')); flds.Append('Менеджер' ,this.adoTypeToExcel('str')); flds.Append('Код клиента' ,this.adoTypeToExcel('str')); flds.Append('Дата отгрузочной накладной' ,this.adoTypeToExcel('date')); flds.Append('Город' ,this.adoTypeToExcel('str')); flds.Append('Краткое наименование' ,this.adoTypeToExcel('str')); flds.Append('Номенклатура' ,this.adoTypeToExcel('str')); flds.Append('Код дет. в спецификации' ,this.adoTypeToExcel('str')); flds.Append('Название' ,this.adoTypeToExcel('str')); flds.Append('Склад' ,this.adoTypeToExcel('str')); flds.Append('Цена ед.' ,this.adoTypeToExcel('num')); flds.Append('Себестоимость' ,this.adoTypeToExcel('num')); flds.Append('Количество' ,this.adoTypeToExcel('num')); flds.Append('Сумма по позиции' ,this.adoTypeToExcel('num')); flds.Append('Прибыль' ,this.adoTypeToExcel('num')); flds.Append('Каталожный номер' ,this.adoTypeToExcel('str')); flds.Append('Серийные номера' ,this.adoTypeToExcel('memo')); flds.Append('REF' ,this.adoTypeToExcel('str')); flds.Append('Квота' ,this.adoTypeToExcel('str')); flds.Append('Конечный клиент' ,this.adoTypeToExcel('str')); flds.Append('Номер партии' ,this.adoTypeToExcel('str')); rstAxa.Open(); // SnQ.setTmpData(SnQ); while select SnQ { rstAxa.AddNew(); // fld = flds.Item('Каталожный номер'); fld.Value(SelloutMsTable.A_PartNumber); fld = flds.Item('Дата закрытия'); fld.Value(SnQ.DeliveryDate); fld = flds.Item('Отгрузочная накладная'); fld.Value(SnQ.PickingListId); fld = flds.Item('Заказ'); fld.Value(SnQ.SalesId); fld = flds.Item('Безналичная накладная'); fld.Value(SnQ.InvoiceIdElect); fld = flds.Item('Менеджер'); fld.Value(SnQ.SalesResponsible); fld = flds.Item('Код клиента'); fld.Value(SnQ.CustAccount); fld = flds.Item('Дата отгрузочной накладной'); fld.Value(SnQ.PickingListDate); fld = flds.Item('Город'); fld.Value(SnQ.City); fld = flds.Item('Краткое наименование'); fld.Value(SnQ.NameAlial); fld = flds.Item('Номенклатура'); fld.value(SnQ.ItemId); fld = flds.Item('Код дет. в спецификации'); fld.Value(SnQ.ChosenPartNumber); fld = flds.Item('Название'); fld.Value(SnQ.ItemName); fld = flds.Item('Склад'); fld.Value(SnQ.InventLocationId); fld = flds.Item('Цена ед.'); fld.Value(SnQ.A_SalesPrice); fld = flds.Item('Себестоимость'); fld.Value(SnQ.SalesCostPrice); fld = flds.Item('Количество'); fld.Value(SnQ.Qty); fld = flds.Item('Сумма по позиции'); fld.Value(SnQ.Amount); fld = flds.Item('Прибыль'); fld.Value(SnQ.SalesProfit); fld = flds.Item('Каталожный номер'); fld.Value(SnQ.A_PartNumber); fld = flds.Item('Серийные номера'); fld.Value(SnQ.findSerialNumbersFromWMS(SnQ)); fld = flds.Item('REF'); fld.Value(SnQ.REF); fld = flds.Item('Квота'); fld.value(SnQ.InventQuotationId); fld = flds.Item('Конечный клиент'); fld.Value(SnQ.EndPartnerCode); fld = flds.Item('Номер партии'); fld.Value(SnQ.InventBatchId); rstAxa.Update(); } iMax = flds.Count() - 1; for (i = 0; i <= iMax; i++) { fld = flds.Item(i); cell = rng.Offset(0,i); cell.Value2(fld.Name()); } cell = rng.Offset(1,0); cell.CopyFromRecordset(rstAxa); rstAxa.close(); actWin = xlApp.ActiveWindow(); // actWin.FreezePanes(true); } |
|
12.03.2009, 11:41 | #6 |
Moderator
|
Следующий код у меня нормально выводит 2400 символов в ячейку:
X++: static void Job148(Args _args) { COM rstAxa; COM flds, fld; COM xlApp; COM wbks, wbk; COM wkss, wks; COM rng; str strValue; int adoTypeToExcel(str _type) { switch (_type) { // используются значения констант перечисления DateTypeEnum из топика TypeProperty (ADO) // (см. справку по ADO в файле ADO210.CHM - можно поискать на своем компе) case 'num' : return 5; // adDouble case 'str' : return 8; // adBSTR case 'date': return 133; // adDBDate } return 8; } ; strValue = '1234567890123, 1234567890123, 1234567890123, 1234567890123, 1234567890123, 1234567890123, 1234567890123, 1234567890123, 1234567890123, 1234567890123, '; strValue += strValue; // 20 значений strValue += strValue; // 40 значений strValue += strValue; // 80 значений strValue += strValue; // 160 значений (2400 символов) rstAxa = new COM('ADODB.Recordset'); flds = rstAxa.Fields(); flds.Append('strValue', adoTypeToExcel('str' )); rstAxa.Open(); rstAxa.AddNew(); fld = flds.Item('strValue'); fld.Value(strValue); rstAxa.Update(); xlApp = new COM('Excel.Application'); wbks = xlApp.Workbooks(); wbk = wbks.Add(); wkss = wbk.Worksheets(); wks = wkss.Item(1); rng = wks.Range('A1'); rng.CopyFromRecordset(rstAxa); rstAxa.Close(); xlApp.Visible(true); } |
|
12.03.2009, 13:42 | #7 |
Участник
|
Но использовать 2007 офис - дополнительные затраты на лицензию для конечного пользователя, что крайне нежелательно, да еще и при нынешней экономической обстановке. Может быть попробовать какой-нибудь другой метод экспорта? Есть мнения?
|
|
12.03.2009, 16:19 | #8 |
Участник
|
Метод 'FormulaR1C1' в COM-объекте класса 'Range' возвратил код ошибки 0x800A03EC (<неизвестно>), который означает: <неизвестно>.
|
|
12.03.2009, 16:32 | #9 |
Moderator
|
А если в формуле запятые на точки с запятой поменять? Думаю, в этом дело. Внес исправления в джоб - попробуйте еще раз. Появился такой фрагмент:
X++: str strFormula; COMVariant sep; .......... xlApp = new COM('Excel.Application'); // создаем формулу с правильным разделителем списка (запятой или точкой с запятой) sep = xlApp.International(#xlListSeparator); strFormula = strFmt('=CONCATENATE(RC[-10]%1RC[-9]%1RC[-8]%1RC[-7]%1RC[-6]%1RC[-5]%1RC[-4]%1RC[-3]%1RC[-2]%1RC[-1])', sep.bStr()); .......... fld = flds.Item('str11'); fld.Value(strFormula); X++: // определение разделителя списка Excel // нужно для диапазонов вида Range('C:C,F:F') // Excel берем параметром из контекста задачи, // чтобы не загружать отдельную сессию из-за единственного символа static str excelListSeparator(COM _excelApplication) { #define.xlListSeparator(5) COMVariant sep = _excelApplication.International(#xlListSeparator); ; return sep.bStr(); } |
|
12.03.2009, 17:28 | #10 |
Участник
|
То Gustav,
огромное спасибо, все работает за исключением последнего куска, выводится файл .xls, но данные остаются в ячейках A1 и A2 Последний раз редактировалось Antoncheg; 12.03.2009 в 17:32. |
|
12.03.2009, 17:36 | #11 |
Moderator
|
Цитата:
Или вам нужны промежуточные "нарезанные" результаты? Ну, не удаляйте их тогда |
|
12.03.2009, 17:56 | #12 |
Участник
|
Спасибо, разобрался, к вечеру сломан мозг, знаете ли с такими конструкциями
|
|
13.03.2009, 11:25 | #13 |
Участник
|
Сегодня попробовал применить результаты на практике, вот что у меня вышло
X++: void Exec(str Path,SnQueryTableTmp SnQ) { str strValue; str strFormula; COMVariant sep; #define.xlPasteValues(-4163) #define.xlListSeparator(5) ; xlApp = new COM('Excel.Application'); xlApp.Visible(true); wbks = xlApp.Workbooks(); wbk = wbks.Add(); wkss = wbk.Worksheets(); wks = wkss.Add(); wks = wkss.Item(1); wks.Name('Итог'); rng = wks.Range('A1'); //==================================Первая вкладка======================= rstAxa = new COM('ADODB.Recordset'); flds = rstAxa.Fields(); flds.Append('Дата закрытия' ,this.adoTypeToExcel('date')); flds.Append('Отгрузочная накладная' ,this.adoTypeToExcel('str')); flds.Append('Заказ' ,this.adoTypeToExcel('str')); flds.Append('Безналичная накладная' ,this.adoTypeToExcel('str')); flds.Append('Менеджер' ,this.adoTypeToExcel('str')); flds.Append('Код клиента' ,this.adoTypeToExcel('str')); flds.Append('Дата отгрузочной накладной' ,this.adoTypeToExcel('date')); flds.Append('Город' ,this.adoTypeToExcel('str')); flds.Append('Краткое наименование' ,this.adoTypeToExcel('str')); flds.Append('Номенклатура' ,this.adoTypeToExcel('str')); flds.Append('Код дет. в спецификации' ,this.adoTypeToExcel('str')); flds.Append('Название' ,this.adoTypeToExcel('str')); flds.Append('Склад' ,this.adoTypeToExcel('str')); flds.Append('Цена ед.' ,this.adoTypeToExcel('num')); flds.Append('Себестоимость' ,this.adoTypeToExcel('num')); flds.Append('Количество' ,this.adoTypeToExcel('num')); flds.Append('Сумма по позиции' ,this.adoTypeToExcel('num')); flds.Append('Прибыль' ,this.adoTypeToExcel('num')); flds.Append('Каталожный номер' ,this.adoTypeToExcel('str')); flds.Append('Серийные номера1' ,this.adoTypeToExcel('str')); flds.Append('Серийные номера2' ,this.adoTypeToExcel('str')); flds.Append('Серийные номера3' ,this.adoTypeToExcel('str')); flds.Append('Серийные номера4' ,this.adoTypeToExcel('str')); flds.Append('Серийные номера5' ,this.adoTypeToExcel('str')); flds.Append('Серийные номера6' ,this.adoTypeToExcel('str')); flds.Append('Серийные номера7' ,this.adoTypeToExcel('str')); flds.Append('Серийные номера8' ,this.adoTypeToExcel('str')); flds.Append('Серийные номера9' ,this.adoTypeToExcel('str')); flds.Append('Серийные номера10' ,this.adoTypeToExcel('str')); flds.Append('Серийные номера11' ,this.adoTypeToExcel('str')); flds.Append('REF' ,this.adoTypeToExcel('str')); flds.Append('Квота' ,this.adoTypeToExcel('str')); flds.Append('Конечный клиент' ,this.adoTypeToExcel('str')); flds.Append('Номер партии' ,this.adoTypeToExcel('str')); rstAxa.Open(); // SnQ.setTmpData(SnQ); while select SnQ { sep = xlApp.International(#xlListSeparator); strFormula = strFmt('=CONCATENATE(RC[-30]%2RC[-29]%2RC[-28]%2RC[-27]%2RC[-26]%2RC[-25]%2RC[-24]%2RC[-23]%2RC[-22]%2RC[-21])', sep.bStr()); strValue = strRep(SnQ.findSerialNumbersFromWMS(SnQ), 60); rstAxa.AddNew(); // fld = flds.Item('Каталожный номер'); fld.Value(SelloutMsTable.A_PartNumber); fld = flds.Item('Дата закрытия'); fld.Value(SnQ.DeliveryDate); fld = flds.Item('Отгрузочная накладная'); fld.Value(SnQ.PickingListId); fld = flds.Item('Заказ'); fld.Value(SnQ.SalesId); fld = flds.Item('Безналичная накладная'); fld.Value(SnQ.InvoiceIdElect); fld = flds.Item('Менеджер'); fld.Value(SnQ.SalesResponsible); fld = flds.Item('Код клиента'); fld.Value(SnQ.CustAccount); fld = flds.Item('Дата отгрузочной накладной'); fld.Value(SnQ.PickingListDate); fld = flds.Item('Город'); fld.Value(SnQ.City); fld = flds.Item('Краткое наименование'); fld.Value(SnQ.NameAlial); fld = flds.Item('Номенклатура'); fld.value(SnQ.ItemId); fld = flds.Item('Код дет. в спецификации'); fld.Value(SnQ.ChosenPartNumber); fld = flds.Item('Название'); fld.Value(SnQ.ItemName); fld = flds.Item('Склад'); fld.Value(SnQ.InventLocationId); fld = flds.Item('Цена ед.'); fld.Value(SnQ.A_SalesPrice); fld = flds.Item('Себестоимость'); fld.Value(SnQ.SalesCostPrice); fld = flds.Item('Количество'); fld.Value(SnQ.Qty); fld = flds.Item('Сумма по позиции'); fld.Value(SnQ.Amount); fld = flds.Item('Прибыль'); fld.Value(SnQ.SalesProfit); fld = flds.Item('Каталожный номер'); fld.Value(SnQ.A_PartNumber); fld = flds.Item('Серийные номера1'); fld.Value(strFormula); fld = flds.Item('Серийные номера2'); fld.Value(strValue); fld = flds.Item('Серийные номера3'); fld.Value(strValue); fld = flds.Item('Серийные номера4'); fld.Value(strValue); fld = flds.Item('Серийные номера5'); fld.Value(strValue); fld = flds.Item('Серийные номера6'); fld.Value(strValue); fld = flds.Item('Серийные номера7'); fld.Value(strValue); fld = flds.Item('Серийные номера8'); fld.Value(strValue); fld = flds.Item('Серийные номера9'); fld.Value(strValue); fld = flds.Item('Серийные номера10'); fld.Value(strValue); fld = flds.Item('Серийные номера11'); fld.Value(strValue); fld = flds.Item('REF'); fld.Value(SnQ.REF); fld = flds.Item('Квота'); fld.value(SnQ.InventQuotationId); fld = flds.Item('Конечный клиент'); fld.Value(SnQ.EndPartnerCode); fld = flds.Item('Номер партии'); fld.Value(SnQ.InventBatchId); rstAxa.Update(); } cell = rng.Offset(1,0); cell.CopyFromRecordset(rstAxa); rng = wks.Range('T2:T%'); rng.FormulaR1C1( rng.Value() ); rng.Copy(); rng.PasteSpecial(#xlPasteValues); xlApp.CutCopyMode(true); rng = wks.Columns(); rng = COM::createFromVariant(rng.Item('U:AD')); rng.Delete(); rstAxa.close(); actWin = xlApp.ActiveWindow(); // actWin.FreezePanes(true); } |
|
13.03.2009, 12:05 | #14 |
Участник
|
да, и если в наборе 1 серийный номер, то результат в виде скленных одних и тех же серийных номеров
|
|
13.03.2009, 12:31 | #15 |
Moderator
|
Ой, йо... Вы не воспринимайте всё уж так буквально в моем джобе!! Там strValue пишется одинаково во все 10 колонок только для того, чтобы В РАМКАХ ПРИМЕРА чем-то их заполнить а-ля "60 серийников" - ну не генерить же мне 600 тестовых уникальных значений! Точно так же функция strRep у меня используется для формирования строки из 60 одинаковых серийников: исходная строка "1234567890123, " повторяется 60 раз. Вы же подаете на вход этой функции свою длиннющую строку, уже содержащую сотни серийников и повторяете ее 60 раз - конечно Excel'ю плохеет! Наверное, строка получается больше и 911, и 32767 символов...
Цитата:
P.S. Подозреваю, что серийники нужно сначала прочитать в массив, а потом писать в колонки: в колонку 1 - с 1-го по 60-й, в колонку 2 - с 61-го по 120-й, в колонку 3 - с 121-го по 180-й и т.д. Добавляя запятую и пробел после каждого серийника и не делая этого после последнего серийника. А можно сразу в элемент массива записывать серийник+запятую+пробел (за исключением последнего). Соответственно, если заполненных элементов массива меньше 600, то оставшиеся непрописанными элементы массива будут давать пустые строки, которые при слиянии в Excel не окажут никакого влияния на результат. Остались неясности? |
|
|
За это сообщение автора поблагодарили: Antoncheg (1). |
16.04.2009, 12:11 | #16 |
Участник
|
Коллеги, еще вопросик, внедрил данный код, работает успешно, но на машинах с английским офисом. На русском эксель выдает вместо результата формулы #ИМЯ?, попробовал заменить CONCACENATE на СЦЕПИТЬ, в русском работает (офисе), английском - нет. Возможно ли как-нибудь обойти данную ситуацию? Заранее благодарен!
|
|
16.04.2009, 13:07 | #17 |
Moderator
|
Фрагмент для превращения текста в формулы и затем в значения (типа нижеследующего) на правильный столбец настроен?
X++: rng = wks.Range('T2:T%'); rng.FormulaR1C1( rng.Value() ); rng.Copy(); rng.PasteSpecial(#xlPasteValues); xlApp.CutCopyMode(true); |
|
16.04.2009, 13:23 | #18 |
Участник
|
Да все так
|
|
16.04.2009, 14:22 | #19 |
Moderator
|
Попробуйте аналогично проверить у себя языковые настройки. Что при английском и что при русском Excel'ях выдает?
|
|
16.04.2009, 17:31 | #20 |
Moderator
|
Тема, на самом деле, интересная и до конца не решенная.
В интернете ответа пока не нашёл, но нашёл достаточно подробное описание проблемы: http://www.gotdotnet.ru/Forums/Windows/107618.aspx Цитата:
Сообщение от туда
Проблема состоит в следующем. Мне надо сформировать из проекта на C# таблицу в Excel. В ней кроме данных должны присутствовать и формулы - самые простые - Сумма. Проблема в том, что в разных версиях Excel функции называются по разному - СУММ() в русской версии, и SUM() - в английской. Мне надо, чтобы выходная таблица корректно формировалась независимо от локализации Excel'я. Среди свойств ячейки (диапазона) есть Formula и FormulaLocal. Как я полагал, в свойстве FormulaLocal хранится локализованный вариант формулы ( например, СУММ(E2:E8) ), а в Formula - независимый от локализации (то есть - SUM(E2:E8) ). Но когда я присваиваю при формировании таблицы из моей программы свойству Formula нужной ячейки значение "=SUM(E2:E8)", Excel его не понимает (экспериментирую с русской версией). Понимает только "=СУММ(E2:E8)". С русской версией Excel моя программа будет работать нормально, а как быть, если на машине пользователя стоит английская версия?
.......... (вопрошающему отвечают, но, как оказывается, не совсем удачно): Используйте свойство Formula. Присваивайте ему формулу в АНГЛИЙСКОМ варианте. Пример: Range("A10").Formula = "=SUM(A1:A9)" В этом случае свойство FormulaLocal вернет правильную локализованную версию формулы: "=СУММ(A1:A9)" для русского офиса. .......... В том то и дело, что это не работает. Я это указал в начальном письме. Если я присваиваю свойству Formula значение "=SUM(A1:A9)", то в ячейке появляется сообщение "#ИМЯ?", т.е. - формула содержит нераспознанный текст. А если я пишу макрос напрямую на VBA, не через C#, то тогда все нормально. Этот глюк не изменился и после апдейта на Office XP. Вот я и задал этот вопрос - может дело еще в чем-то. X++: static void Job161_TestFormula(Args _args) { COM xlApp = new COM ('Excel.Application'); COM wbk; COM rng; COMVariant cv; ; wbk = xlApp.Workbooks(); wbk = wbk.Add(); rng = xlApp.Range('A1'); rng.Formula('=СУММ(1+1)'); cv = rng.Text(); // здесь ожидаются значения: #NAME? или #ИМЯ? или 2 box::info(cv.bStr()); wbk.Close(false); // чтобы не оставался скрытый экземпляр Excel xlApp.Quit(); } |
|