frappe-reports について
frappe-reports は、Frappe 開発者向けのレポート作成スキルです。Report Builder、SQL の Query Report、Python と JavaScript の Script Report の選び方に加え、フィルター、チャート、印刷形式を説明します。
主な機能
- 01Report Builder の設定
- 02SQL Query Report
- 03Python と JavaScript の Script Report
- 04フィルター、書式、チャート
- 05カスタム印刷形式
活用シーン
- 01DocType のグループ集計作成
- 02パラメーター付き SQL レポート作成
- 03計算フィールドを含むレポート作成
- 04フィルターと印刷レイアウトの追加
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ lubusin/ frappe-skills --skill frappe-reportsインストール後、Claude Code で/frappe-reportsを入力して読み込まれたか確認します。
同梱ファイルreferences/reports.md
詳細
Frappe レポート
Report Builder、Query Reports(SQL)、または Script Reports(Python + JS)を使ってレポートを作成します。
使用する場面
- データ分析やサマリーレポートを作成するとき
- SQL ベースのクエリレポートを作成するとき
- Python ロジックと JS UI を使って複雑なレポートを実装するとき
- レポートにカスタムフィルター、フォーマッター、チャートを追加するとき
- 印刷用のレポート形式を作成するとき
必要な入力
- レポートの目的とデータ要件
- レポートのソース DocType
- フィルター要件
- 列の定義(フィールド、型、書式)
- レポートが標準(アプリに同梱)かカスタム(サイト固有)か
手順
0) レポートの種類を選択
| 種類 | 複雑さ | コードの必要性 | 最適な用途 |
|---|---|---|---|
| Report Builder | 低 | なし | シンプルなフィールド選択、グループ化、並べ替え |
| Query Report | 中 | SQL のみ | 直接的な SQL クエリ、結合、集計 |
| Script Report | 高 | Python + JS | 複雑なロジック、計算フィールド、動的フィルター |
1) Report Builder
コードを使わず、UI から作成します。
- Report リストに移動し、New Report を選択します
- Reference DocType を選択します
- Report Type = "Report Builder" を選択します
- ビルダー UI から列、フィルター、並べ替え、グループ化を追加します
2) Query Report
生の SQL クエリを使ったレポートです。
- Create Report → Type = "Query Report" を選択します
- Reference DocType を設定します(権限を制御します)
- SQL クエリを記述します
SELECT
`tabSales Order`.name AS "Sales Order:Link/Sales Order:200",
`tabSales Order`.customer AS "Customer:Link/Customer:200",
`tabSales Order`.transaction_date AS "Date:Date:120",
`tabSales Order`.grand_total AS "Grand Total:Currency:150",
`tabSales Order`.status AS "Status:Data:100"
FROM `tabSales Order`
WHERE `tabSales Order`.docstatus = 1
{% if filters.company %}
AND `tabSales Order`.company = %(company)s
{% endif %}
{% if filters.from_date %}
AND `tabSales Order`.transaction_date >= %(from_date)s
{% endif %}
ORDER BY `tabSales Order`.transaction_date DESC
SELECT の列形式: "Label:Fieldtype/Options:Width"
| Fieldtype | 例 |
|---|---|
| Link | "Customer:Link/Customer:200" |
| Currency | "Amount:Currency:150" |
| Date | "Date:Date:120" |
| Int | "Quantity:Int:100" |
| Data | "Status:Data:100" |
フィルター変数: パラメーター化クエリには %(filter_name)s を使用します。
3) Script Report(標準)
Python + JS を完全に制御できる、アプリに同梱されたレポートを作成します。
レポートの構成を作成します。
my_app/
└── my_module/
└── report/
└── sales_summary/
├── sales_summary.json # Report metadata
├── sales_summary.py # Python data logic
└── sales_summary.js # JS filters and UI
Python スクリプト(sales_summary.py):
import frappe
from frappe import _
def execute(filters=None):
columns = get_columns()
data = get_data(filters)
chart = get_chart(data)
return columns, data, None, chart
def get_columns():
return [
{
"label": _("Customer"),
"fieldname": "customer",
"fieldtype": "Link",
"options": "Customer",
"width": 200
},
{
"label": _("Total Orders"),
"fieldname": "total_orders",
"fieldtype": "Int",
"width": 120
},
{
"label": _("Total Amount"),
"fieldname": "total_amount",
"fieldtype": "Currency",
"width": 150
},
{
"label": _("Average Order"),
"fieldname": "avg_order",
"fieldtype": "Currency",
"width": 150
}
]
def get_data(filters):
conditions = get_conditions(filters)
data = frappe.db.sql("""
SELECT
customer,
COUNT(name) as total_orders,
SUM(grand_total) as total_amount,
AVG(grand_total) as avg_order
FROM `tabSales Order`
WHERE docstatus = 1 {conditions}
GROUP BY customer
ORDER BY total_amount DESC
""".format(conditions=conditions), filters, as_dict=True)
return data
def get_conditions(filters):
conditions = ""
if filters.get("company"):
conditions += " AND company = %(company)s"
if filters.get("from_date"):
conditions += " AND transaction_date >= %(from_date)s"
if filters.get("to_date"):
conditions += " AND transaction_date <= %(to_date)s"
return conditions
def get_chart(data):
if not data:
return None
return {
"data": {
"labels": [d.customer for d in data[:10]],
"datasets": [{
"name": _("Total Amount"),
"values": [d.total_amount for d in data[:10]]
}]
},
"type": "bar"
}
JavaScript スクリプト(sales_summary.js):
frappe.query_reports["Sales Summary"] = {
filters: [
{
fieldname: "company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company"),
reqd: 1
},
{
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.datetime.add_months(frappe.datetime.get_today(), -1)
},
{
fieldname: "to_date",
label: __("To Date"),
fieldtype: "Date",
default: frappe.datetime.get_today()
}
],
onload(report) {
// Custom initialization
},
formatter(value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
// Highlight high-value customers
if (column.fieldname === "total_amount" && data.total_amount > 100000) {
value = `<span style="color: green; font-weight: bold">${value}</span>`;
}
return value;
}
};
レポート JSON(sales_summary.json):
{
"name": "Sales Summary",
"doctype": "Report",
"report_type": "Script Report",
"ref_doctype": "Sales Order",
"module": "My Module",
"is_standard": "Yes",
"disabled": 0
}
4) レポートの印刷形式を追加
カスタム印刷レイアウトを作成するには、レポートフォルダーに sales_summary.html を作成します。
<h2>Sales Summary Report</h2>
<table class="table table-bordered">
<tr>
<th>Customer</th>
<th>Orders</th>
<th>Total</th>
</tr>
{% for row in data %}
<tr>
<td>{{ row.customer }}</td>
<td>{{ row.total_orders }}</td>
<td>{{ frappe.format(row.total_amount, {fieldtype: 'Currency'}) }}</td>
</tr>
{% endfor %}
</table>
5) hooks にレポートを登録(任意)
標準のディレクトリー構成に従っていれば、レポートは自動検出されます。標準レポートには hooks.py のエントリーは不要です。
検証
- レポートが Report リストに表示される
- フィルターが正しく機能し、結果に反映される
- 列が適切な書式で表示される
- チャートが描画される(該当する場合)
- 権限が適用される(許可されたユーザーのみがデータを閲覧できる)
- 印刷形式が機能する
- 想定されるデータ量に対して十分なパフォーマンスが得られる
失敗のパターン/デバッグ
- レポートが見つからない: モジュールパスと
is_standardの設定を確認し、bench migrateを実行します - SQL 構文エラー: まず
bench --site <site> mariadbでクエリをテストします - データが返されない:
docstatusフィルターを確認し、フィルターがデータと一致していることを確認します - 権限が拒否される: ユーザーのロールに対する Reference DocType の権限を確認します
- クエリが遅い: インデックスを追加し、Query Builder を使用して、結果セットを制限します
エスカレーション
- DocType スキーマについて →
frappe-doctype-development - API エンドポイント(API 経由のレポートデータ)について →
frappe-api-development - Desk UI のカスタマイズについて →
frappe-desk-customization
参考資料
- references/reports.md — レポートの種類、作成方法、例
ガードレール
- フィルターを検証する: クエリの構築前にフィルター値を確認し、空または無効な入力を処理します
- 空の結果を処理する: クエリからデータが返されない場合に備えて、必ず処理を行い、適切なメッセージを表示します
frappe.db.escape()を使用する: インジェクションを防ぐため、SQL クエリ内のユーザー入力をエスケープします- 結果セットを制限する: 大規模なデータセットには LIMIT 句またはページネーションを追加します
- execute 内で権限を確認する: ユーザーにデータを閲覧する権限があることを確認します
よくある間違い
| 間違い | 失敗する理由 | 修正方法 |
|---|---|---|
| フィルター経由の SQL インジェクション | セキュリティ上の脆弱性 | frappe.db.escape() を使用するか、パラメーター付きの Query Builder を使用します |
| 権限チェックの欠落 | 権限のないデータアクセス | frappe.has_permission() を確認するか、許可されたレコードでフィルターします |
| 上限のないクエリ | タイムアウト、メモリの問題 | LIMIT を追加する、ページネーションを使用する、または日付範囲でフィルターします |
| 列のフィールドタイプの誤り | 書式設定の問題 | 列の fieldtype をデータ(Currency、Date など)に合わせます |
| 集計で None を処理しない | エラーや誤った合計 | SQL で COALESCE() または IFNULL() を使用します |
docstatus の前提のハードコード | 下書き/キャンセル済みレコードの欠落 | レポートの要件に応じて docstatus を明示的にフィルターします |
コメント
Markdown 対応