← レシピ一覧

Statement report

Build a report that summarises a month of activity. You use line, pie and donut charts in one document, and learn number formatting and automatic height.

このテンプレートを取り込む

このレシピで身につくこと

  • chart-line
  • chart-pie
  • chart-donut
  • expr-format-number
  • node-autoheight

エディタで作る

  1. Add a chart and set it to line. Three values are all it needs: the array path, the label expression and the value expression.
  2. Add two more charts as pie and donut. They read the same array in a different shape, so the value expression stays the same.
  3. Pie and donut show proportions. Past about eight slices they become unreadable, so switch to bar at that point.
  4. Write the summary figures as labels: {{ format(sum(data.daily, "amount"), "number", "0,0", "en-US") }} can never disagree with the table.

スキーマと API で作る

  1. A chart picks its kind with chartKind: "bar" (the default), "line", "pie" or "donut".
  2. The data shape is the table one: bind is the array, columns[0].cell is the label expression and columns[1].cell is the value expression. chartKind is the only new key.
  3. A value expression that is not a number contributes 0. It is the same reason currency formatting shows NaN for a string — send numbers.
  4. format(value, "number", "0,0", locale) adds thousand separators. Leaving the pattern empty defaults to 0,0.

出来上がったドキュメント

下の JSON は、2 つの道筋が生むドキュメントそのものです。テストがこれを検証し、警告なしでレンダリングされることも確認しているので、そのまま貼り付けてお使いいただけます。

例
{
  "v": 1,
  "unit": "px",
  "page": {
    "size": "a4",
    "orientation": "portrait",
    "margin": {
      "t": 48,
      "r": 48,
      "b": 56,
      "l": 48
    },
    "header": "{{ data.company }} · {{ data.period }}",
    "footer": "[[page]] / [[pages]]"
  },
  "fonts": [],
  "styles": [
    {
      "id": "s_h1",
      "fontFamily": "Liberation Sans",
      "fontSize": 22,
      "fontWeight": 700,
      "italic": false,
      "color": "#111111",
      "bg": "transparent",
      "align": "left",
      "valign": "top",
      "lineHeight": 1.5,
      "border": "none",
      "radius": 0,
      "opacity": 1
    },
    {
      "id": "s_l",
      "fontFamily": "Liberation Sans",
      "fontSize": 12,
      "fontWeight": 400,
      "italic": false,
      "color": "#333333",
      "bg": "transparent",
      "align": "left",
      "valign": "top",
      "lineHeight": 1.5,
      "border": "none",
      "radius": 0,
      "opacity": 1
    },
    {
      "id": "s_big",
      "fontFamily": "Liberation Sans",
      "fontSize": 28,
      "fontWeight": 700,
      "italic": false,
      "color": "#111111",
      "bg": "transparent",
      "align": "left",
      "valign": "top",
      "lineHeight": 1.5,
      "border": "none",
      "radius": 0,
      "opacity": 1
    },
    {
      "id": "s_cap",
      "fontFamily": "Liberation Sans",
      "fontSize": 11,
      "fontWeight": 400,
      "italic": false,
      "color": "#666666",
      "bg": "transparent",
      "align": "left",
      "valign": "top",
      "lineHeight": 1.5,
      "border": "none",
      "radius": 0,
      "opacity": 1
    }
  ],
  "nodes": [
    {
      "id": "n_title",
      "parent": "",
      "order": 0,
      "type": "label",
      "x": 0,
      "y": 0,
      "w": 697,
      "h": 28,
      "show": "",
      "styleId": "s_h1",
      "props": {
        "text": "Monthly statement",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [],
        "rows": 0,
        "cells": [],
        "chartKind": "",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Title"
    },
    {
      "id": "n_sum",
      "parent": "",
      "order": 1,
      "type": "label",
      "x": 0,
      "y": 40,
      "w": 340,
      "h": 36,
      "show": "",
      "styleId": "s_big",
      "props": {
        "text": "{{ format(sum(data.daily, \"amount\"), \"number\", \"0,0\", \"en-US\") }}",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [],
        "rows": 0,
        "cells": [],
        "chartKind": "",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Total"
    },
    {
      "id": "n_sum_l",
      "parent": "",
      "order": 2,
      "type": "label",
      "x": 0,
      "y": 78,
      "w": 340,
      "h": 0,
      "show": "",
      "styleId": "s_cap",
      "props": {
        "text": "Total this month",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [],
        "rows": 0,
        "cells": [],
        "chartKind": "",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Total caption"
    },
    {
      "id": "n_line_l",
      "parent": "",
      "order": 3,
      "type": "label",
      "x": 0,
      "y": 116,
      "w": 697,
      "h": 18,
      "show": "",
      "styleId": "s_l",
      "props": {
        "text": "Daily amount",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [],
        "rows": 0,
        "cells": [],
        "chartKind": "",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Line heading"
    },
    {
      "id": "n_line",
      "parent": "",
      "order": 4,
      "type": "chart",
      "x": 0,
      "y": 140,
      "w": 697,
      "h": 200,
      "show": "",
      "styleId": "s_l",
      "props": {
        "text": "",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "data.daily",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [
          {
            "header": "Day",
            "cell": "{{ item.day }}",
            "width": 0,
            "align": "left"
          },
          {
            "header": "Amount",
            "cell": "{{ item.amount }}",
            "width": 0,
            "align": "right"
          }
        ],
        "rows": 0,
        "cells": [],
        "chartKind": "line",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Daily trend"
    },
    {
      "id": "n_pie_l",
      "parent": "",
      "order": 5,
      "type": "label",
      "x": 0,
      "y": 364,
      "w": 340,
      "h": 18,
      "show": "",
      "styleId": "s_l",
      "props": {
        "text": "Share by product group",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [],
        "rows": 0,
        "cells": [],
        "chartKind": "",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Pie heading"
    },
    {
      "id": "n_pie",
      "parent": "",
      "order": 6,
      "type": "chart",
      "x": 0,
      "y": 388,
      "w": 340,
      "h": 220,
      "show": "",
      "styleId": "s_l",
      "props": {
        "text": "",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "data.mix",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [
          {
            "header": "Group",
            "cell": "{{ item.name }}",
            "width": 0,
            "align": "left"
          },
          {
            "header": "Amount",
            "cell": "{{ item.amount }}",
            "width": 0,
            "align": "right"
          }
        ],
        "rows": 0,
        "cells": [],
        "chartKind": "pie",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Share pie"
    },
    {
      "id": "n_donut_l",
      "parent": "",
      "order": 7,
      "type": "label",
      "x": 357,
      "y": 364,
      "w": 340,
      "h": 18,
      "show": "",
      "styleId": "s_l",
      "props": {
        "text": "Share by channel",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [],
        "rows": 0,
        "cells": [],
        "chartKind": "",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Donut heading"
    },
    {
      "id": "n_donut",
      "parent": "",
      "order": 8,
      "type": "chart",
      "x": 357,
      "y": 388,
      "w": 340,
      "h": 220,
      "show": "",
      "styleId": "s_l",
      "props": {
        "text": "",
        "src": "",
        "fit": "",
        "thickness": 0,
        "stroke": "",
        "fill": "",
        "repeat": "",
        "as": "",
        "break": "",
        "keep": "",
        "padding": 0,
        "padY": 0,
        "padX": 0,
        "align": "",
        "valign": "",
        "wrap": "",
        "bind": "data.channel",
        "headerHeight": 0,
        "rowHeight": 0,
        "max": 0,
        "columns": [
          {
            "header": "Channel",
            "cell": "{{ item.name }}",
            "width": 0,
            "align": "left"
          },
          {
            "header": "Amount",
            "cell": "{{ item.amount }}",
            "width": 0,
            "align": "right"
          }
        ],
        "rows": 0,
        "cells": [],
        "chartKind": "donut",
        "codeKind": "",
        "ecc": "",
        "fieldKind": "",
        "fieldName": "",
        "options": [],
        "required": false,
        "readonly": false,
        "html": ""
      },
      "name": "Share donut"
    }
  ]
}

リクエストの本文です。上のドキュメントを template に、下のデータを data に入れてください。

リクエスト
{
  "template": "(the document above)",
  "data": {
    "company": "brewmypdf",
    "period": "2026-08",
    "daily": [
      {
        "day": "1",
        "amount": 1200
      },
      {
        "day": "8",
        "amount": 1850
      },
      {
        "day": "15",
        "amount": 1400
      },
      {
        "day": "22",
        "amount": 2100
      },
      {
        "day": "29",
        "amount": 1650
      }
    ],
    "mix": [
      {
        "name": "Subscription",
        "amount": 4200
      },
      {
        "name": "One-off",
        "amount": 2600
      },
      {
        "name": "Consulting",
        "amount": 1400
      }
    ],
    "channel": [
      {
        "name": "Direct",
        "amount": 5100
      },
      {
        "name": "Partner",
        "amount": 3100
      }
    ]
  }
}