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

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

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