構成の全体像
- カスタマイズ台帳(Design Catalog)
- 形式:YAML/JSON(Git管理)
- 1レコード=1カスタマイズ(タイプごとにスキーマを持つ)
- 主キー:custom_id(例:
SO-SB-001)、xmlid(あれば) - 共通フィールド:
type(view/smart_button/act_window/server_action/automation/approval/menu/field/model…)、model、summary、owner、risk_level、jira、depends_on、created_at - 変更の期待挙動(acceptance)とガード条件を記述(→テストの元)
- メタ収集スクリプト(Introspector)
- Odoo API(
fields_view_get(load='full')、ir.model.fields、ir_ui_menu、ir.actions.*、base.automation、Studio承認モデルなど)で 現状メタをスナップショット - 生成物:facts.json(実体メタ:タブ、スマートボタン、メニュー、アクション、オートメーション、承認ルール…)
- Kubernetesなら CronJob で夜間に自動実行 → S3/Artifact へ保存 & GitにPR
- テスト生成器(Test Generator)
- 入力:台帳(YAML)+ facts.json
- 出力:テスト雛形:
- ユニット/ロジック:
unittest/pytest(サーバアクション、ドメイン、compute等) - UI/遷移:Playwright(スマートボタン/メニュー → 目的のビュー/ドメインに到達するか)
- API:XML-RPC/JSON-RPC のコール例(権限・レスポンス)
- 受け入れ(Gherkin):Cucumber的に人が読める仕様も並行生成
- ユニット/ロジック:
- 生成規則はタイプ別テンプレートで実装
- CI/CD(検証ライン)
- PRで台帳 or Odooモジュールが変わったら:
- Introspectorで facts.json 更新
- Test Generator が該当テストのみ再生成
- staging にデプロイ → Playwright/E2E と pytest 実行
- レポート/失敗時は台帳IDでフィードバック
台帳のスキーマ例(YAML)
a) スマートボタン
- custom_id: SO-SB-001
type: smart_button
model: sale.order
xmlid: my_mod.sb_sale_invoices
selector:
class: o_stat_button
type: action
name: sale.action_orders
badge:
count_field: invoice_count
expect:
navigate:
action_xmlid: sale.action_orders
target_model: sale.order
view_mode: list,kanban,form
domain_contains:
- "('state','not in',('draft','sent','cancel'))"
badge_non_negative: true
groups_any_of:
- account.group_account_user
b) 画面アクション(メニュー/遷移)
- custom_id: MENU-SALES-001
type: act_window
xmlid: sale.menu_sale_quotations
res_model: sale.order
view_mode: tree,form
context:
search_default_my_quotation: 1
expect:
opens: sale.order
default_filters:
- my_quotation
c) オートメーション + サーバアクション
- custom_id: SO-AUTO-001
type: automation
model: sale.order
trigger: on_write
filter_domain: "[('amount_total','>',100000)]"
server_action_xmlid: my_mod.sa_notify_high_value
expect:
server_action:
state: code
side_effects:
- "mail sent to group:sales_manager"
d) タブ(page)
- custom_id: SO-TAB-001
type: tab
model: sale.order
page_name: order_lines # 技術名
page_label: Order Lines # 表示
o2m_field: order_line
relation: sale.order.line
inverse: order_id
expect:
visible: true
editable_tree: true
台帳は「どう動くべきか」が中心。facts.json は「今どうなっているか」。
生成テストのテンプレート例
1) UI(Playwright, TypeScript)
- スマートボタン → アクション遷移 & ドメイン適用
test('SO-SB-001: Invoices button navigates and filters', async ({ page }) => {
await loginAs(page, 'account_user');
await openForm(page, 'sale.order', sampleOrderId);
const btn = page.locator('header .o_stat_button:has-text("Invoices")').first();
await expect(btn).toBeVisible();
const count = await btn.locator('.o_stat_value').innerText();
expect(Number(count)).toBeGreaterThanOrEqual(0);
await btn.click();
await expect(page).toHaveURL(/#action=\d+&model=sale\.order/);
// 代表フィルタの確認(例)
await expect(page.locator('.o_searchview .o_facet:has-text("State")')).toBeVisible();
});
2) サーバアクション/オートメーション(pytest)
def test_SO_AUTO_001_high_value_triggers_mail(env):
so = env['sale.order'].create({'partner_id': 1, 'order_line': [...], 'amount_total': 150000})
so.write({'note': 'bump'}) # on_write trigger
mails = env['mail.mail'].search([('subject','ilike','High value order'), ('res_id','=',so.id)])
assert mails, "Expected notification mail"
3) Gherkin(読みやすい受け入れ)
Feature: SO-SB-001 Invoices Smart Button
Scenario: Navigate to Orders from the Invoices stat button
Given I am logged in as "account_user"
And I open a Sale Order form with invoices
When I click the "Invoices" smart button
Then I should see the Orders list view
And the search should exclude states "draft", "sent", "cancel"
Introspector(抽出)で集める“事実”の例(facts.json)
- tabs:
{ view_id, page_name, label, o2m_fields: [{name, relation, inverse}], ... } - smart_buttons:
{ view_id, label, type, name(xmlid/ID), groups, stat_field, action_resolved: {...} } - actions:
ir.actions.*のすべて(res_model, view_mode, domain, context, target) - menus:
ir_ui_menu + groups + actionリンク - automations:
base_automation + ir_act_server - approvals(Studio):ルールとログ(モデル名は環境探索)
これを Gitにコミット しておけば、差分=回帰テストの対象。
Kubernetes運用のひな型
- Namespace:
odoo-tools - Secret: 認証情報(URL/DB/USER/PASS)
- CronJob:
python:3.11-slim起動pip install lxml playwright- Introspectorで
facts.json生成 - Generatorで
tests/を再生成 playwright install --with-deps- staging Odoo に対してテスト実行
- 成果物とレポートを Artifact/S3 へ、失敗は Slack/メール通知
- Pipeline: PR時は該当
custom_idのテストだけ部分実行
実装のコツ(抜粋)
- タブの識別は
@name(技術名)固定。ラベルは翻訳で変わるのでキーにしない。 - スマートボタンは
class="o_stat_button"+type="action"+XMLID。バッジはstatinfo+*_count。 - ドメイン/コンテキストは Python文字列なので、テストでは 部分一致(contains) で検証。
- Studio承認は環境差が出やすいので、まず ORMでモデル名を列挙→read。
- テストデータは demoタグ 付きのファクトリで用意(ゼロ件でもボタンが出る/出ないを検証)。
まとめ
- **台帳(要件)と事実(facts.json)**を分離し、テンプレートでテスト自動生成するのが再現性と保守性に優れます。
- 「どう動くべきか」は人が書き、「今どうなってるか」は機械が取る。CIで両者を突き合わせれば回帰に強い。
- ここまで準備すれば、**新しいカスタマイズも“書けば自動でテストが増える”**体制になります。
必要なら、台帳YAMLの最小スキーマと、facts抽出・テスト生成の実装雛形(Python)をすぐお渡しします。
あなた:
カスタマイズ台帳(Design Catalog)はこちらが作って、後はdevinがやる、というのはどうでしょうか。
ChatGPT:
いいと思います。その分業が一番回しやすいです。
あなたは台帳(Design Catalog)=仕様の唯一のソースだけを整備し、あとは「Devin(自動実装/自動テスト生成のワーカー)」が抽出→テスト生成→実行→レポートを回す形にします。
役割分担(超要約)
- あなた(人):台帳を書く・更新する(YAML/JSON)。レビューして承認する。
- Devin:facts抽出(Odoo API)、台帳と突合、テスト生成、K8s上で実行、レポート、PR作成。
リポジトリ構成(最小)
repo/
├─ catalog/ # ← あなたの守備範囲
│ ├─ smart_buttons.yaml
│ ├─ tabs.yaml
│ ├─ actions.yaml # act_window / report / client / url
│ ├─ automations.yaml # base.automation + ir_act_server
│ ├─ approvals.yaml # Studio 承認(あれば)
│ └─ meta.yaml # 命名規約・共通定義
├─ tools/ # Devin 管理(自動生成OK)
│ ├─ introspect/ # fields_view_get 等で facts.json を作る
│ ├─ generator/ # catalog + facts -> tests を生成
│ └─ runners/ # pytest / Playwright 実行ラッパ
├─ facts/ # Devinが nightly に更新(生成物)
│ └─ odoo18-staging-facts.json
├─ tests/ # 生成されたテスト(コミットされてもOK)
└─ .github/workflows/ci.yml # Devin が回すCI(K8s実行でも可)
台帳スキーマ(抜粋:そのまま流用OK)
共通(catalog/meta.yaml)
version: 1
defaults:
lang: ja_JP
env: staging
naming:
id_prefixes:
smart_button: SB
tab: TAB
action: ACT
automation: AUTO
rules:
- "custom_id は {PREFIX}-{model}-{seq:03d} 例: SB-sale.order-001"
conventions:
page_name_must_exist: true
smart_button_must_use_action_xmlid: true
スマートボタン(catalog/smart_buttons.yaml)
- custom_id: SB-sale.order-001
model: sale.order
selector:
class: o_stat_button
type: action
name_xmlid: sale.action_orders # 数値ID禁止
label: Invoices
badge:
field: invoice_count # statinfo のフィールド
expect:
action_type: ir.actions.act_window
target_model: sale.order
view_mode_contains: ["list","form"]
domain_contains:
- "('state','not in',('draft','sent','cancel'))"
groups_any_of:
- account.group_account_user
タブ(catalog/tabs.yaml)
- custom_id: TAB-sale.order-001
model: sale.order
page_name: order_lines # 技術名を必須化
label: Order Lines
o2m_field: order_line
relation: sale.order.line
inverse: order_id
expect:
visible: true
editable_tree: true
画面アクション(catalog/actions.yaml)
- custom_id: ACT-sale.orders
type: act_window
xmlid: sale.action_orders
res_model: sale.order
view_mode: [list, kanban, form]
context:
search_default_my_quotation: 1
expect:
opens_model: sale.order
オートメーション(catalog/automations.yaml)
- custom_id: AUTO-sale.order-001
model: sale.order
trigger: on_write
domain: "[('amount_total','>',100000)]"
server_action_xmlid: my_mod.sa_notify_high_value
expect:
server_action_state: code
observable_effects:
- "mail sent to group:sales_manager"
Devin 側の仕事(自動)
- facts 抽出(tools/introspect)
fields_view_get(load='full')で最終XML+fieldsir_ui_menu・ir.actions.*・base_automation・ir_act_server- Studio承認:存在すれば
studio.approval.*を ORM で探索 - 生成:
facts/odoo18-<env>-facts.json
- 突合/検証
- catalogの each
custom_idを facts で解決(XMLID/モデル名一致) - 欠落・不一致は PR コメントに差分を列挙
- catalogの each
- テスト生成(tools/generator)
- UI系:Playwright(スマートボタン、メニュー、タブ可視性)
- ロジック系:pytest(サーバアクション、オートメーション)
- 受入仕様:Gherkinもオプションで生成
- 実行 & レポート
- K8s Job(python:3.11 + playwright)で実行
- 失敗は
custom_id単位で報告(Slack/PRアノテーション)
CI/Kubernetes ざっくり
- Secret:
ODOO_URL, DB, USER, PASS(staging/prod 分離) - Nightly CronJob: facts 更新 → テスト再生成 → 実行 → レポート保存
- PR時:変更された
catalog/*のcustom_idだけ再生成・実行(高速)
運用ルール(これだけ守れば楽)
- タブは
@name(技術名)を必ず固定。ラベルは変えてOK。 - スマートボタンは
type="action"+ XMLID、バッジはstatinfo+*_count。 - サーバアクションは XMLID付与、Automation からは XMLID参照。
- 台帳の
expect.*は厳格すぎず“部分一致”で書く(domain/contextは文字列なので)
まず最初にやること(今日できる)
- この回答の YAMLスキーマを catalog/ に置く
- あなたが 既存の重要カスタム 3件だけ記入
- Devinに facts抽出→テスト生成→実行を1回回させる
- レポートを見て
expectを微調整 → 収束
この流れにしておけば、**「台帳を書けばテストが勝手に増える」**状態にできます。必要なら、最初のfacts抽出スクリプトと生成テンプレートをすぐ用意します。
コメントを残す