標準からの抽出
-- =========================================================
-- portal_menu: Create-from-scratch DDL (i18n + notes_i18n)
-- =========================================================
-- 必要なら明示的に落とす(本番は慎重に)
-- DROP TABLE IF EXISTS public.portal_menu CASCADE;
-- テーブル作成
CREATE TABLE IF NOT EXISTS public.portal_menu (
id BIGSERIAL PRIMARY KEY,
-- Odoo 参照(任意)
menu_id INTEGER, -- ir_ui_menu.id(同居してなくても値は保持可)
menu_xml_id TEXT, -- <module>.<name>
-- 表示
menu_path_i18n JSONB NOT NULL DEFAULT '{}'::jsonb, -- {"ja_JP":"販売/受注/...","en_US":""}
sequence INTEGER,
-- アクション
action_type TEXT NOT NULL DEFAULT 'other' CHECK (
action_type IN ('act_window','report','client','server_action','url','other')
),
action_xml_id TEXT,
action_label_i18n JSONB NOT NULL DEFAULT '{}'::jsonb, -- {"ja_JP":"見積","en_US":""}
-- 遷移先
target_model TEXT, -- 例: "sale.order"
view_mode TEXT, -- 例: "tree,form"
-- 初期フィルタ
initial_domain_expr TEXT, -- 技術文字列(例: "[('state','=','draft')]")
initial_domain_i18n JSONB NOT NULL DEFAULT '{}'::jsonb, -- {"ja_JP":"下書き","en_US":"Draft"}
-- 由来
origin TEXT NOT NULL DEFAULT 'portal' CHECK (
origin IN ('standard','module','studio','portal','unknown')
),
origin_module TEXT, -- 例: "sale"
-- コード連携
code_present BOOLEAN NOT NULL DEFAULT FALSE,
code_github_url TEXT,
-- 備考(プレーンテキスト+多言語)
notes TEXT,
notes_i18n JSONB NOT NULL DEFAULT '{}'::jsonb, -- {"ja_JP":"...", "en_US":"..."}
-- タイムスタンプ
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 部分ユニーク(NULL を許容したまま重複抑止)
CREATE UNIQUE INDEX IF NOT EXISTS uq_portal_menu_menu_id
ON public.portal_menu (menu_id) WHERE menu_id IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS uq_portal_menu_menu_xml_id
ON public.portal_menu (menu_xml_id) WHERE menu_xml_id IS NOT NULL;
-- よく使う検索キー
CREATE INDEX IF NOT EXISTS idx_portal_menu_target_model ON public.portal_menu (target_model);
CREATE INDEX IF NOT EXISTS idx_portal_menu_action_type ON public.portal_menu (action_type);
CREATE INDEX IF NOT EXISTS idx_portal_menu_origin ON public.portal_menu (origin);
CREATE INDEX IF NOT EXISTS idx_portal_menu_sequence ON public.portal_menu (sequence);
-- JSONB の GIN インデックス(i18n 検索用)
CREATE INDEX IF NOT EXISTS idx_portal_menu_path_gin
ON public.portal_menu USING GIN (menu_path_i18n);
CREATE INDEX IF NOT EXISTS idx_portal_menu_action_label_gin
ON public.portal_menu USING GIN (action_label_i18n);
CREATE INDEX IF NOT EXISTS idx_portal_menu_domain_i18n_gin
ON public.portal_menu USING GIN (initial_domain_i18n);
CREATE INDEX IF NOT EXISTS idx_portal_menu_notes_i18n_gin
ON public.portal_menu USING GIN (notes_i18n);
-- updated_at 自動更新トリガ
CREATE OR REPLACE FUNCTION public.portal_menu_touch() RETURNS trigger AS $$
BEGIN
NEW.updated_at := now();
RETURN NEW;
END
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_portal_menu_touch ON public.portal_menu;
CREATE TRIGGER trg_portal_menu_touch
BEFORE UPDATE ON public.portal_menu
FOR EACH ROW EXECUTE FUNCTION public.portal_menu_touch();
-- ==========================================
-- 動作確認用サンプル(必要な場合のみ実行)
-- ==========================================
-- 日本語だけ埋めて英語欠損 → 翻訳エンドポイントの dry-run で補完候補に出ます
-- INSERT INTO public.portal_menu (
-- menu_xml_id, menu_path_i18n, sequence,
-- action_type, action_xml_id, action_label_i18n,
-- target_model, view_mode, origin,
-- notes, notes_i18n
-- ) VALUES (
-- 'sale.menu_sale_quotations',
-- '{"ja_JP":"販売/見積","en_US":""}',
-- 10,
-- 'act_window',
-- 'sale.action_quotations',
-- '{"ja_JP":"見積","en_US":""}',
-- 'sale.order',
-- 'tree,form',
-- 'portal',
-- '営業チームが使う見積リスト',
-- '{}'::jsonb
-- );
-- SELECT * FROM public.portal_menu ORDER BY id DESC LIMIT 5;
追加:必要なフィールドのみ表示させるため
ALTER TABLE portal_fields
ADD COLUMN visibility TEXT NOT NULL DEFAULT 'visible',
ADD CONSTRAINT ck_visibility CHECK (visibility IN ('visible','invisible'));
| ポータル項目 | Odooメタの出所 | 同じ表現で可能 | 別表現で可能(導出/解決方法) | 備考 |
|---|---|---|---|---|
メニューパス(menu_path_i18n) | ir_ui_menu.name(翻訳:ir_translation)+親チェーン | 部分的(各階層の名前は同一表現) | 可:親を再帰で辿って name を / 連結してフルパスを生成 | i18nは各ir_ui_menu名の翻訳で再現。フルパス自体は計算結果(別表現)。 |
インデント(indent) | 親子関係(ir_ui_menu.parent_id) | ― | 可:親の段数を再帰CTEでカウント(depth=0がルート) | 画面表示用の補助値。 |
アクション(論理参照:action_xml_id) | ir_model_data(対象:アクションレコード) | 可 | ― | ir_ui_menu.action → 汎用ir.actions.actions → 各種ir_actions_*へ解決 → そのidに対応するXMLIDを取得。 |
アクション名(action_label_i18n) | アクションの name(翻訳:ir_translation) | 可 | ― | ir.actions.act_window.name 等をそのままi18nで取得。 |
アクション種別(action_type) | ir.actions.actions.type | 可(マッピング) | ― | 代表:ir.actions.act_window→act_window、ir.actions.server→server_action、ir.actions.report→report、ir.actions.client→client、ir.actions.act_url→url。 |
遷移先モデル(target_model) | ir_actions_act_window.res_model / ir_actions_report.model / ir_actions_server.model_id | 可(act_window, report) | 可(server_actionはmodel_id→ir_model.modelへ解決) | url/clientなどモデルが無い種別は空(手入力不要)。 |
ビュー種(view_mode) | ir_actions_act_window.view_mode | 可 | 補完可:ir_actions_act_window_viewの並びからview_mode連結を再構築 | 通常はaction.view_modeで十分。 |
初期ドメイン(式)(initial_domain_expr) | ir_actions_act_window.domain | 可 | ― | テクニカル表現のまま保存。 |
初期ドメイン(表示名)(initial_domain_i18n) | ― | ― | 不可(手入力) | 「下書き/送信済み」等の人が読むラベルはメタに無いので手入力。 |
出自(origin) | ir_model_data.module(対象:メニュー/アクション) | 可(判定) | ― | モジュールがweb_studio→studio、値あり→module、無し→standard。 |
| origin_module | ir_model_data.module | 可 | ― | 例:sale、stock。 |
コード有無(code_present) | origin の判定結果から推測 | 可(推定) | ― | module/standard は true、studio/portal は false が目安(運用方針で調整可)。 |
コード参照(GITHUB)(code_github_url) | ― | ― | 不可(手入力) | リポジトリやXMLID→URLのマッピングは環境依存。別テーブルで管理すれば半自動化可。 |
| menu_id / menu_xml_id | ir_ui_menu.id / ir_model_data | 可 | ― | 参照キーとして保存推奨。 |
| sequence | ir_ui_menu.sequence | 可 | ― | 同階層の並び順。 |
| notes | ― | ― | 不可(手入力) | 運用メモ。 |
画面項目
| English column | 日本語名 | 備考 |
|---|---|---|
| id | ID | 主キー |
| menu_id | メニューID | ir_ui_menu.id |
| menu_xml_id | メニューXMLID | <module>.<name> |
| menu_path_i18n | メニューパス(i18n) | 例:{"ja_JP":"販売/受注/見積","en_US":"Sales/Orders/Quotations"} |
| sequence | 並び順(インデント代用) | 同階層の順序。表示上のインデント計算に利用可 |
| action_type | アクション種別 | act_window/report/client/server_action/url/other |
| action_xml_id | アクションXMLID | 例:sale.action_quotations(URL系はNULL可) |
| action_label_i18n | アクション名(i18n) | 例:{"ja_JP":"見積","en_US":"Quotations"} |
| target_model | 遷移先モデル | 例:sale.order(URL/Client等はNULL可) |
| view_mode | ビュー種 | 例:tree,form |
| initial_domain_expr | 初期ドメイン式 | 例:[('state','=','draft')] |
| initial_domain_i18n | 初期ドメイン(表示名 i18n) | 例:{"ja_JP":"下書き","en_US":"Draft"} |
| origin | 出自 | standard/module/studio/portal/unknown |
| origin_module | 出自モジュール | 例:sale |
| code_present | コード有無 | true/false |
| code_github_url | コード参照(GitHub) | パーマリンク推奨 |
| notes | 備考 | 運用メモ |
| created_at | 作成日時 | 自動設定 |
| updated_at | 更新日時 | 自動更新推奨 |
コメントを残す