/** * WPML compatibility functions * * @global array $duplicated_posts Array to store the posts being duplicated. * * @package Yoast\WP\Duplicate_Post * @since 3.2 */ add_action( 'admin_init', 'duplicate_post_wpml_init' ); /** * Add handlers for WPML compatibility. */ function duplicate_post_wpml_init() { if ( defined( 'ICL_SITEPRESS_VERSION' ) ) { add_action( 'dp_duplicate_page', 'duplicate_post_wpml_copy_translations', 10, 3 ); add_action( 'dp_duplicate_post', 'duplicate_post_wpml_copy_translations', 10, 3 ); add_action( 'shutdown', 'duplicate_wpml_string_packages', 11 ); } } global $duplicated_posts; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: Renaming a global variable is a BC break. $duplicated_posts = []; /** * Copy post translations. * * @global SitePress $sitepress Instance of the Main WPML class. * @global array $duplicated_posts Array of duplicated posts. * * @param int $post_id ID of the copy. * @param WP_Post $post Original post object. * @param string $status Status of the new post. */ function duplicate_post_wpml_copy_translations( $post_id, $post, $status = '' ) { global $sitepress; global $duplicated_posts; remove_action( 'dp_duplicate_page', 'duplicate_post_wpml_copy_translations', 10 ); remove_action( 'dp_duplicate_post', 'duplicate_post_wpml_copy_translations', 10 ); $current_language = $sitepress->get_current_language(); $trid = $sitepress->get_element_trid( $post->ID ); if ( ! empty( $trid ) ) { $translations = $sitepress->get_element_translations( $trid ); $new_trid = $sitepress->get_element_trid( $post_id ); foreach ( $translations as $code => $details ) { if ( $code !== $current_language ) { if ( $details->element_id ) { $translation = get_post( $details->element_id ); if ( ! $translation ) { continue; } $new_post_id = duplicate_post_create_duplicate( $translation, $status ); if ( ! is_wp_error( $new_post_id ) ) { $sitepress->set_element_language_details( $new_post_id, 'post_' . $translation->post_type, $new_trid, $code, $current_language ); } } } } // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: see above. $duplicated_posts[ $post->ID ] = $post_id; } } /** * Duplicate string packages. * * @global array() $duplicated_posts Array of duplicated posts. */ function duplicate_wpml_string_packages() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: renaming the function would be a BC-break. global $duplicated_posts; foreach ( $duplicated_posts as $original_post_id => $duplicate_post_id ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. $original_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $original_post_id ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. $new_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $duplicate_post_id ); if ( is_array( $original_string_packages ) ) { foreach ( $original_string_packages as $original_string_package ) { $translated_original_strings = $original_string_package->get_translated_strings( [] ); foreach ( $new_string_packages as $new_string_package ) { $cache = new WPML_WP_Cache( 'WPML_Package' ); $cache->flush_group_cache(); $new_strings = $new_string_package->get_package_strings(); foreach ( $new_strings as $new_string ) { if ( isset( $translated_original_strings[ $new_string->name ] ) ) { foreach ( $translated_original_strings[ $new_string->name ] as $language => $translated_string ) { do_action( // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. 'wpml_add_string_translation', $new_string->id, $language, $translated_string['value'], $translated_string['status'] ); } } } } } } } } View同期まわりの現状整理と今後の検討メモ – Raqqa

View同期まわりの現状整理と今後の検討メモ


1. 先に結論

現状の /admin/ir_src/sync/viewsView-centric です。
つまり、Odoo の ir.ui.view を主語にして同期しています。

一方で、portal_view_common は設計思想として 業務画面単位 を表すものであり、実質的には Action-centric に近い役割 を持っています。

ただし現時点では、portal_view_commonOdoo の action / view 構造を正確に再現することが目的ではない ため、最低ラインとしては

  • portal_view_common を業務画面の代表単位として持てる
  • そのための安定キーを持てる
  • portal_view で tree/form/kanban 等の詳細を後から持てる

のであれば成立します。

そのため、今すぐ /admin/ir_src/sync/views を Action-centric に全面改修しないと破綻するわけではない です。
ただし、将来 Odoo 構造との対応を強めたい場合は、Action-centric 化を検討する余地があります。


2. 現在の状態(View-centric とは何か)

2-1. いま /admin/ir_src/sync/views がやっていること

router

api/app/routers/admin_ir_src_sync.py

  • POST /admin/ir_src/sync/views
  • IRViewSrcSyncService を呼ぶ

service

api/app/services/ir_view_src_sync_service.py

  • sync_views() / sync_views_incremental()
  • Odoo から取った ir.ui.view の行を _map_view_item() で整形
  • IRViewSrcWriteRepo.upsert_many()public.ir_view_src に保存

source repo

api/app/repos/odoo_ir_view_repo.py

  • Odoo JSON-RPC で ir.ui.viewsearch_read
  • 必要なら ir.model.data を引いて xml_id を付与

target write repo

api/app/repos/ir_view_src_write_repo.py

  • public.ir_view_src に UPSERT
  • 実質 id = ir.ui.view.id を主キーとして扱っている

2-2. 取得している主データ

OdooIrViewRepo.list_views() では、ir.ui.view から主に以下を取っています。

  • id
  • name
  • model
  • type
  • inherit_id
  • mode
  • priority
  • active
  • create_date
  • write_date
  • 必要なら arch_db

つまり主語は完全に view レコード です。


2-3. いまの xml_id の意味

_map_view_xml_ids() では ir.model.data を使って

  • model = 'ir.ui.view'
  • res_id = view.id

を引いています。

したがって、今 xml_id に入っているのは
ir.ui.view の external id です。

これは view xmlid です。
action xmlid ではありません。


2-4. 現在の ir_view_src への保存内容の実態

service の _map_view_item() と write repo から見ると、現状の public.ir_view_src は概ね

  • id
  • xml_id
  • name
  • model
  • model_table
  • view_type
  • inherit_id
  • mode
  • priority
  • active
  • arch_db
  • arch_db_hash
  • created_at
  • updated_at

のような view mirror に近い形 です。

つまり現状の ir_view_src は、名前に view_src とある通り、
Odoo の view 情報ソース としては自然ですが、
業務画面アクションのソース としてはまだ弱いです。


3. 今の「キー」はどう作られているか

3-1. portal_view_common.action_xmlid の本来の思想

本来の思想は、すでに portal_import.py にあります。

action_xmlid = f"portal_view_common:{model_lower}"

つまり portal_view_common.action_xmlid は、もともと

Odoo ネイティブの action xmlid に限定せず、業務画面を一意に表す安定キーとして合成してよい

という設計です。


3-2. natural key の作り方

utils/natural_key.py では

view_common::{action_xmlid}::{target}

を作っています。

ここでの action_xmlid も、現時点では

  • Odoo ir.actions.act_window の external id に限らない
  • portal_view_common:<model> のような合成キーを許容する

という意味で扱うのが現方針です。


3-3. 現状で迷いが生まれる理由

迷いが生まれるのは、名前が action_xmlid だからです。

コード上は

  • action_xmlid
  • import_view_common_by_action_xmlids
  • get_by_action_xmlid
  • view_common::{action_xmlid}::...

となっているので、普通に読むと
「これは Odoo の action xmlid だろう」
と解釈されやすいです。

しかし実際の設計思想は

「業務画面キーとして合成してよい」

なので、ここに認識差が生まれます。


4. 今の状態の何が問題で、何が問題でないか

4-1. 問題ではないこと

問題は
「Odooから既存のViewを全部取り出せない」ことではない
です。

今の sync/views は ir.ui.view を素直に取っているので、
view レコード自体は広く取れる構造 です。


4-2. 問題の本質

本質は、

今の同期は View-centric なのに、後段の portal_view_common は業務画面単位(Action-centric に近い)で使いたい

ことです。

つまり、

  • 取れているもの: view 単位の情報
  • 欲しいもの: 業務画面単位のヘッダ

という 主語のずれ がある、ということです。


4-3. ただし現時点では最低ラインは守れる

現時点では portal_view_common の第一義は

Odoo構造の厳密再現ではなく、業務画面を表現すること

なので、
action_xmlid

  • Odoo native action xmlid ではなく
  • 合成キーの stable business-screen key

として扱えば、最低ラインは守れます。


5. View-centric のまま運用する意味

今のままでも価値はあります。

5-1. Odoo view 情報の基礎ソースとして使える

ir.ui.view を素直に同期しているので、

  • model
  • type
  • xml_id
  • arch_db

などの view 情報の棚卸しには有効です。

5-2. portal_view 側の将来素材として使える

将来 portal_view を強化するとき、

  • tree
  • form
  • kanban
  • search

などの個別 view の材料として使いやすいです。

5-3. Odoo native に近い構造をあとから参照しやすい

今の ir_view_src はほぼ view mirror なので、Odoo の元構造を追いやすいです。


6. Action-centric にする意味

今後、portal_view_common をより強く業務画面単位で扱うなら、Action-centric 化には意味があります。

6-1. portal_view_common と source の主語が揃う

今は

  • source: view
  • portal_view_common: 業務画面

ですが、Action-centric にすると

  • source: action / screen
  • portal_view_common: 業務画面

となり、対応が自然になります。

6-2. import / bootstrap / package が分かりやすくなる

今の後段はすでに action_xmlid 前提です。

  • import_view_common_by_action_xmlids
  • get_by_action_xmlid
  • view_common::{action_xmlid}::...

なので source も action 寄りにすると、意味のぶれが減ります。

6-3. Odoo メニュー/画面との対応を強めやすい

将来的に

  • menu
  • action
  • view

の関係を追いたくなったとき、Action-centric source の方が自然です。


7. Action-centric にするとは何を意味するか

Action-centric にするとは、
Odoo の ir.ui.view を単純に同期するのではなく、業務画面アクションを主語に source を作る
ことです。

たとえば source 1行が

  • action_id
  • action_xmlid
  • action_name
  • model
  • model_table
  • 必要なら代表 view / 関連 view 情報

を持つような形です。


8. Action-centric にする方法(今後の候補)

方式A: /sync/views 自体を action-centric に再設計する

内容

今の ir.ui.view ベース取得をやめて、最初から action を主語に Odoo から引く。

必要な修正

  • OdooIrViewRepo の取得ロジック変更
  • IRViewSrcSyncService._map_view_item() の形変更
  • IRViewSrcWriteRepo の UPSERTキー見直し
  • ir_view_src のスキーマ見直しの可能性

メリット

  • source と portal_view_common の意味が揃う
  • 後段で分かりやすい

デメリット

  • 変更範囲が大きい
  • 既存の view mirror 的な用途が弱くなる

方式B: 現行 /sync/views は残し、別に action-centric sync を追加する

内容

今の view-centric sync はそのまま残し、新しく action-centric source 用の同期を作る。

例:

  • 既存: /admin/ir_src/sync/views
  • 新規: /admin/ir_src/sync/view_actions

メリット

  • 既存を壊しにくい
  • View-centric / Action-centric の両方を持てる
  • 段階移行しやすい

デメリット

  • source が2系統になる
  • 設計整理が少し必要

方式C: 今は Action-centric 化せず、合成キー運用を明文化して進める

内容

portal_view_common.action_xmlid は今後も stable business-screen key とし、必要なら portal_view_common:<model> のような合成キーを使う。

メリット

  • 今すぐ実装変更不要
  • 現方針に最も合う
  • portal_view_common を業務画面定義として先に整備できる

デメリット

  • action_xmlid という名前とのズレは残る
  • 将来 Odoo native action と厳密対応したくなったときに再整理が必要

9. 現時点での実務上のおすすめ

現段階では、まずは方式Cでよい です。

理由は以下です。

  • portal_view_common の第一義は業務画面表現である
  • portal_view はまだ飾り・後続強化の位置づけである
  • Odoo native action/view 構造の厳密再現はまだ必須ではない
  • すでに portal_import.py に合成キー思想が入っている

したがって現時点では、

action_xmlid = Odoo native action xmlid とは限らない。portal_view_common の安定キーである

と明文化して進めるのが実務上もっとも自然です。


10. 次回開発時に判断すべきこと

次回、以下を改めて判断する。

10-1. portal_view_common をどこまで Odoo native に寄せたいか

  • 現状通り、業務画面の抽象表現でよいか
  • 将来、実際の Odoo menu/action と紐づけたいか

10-2. ir_view_src の役割を固定する

  • Odoo view mirror として残すか
  • Action-centric source に変えるか
  • 両方持つか

10-3. action_xmlid 名称の扱い

  • 列名はそのまま維持し、意味だけ明文化するか
  • 将来的に別名 (screen_key など) へ寄せるか

11. 今回のメモとして固定しておく文言

以下を設計メモとして固定してよいです。

portal_view_common.action_xmlid is currently treated as a stable business-screen identifier.
It is not restricted to the native Odoo external ID of ir.actions.act_window.
Synthetic keys such as portal_view_common:<model> are allowed.
At this stage, portal_view_common is intended to represent a business screen first, while detailed Odoo view structures are handled separately in portal_view and source tables.

日本語版:

portal_view_common.action_xmlid は、現時点では業務画面を一意に識別する安定キーとして扱う。
Odoo の ir.actions.act_window の外部IDに限定しない。
portal_view_common:<model> のような合成キーを許容する。
現段階では portal_view_common は業務画面の表現を第一義とし、詳細な Odoo view 構造は portal_view や source テーブル側で別途扱う。


必要なら次に、この内容を GitHub に残すための markdown メモ形式 に整えて返します。


Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です