Перейти к содержанию

Отчёты: DSL и JavaScript

Типовой отчёт в Каяле — это форма (ReportForm в *.form), логика на C# (BaseService) и макет .tcr. Для новых отчётов логику можно описать декларативно в той же форме или скриптом рядом с ней. И то и другое выполняется на сервере (запросы и CreateReport). Клиентский ScriptAction / DeclareScript для генерации отчёта не подходит.

Сложные отчёты (ABC, прайс с динамическими колонками, свод касса+банк) по-прежнему пишутся на C#.

Порядок использования

  1. C# ReportBuilder — укоротить существующий сервис без смены языка.
  2. JavaScript рядом с формой или System.ExecuteScript.
  3. XML <Report> внутри ReportForm, кнопка вызывает сервис ReportEngine.

1. C# helper

var rb = new ReportBuilder(this);
var groups = ReportBuilder.GroupsFromLines(linesList, lines,
    new ReportGroupMap("Склады", "Склад"),
    new ReportGroupMap("Товары", "Товар", showGoodGroups, groupsCount));

var criteria = CreateRestQuery("Остатки товаров").Sum("Количество").Sum("Сумма").OnDate(date);
foreach (var field in ReportGroups.FieldNames(groups))
    criteria.Select(field);
rb.WhereInDict(criteria, "Товар", "Товары", goods);

var layout = rb.Create("ОстаткиТоваров.tcr").Header().Set("ДатаНа", date).Table();

Деньги: ReportBuilder.Div, Pct, Neg — не делить decimal через JS number.

2. JavaScript

Файл Остатки товаров.js в work folder (имя = имя формы) или блок:

<ReportForm Name="Остатки товаров" DefaultServiceName="ReportEngine">
  ...
  <ReportScript File="Остатки товаров.js" Method="Run" />
</ReportForm>

Скрипт:

/// <reference path="../Scripts/kayala-report.d.ts" />

function Run(p) {
    report.build({
        template: "ОстаткиТоваров.tcr",
        query: {
            source: "Остатки товаров",
            mode: "rest",
            onDate: p.Date,
            sums: ["Количество", "Сумма"],
            groups: {
                from: p.Lines,
                order: p.LinesList,
                map: {
                    "Склады": { field: "Склад" },
                    "Товары": { field: "Товар", hierarchy: p.ShowGoodGroups, levels: p.GroupsCount }
                }
            },
            filters: [
                { field: "Товар", dict: "Товары", values: p.Goods },
                { field: "Склад", dict: "Склады", values: p.Subdivs }
            ]
        },
        header: { ДатаНа: p.Date },
        columns: {
            Кол: "Количество",
            Цена: "div(Сумма, Количество)",
            Сум: "Сумма"
        }
    });
}

Глобалы: p (параметры формы), report.build, div / pct / neg / names. Формулы колонок — строка div(Сумма, Количество) или функция function (row) { return div(row.num("Сумма"), row.num("Количество")); }. Не писать row.num("Сумма") / row.num("Количество").

Режимы запроса:

mode Хост
rest CreateRestQuery + OnDate
turnover CreateTwoDatesRestQuery + OnDates, итоги +| / -|
query QueryCriteria + период from/to

Песочница: форма «Выполнить скрипт» вызывает System.ExecuteScript (ScriptCode, опционально ScriptMethod).

3. XML DSL

<ReportForm Name="Остатки товаров" DefaultServiceName="ReportEngine">
  <Button Label="#Common/Generate" IsDefault="true">
    <Event EventType="Click">
      <ServiceAction MethodName="Run" />
    </Event>
  </Button>
  <!-- UI: Date, Lines, Goods, ... -->

  <Report Template="ОстаткиТоваров.tcr" Source="Остатки товаров" Mode="Rest" OnDate="[Date]">
    <Sum Field="Количество" />
    <Sum Field="Сумма" />
    <Groups From="[Lines]" OrderFrom="[LinesList]">
      <Map Value="Склады" Field="Склад" />
      <Map Value="Товары" Field="Товар" Hierarchy="[ShowGoodGroups]" Levels="[GroupsCount]" />
    </Groups>
    <Filter Param="Goods" Field="Товар" Dictionary="Товары" />
    <Filter Param="Subdivs" Field="Склад" Dictionary="Склады" />
    <HeaderParam Name="ДатаНа" From="Date" />
    <HeaderParam Name="Товары" NamesOf="Goods" Dictionary="Товары" />
    <Column Name="Кол" Field="Количество" />
    <Column Name="Цена" Expr="div(Сумма, Количество)" />
    <Column Name="Сум" Field="Сумма" />
    <Footer FromTotals="true" />
  </Report>
</ReportForm>

ReportEngine.Run копирует все параметры формы (AcceptFormParams) и подставляет FormName. Автоотчёты тоже передают FormName.

Expr колонки: имя поля, div(a,b), pct(a,b), neg(a), вложенные вызовы.

VS Code

В корне work folder (Base):

  • jsconfig.json — checkJs, target ES5
  • Scripts/kayala-report.d.ts — автодополнение report.build, p.Date

В скрипте отчёта:

/// <reference path="../Scripts/kayala-report.d.ts" />

Отладка: console.log / report.writeToConsole, форма «Выполнить скрипт», превью запроса. Breakpoint DAP для NiL.JS не подключён.

Что не входит в DSL/JS v1

Нулевые остатки, бегущий остаток по документам, динамические колонки прайса, ABC, pivot по складам — C# (при желании с ReportBuilder для групп и фильтров).