mcpField
F
frappe-impl-serverscripts

frappe-impl-serverscripts

github.com/impertio-studio開発198109 回インストール30 週間前に更新
リポジトリ

frappe-impl-serverscripts について

Frappe 開発者向けに、Server Script でサーバー側機能を実装する手順をまとめたガイドです。制限付きサンドボックスでのドキュメントイベント、API、スケジュール処理、権限クエリを扱い、Controller への移行時期も示します。

主な機能

  1. 01スクリプト種別の選択と設定
  2. 02ドキュメントの検証とフィールド計算
  3. 03API とスケジューラー用スクリプトの作成
  4. 04サンドボックス対応 Frappe API の利用
  5. 05Controller へ移行する時期の判断

活用シーン

  1. 01保存前のフィールド検証や値の計算
  2. 02Frappe で認証付き API エンドポイントを作成
  3. 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.md
decision-tree.md
examples.md
workflows.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:サーバースクリプトを作成する

  1. サーバースクリプトを有効にします:bench set-config server_script_enabled true
  2. Setup > Server Scriptに移動します(またはawesomebarで「New Server Script」を選択します)。
  3. Script Typeを選択します(下の判断ツリーを参照してください)。
  4. タイプ固有の設定を行います(DocType、イベント、APIメソッド、cron)。
  5. エディターにスクリプトを記述します。
  6. 保存します。スクリプトはすぐに有効になります。
  7. 設定したイベントを実行してテストします。
  8. 「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 Validatebefore_validate検証前のデフォルト値設定
Before Savevalidate検証と計算(最も一般的)
After Saveon_update通知、監査ログ
After Insertafter_insert関連ドキュメントの作成(新規のみ)
Before Submitbefore_submit送信時の検証
After Submiton_submit送信後の自動処理
Before Cancelbefore_cancelキャンセルの防止
After Cancelon_cancelキャンセル後のクリーンアップ
Before Deleteon_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 GETfrappe.make_get_request(url)
HTTP POSTfrappe.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 を参照してください。

コメント