mcpField
O
odoo-workflow

odoo-workflow

github.com/unclecatvn開発14856 回インストール2 週間前に更新
リポジトリ

odoo-workflow について

odoo-workflowは、変更前にOdooのコードと参照関係を追跡する開発者向けスキルです。バージョン対応のヘルパーコマンドと根拠を示すコンテキスト要約を用い、Python、XML、JavaScript、データの変更に伴う誤りを抑えます。

主な機能

  1. 01バージョンと実行環境の特定
  2. 02モデル、フィールド、メソッドの追跡
  3. 03XML、JavaScript、参照箇所の検索
  4. 04根拠に基づくコンテキスト要約
  5. 05テストとマイグレーションの手順

活用シーン

  1. 01変更前にフィールドの存在を確認
  2. 02適切なメソッドオーバーライドの特定
  3. 03継承ビューのアンカー確認
  4. 04マイグレーション前の依存関係追跡

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/unclecatvn/agent-skills --skill odoo-workflow

インストール後、Claude Code で/odoo-workflowを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
definition-of-done.md
scripts/
odoo_trace.py
templates/
CLAUDE.md

詳細

Odoo ワークフロー - まず追跡し、それからコードを書く

Odoo の変更すべてに適用する手順です。コストのかかる失敗はいつも同じです。存在しないフィールドやメソッド、実際には存在しなかった xpath アンカー、誤ったメソッドにフックされたオーバーライド、マイグレーションせずに名前を変更した保存フィールド、監査できない sudo()、テストを一件も実行しなかったテスト実行などです。解決策は機械的です。根拠を示せないなら、コードを書かないこと。

適用される場合

  • フルパス - Odoo の Python、XML、JS、またはデータに関わるものすべてです。新しいフィールド、オーバーライド、継承ビュー、OWL パッチ、ウィザード、cron、コントローラー、レポート、セキュリティ、マイグレーション、バグ修正、リファクタリング、「最適化」、「クリーンアップ」が該当します。貼り付けられたトレースバックもここから始めます。ワークスペース内のモジュールにある最も内側のフレームが、最初の根拠です。
  • 概要のみ - Odoo のシンボルに関する質問(「X はどこで定義されていますか」、「sale.order に F はありますか」、「19 ではこのフィールドはどのモデルにありますか」など)では、ステップ 0~2 を実行し、概要を使ってコードなしで回答します。
  • ファストパス - 変更で編集対象のモジュール外のシンボルに触れず、フィールド、オーバーライド、xmlid、xpath、グループ、depends のいずれも追加しない場合です。たとえば、モジュール自身のコードにあるラベル、ヘルプテキスト、誤字、マニフェストのバージョン更新、テストのみの変更が該当します。概要には編集した行を示す一行を記載します。ステップ 1、3、6 は省略し、ステップ 5 は引き続き適用します(文字列を変更した場合は i18n が必要です)。判断に迷った場合はフルパスを使います。
  • ドキュメントのみ、または git のみのタスクは対象外です(odoo-commit を使います)。
  • ブレインストーミングと計画のスキルを使うのはステップ 2 の後で、ビジネス上の意図がまだ不明な場合に限ります。コンテキスト概要を入力として使います。

ステップ 0 - ランタイム、バージョン、パック

同梱のヘルパー scripts/odoo_trace.py(標準ライブラリのみを使う Python 3.8 以降、読み取り専用)は、ランタイムを特定し、Python 側のトレースに関する質問に回答します。呼び出すたびに、スキルのベースディレクトリをフルパスで指定してください。シェル変数はツール呼び出し間で保持されません。以下では、odoo_trace は python3 <skill_dir>/scripts/odoo_trace.py を表します。

odoo_trace env

最初に見つかったものを使用します。フラグ、.claude/odoo.json、現在の作業ディレクトリを対象とするアドオンを指定した .claude/launch.json 内の odoo-bin 設定(両ファイルとも現在の作業ディレクトリ、または最も近い親ディレクトリにあるもの)、次に addons_path が現在の作業ディレクトリを対象とする *.conf を親ディレクトリから上に向かって検索します(各階層とそのサブディレクトリを検索)。version(odoo/release.py から取得)、odoo_root、conf、python、dev_db、および Odoo が使用する順序でアドオンのルート(モジュール名を含む最初のルートが優先)を出力します。.claude/odoo.json の odoo_version または .odoo-version ファイルのバージョンは、release.py と一致する必要があります。

  • 終了コード 2 の場合、SETUP ERROR: と原因が出力されます。ランタイムを特定できない場合は、コアのチェックアウト先と conf の場所を一度だけ尋ね、毎回の呼び出しで(roots を含めて)odoo_trace --odoo-root <core> --conf <conf> <command> に渡します。また、odoo_version、odoo_root、conf、python、dev_db(マシン固有のパス)を含む、gitignore 対象の .claude/odoo.json を作成するよう提案します。バージョンの情報源が一致しない場合、または現在の作業ディレクトリを対象とする conf が複数ある場合は、どれが正しいか尋ねます。カスタムルートだけを使ってトレースしてはいけません。
  • ユーザーまたはプロジェクトの CLAUDE.md / AGENTS.md に明示された内容は、ヘルパーより優先されます。内容が食い違う場合は停止して確認します。バージョンを暗黙に決めつけてはいけません。
  • そのメジャーバージョン用の Odoo リファレンスパックを読み込みます。skills CLI では odoo-<major>、Claude Code プラグインでは <plugin>:odoo-<major>-0(例:agent-skills:odoo-18-0)を使います。references/api-highlights.md(このバージョンで削除または名前が変更された API)と、タスクに必要なガイドのセクションだけを読みます(grep -n '^## ' <guide> を実行してから、該当範囲を読みます)。パックが見つからない場合は、その旨を伝えて続行します。正しい情報源は実際のソースコードであり、記憶ではありません。

ステップ 1 - 呼び出し経路を追跡する

Python シンボル:ヘルパー。 ast でクラスを解析し、バージョンごとの _name ルールを適用し、_inherit の親(mail.thread、portal.mixin などのミックスイン)、_inherits による委譲、base の拡張、コアの BaseModel をたどります。また、モジュールがインポートしないファイル内のクラスに UNIMPORTED の印を付け、絶対パスの file:line を出力します。

質問コマンド
MODEL、そのミックスイン、type、_auto、_log_access を定義/拡張するクラスodoo_trace model MODEL
FIELD は MODEL に存在するか(独自定義、親、_inherits、マジックフィールドを含む)。すべての定義と comodel。ドット区切りのパスは各段階で確認されますodoo_trace field MODEL FIELD[.FIELD...]
super() の順序での METHOD の全定義(#1 が最初に実行)、super() 呼び出し、フック、AMBIGUOUS の組み合わせodoo_trace method MODEL METHOD --module MODULE
MODULE の推移的な depends(読み込み順)odoo_trace depends MODULE
xmlid は存在するか(読み込まれる XML レコードまたは CSV 行、あるいはインストール時に生成されるもの)odoo_trace xmlid module.name
読み込まれない MODULE のファイル(data/assets に含まれず、インポートもされないもの)odoo_trace unlisted MODULE

最後の行は VERDICT です。終了コード 0 = 見つかった、1 = NOT FOUND(verdict には最も近い実在のシンボルが示されます)、2 = セットアップエラーまたは回答不明です。NOT FOUND のセルを使えるのは終了コード 1 の場合だけです。ハブモデル(sale.order、mail.thread)のリストは上限があります。model、method、unlisted の後ろに --all を付けると、リスト全体が出力されます。

XML、JS、コントローラー、使用箇所:同じルートを grep で検索します。 以下の ROOTS は、完全な形で記述した $(odoo_trace roots) を意味します。zsh と bash ではコマンド置換がルートごとに 1 つの引数に分割されますが、単なる $ROOTS 文字列は zsh では分割されません。--include の glob を囲む引用符は残してください。No such file or directory という警告が出る grep、または終了コード 2 の grep は、NOT FOUND ではなくコマンドの不具合です。odoo_trace env が終了コード 2 を返しているときに実行した grep も同様です(空の $(odoo_trace roots) により、grep -r はカレントディレクトリだけを検索します)。また、パスに空白を含むルートを対象にした grep も該当します。パスが分割され、macOS の grep はその断片を黙ってスキップするため、そのルートは引用符で囲んで個別に検索してください。

# V1. Views on MODEL
grep -rlE --include='*.xml' "<field name=\"model\">MODEL</field>" ROOTS
# V2. Every view and QWeb template inheriting VIEW; -A1 also catches inherit_id split over two lines
grep -rnE -A1 --include='*.xml' "name=['\"]inherit_id['\"]|inherit_id=['\"]" ROOTS | grep -E ":[0-9]+:.*(ref|inherit_id)=['\"](MODULE\.)?VIEW['\"]|-[0-9]+-\s*ref=['\"](MODULE\.)?VIEW['\"]"
# V3. ANCHOR nodes inside one view record, absolute lines. A = the id line, B = the next </record>: grep -nE 'id="VIEW"|</record>' VIEW_FILE
awk -v a=A -v b=B 'NR>=a && NR<=b && /name="ANCHOR"/ {print FILENAME":"NR": "$0}' VIEW_FILE
# W1. OWL / QWeb client template: definition and every t-inherit (extension or primary)
grep -rnE --include='*.xml' "t-name=['\"]TEMPLATE['\"]|t-inherit=['\"]TEMPLATE['\"]" ROOTS
# W2. JS class or component: its definition (= import path) and every patch of it
grep -rnE --include='*.js' "^export (default )?(class|function|const) NAME[^A-Za-z0-9_]|patch\(\s*NAME[^A-Za-z0-9_]" ROOTS
# C1. Route (the second pattern finds a decorator split over lines), then every subclass of
#     its controller class (an `import CLASS as ALIAS` hit: run the class grep again with ALIAS)
grep -rnE --include='*.py' "@(http\.)?route\(.*['\"]URL['\"]|^\s*(route=)?\[?\s*['\"]URL['\"]" ROOTS
grep -rnE --include='*.py' "^class \w+\(([^)]*[ ,])?([A-Za-z_.]+\.)?CLASS[,)]|import.*[ ,]CLASS as " ROOTS
# S1. ACL rows and record rules on MODEL (dots as underscores)
grep -rn --include='ir.model.access.csv' "model_MODEL_UNDERSCORED," ROOTS
grep -rlE --include='*.xml' "model_MODEL_UNDERSCORED['\"]" ROOTS | grep security
# U1. Every use of NAME before renaming, removing, or changing the signature of a field or method
grep -rnwE --include='*.py' --include='*.xml' --include='*.js' --include='*.csv' --include='*.scss' "NAME" ROOTS
# T1. Tests to mirror
ls BASE_MODULE/tests/; grep -rnE "def test_.*METHOD" MODULE/tests

U1 では MODEL に関するヒットを残し、ビュー、QWeb、@api.depends、related=、ドメイン、レコードルール、cron とサーバーアクションの code、<function>、ボタンの name=、JS の orm.call 文字列、テスト、マイグレーションをそれぞれ列挙します。一般的な名前(name、state、partner_id)の場合は、odoo_trace model MODEL が出力したモジュールと V1/V2 のビューに絞ってください。それでもヒットが 50 件を超える場合は、モジュールごとの件数を示し、続行する前に確認してください。

grep のヒットとして数えるのは、そのファイルが読み込まれる場合(読み込まれないファイルは odoo_trace unlisted MODULE に一覧表示されます)で、Python の場合は MODEL 自身のクラス内にあるものに限ります。同じファイル内の別のクラスにあるものは含めません。

読み取りの予算 - ここでトークンを使います。

  • ファイル全体ではなく、範囲を読み取ります。offset/limit を指定して読むか、awk 'NR>=A && NR<=B' を使います。行番号が 1 から振り直され、引用できなくなる sed -n 'A,Bp' | grep -n は使わないでください。1 ホップにつき最大 120 行です。
  • インラインでのホップは最大 3 回です。さらに深い処理やモジュールをまたぐ処理は、odoo-code-tracer エージェントに odoo_version、pack_dir(読み込まれたパックのディレクトリ)、roots、helper(ヘルパーの完全パスと、必要な env フラグ)を渡して依頼してください。インストールされている場合に限ります。それ以外の場合は、上記のコマンドを使って続けてください。
  • Brief ですでに引用したファイルは、再度読み込まないでください。

ステップ 2 - コンテキストブリーフ(引用なし、コードなし)

コードを書く前にブリーフを作成します。変更が関わるシンボルごとに 1 行とします(Odoo 18 の例):

レイヤーシンボル出典(ファイル:行)基本の動作変更
モデルpurchase.orderaddons/purchase/models/purchase_order.py:19_name、mail.thread を継承継承
メソッド+superbutton_approvepurchase_order.py:528;purchase_stock/models/purchase_order.py:117 のオーバーライドが _create_picking を呼び出すピッキングが存在するのは purchase_stock がある場合のみpurchase_stock に依存し、super を呼び出した後、X を実行
パスstock.picking partner_id.commercial_partner_id.vatstock/models/stock_picking.py:630 -> base/models/res_partner.py:304 -> :247Many2one、Many2one、Char関連フィールド
ビュー+xpathpurchase.purchase_order_formpurchase/views/purchase_views.xml:130、アンカー partner_ref :185、フォーム内にノードが1つ-アンカーの後にフィールドを追加

レイヤー:モデル / フィールド / メソッド+super / パス / ビュー+xpath / クライアント / コントローラー / セキュリティ / 会社 / データ / 移行 / i18n / テスト。

出典セルには、次のいずれかを正確に記載します。

  • file:line - 引用した行自体が主張を示しています(定義、レコード、アンカーノード)。
  • NOT FOUND - ヘルパーが1を返した場合、またはすべての ROOTS を対象に MODEL を検索して一致するものがなかった場合です。 実在する最も近いシンボルがある場合は、その名前を記載します。
  • UNCERTAIN: <不足している情報> - 読んでいないファイル、実行時に構築されるドメイン、またはアンカーを提供する別モジュールのビューを記載します。上記のコマンドで答えられる質問には使いません。
  • AMBIGUOUS: <modules> - 相互に依存していないモジュール内のオーバーライドを記載します。

file:line 以外の値を含むセルが1つでもある行はコードがないため、その行を削除するか、不足している depends を追加するか、質問してください。行数が15を超える場合はタスクを分割してください。行数を合わせるために行を削除してはいけません。

ルール:

  • 再定義されたフィールド。 odoo_trace field は、読み込み順にすべての定義を一覧表示します。変更が依存する属性を持つ定義を、それぞれ引用してください。再指定されていない属性は以前の値を維持します(selection_add はリストを拡張します)。ただし、新しいクラスが以前のクラスをサブクラス化していない場合は、以前の属性は破棄され、ヘルパーは final class と表示します。
  • パス(related=、@api.depends、depends=、domain=、domain_force)。ドット区切りのパスに対して field を1回呼び出し、各階層を1つずつ引用してください。階層が見つからない場合、または終端より前の階層がリレーショナルでない場合は、その階層で NOT FOUND になります。
  • 依存関係。 odoo_trace depends MODULE の対象外のモジュールでのみ定義されているシンボルは、現在のデータベースに存在していても、インストールされるとは限りません。依存関係を追加するか、その行を削除してください。
  • オーバーライドの順序。 記憶や grep の順序ではなく、odoo_trace method ... --module MODULE の結果を使用してください。最後に読み込まれたクラスが先に実行され、その super() が以前のクラスに到達します。変更が依存するオーバーライドを持つ各モジュールに対して、depends を宣言してください。AMBIGUOUS と表示されたペアは、AMBIGUOUS のままにしてください。
  • Xmlid。 odoo_trace xmlid の結果を基準にしてください。対象は、マニフェストが読み込む XML または CSV ファイル内のレコードか、インストール時に生成されるレコード(model_*、field_*__*、selection__*、base.module_*、チャートテンプレート由来の account.<company id>_<name>)で、いずれもヘルパーが示すモジュールに紐付いています。マニフェストが読み込まないファイルにしかないレコードは NOT FOUND です(init フックが読み込む可能性がある場合は UNKNOWN)。xmlid を決して捏造せず、生成された xmlid を見つからないものとして報告しないでください。ビューの groups= に指定されたグループが見つからなくても、インストールは停止しません。Odoo は WARNING を記録するだけで、存在しないグループに属するユーザーはいないため、そのノードは非表示になります。
  • マジックフィールド。 存在を決めつけないでください。odoo_trace field の結果を基準にしてください。create_uid、create_date、write_uid、write_date が存在するのは _log_access が true の場合だけです。デフォルト値は _auto なので、レポートモデルと SQL ビューモデル(_auto = False)にはこれらのフィールドはありません。
  • Xpath アンカー。 アンカーは、ベースビュー、または depends の対象モジュールに属する継承ビューに存在する必要があります。そうでない場合は UNCERTAIN です。ロケーターは最初のノードに一致するため、統合されたビュー(V2 + V3)でその名前を持つノードの数を明記してください。
  • 会社。 MODEL またはコムモデルに company_id がある場合は、それを引用し、そのモデルで _check_company_auto が設定されているかどうかも示してください。会社に紐付くコムモデルへの新しいリレーショナルフィールドには check_company=True を設定します。company_id を持つ新しいモデルには、コアが書き込む形式のレコードルールを設定します。[('company_id', 'in', company_ids)]、または会社が任意の場合は [('company_id', 'in', company_ids + [False])] です。
  • スキーマ変更。 保存フィールドの名前変更、削除、型変更、選択肢キーの追加・変更・削除、required の追加、データが入力済みのテーブルへの保存計算フィールドの追加、またはモデル名の変更には、migration 行が必要です。新しいマニフェストのバージョンと、データを移行する migrations/<version>/pre-*.py / post-*.py スクリプト(def migrate(cr, version))を記載してください。スクリプトは、そのバージョンがインストール済みのバージョンより上で、マニフェストのバージョン以下の場合にのみ実行されます。保存フィールドを移行スクリプトなしで名前変更すると、-u 時にその列とデータが警告なしに失われます。重要なデータベースにそのモジュールが一度もインストールされていないことをユーザーが確認した場合に限り、省略できます。手順はパックの移行ガイドにあります。
  • sudo。 追加した sudo() または with_user(SUPERUSER_ID) ごとに、どの ACL またはレコードルールをバイパスするか(S1)と、その前に行うチェック(18 以降では check_access / has_access、アクセス トークン、またはグループチェック)を示す security 行が必要です。行がなければ sudo も使用できません。
  • 新しく作るより再利用してください。フィールド、ヘルパー、計算フィールドを追加する前に、既存のものがないか確認してください。
  • 「クリーンなコード」や「ベースに沿う」とは、最小限の差分でベース実装のパターンに従い、super() がすでに行う処理を書き換えないことを意味します。「最適化」が速度を指す場合は、まず計測(名前を明記したケースでのクエリー数または実行時間)し、変更後にもう一度計測してください。

ステップ 3 - 最小限の計画

箇条書きは最大 10 項目です。変更するファイル、明示的に変更しないファイル、延期する内容を記載します。Brief に変更が 2 つのモジュールにまたがる根拠がない限り、1 つのモジュールに限定します。変更する Git リポジトリごとに、コミットと PR を 1 つずつ作成します。YAGNI:決して変わらない値の設定は追加しません。一度しか使わないヘルパーは作りません。実装が 1 つしかない抽象化は作りません。

ステップ 4 - 実装

  • このバージョンのパックにある Coding Conventions と api-highlights.md の一覧を適用します。個人の好みよりも、既存の stable-addon のスタイルを優先します。
  • 変更に必要なときに実行される最も限定的なフック(odoo_trace method が出力するフック)をオーバーライドし、それをラップする公開メソッドはオーバーライドしません。super() を呼び出して、最小限の差分を追加します。ベースの処理内容をコピーしてはいけません。
  • _inherit = 'model'(文字列)で拡張します。新しいモデルでは _name を明示的に設定します。_name のないリスト形式の _inherit は、クラス名に基づく名前で新しいモデルを作成します。19 では常に(警告が出ます)、18 ではリストに 2 項目以上ある場合に作成されます。
  • 新しい XML または CSV ファイルはすべて、読み込み順(グループ、ir.model.access.csv、ビュー、メニュー)に従ってマニフェストの data に追加します。新しい静的ファイルはすべて、適切な assets バンドルのエントリーに対応させます。一覧にないファイルは、インストール時にエラーにはなりませんが、何も行いません。
  • sudo() の命名規則:sudo() で取得したレコードセットはすべて、_sudo で終わる名前にバインドします(partner_sudo = self.partner_id.sudo()、orders_sudo = self.env['sale.order'].sudo().search(domain))。1 回限りの呼び出し(record.sudo().write(vals))では変数は不要です。公開メソッドから _sudo レコードセットを返したり、self に保持したりしてはいけません。
  • Odoo コードに ponytail: などの「とりあえず簡略化」といったマーカーを入れてはいけません。コードを正しくするか、ステップ 3 の計画に延期内容を記載します。
  • ユーザーに表示される新しい文字列は、抽出可能になるよう _()/_t()/string=/help= を通します。

ステップ 5 - 完了の定義

完了を報告する前に references/definition-of-done.md を読んでください。バージョンごとの正確なコマンドが記載されています。要約:

  • チェックリスト全体で使い捨てデータベース scratch_<module> を 1 つ使用します。モジュールのテストを実行しながらインストールし、テスト概要で N > 0 件のテストがカウントされていることを確認します(概要がない場合や of 0 tests の場合は合格ではありません)。ERROR/CRITICAL がなく、モジュールに関するすべての WARNING が説明されていることを確認します。ユーザーから依頼されない限り、名前付きデータベース(conf の db_name、ステージング、開発環境)に対して -i/-u を実行してはいけません。
  • Brief に migration 行がある場合は、以前のバージョンを使っているデータベースのコピーでアップグレードをテストします。
  • odoo_trace unlisted <module> で、すべてのファイルが読み込まれることを確認します。
  • 新しいモデルごとに ACL 行があり、会社ルールが適用される場合は会社ルールがあり、すべての sudo() に対応する security 行があることを確認します。
  • 文字列を変更した場合は i18n を再生成します。出荷する .po ファイルの名前は変更しません。
  • プロジェクト独自のリンターがある場合は、変更したファイルに対して合格することを確認します。
  • 変更を加えたすべての Git リポジトリで、衛生チェックに合格することを確認します。
  • スクラッチデータベースとそのフィルストアを削除します。odoo-commit を使ってコミットします([TAG] module: description、AI のクレジット表記なし)。リポジトリごとにコミットと PR を 1 つずつ作成します。

ステップ 6 - 引き継ぎ

odoo-code-review エージェントがインストールされている場合は、odoo_version、pack_dir、roots、helper を指定して実行します。インストールされていない場合は、完了を報告する前に references/definition-of-done.md のセキュリティと衛生の一覧を自分で確認します。

コメント