Закажите бесплатную презентацию продуктов РосБизнесСофт прямо сейчас!
Отчет представляет собой особый тип «Формы», называемый «Индивидуальная форма редактирования». Это форма редактирования, привязанная к конкретному «Объекту» (id), а не «Типу объекта». Т.е. у каждого объекта есть своя «Индивидуальная форма редактирования».
Для создания нового отчета необходимо зайти в “Справочники” — “Настройки” — “Отчеты” (/settings/references/reports).
При создании нового отчета заполнить поля:
Затем необходимо зайти в “Конфигуратор” — “Отчеты” (references.reports) /configurator/references.reports/, создать новую «Форму», выбрать тип формы «Инд. форма редактирования», заполнить поля:
Созданный отчет будет отображаться в разделе “Отчеты” (/olap/) и будет доступен по ссылке /olap/budgets/.
Любой отчет должен наследоваться от \Kernel\Actions\Reports\Base и должен содержать следующие методы:
Предустановленные фильтры отчета addFilters():
protected function addFilters() { ... }
Фильтры представляет собой объекты Http\Elements\Input и предоставляют интерфейс для работы со значениями.
Для установки значения в поле фильтр по умолчанию используется метод setDefault():
$this->filters->add("engineer", "Select", $this->Data->{'references.employees'}, true)->setDefault($this->User->getEmployee()->id);
В разделе «Фильтры» в поле «Инженер» подставится текущий пользователь.
Все условия, запрос к базе и вывод таблицы прописываются в методе buildReport():
protected function buildReport() { ... }
Для работы с базой данных необходимо подключить класс:
use Kernel\Database\Database as DB;
Для выборки используется метод select().
Чтобы указать алиас, необходимо указать поле как массив и вторым параметром указать алиас.
$this->DB->{"documents.orders"} // название структуры, откуда берем данные ->select( DB::Field('number', 'documents.orders'), // номер счета [DB::Field('name', 'references.employees'), 'responsible'] // имя ответственного менеджера за счет )
Условие «РАВНО (=)»
Пример: Поле «Номер» в документе «Счете» = «10»
$where = DB::_equal(DB::Field('responsible', 'documents.orders'), 10);
Условие “БОЛЬШЕ (>)”
Пример: Поле «Номер» в документе «Счете» > «4»
$where = DB::_more(DB::Field(number, 'documents.orders'), 4);
Условие “МЕНЬШЕ (<)”
Пример: Поле «Номер» в документе «Счете» < «100»
$where = DB::_less(DB::Field('responsible', 'documents.orders'), 100);
Условие “И”
Пример: Добавить еще одно условие к существующему, например «Статус» = «1»
$where = DB::_and($where, DB::_equal(DB::Field('status', 'documents.orders'), 2))
Условие “ИЛИ”
Пример: Добавить условие ИЛИ к существующему условию
$whereOr = DB::_or( DB::Field(DB::Field('status', 'documents.orders'), 2)), DB::Field(DB::Field('status', 'documents.orders'), 4)) ); $where = DB::_and($where, $whereOr);
Для этого используется DBFunction.
Пример: добавим функцию «SUM»
$this->DB->{"documents.orders"} ->select( [DB::DBFunction('SUM', DB::Field('total', 'documents.orders')), 'total'] )
Пример:
$this->DB->{"documents.orders"} ->select( [DB::DBFunction('SUM', DB::Field('total', 'documents.orders')), 'total'] ) ->leftJoin('references.companies', DB::_equal(DB::Field('id', 'references.companies'), DB::Field('owner', 'references.companies')))
Метод вызывается в самом конце запроса.
->group(DB::Field('owner', 'documents.orders'))
Сортировка записей. По умолчанию идет ASC.
Пример:
->order(DB::Field('owner', 'documents.orders')) // или DESC ->order([DB::Field('owner', 'documents.orders'), DESС])
Ограничение количества записей на вывод.
->limit(100)
Пример: вывести список сгруппированных счетов с суммой больше 4000
->having(DB::_more(DB::DBFunction('SUM', DB::Field('total', 'documents.orders')), 4000))
Для вывода данных в шаблон используется метод fillFromArray().
fillFromArray($data, $mapping, $afterFunc = null)
Где:
Пример:
$this->tables->report->fillFromArray($data, [ "supplier_order" => function($row){ return "<a href='/supplier_order/". $row['supplierOrderId'] ."' target='_blank'>№ ". $row['supplierOrderId'] ." от ". date('d.m.Y', strtotime($row['supplierOrderDate'])) ."</a>"; }, "contract" => function($row){ return "<a href='/contracts/". $row['contractId'] ."' target='_blank'>№ ". $row['contractNumber'] ." от ". date('d.m.Y', strtotime($row['contractDate'])) ."</a>"; }, "product" => function($row){ return "<a href='/products/". $row['productId'] ."' target='_blank'>". $row['productName'] ."</a>"; }, "number" => function($row){ return $row['productNumber']; }, "status" => function($row){ return ""; }, ], function($row, $id) { // Используется для группировки, см. ниже. $row->correspondId = $id; } )->printExport($this->page);
Для вывода группировки в таблицу используется метод titleGroup(). Функция группирует таблицу по полю и может подсчитывать суммы по набору полей для поля.
titleGroup(array $data, Table $table, array $fieldPairs, array $sumFields, array $map, [Function $afterFunc])
Где:
Важно! Метод ожидает уже отсортированных данных и не будет переупорядочить их. Он не может изменить входные данные. Он только добавляет поля в таблицу.
Такой подход позволяет последовательно применять метод по нескольку раз. Например, нужно сгруппировать вывод по товару, а для каждого товара подсчитать сумму по менеджерам. Просто примените метод два раза подряд, передав разные поля вторым аргументом.
Если требуется группировка по менеджерам, а затем по товарам, то порядок вызовов метода неважен, важно то, как отсортированы входные данные. Измените запрос к БД (или к ORM) для достижения целей.
Пример:
$this->titleGroup($data, $this->tables->report, ["productId" => "product"], ["number"], [ "product" => function($row) { return $row["productName"]; }, "number" => function($row) { // выведет сумму по полю number return abs($row["number"]); }, ],function($row) { $row->params = "class='sumRow'"; });
Где:
Важно! Чтобы работал группировка, необходимо в метод fillFromArray() добавить в конец параметров анонимную функцию:
function($row, $id) { $row->correspondId = $id; }
См. полный пример выше.
Для суммирования используется функция sumData(). Она добавляет строку в конец отчёта.
$this->sumData($data, $this->tables->report, ["number", "total", "profit"], [ "number", "total" => function($row) { return Text::money($row["total"]); }, "profit" => function($row) { return Text::money($row["profit"]); } ], "company" );
Где:
Для построения графиков используется сторонняя графическая библиотека.
С примерами построения отчетов можно ознакомиться по ссылкам:
/configurator/references.reports/?formname=reports/top10/edit /configurator/references.reports/?formname=olap/funnel_sales/edit
<?php namespace Applications\Reports\Debts; // Импортируем модули use Kernel\Database\Database as DB; ... use Kernel\Framework\Format; // Создаем класс отчета (обратите внимание на наследование) class Edit extends \Kernel\Actions\Reports\Base { // Добавление фильтров (см. выше) protected function addFilters() { // Метод setDefault работает так же, как и для полей filters->add("dateMin", "Date")->setDefault(time()); ... // Третий аргумент для поля выбора — структура значений. Четвертый — значение пустого поля //(если не задано, выбор поля обязателен). $this->filters->add("manager", "Select", $this->Data->References->Employees, true); .. } // Обратите внимание, названия составляющих шаблонного метода немного отличаются. protected function buildReport() { // Опережающее определение полей позволяет экспорту работать корректно. $this->tables->report->defineColumns("num", .. , "manager"); // Условие для поиска по БД. $condition = DB::_and( ... ); // С учетом фильтра if ($this->filters->manager->get()) ... // Строим запрос к базе $data = $this->DB->{"documents.orders"}->select( [DB::Field("name", "references.employees"), "manager"], ... )-> where($condition)-> join( ... )-> // Обратите внимание на передачу константы для поиска по именам структуры. execute(DATA_NAMES)->result(); ... // Для начала, наполняем таблицу $this->tables->report->fillFromArray($data, ... ); // Добавляем суммирование полей (см. выше) $this->sumData($data, $this->tables->report, ["sum", "dsum", "prepay"], ... ); // Эта переменная означает количество полей, "без данных" (в данном случае одно, для суммы). $this->maxRow = 1; } // Заканчиваем обычным для такого дела определением абстрактных методов базового класса. protected function onPlay() { } protected function onChange() { } protected function onSave() { } } ?>
Подробнее про методы класса DataBase можно ознакомиться по ссылке.