やり方は「モデル(model_id)を1つ渡したら、Fields / Views / Tabs / Smart Buttons の“空の器”を一括で冪等に作る」スキャフォールド(雛形生成)を用意する、という設計が安全です。まずは手順と方針を固めましょう(コードはこのあとでOK)。
ゴール
- 新しい model_id を追加 → すぐ編集できるように
portal_view(各 view_type)+ *portal_view_common / _settings / permissions / action / menu
+ portal_fields(不可視の初期行)
+ tabs(フォーム用の空タブ)
+ smart_buttons(ワイヤ未設定のプレースホルダ)
を 1トランザクションで生成。 - 既に存在するものは作らない(冪等)。
いつ動かす?(トリガー)
- 推奨:明示API
POST /models/scaffold(引数:modelまたはmodel_id、オプション多数)- UIボタンやSwaggerから実行できるようにする
- 将来の条件分岐(kanban/calendarを作るか 等)を柔軟に制御しやすい
- 代替:
portal_modelに insert されたら DB トリガーで最小雛形だけ作る(高度な判定はアプリ側)
スキャフォールドの中身(何を作る?)
以下は model 単位でまとめて作成します(既存があれば skip)。
1) Fields(portal_fields)
- データ源:
- Odoo の
ir_model_fields(推奨) - 既に持っている
portal_fields(別モデルからのコピーや再取込) - 最終手段:DBテーブルからの反射(列名→粗い型推定)
- Odoo の
- 全フィールドを行として作成(まだ表示しないので OK)
model= 追加モデル、field_name,ttype,relation_model,label_i18nなど- 既定は不可視の方針に沿うため、view側では
display_fields=[]のままにします(可視制御は view 側)
これで「既存が持っているフィールドはデフォルトで invisible」の下地が整います。
2) Views(portal_view と 1:1 付随)
各 view_type ごとに“空”の1行を作成(最低 list, form, search。必要なら kanban, calendar は条件付き)。
portal_view:(model_id, model, view_type, view_name, enabled=true, priority_ui='standard', priority_num=16, ai_purpose=NULL, notes=NULL)- 一意制約イメージ:
UNIQUE(model_id, view_type)
portal_view_common:display_fields = [](空)/sort_*,default_*= NULL
portal_view_permissions:can_create=true, can_edit=true, can_delete=false, inline_edit=false, mass_edit=false, show_invisible=false
portal_list_settings:inline_edit=false, page_hint=NULL, export_policy='inherit'portal_form_settings:show_header=true, show_footer=trueportal_search_settings:priority_ui/num=NULL, search_fields=NULL, filters=NULL, group_by_filters=NULLportal_action:view_mode='tree,form', domain=NULL, context=NULL, target='current'portal_menu:create_menu=false(既定は作らない)
kanban は画像やカード表現が決まってから、calendar は date/datetime が選べてから後追いで生成にするのが安定です。
3) Tabs(フォーム用)
- 空タブを雛形として複数行作成(例:
main,relations,notes)- 例:
{"tab_key":"main","label_i18n":{"ja_JP":"基本"},"sequence":10},{"tab_key":"relations","label_i18n":{"ja_JP":"関連"},"sequence":20},{"tab_key":"notes","label_i18n":{"ja_JP":"メモ"},"sequence":30}
- 例:
- 中身(どのフィールドをどのタブへ)は未割当。UI で後から移動可能に。
- タブ関連のテーブル(
portal_tabs等)の実 DDL に従い、最低限の必須列だけ埋めます。
4) Smart Buttons
- プレースホルダとして作成(すぐ動かないが配置の器だけ作る):
button_key:to_<relation>などlabel_i18n: 関連名(例:「見積」「請求書」)origin='portal',action_type='other',action_ref=NULL,target='current',show_count=false,sequence=10から採番
- 候補の決め方(自動):
- モデルの one2many / many2many を列挙 → 代表的な関連だけ雛形化
- many2one の逆参照は後で要件次第。最初は x2many系だけで十分
作成順序(1トランザクション)
- アドバイザリロック(
hashtext(model))で競合を抑止(任意) - portal_model を upsert(あれば取得だけ)
- portal_fields を投入(存在確認して差分のみ)
- 各 view_type の portal_view を upsert
- 4で作った
view_idに対して、1:1 付随テーブルを upsert(空値) - tabs 雛形(フォーム view のみ)を upsert
- smart_buttons 雛形を upsert
- コミット
- (任意)Chroma エクスポート(
/views/export/chroma&/fields/export/chroma)を直後に実行
各 upsert は 冪等(既にあれば何もしない)。
ON CONFLICT DO NOTHINGまたは事前SELECTを使用。
オプション(APIパラメータで制御)
view_types=["list","form","search"](追加で"kanban","calendar")make_tabs=true/false(既定 true)make_buttons=true/false(既定 true)fields_source="odoo"|"reflect"|"none"(取得の仕方)also_export_to_chroma=true/false(完了後に自動エクスポート)dry_run=true/false(作成予定のプレビューだけ返す)
期待できる“空の器”の状態
- 画面定義は すべて1行ずつ存在(まだ何も表示しない)
- display_fields は空 → 既存フィールドは不可視の運用と整合
- タブやスマートボタンは 見出し/順序だけ決まっている(ワイヤは未接続)
- 以後は UI で
display_fieldsを追加(見せたいフィールドだけ)search_fields/filtersを設定- 必要に応じて smart button の action_ref を配線
- タブにフィールドをドラッグ配置
- 最後に Chroma エクスポートで検索・NL2SQL に即反映
失敗しないための注意
- 全部まとめてトランザクションにする(失敗時は何も残さない)
- 競合(同時実行)はアドバイザリロック or
UNIQUE+ON CONFLICTで抑止 - 権限:生成系は管理者のみ実行
- ログ:
notesに「scaffolded at <ts> by <user>」を追記しておくと監査に便利
コメントを残す