Методы, применяемые при создании отчетов в 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.

Система должна заработать. 

Метки:



Комментариев: 5

  1. Fordewind пишет:

    Отмечу, что слова “Система должна заработать” обычные, когда речь идет о NAV ;)

  2. Cadog пишет:

    спасибо

  3. Andre_Cleaner пишет:

    Интересует обмен ссылками с Вашим блогом

  4. apanko пишет:

    На сайте пока нет раздела для ссылок.
    Данный раздел планируется.
    Направьте на почту (apanko@nav4u.ru) ссылку на ваш ресурс.
    Когда раздел будет запущен я вас уведомлю, чтобы вы тоже смогли поставить ссылку на онлайн-журнал NAV4U

    Обратите внимание, мы можем разместить ссылки исключительно на сайт посвященный продукту Dynamics NAV.

  5. Zbydik пишет:

    Приятно, когда люди досконально знают предмет, о котором пишут; это как раз ваш случай.

Оставьте свой отзыв!