frappe-impl-serverscripts について
Frappe 開発者向けに、Server Script でサーバー側機能を実装する手順をまとめたガイドです。制限付きサンドボックスでのドキュメントイベント、API、スケジュール処理、権限クエリを扱い、Controller への移行時期も示します。
主な機能
- 01スクリプト種別の選択と設定
- 02ドキュメントの検証とフィールド計算
- 03API とスケジューラー用スクリプトの作成
- 04サンドボックス対応 Frappe API の利用
- 05Controller へ移行する時期の判断
活用シーン
- 01保存前のフィールド検証や値の計算
- 02Frappe で認証付き API エンドポイントを作成
- 03定期リマインダーや権限クエリ条件の設定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ impertio-studio/ frappe_claude_skill_package --skill frappe-impl-serverscriptsインストール後、Claude Code で/frappe-impl-serverscriptsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/anti-patterns.mddecision-tree.mdexamples.mdworkflows.md詳細
サーバースクリプト — 実装ワークフロー
カスタムアプリを使わずにサーバー側の機能を構築するためのステップごとのワークフローです。正確な構文については、frappe-syntax-serverscriptsを参照してください。
バージョン:v14/v15/v16 | v15 以降の注意:サーバースクリプトはデフォルトで無効です。bench set-config server_script_enabled trueで有効にしてください。
重要:サンドボックスの制限
ALL IMPORTS BLOCKED — RestrictedPython sandbox
import json → ImportError: __import__ not found
from frappe.utils → ImportError
import requests → ImportError
SOLUTION: Use pre-loaded namespace:
frappe.utils.nowdate() frappe.utils.flt()
frappe.parse_json(data) json.loads() (json IS available)
frappe.as_json(obj) json.dumps()
frappe.make_get_request(url) (replaces requests.get)
ルール:json以外のimport文が必要な場合は、必ず代わりにコントローラーを使用してください。
ワークフロー 1:サーバースクリプトを作成する
- サーバースクリプトを有効にします:
bench set-config server_script_enabled true - Setup > Server Scriptに移動します(またはawesomebarで「New Server Script」を選択します)。
- Script Typeを選択します(下の判断ツリーを参照してください)。
- タイプ固有の設定を行います(DocType、イベント、APIメソッド、cron)。
- エディターにスクリプトを記述します。
- 保存します。スクリプトはすぐに有効になります。
- 設定したイベントを実行してテストします。
- 「Compare Versions」ボタンを使って変更を比較します。
ワークフロー 2:スクリプトのタイプを選択する
WHAT DO YOU NEED?
│
├── React to document save/submit/cancel?
│ └── Document Event
│ └── Select DocType + Event (Before Save, After Save, etc.)
│
├── Create a REST API endpoint?
│ └── API
│ └── Set method name + guest access setting
│ └── Endpoint: /api/method/{method_name}
│
├── Run task on schedule (daily/hourly/cron)?
│ └── Scheduler Event
│ └── Set cron pattern or frequency
│
└── Filter list views per user/role?
└── Permission Query
└── Select DocType — set `conditions` variable
完全な判断ツリーについては、references/decision-tree.mdを参照してください。
ワークフロー 3:ドキュメントイベント:検証
目的:保存前にSales Orderを検証します。
手順 1:「Before Save」イベントを選択します。これはvalidateフックに対応します。
手順 2:サンドボックスで安全に実行できるスクリプトを記述します。
# Type: Document Event | Event: Before Save | DocType: Sales Order
errors = []
if not doc.customer:
errors.append("Customer is required")
if doc.delivery_date and doc.delivery_date < frappe.utils.today():
errors.append("Delivery date cannot be in the past")
for item in doc.items:
if item.qty <= 0:
errors.append(f"Row {item.idx}: Quantity must be positive")
if errors:
frappe.throw("<br>".join(errors), title="Validation Error")
ルール:
- 必ずエラーを収集してから一度だけスローします(複数回スローするよりユーザー体験が向上します)。
- Before Saveで
doc.save()を呼び出してはいけません。フレームワークが処理します。 - 必ず
frappe.throw()を使用してください。msgprintでは保存は停止しません。
ワークフロー 4:ドキュメントイベント:自動計算
目的:合計を自動計算し、派生フィールドを設定します。
# Type: Document Event | Event: Before Save | DocType: Purchase Order
doc.total_qty = sum(item.qty or 0 for item in doc.items)
doc.total_amount = sum((item.qty or 0) * (item.rate or 0) for item in doc.items)
if doc.total_amount > 50000:
doc.requires_approval = 1
doc.approval_status = "Pending"
if doc.supplier and not doc.supplier_name:
doc.supplier_name = frappe.db.get_value("Supplier", doc.supplier, "supplier_name")
ルール:Before Saveでは必ずdocのフィールドを直接変更してください。変更は自動的に保存されます。
ワークフロー 5:ドキュメントイベント:関連ドキュメントを作成する
目的:新しいLeadが挿入されたときにToDoを作成します。
# Type: Document Event | Event: After Insert | DocType: Lead
frappe.get_doc({
"doctype": "ToDo",
"allocated_to": doc.lead_owner or doc.owner,
"reference_type": "Lead",
"reference_name": doc.name,
"description": f"Follow up with new lead: {doc.lead_name}",
"date": frappe.utils.add_days(frappe.utils.today(), 1),
"priority": "High" if doc.status == "Hot" else "Medium"
}).insert(ignore_permissions=True)
ルール:
- 関連ドキュメントの作成には、必ずAfter InsertまたはAfter Saveを使用してください。
- Before Saveでドキュメントを作成してはいけません。
doc.nameがまだ存在しない場合があります。 - システム生成ドキュメントには、必ず
ignore_permissions=Trueを使用してください。
ワークフロー 6:APIエンドポイント
目的:顧客データを返す認証済みREST APIを作成します。
# Type: API | Method: get_customer_dashboard | Allow Guest: No
# Endpoint: /api/method/get_customer_dashboard
customer = frappe.form_dict.get("customer")
if not customer:
frappe.throw("Parameter 'customer' is required")
# ALWAYS check permissions
if not frappe.has_permission("Customer", "read", customer):
frappe.throw("Access denied", frappe.PermissionError)
orders = frappe.db.count("Sales Order", {"customer": customer, "docstatus": 1})
revenue = frappe.db.get_value("Sales Invoice",
filters={"customer": customer, "docstatus": 1},
fieldname="sum(grand_total)") or 0
frappe.response["message"] = {
"customer": customer,
"total_orders": orders,
"total_revenue": revenue
}
ルール:
- 必ず入力パラメーターを検証してください。
- Allow Guestが「No」に設定されていても、必ず権限を確認してください。
- クエリの上限を必ず設定してください:
min(frappe.utils.cint(limit), 100) - ドキュメント全体を公開してはいけません。必要なフィールドのみを返してください。
ワークフロー 7:スケジューラーイベント
目的:期限超過の請求書に対する毎日のリマインダーを送信します。
# Type: Scheduler Event | Cron: 0 9 * * * (daily at 9:00)
BATCH_SIZE = 50
today = frappe.utils.today()
overdue = frappe.get_all("Sales Invoice",
filters={
"status": "Unpaid",
"due_date": ["<", today],
"docstatus": 1
},
fields=["name", "customer", "owner", "due_date", "grand_total"],
limit=BATCH_SIZE
)
for inv in overdue:
days = frappe.utils.date_diff(today, inv.due_date)
if not frappe.db.exists("ToDo", {
"reference_type": "Sales Invoice",
"reference_name": inv.name,
"status": "Open"
}):
frappe.get_doc({
"doctype": "ToDo",
"allocated_to": inv.owner,
"reference_type": "Sales Invoice",
"reference_name": inv.name,
"description": f"Invoice {inv.name} is {days} days overdue"
}).insert(ignore_permissions=True)
frappe.db.commit() # REQUIRED in scheduler scripts
ルール:
- スケジューラースクリプトの末尾には、必ず
frappe.db.commit()を追加してください。 - メモリの枯渇を防ぐため、クエリには必ず
limitを追加してください。 - ループ内では必ず
try/exceptとfrappe.log_error()を使用してください。 - 無制限のレコードを処理するスケジューラースクリプトを実行してはいけません。
ワークフロー 8:権限クエリ
目的:ユーザーが自分の担当区域の顧客のみを参照できるようにします。
# Type: Permission Query | DocType: Customer
user_territory = frappe.db.get_value("User", user, "territory")
user_roles = frappe.get_roles(user)
if "System Manager" in user_roles:
conditions = "" # Full access
elif user_territory:
conditions = f"`tabCustomer`.territory = {frappe.db.escape(user_territory)}"
else:
conditions = f"`tabCustomer`.owner = {frappe.db.escape(user)}"
ルール:
- System Managerには必ず全件アクセスを許可してください(
conditions = "")。 - SQLでユーザー入力を使う場合は、必ず
frappe.db.escape()を使用してください。 - 必ず
conditions変数を設定してください。これが出力になります。 - 権限クエリの影響を受けるのは
frappe.db.get_listのみで、frappe.db.get_allには影響しません。
イベント名の対応表
| UI名 | 内部フック | 最適な用途 |
|---|---|---|
| Before Validate | before_validate | 検証前のデフォルト値設定 |
| Before Save | validate | 検証と計算(最も一般的) |
| After Save | on_update | 通知、監査ログ |
| After Insert | after_insert | 関連ドキュメントの作成(新規のみ) |
| Before Submit | before_submit | 送信時の検証 |
| After Submit | on_submit | 送信後の自動処理 |
| Before Cancel | before_cancel | キャンセルの防止 |
| After Cancel | on_cancel | キャンセル後のクリーンアップ |
| Before Delete | on_trash | 削除の防止 |
サンドボックス対応 API クイックリファレンス
| 必要な処理 | 使用するもの(インポートしない) |
|---|---|
| JSON の解析 | frappe.parse_json() または json.loads() |
| JSON のシリアライズ | frappe.as_json() または json.dumps() |
| 今日の日付 | frappe.utils.today() |
| 現在の日時 | frappe.utils.now() |
| 日数の加算 | frappe.utils.add_days(date, n) |
| 日付の差 | frappe.utils.date_diff(d1, d2) |
| 浮動小数点数への変換 | frappe.utils.flt(val) |
| 整数への変換 | frappe.utils.cint(val) |
| HTTP GET | frappe.make_get_request(url) |
| HTTP POST | frappe.make_post_request(url, data) |
| テンプレートのレンダリング | frappe.render_template(tmpl, ctx) |
| エラーのログ記録 | frappe.log_error(msg, title) |
| メールの送信 | frappe.sendmail(recipients, subject, message) |
コントローラーへの移行が必要な場合
次の場合は必ず Document Controller に移行してください。
import文(json以外)が必要な場合- スクリプトが 100 行を超える場合
- ロールバックを伴う try/except が必要な場合
- バックグラウンドジョブに
frappe.enqueue()が必要な場合 - 既存の ERPNext DocType を拡張する必要がある場合
- 同じ DocType の複数のスクリプトを管理しにくくなった場合
移行方法:コントローラーの実装については frappe-impl-controllers を参照してください。
関連スキル
frappe-syntax-serverscripts— サンドボックス API の正確なリファレンスfrappe-errors-serverscripts— エラー処理とアンチパターンfrappe-core-database—frappe.db.*の操作frappe-core-permissions— 権限システムの詳細frappe-impl-controllers— Server Script からの移行時期
完全な意思決定ツリーについては、references/decision-tree.md を参照してください。 拡張パターンについては、references/workflows.md を参照してください。 10 件以上の完全な例については、references/examples.md を参照してください。