MY HATCHロゴ

MyHatch

やってやるぜ!を全力応援

サーバースクリプト

ERPNextのサーバースクリプトで、User作成時にEmployeeを自動生成する方法を解説します。

7分
最終更新: 2025年9月6日

サーバースクリプト

ERPNextのサーバースクリプト活用:
ユーザと従業員を自動連携する手順

ERPNextを導入したけれど、ユーザー登録と従業員登録を二重で行うのが手間… と感じていませんか?
本記事では、サーバースクリプト(Server Script)を使って User作成と同時にEmployeeを自動生成する方法 を解説します。

はじめに

ERPNextには標準で以下の 5つのカスタマイズ手段 があります。

  • カスタムフィールド
  • カスタムフォーム
  • クライアントスクリプト
  • サーバースクリプト
  • アプリ

用途に応じて段階的に拡張でき、現場に寄り添った調整が可能です。

本記事では バックエンド処理を軽量・柔軟に変えられるサーバースクリプト に絞り、 実際の使い方を交えてご紹介します。

今回は、User作成と同時にEmployeeを自動生成する方法を解説します。

外部アプリを作らなくても、ERPNext標準の Server Script機能 と短いPythonコードだけで実現できます。

またERPNextは 本体とカスタムの分離、および 公式Hook/APIによる拡張設計 により、
アップデートに強い「壊れにくいカスタマイズ」を実現できます。
(詳細は別記事 壊れないカスタマイズクライアントスクリプト をご覧ください。)

では、まずは最小構成でサーバースクリプトを体験し、段階的に理解を広げていきましょう。


サーバースクリプト(Server Script)とは?

サーバースクリプト(Server Script)とは、ERPNextのサーバー側(Python)で動作する 小さな処理を埋め込む仕組み です。

イメージとしては、SalesforceのApexトリガーkintoneのWebhook+サーバー関数 に近く、
ドキュメントのイベントにフックして自動処理や連携を走らせる ことができます。

内部では frappe API(DBアクセス、ドキュメント操作、メッセージ出力など) にアクセスできるため、
カスタムアプリを作らなくても「ちょっとした自動化」を即実装できます。

クライアントスクリプトがUI改善を担うのに対して、
サーバースクリプトは 業務ロジックの自動化・裏側の制御 を担う位置づけです。

例えば

  • ドキュメントイベント処理

    • Userが作られたらEmployeeも自動作成
    • Sales Invoiceが確定したら在庫を引き落とす
  • 定期バッチ

    • 毎晩の請求書リマインド送信
    • 未処理タスクの自動通知
  • APIエンドポイント

    • 外部サービスから呼び出せる簡易Webhook
    • 独自のRESTエンドポイントを追加
  • 権限制御・検証

    • 保存前に独自のビジネスルールをチェック
    • 条件に合わない場合はエラーを返す

など、
「アプリ化するまでもない処理を、ERPNext本体に安全に差し込める」軽量拡張 が可能です。


実装ステップ

では、実際にサーバースクリプトを登録してみましょう。

ここでは 「ユーザーを作成したら、自動的に従業員レコードも作成される」最小構成 を実装します。

手順

  1. 設定 → カスタマイズ → サーバースクリプト へ移動
    手順1

  2. 新規作成 → 下記を設定

    • Doctype: ユーザ
    • Script Type: DocType Event
    • Event: 挿入後
      手順2
  3. 下記コードを貼り付け → 保存

# Website User は対象外
def split_first_last(name):
    name = (name or "").strip()
    if not name:
        return {"first": "", "last": ""}
    p = name.find(" ")
    if p == -1:
        return {"first": name, "last": ""}
    return {"first": name[:p], "last": name[p+1:]}
 
# ループ防止
if not frappe.flags.get("sync_user_employee_in_progress"):
 
    u = doc  # User
 
    # Website User は対象外(必要ならこの条件を外す)
    if u.user_type != "Website User":
 
        # 既に user_id で紐づいた Employee があれば何もしない
        if not frappe.db.exists("Employee", {"user_id": u.name}):
 
            # メール一致で既存Employeeを探索
            employee_name = None
            if u.email:
                employee_name = (
                    frappe.db.get_value("Employee", {"company_email": u.email}, "name")
                    or frappe.db.get_value("Employee", {"personal_email": u.email}, "name")
                )
 
            frappe.flags["sync_user_employee_in_progress"] = True
            try:
                if employee_name:
                    emp = frappe.get_doc("Employee", employee_name)
                    if not emp.user_id:
                        emp.db_set("user_id", u.name, update_modified=True)
                else:
                    # ここでタプルアンパックを使わない
                    basis = (u.full_name or u.first_name or u.username or "").strip()
                    # 最速・禁則回避の名前分解
                    sp = basis.find(" ")
                    if sp == -1:
                        first = basis
                        last = ""
                    else:
                        first = basis[:sp]
                        last = basis[sp+1:]
 
                    emp = frappe.get_doc({
                        "doctype": "Employee",
                        "first_name": first or (u.first_name or u.username),
                        "last_name": last,
                        "status": "Active",
                        "company": "MyHaTch ホールディングス",
                        "date_of_joining": frappe.utils.today(),
                        "user_id": u.name,
                        "personal_email": u.email or None,
                    })
                    emp.insert(ignore_permissions=True)
            finally:
                frappe.flags["sync_user_employee_in_progress"] = False
 

4. コード解説

def split_first_last(name)

  • 名前の文字列を「最初の半角スペース」で名/姓に分ける。
  • if not frappe.flags.get("sync_user_employee_in_progress"):

  • 再入防止フラグです。User保存 → Employee作成 → User更新…の無限ループを防ぐスイッチ。最初にONにして、finallyで必ずOFFに戻します。
  • emp.insert(ignore_permissions=True)

  • 権限チェックを無視して Employee を強制作成します。自動連携などのバックグラウンド処理で確実に作るために使います(ログは残るので、信頼できる場面だけで使うのが前提)
  • 5. 留意点

    サーバースクリプトはとても便利ですが、いくつか注意すべき点があります。
    よくハマるポイントを以下に整理します。

    1. Safe Exec による制限

    サーバースクリプトは RestrictedPython という安全モードで動きます。
    これは「自由にコードが書ける代わりに、危険な操作は禁止するルール」と考えてください。

    • import 文が使えません
      frappefrappe.utils はOK)
    • getattr / setattr / タプルアンパック(a, b = ...)などは無効化
    • 代わりに frappe.flags.get() など安全APIを使います

    2. doc オブジェクトの扱い

    イベントスクリプトでは doc という変数に処理対象のドキュメントが入っています。
    ただし関数の中に閉じ込めると参照できなくなることがあるため、
    トップレベルで if を並べるシンプルな書き方が推奨です。

    3. エラーとデバッグ

    • 入力が不正な場合は frappe.throw() でユーザーに警告できます
    • デバッグは frappe.log_error("内容", "タイトル")Error Log に残せます
    • v15には「Server Script Log」というDocTypeは存在しないので、Error Logで統一管理されます

    5. パフォーマンスへの影響

    サーバースクリプトは「保存処理の最中に実行」されるため、処理が重いと画面全体が遅くなります。
    外部サービス連携など時間がかかる処理は、バックグラウンド処理に任せるのが適切です。

    6. 運用上の注意

    サーバースクリプトはデータベースに保存されるため、Gitなどのコード管理には含まれません。
    そのため、export-fixturesでJSONに出力してリポジトリに含めるなど、
    追跡できる運用ルールを決めておくことが大切です。

    6. まとめ

    サーバースクリプトは “自社の業務ロジックを自動化” するための最短ルート です。

    • SalesforceのApexトリガーやkintoneのWebhook関数のように使える
    • ERPNext本体とは分離された仕組みで、公式のHook/APIに基づいて動作する
    • そのため、アップデートに強く、壊れにくい拡張が可能

    さらにAIの補助を使えば、こうした小さな自動化コードは誰でも短時間で書ける時代になりました。
    オープンソースであるERPNextは、そのための 最良の“発射台” と言えます。

    複数システムやExcelで分散管理している現場も、ERPNextで一元化すれば作業負担を大きく減らせます。
    しかも、導入コストは大手ERPに比べて圧倒的に低い水準です。

    ぜひERPNextとサーバースクリプトを活用し、業務の自動化を加速させてください。
    導入・運用の伴走は、私たち MyHatch にお任せください。


    よくある質問(FAQ)

    Q. サーバースクリプトはアップデートで壊れませんか?

    ERPNextのサーバースクリプトは 本体コードと分離 して管理されるため、 アップデートによる直接的な破損リスクは小さいです。
    ただし、Doctypeのフィールド構成やAPI仕様が変更された場合は影響を受ける可能性があります。


    そのため、定期的にテスト環境でアップデートを試し、 サーバースクリプトが正常に動作するかを確認する運用が推奨されます。

    Q. サーバースクリプトとクライアントスクリプトの違いは?

    • クライアントスクリプト はブラウザ(フロント側)で動作し、フォーム入力やUIの挙動を制御します。
      例: フィールドの自動入力、保存前バリデーション、UIエフェクト。


    • サーバースクリプト はERPNextのサーバー(バックエンド側)で動作し、 データの保存時イベントやバッチ処理、API連携などを制御します。
      例: User作成時にEmployeeを自動生成、毎晩のリマインド送信。


    → UIを変えるならクライアントスクリプト、業務ロジックを自動化するならサーバースクリプト、という住み分けです。

    Q. サーバースクリプトはどこまで自由に書けますか?

    サーバースクリプトは RestrictedPython(安全サンドボックス) 上で動作するため、
    通常のPythonと違い制約があります。

    • import は原則禁止(frappeとfrappe.utilsは利用可)
    • 危険な組み込み(getattr/setattr、タプルアンパックなど)は無効化
    • DBアクセスやDoc操作は frappe APIを通じて行う

    小さな自動化や検証に適しており、複雑な処理はカスタムアプリで実装するのが推奨です。

    まだ疑問が残りますか?

    この記事で解決しない疑問は、無料相談でお気軽にご質問ください。ERPNext導入の専門家が直接お答えします。