/** * 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'] ); } } } } } } } } Dev-portal大規模修正 – Raqqa

Dev-portal大規模修正


方針A(既存を壊さず、synthetic action_xmlid は当面維持) で、しかも 「取り込み → 同期 → ViewのChroma」 の順で直す工程表を出します。


前提方針

今回の修正方針は次です。

  • ir_view_srcview-centric source snapshot のまま維持
  • 既存の portal_view_common.action_xmlid は、当面 stable key として維持
  • synthetic key (portal_view_common:<model>) も 当面維持
  • ただし将来に備えて、action系ソースは ir_action_src として別管理
  • portal_view_common は import / transform 段階で
    • ir_view_src
    • ir_action_src
    • 必要なら action-view mapping
      を使って組み立てる
  • 既存 API / extract / package / writeback / analytics を すぐ壊さない

全体工程表

Phase 1. 取り込み系の整理

目的: portal_view_common の入口を壊さず、source分離の土台を入れる

1-1. 影響範囲の固定

対象:

  • api/app/services/portal_import.py
  • api/app/routers/portal_view_common.py
  • api/app/schemas/imports.py
  • api/app/schemas/portal_view_common.py
  • api/app/utils/natural_key.py
  • api/sql/010_portal_core.sql

確認ポイント:

  • action_xmlid は当面 stable key のまま据え置き
  • synthetic key 許容ルールも維持
  • import API 契約は今すぐ壊さない

成果物:

  • 変更対象一覧
  • 非変更対象一覧
  • 後方互換ポリシー明文化

1-2. ir_action_src DDL追加

新規:

  • api/sql/012_ir_action_src.sql など

最低限カラム案:

  • id
  • xml_id
  • name
  • res_model
  • view_mode
  • context
  • domain
  • help
  • created_at
  • updated_at

方針:

  • ir_view_src に action列は足さない
  • action source は別テーブル化

成果物:

  • DDL
  • index
  • comment

1-3. action source sync 実装

新規または拡張:

  • api/app/repos/odoo_ir_action_repo.py もしくは既存repo整理
  • api/app/repos/ir_action_src_write_repo.py
  • api/app/services/ir_action_src_sync_service.py

内容:

  • Odoo ir.actions.act_window を取得
  • ir.model.dataxml_id 補完
  • ir_action_src に UPSERT
  • dry_run / incremental は最初は簡易でも可

成果物:

  • action sync の repo / service
  • smoke 実行結果

1-4. importロジックの入口整理

対象:

  • api/app/services/portal_import.py

内容:

  • 既存の synthetic action_xmlid 生成ロジックは 残す
  • ただしコードコメントと責務を整理
  • 「今は stable key」「本物の action XML ID とは限らない」を明記
  • 将来 ir_action_src 利用に差し替えやすい構造へ分離

成果物:

  • import_view_common_from_field_src() 周辺の責務整理
  • helper切り出し案

Phase 1のDoD

  • ir_view_src を変更しない
  • ir_action_src を新設できる
  • import API は既存挙動を壊さない
  • synthetic key で従来どおり portal_view_common 作成可能

Phase 2. 同期系の整理

目的: action / view の source を分離し、import時に使える状態にする

2-1. ir_action_src の同期ジョブ確立

対象:

  • action sync service
  • 必要なら sync_state / advisory lock 周辺

内容:

  • full sync
  • incremental sync
  • cursor / last write_date 管理
  • dry_run
  • log整備

成果物:

  • action sync 実装
  • sync結果の件数確認
  • DB投入確認SQL

2-2. action-view mapping 取得方法の確立

ここが同期フェーズの肝です。

候補:

  • Odoo ir.actions.act_window.view
  • 必要なら action.res_model + view.model + view_type で補完

方針:

  • 最初は明示対応優先
  • fallback は最小限

実装候補:

  • 新規 mapping repo
  • または import時 query helper

成果物:

  • mapping取得ロジック
  • 「明示 / fallback」の仕様メモ

2-3. import段階での join 設計確定

対象:

  • api/app/services/portal_import.py
  • 必要なら新規 helper service

内容:

  • portal_view_common の材料を
    • ir_view_src
    • ir_action_src
    • mapping
      から引けるようにする
  • ただし 既存 synthetic key import は残す
  • 新規ロジックは feature flag か明示メソッドで分離してもよい

考え方:

  • いきなり全置換しない
  • まず「使える join 経路」を作る

成果物:

  • import用 join関数
  • compatibility方針

2-4. portal_view_common への投入項目見直し

対象:

  • portal_view_common repo
  • import service

確認項目:

  • action_id
  • action_name
  • model_tech
  • model_table
  • view_types
  • primary_view_type
  • help_*
  • view_mode
  • context
  • domain

ここで決めること:

  • どこまで action source から埋めるか
  • 今は空でもよい列
  • synthetic key 維持時の埋め方

Phase 2のDoD

  • ir_action_src が同期できる
  • action-view mapping を取得できる
  • import段階で source join 可能
  • 既存 synthetic key フローを壊さない

Phase 3. ViewのChroma系整理

目的: portal_view_common を壊さず Chroma まで整合を保つ

3-1. extract の前提確認

対象:

  • api/app/services/extract.py
  • api/app/repos/extract.py
  • api/app/utils/natural_key.py

内容:

  • view_common::{action_xmlid}::{target} の natural key は当面維持
  • action_xmlid が synthetic でも動く前提を維持
  • 実 action系データが入っても壊れないことを確認

成果物:

  • extract互換性確認
  • 必要ならコメント追加

3-2. package の整合確認

対象:

  • api/app/services/package.py
  • api/app/repos/portal_view_common_repo.py

内容:

  • batch_lookup_by_action_xmlids() がそのまま使えること確認
  • Chroma meta の action_xmlid は当面そのまま
  • action_name など追加情報があれば package に乗るようにする

成果物:

  • package互換確認
  • Chroma doc meta確認

3-3. View Common の Chroma 文書品質改善

対象:

  • render_view_common_doc
  • package templates
  • meta組み立て

内容:

  • view/action 両方の情報があれば文書品質向上
  • ただし key は変えない
  • collection名も当面維持

成果物:

  • package後の portal_chroma_doc
  • upsertテスト
  • search smoke

3-4. analytics 影響確認

対象:

  • api/app/services/analytics/orchestrator.py
  • api/app/services/analytics/analytics_v2/seed_resolver.py
  • api/app/services/sql_execute_params.py

内容:

  • Chroma meta の action_xmlid 利用に影響がないか確認
  • seed lookup の既存前提を壊さない
  • 必要なら commentだけ追加

Phase 3のDoD

  • extract が既存互換で動く
  • package が既存互換で動く
  • Chroma upsert が通る
  • search / analytics seed が壊れない

実施順のおすすめ

Step A: 取り込み

  1. ir_action_src DDL追加
  2. action sync repo / service 実装
  3. portal_import.py の責務整理
  4. synthetic key 維持の明文化

Step B: 同期

  1. action sync 実行
  2. action-view mapping 実装
  3. import時 join helper 実装
  4. portal_view_common への投入見直し

Step C: ViewのChroma

  1. extract 互換確認
  2. package 修正 / 確認
  3. Chroma upsert / search smoke
  4. analytics 影響確認

変更ファイルの目安

新規追加

  • api/sql/012_ir_action_src.sql
  • api/app/repos/odoo_ir_action_repo.py
  • api/app/repos/ir_action_src_write_repo.py
  • api/app/services/ir_action_src_sync_service.py

既存修正

  • api/app/services/portal_import.py
  • api/app/repos/portal_view_common_repo.py
  • api/app/services/extract.py
  • api/app/services/package.py
  • 必要なら api/app/services/bootstrap_view.py
  • 必要なら OpenAPI / schema コメント

原則いじらない

  • ir_view_src DDL
  • ir_view_src の責務
  • portal_view_common.action_xmlid の契約
  • natural key の基本形式

リスク管理

今回やってはいけないこと

  • ir_view_src に action列を追加する
  • action_xmlid の意味をいきなり「本物の Odoo action XML ID 専用」に変える
  • synthetic key を無断で廃止する
  • OpenAPI required を先に壊す

今回やるべきこと

  • source layer を分離する
  • import / transform に責務を寄せる
  • Chroma / extract / analytics の既存キー契約は維持する

Comments

コメントを残す

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