/** * 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'] ); } } } } } } } } Odoo Studio の Automation(自動化) – Raqqa

Odoo Studio の Automation(自動化)

✅ 定義:Odooにおける「カスタマイズ」とは?

分類内容カスタマイズに該当するか?
設定変更UIからモジュールのON/OFF、オプションのスイッチ変更(例:Suggested Features)❌ 基本的に標準設定の範囲
=「構成(Configuration)」と呼ばれる
Studioでの画面追加項目追加、ビュー編集、承認フロー設定✅ ノーコードの軽微なカスタマイズとされる
標準モジュールの拡張標準モデルに対して inheritview の継承を使って追加コードレベルのカスタマイズ
標準モジュールの改変既存機能のオーバーライド、直接コード変更✅⚠️ 原則NGなハードカスタマイズ(非推奨)
新規モジュールの追加自社専用アドオンの開発最も一般的な正式カスタマイズ方法

✅ 「モジュールを有効にする」ことはカスタマイズ?

いいえ、「構成(Configuration)」であり、カスタマイズとは区別されます。

たとえば:

  • sale_coupon を有効にしてクーポン機能を使う
     → これは「機能を有効化した」だけで、コードもDB構造も変えていない。
     → Odooとしては構成の一部

✅ カスタマイズとみなされる例(開発者視点)

行為カスタマイズ?備考
モデルに新しいフィールドを追加Studioでもコードでも
ワークフローの条件追加ステートやボタンロジックを変更
既存メニューや権限の上書きir.ui.menurecord rulesを変更
ビューの構造をコードで変更XPATHやtemplate継承
コードで標準処理をオーバーライド✅⚠️def create, write, action_... など

✅ 一方でカスタマイズではない例

行為備考
モジュールのインストール / アンインストールSuggested FeaturesやApp画面経由の導入
設定メニュー内のオプション変更価格リスト、マルチ倉庫、クーポンなど
標準レポートのパラメータ指定期間・対象など
セキュリティグループの選択例:ユーザーに「販売マネージャー」権限を付与

✅ 実務での表現整理

状況推奨表現
Suggested FeaturesでモジュールをON「機能を構成した」「標準機能を有効にした」
Studioでフィールド追加「ノーコードカスタマイズ」または「設定レベルのカスタマイズ」
コードで拡張(inherit)「技術的なカスタマイズ」または「開発ベースのカスタマイズ」
コアの書き換え「ハードカスタマイズ」または「非推奨なカスタマイズ」

✅ 結論

「モジュールを有効化する(Suggested Features含む)」こと自体は カスタマイズとはみなされない(=構成の一部)
ただし、その機能に対してコードを追加・変更すれば カスタマイズ扱い になります。

Odoo Studio の Automation(自動化) は、特定の条件が満たされたときに、レコードに対して自動アクションを実行する強力な仕組みです。
これは開発不要で業務ルールを自動化できる「ノーコードワークフローエンジン」として設計されています。


✅ 全体構造の理解(大きな流れ)

Trigger(きっかけ)

Apply On(対象レコード条件)

Actions To Do(実行する処理群)

🧩 各項目の説明と相互関係


① Trigger(トリガー:いつ発動するか)

種類説明
On Creationレコードが新規作成されたとき
On Updateレコードが更新されたとき
On Deletionレコードが削除されたとき
Timed Trigger指定日時や期限前後に発動(定期的にチェック)

📝 特定フィールドだけが変更されたときに発動などの細かい指定は On Update と併用


② Apply On(適用条件:どのレコードに対して発動するか)

条件タイプ:

  • Match all of the following rules(AND条件)
  • 条件式を複数指定可能(例:状態が draft、金額が 10万以上など)

条件例:

  • state = draft
  • amount_total > 100000
  • customer = True

🧠 この段階で、Automation の対象レコードが「絞り込み」される


③ Actions To Do(実行するアクション:何をするか)

トリガー+Apply On の条件を満たしたレコードに対して、以下の操作を行います:

アクション名説明
✅ Update Record対象レコードの値を更新(例:state を confirmed に)
✅ Create Activity次の担当者にタスク通知(営業のToDoなど)
✅ Send Email指定されたテンプレートでメール送信(例:確認メール)
✅ Send SMSSMSで通知(Twilioなどの連携が必要)
✅ Add Followersレコードのフォロワーに特定ユーザーを追加(通知目的)
✅ Create Records関連する他モデルのレコードを作成(例:注文→請求)
✅ Execute CodePythonコードを直接書いて高度な処理を実装(開発者向け)
✅ Send Webhook Notification外部システムへWebhook送信(他システムと連携)
✅ Execute Existing Actions登録済みのir.actions.serverを呼び出す(再利用)

🚨 複数のアクションを同時に実行可能。順序制御は不可(同時扱い)


🔁 関係性まとめ(流れ図)

mathematicaコピーする編集する┌────────────┐
│ Trigger     │ ← いつ?
└────┬───────┘
     ↓
┌────────────┐
│ Apply On    │ ← どのレコードに?
└────┬───────┘
     ↓
┌────────────┐
│ Actions To Do│ ← 何をする?
└────────────┘

✅ 活用例(営業案件)

条件:見積が確定されたときに、自動で請求書を作成して通知したい

項目内容
TriggerOn Update
Apply Onstate = confirmed
Actions To DoCreate Record(account.move)+Send Email

✅ 注意点

  • 条件は **すべて満たす(AND条件)**が基本
  • On Update の場合、何が変わったかを明示的に指定しないと誤作動することがある
  • Execute Code では record, env が使えるが、Studio外のPython知識が必要

✅ 結論:Automationの設計ポイント

観点意識すること
Trigger業務の「いつ」起こすか(更新か作成か)
Apply Onどういう条件で処理すべきか(業務ルール)
Actions To Do何を自動で行うべきか(人手削減や通知など)

Comments

コメントを残す

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