8 (800) 302-62-68
+7 (499) 348-29-58
sale@rbs-crm.ru

Документация разработчика

Отчеты

Отчет представляет собой особый тип «Формы», называемый «Индивидуальная форма редактирования». Это форма редактирования, привязанная к конкретному «Объекту» (id), а не «Типу объекта». Т.е. у каждого объекта есть своя «Индивидуальная форма редактирования».

Добавление нового отчета

Для создания нового отчета необходимо зайти в “Справочники” — “Настройки” — “Отчеты” (/settings/references/reports).

При создании нового отчета заполнить поля:

  • Наименование — Название отчета на русском (например “По бюджетам”)
  • Наименование (анг.) — Название формы отчета из “Конфигуратора” объекта “Отчеты” (Например: “budgets”).

Затем необходимо зайти в “Конфигуратор” — “Отчеты” (references.reports) /configurator/references.reports/, создать новую «Форму», выбрать тип формы «Инд. форма редактирования», заполнить поля:

  • Название (EN) — ссылка /olap/bugdets
  • Комментарий — название на русском языке
  • Целевой объект — созданная форма в модуле отчет (По бюджетам)

Созданный отчет будет отображаться в разделе “Отчеты” (/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']
)

Объединение в отчете несколько объектов (leftJoin)

Пример:

$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)

Метод вызывается в самом конце запроса.

->group(DB::Field('owner', 'documents.orders'))

Добавление сортировки (order)

Сортировка записей. По умолчанию идет ASC.

Пример:

->order(DB::Field('owner', 'documents.orders'))
// или DESC
->order([DB::Field('owner', 'documents.orders'), DESС])

Добавление ограничения по количеству записей (limit)

Ограничение количества записей на вывод.

->limit(100)

Добавление условия having на группировку

Пример: вывести список сгруппированных счетов с суммой больше 4000

->having(DB::_more(DB::DBFunction('SUM', DB::Field('total', 'documents.orders')), 4000))

Вывод данных в отчет

Для вывода данных в шаблон используется метод fillFromArray().

fillFromArray($data, $mapping, $afterFunc = null)

Где:

  • data — Ассоциативный массив данных
  • mapping — Карта вывода. Представляет ключи — название полей таблицы, в качестве значений могут использоваться анонимные функции или строки.
  • afterFunc
  • afterFunc — Анонимная функция постпроцессинга. Принимает на вход только что сгенерированную строку таблицы. Может использоваться для установки дополнительных параметров, изменения стилей и пр. Применяется к каждой строке в момент после создания.

Пример:

$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(). Функция группирует таблицу по полю и может подсчитывать суммы по набору полей для поля.

titleGroup(array $data, Table $table, array $fieldPairs, array $sumFields, array $map, [Function $afterFunc])

Где:

  • data — Ассоциативный массив данных
  • table — Ссылка на таблицу, в которую необходимо вывести данные (как правило, $this->table->reports)
  • fieldPairs — Поля группировки. В качестве ключей принимает названия полей, по которым Вам нужно провести группировку, в качестве значений — названия строк, которые нужно записать в выходной массив (они могут отличаться, если Вам это требуется, см. ниже). Здесь действует общий принцип, принятый в системе для «хешей» отображения: если ключ и значение совпадают (то есть в выводе поле должно предстать с тем же названием), указание ключа можно опустить (то есть …, «foo» => «foo», … эквивалентно …, «foo»,…)
  • sumFields — Массив (перечисление) полей, которые необходимо просуммировать
  • map — Карта вывода. Представляет ключи — название полей таблицы, в качестве значений могут использоваться анонимные функции или строки. См. метод printDataTable
  • 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'";
});

Где:

  • [«productId» => «product»] — первый параметр — поле из запроса, второй поле для вставки в шаблоне. Группировка осуществляется по полю productId
  • [«number»] — поле для суммирования. Если его вывести в 5 параметре, то выведется именно сумма по этому полю.
  • 6 параметр — подкрашивает строку с суммой (при необходимости).

Важно! Чтобы работал группировка, необходимо в метод fillFromArray() добавить в конец параметров анонимную функцию:

function($row, $id) { $row->correspondId = $id; }

См. полный пример выше.

Создать промежуточный итог по таблице (sumData)

Для суммирования используется функция sumData(). Она добавляет строку в конец отчёта.

$this->sumData($data, $this->tables->report, ["number", "total", "profit"], 
    [
        "number",
        "total" => function($row) { 
            return String::money($row["total"]); 
        }, 
        "profit" => function($row) { 
            return String::money($row["profit"]); 
        }
    ],
    "company"
);

Где:

  • [«number», «total», «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 можно ознакомиться по ссылке.