Методы, применяемые при создании отчетов в Dynamics NAV.
В настоящей статье в кратком виде будут указаны некоторые методы используемые автором при создании отчетов. Чтобы статья принесла пользу, требуется уметь создавать простые отчеты.
Перед тем как приступить к чтению статьи рекомендуется выполнить тестовое задание, чтобы оценить свои знания.
Задания предложенные в статье рекомендуется выполнять последовательно. В качестве базы данных можно использовать демо-базу Cronus.
Тестовое задание.
Разработать отчет, который будет отображать список товаров с заполненным полем «Спецификация Но.», значение поля «Спецификация Но.», также должно быть выведено в отчет. В отчете должно присутствовать поле «№ п/п».
Пример:
| № п/п | Товар Но. | Описание | Спецификация Но. |
| 1 | 1000 | Велосипед | 1000 |
| 2 | 1001 | Туристический Велосипед | 1001 |
| … | |||
| 9 | LS-100 | Аудисистема, дуб, 100W | LS-100 |
Справились? Отлично.
Нет, тогда следует обратиться к учебной литературе. Ниже в краткой форме дан правильный ответ:
Dataitem = 27 Item.
Свойство DataItemTableView = SORTING(Production BOM No.) WHERE(Production BOM No.=FILTER(<>”))
В триггере onAfterGetRecord прописать код
iNom+=1; //iNom – переменная типа integer
Нарисовать секции Header и Body.
Что-то, затянулось вступление. Идем дальше.
Группировки.
Отчеты Navision можно заставить выводить промежуточные итоги. Создадим отчет по товарным операциям с групппировкой по товару.
Dataitem = 32 Item Ledger Entry
Свойства:
DataItemTableView = SORTING(Item No.,Positive,Location Code,Variant Code)
ReqFilterFields = Item No.
GroupTotalFields = Item No.
TotalFields = Quantity
Нарисовать секции Header, Body, Group Header, Group Footer, Footer.
В секции Group Header и Group Footer вывести «Item No.»
В секции Body, Group Footer, Footer вывести «Quantity»
Запустить отчет, посмотреть, что получилось.
Для наглядности, отчет лучше отфильтровать товару: 1000..1110.
У меня получились следующие суммы в секциях Group Footer и Footer:
1000 = 32
1100 = 152
1110 = 402
Итого = 586
Подробнее в учебном пособии от Microsoft, однако, пособие поставляется исключительно на английском. Единственно, что я позволю себе процитировать правило: поля указанные в свойстве GroupTotalFields также должны присутствовать в ключе, выбранном в свойстве DataItemTableView.
Раз мы уже размялись, можно усложнить задачу. Теперь мы хотим кроме итогов по товару, еще и итоги по складу.
Тут у нас вариантов целое множество, но для начала мы рассмотрим два.
Вариант 1.1.
Создаем еще один Dataitem = 14 Location. Подчиняем ему Dataitem = 32 Item Ledger Entry, связь через поле Location Code. И вот основа готова, осталось отполировать:
1. Свойство PrintOnlyIfDetail = Yes
2. В триггере Location - OnPreDataItem() напишем следующий код:
CurrReport.CREATETOTALS(”Item Ledger Entry”.Quantity);
3. Отредактировать секции: удалить Item Header, добавить Location Header, Location Body, Location Footer.
Как недостаток – в отчет не попадают операции по «пустому» складу.
Вариант 1.2.
Усовершенствуем отчет с одноуровневыми группировками. Для начала создадим новый ключ в таблице 32 Item Ledger Entry, поля входящие в ключ Location Code и Item No. Идея создавать ключи для таблиц с миллионами записей не слишком хороша, но в целях демонстрации мы себе это позволим.
Dataitem = 32 Item Ledger Entry
Свойства:
DataItemTableView = SORTING(Location Code, Item No.)
ReqFilterFields = Item No.
GroupTotalFields = Location Code, Item No.
TotalFields = Quantity
Нарисовать секции Header, Group Header 1, Group Header 2, Body, Group Footer 1, Group Footer 2, Footer.
В секции Group Header 1 и Groop Footer 2 вывести «Location Code»
В секции Group Header 2 и Groop Footer 1 вывести «Item No.»
В секции Body, Group Footer 1, Group Footer 2, Footer вывести «Quantity»
Запустить отчет, посмотреть, что получилось.
Получилась, естественно, полная ерунда.
Причина кроется в том, что система не знает, какая из двух секций типа Groop Header должна выводиться для группировки. Поэтому для каждого нового склада и для каждого нового товара выводятся обе секции. Тоже касается и Groop Footer.
Чтобы решить эту проблему нужно в соответствующие секции добавить определенный код. Предполагаю, нас уже покинули читатели, которые не знают назначение конструкции CurrReport.SHOWOUTPUT(bool). Однако, если вдруг среди них оказались стойкие ребята, то в качестве поощрения сообщу, что данная конструкция используется исключительно в триггерах секции отчета (onPreSection и onPostSection) и сообщает системе нужно ли выводить данную секцию на печать.
Добавим в отчет следующий код:
Item Ledger Entry, GroupHeader 1 (Location) - OnPreSection() CurrReport.ShowOutput(CurrReport.TOTALSCAUSEDBY=FIELDNO("Location Code")); //Выводить секцию, если текущая группировка по полю «Location Code» Item Ledger Entry, GroupHeader 2 (Item) - OnPreSection() CurrReport.ShowOutput(CurrReport.TOTALSCAUSEDBY=FIELDNO("Item No.")); //Выводить секцию, если текущая группировка по полю «Item No.» Item Ledger Entry, GroupFooter 1 (Item) - OnPreSection() CurrReport.ShowOutput(CurrReport.TOTALSCAUSEDBY=FIELDNO("Item No.")); //Выводить секцию, если текущая группировка по полю «Item No.» Item Ledger Entry, GroupFooter 2 (Location) - OnPreSection() CurrReport.ShowOutput(CurrReport.TOTALSCAUSEDBY=FIELDNO("Location Code")); //Выводить секцию, если текущая группировка по полю «Location Code»
Запустим отчет на выполнение. Совсем другое дело.
Сводные таблицы
Готовы к следующему усложнению?
В любом случае идем дальше. Теперь мы хотим получить сводную таблицу. Т.е. чтоб в отчет попадала только информация сколько товара обернулось на складе.
Сейчас наш отчет показывает следующую информацию:
Склад СИНИЙ
Товар 70001
2 325 Приход
-15 Перемещение
Итого по 70001 = 2 310Товар 70002
2 511 Приход
-3 Перемещение
Итого по 70002 = 2 508
Итого по складу СИНИЙ 4 818
ВСЕГО = 4 818
А надо:
СИНИЙ, 70001, 2 310
СИНИЙ, 70002, 2 508
ВСЕГО = 4 818
Вариант 2.1.
Решать поставленную задачу будем просто:
Удалим секции: Group Header 1, Group Header 2, Body, Group Footer 2, оставив Header, Group Footer 1 (группировка по товару), Footer.
В секцию Group Footer 1 выведем поле «Location Code».
Можно запустить отчет на выполнение.
Это как вы догадались не единственный способ.
Вариант 2.2.
Отредактируем свойства Dataitem нашего отчета
Dataitem = 32 Item Ledger Entry
Свойства:
DataItemTableView = SORTING(Location Code, Item No.)
ReqFilterFields = Item No.
GroupTotalFields = [пусто]
TotalFields = [пусто]
Создадим секцию Body с полями «Location Code», «Item No.» и «Quantity».
Удалим секцию Group Footer 1.
В ключ «Location Code», «Item No.» добавим SumIndexField Quantity.
Теперь добавим немного кода в триггеры:
Item Ledger Entry - OnPreDataItem() tLocationFilter:="Item Ledger Entry".GETFILTER("Location Code"); tItemFilter:="Item Ledger Entry".GETFILTER("Item No."); // tLocationFilter и tItemFilter – глобальные переменные типа text 1024. Item Ledger Entry - OnAfterGetRecord() SETRANGE("Location Code","Location Code"); SETRANGE("Item No.","Item No."); FIND('+'); CALCSUMS(Quantity); SETFILTER("Location Code",tLocationFilter); SETFILTER("Item No.",tItemFilter);
Запустим отчет на выполнение.
Все почти хорошо, только общий итог не правильный. Но это мы легко исправим. Добавим в триггер Item Ledger Entry - OnPreDataItem() строку:
CurrReport.CREATETOTALS(Quantity);
Система должна заработать.
Думаю тут надо пояснить. Запустим отчет по товарам 70001..70002 и складу <>”. С учетом ключа заданного в свойстве DataItemTableView обрабатываемая выборка примет следующий вид:
| Операция Но. | Товар Но. | Код Склада | Кол-во |
| 219 | 70001 | ВНЕШ. ЛОГ. | 15 |
| 237 | 70001 | ВНЕШ. ЛОГ. | -15 |
| 221 | 70002 | ВНЕШ. ЛОГ. | 3 |
| 239 | 70002 | ВНЕШ. ЛОГ. | -3 |
| 238 | 70001 | ЖЕЛТЫЙ | 15 |
| 240 | 70002 | ЖЕЛТЫЙ | 3 |
| 105 | 70001 | СИНИЙ | 2325 |
| 218 | 70001 | СИНИЙ | -15 |
| 106 | 70002 | СИНИЙ | 2511 |
| 220 | 70002 | СИНИЙ | -3 |
Система становится на первую запись(Операция Но. = 219). Затем, накладываются фильтры на таблицу по полям Товар Но. и Код Склада на основании значений текущей записи. Таким образом, таблица примет вид:
-15
Затем мы перемещаем указать в конец выборки (на операцию 237), рассчитываем итог по полю Количество и отключаем фильтрацию по полям Товар Но. и Код Склада.
Следующей записью, на которую спозиционируется система будет операция с номером 221, и так далее: 238, 240, 105, 106.
Кстати, задача для сообразительных: почему операция FIND(’+') должна выполняться раньше, чем CALCSUMS(Quantity).
Ну и для самых толковых: почему мы использовали GETFILTER и SETFILTER, а не GETVIEW и SETVIEW.
Ваши версии пишем в комментариях.
Устали? Надо терпеть. Осталась совсем чуть-чуть.
Вариант 2.3.
В данном случае мы используем целых две интересных технологии: временные таблицы и таблицу Integer (не надо ее искать, она в Object Designer все равно не видна).
Модифицируем наш многострадальный отчет.
Заведем новую глобальную переменную: tmpILE record.Item Ledger Entry, Temporary=Yes.
Изменим код в триггерах:
Item Ledger Entry - OnPreDataItem() tLocationFilter:="Item Ledger Entry".GETFILTER("Location Code"); tItemFilter:="Item Ledger Entry".GETFILTER("Item No."); Item Ledger Entry - OnAfterGetRecord() SETRANGE("Location Code","Location Code"); SETRANGE("Item No.","Item No."); CALCSUMS(Quantity); tmpILE.INIT; tmpILE."Entry No.":="Entry No."; tmpILE."Item No.":="Item No."; tmpILE."Location Code":="Location Code"; tmpILE.Quantity:=Quantity; tmpILE.INSERT; //вставка записей во временную таблицу. //Если вы забыли пометить таблицу как временную, то вставить записи скорее всего не удастся. FIND('+'); SETFILTER("Location Code",tLocationFilter); SETFILTER("Item No.",tItemFilter);
Итак, мы заполнили временную таблицу данными. Теперь нам нужно их вывести на печать.
Но не будем спешить. В процессе отладки, часто используется следующий способ, позволяющий определить, правильно ли заполнены данные во временной таблице.
FORM.RUNMODAL(0,tmpILE);
В нашем случае, лучшее для размещения этого кода места будет триггер
Item Ledger Entry - OnPostDataItem().
Итак, мы выяснили, что временная таблица заполнена верно. Теперь выведем их в отчет. Для этого создадим новый DataItem
Dataitem = 2000000026 Integer
Свойства оставим по умолчанию.
А вот в триггеры напишем следующий код:
Integer - OnPreDataItem() tmpILE.RESET; tmpILE.SETCURRENTKEY("Location Code","Item No."); bFirstLoop:=TRUE; //глобальная переменная типа boolean CurrReport.CREATETOTALS(tmpILE.Quantity); Integer - OnAfterGetRecord() IF bFirstLoop THEN BEGIN IF NOT tmpILE.FIND('-') THEN CurrReport.BREAK; bFirstLoop:=FALSE; END ELSE IF tmpILE.NEXT=0 THEN CurrReport.BREAK;
Теперь дело за малым – нарисовать структуру отчета.
Удалим все существующие секции DataItem – Item Ledger Entry.
Добавим секции к DataItem – Integer: Header, Body, Footer.
В секции Body добавим поля tmpILE.“Item No.”, tmpILE.“Location Code” и tmpILE.Quantity.
Также добавим поле tmpILE.Quantity в секцию Footer.
Заголовки полей укажем в секции Header.
Система должна заработать.
Метки: Андрей Панько
22 Июль 2008 в 11:57
Отмечу, что слова “Система должна заработать” обычные, когда речь идет о NAV
26 Октябрь 2008 в 13:48
спасибо
28 Октябрь 2008 в 19:45
Интересует обмен ссылками с Вашим блогом
28 Октябрь 2008 в 19:58
На сайте пока нет раздела для ссылок.
Данный раздел планируется.
Направьте на почту (apanko@nav4u.ru) ссылку на ваш ресурс.
Когда раздел будет запущен я вас уведомлю, чтобы вы тоже смогли поставить ссылку на онлайн-журнал NAV4U
Обратите внимание, мы можем разместить ссылки исключительно на сайт посвященный продукту Dynamics NAV.
19 Ноябрь 2008 в 22:46
Приятно, когда люди досконально знают предмет, о котором пишут; это как раз ваш случай.