/** * 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 automations studio – Raqqa

Odoo automations studio

使い方

販売注文が確認されたときに、店舗商品の配送注文を自動で作成するなど

不動産の場合:

100万ドル以上の物件で、所有者にVIPタグを個人のユーザーとしてタグを付与する場合のやりかた

studio > automations > crate new automations

設定事項

ルールの名前

設定できるトリガー

  • Value updated: stage is set to, user is set, on unarchived
  • Email events: On incoming message, On outgoing message
  • Timing conditions: Based on date field, After creation, After last update
  • custom: On save, On deletion, On UI change
  • external: On webhook

今回の場合は On saveを選択

Apply on :ターゲットのレコード 今回は match all records

When updating: Value(propertries)

Apply onの match all of the following rules:

add condition→ value >= 1000000 を入力  タブの add an actionを選択

create actionsのモーダル

type: update record, create activity, send email, send SMS, add followers, remove followers, create record, exclude code, send webhook notification, execute existing actionsからtypeを選択。update : agent (フィールド)    to : ユーザー

でだれがレコードを更新したかを指定できる save & new

2nd actionの設定

オーナーにタグを付与

typeから update recordを選択 update : owner > tag by adding VIPを選択 save&close

物件をウェブサイトに公開するようにリマインダーのアクティビティ

カンバンviewからautomationsを設定する場合

triggerがStage is set to にプリセットされている

add action > type: create activity > activity type : To-do > due date : 3 days
> User type :Dynamic User (based on record) > Title : Publish Listing > User field : x_studio_user_id > save

Activity type で主に設定できる項目:Emai, Call, Meeting, To-do, Reminder, Upload Document, Grant Approval

User type の設定項目:specific, dynamic User

User : カスタムアプリケーションのエージェントフィールドのテクニカルネーム
フィールドのテクニカルネームはどうやって確認するか?開発者モードのツールチップから

この設定をしておくと、100万ドル以上の物件登録した際にVIPタグが自動で付与されている

以下清書済み

Odoo Studio|Automations 実装ガイド(コンサル向け)

1. 目的と典型ユースケース

  • 販売: 「販売注文が確認されたら、店舗在庫の**配送注文(Delivery/Transfer)**を自動作成」
  • 不動産:価格が100万ドル以上の物件なら、所有者にVIPタグを自動付与」「公開リマインドのアクティビティを自動作成」

2. 作成手順(Studio 操作)

  1. Studio > Automations > Create new automation
  2. ルール名を入力(例:[RealEstate] VIP付与&公開リマインド
  3. **Trigger(トリガー)**を選択
    • Value updated: Stage is set to, User is set, On unarchived
    • Email events: On incoming message, On outgoing message
    • Timing conditions: Based on date field, After creation, After last update
    • Custom: On save, On deletion, On UI change
    • External: On webhook
      今回:On save
  4. Apply on(ターゲット)
    • モデル(対象)を選択 → ルールを当てるレコード集合を指定
    • Match: all records もしくは Match all of the following rules(条件一致)
  5. When updating(値変更での反応)
    • プロパティを選んで「更新時に動く」条件を追加可(必要に応じて)
  6. 条件(ドメイン)
    • Apply on > Match all of the following rules 内で 条件行を追加
    • 例:value >= 1000000(価格フィールドが100万以上)
  7. **Actions to do(アクション)**を追加
    • Type から選択:
      • Update record, Create activity, Send email, Send SMS, Add followers, Remove followers, Create record, Execute code(※SaaSは不可), Send webhook notification, Execute existing actions
    • 必要な数だけ Save & New で連続追加 → 最後に Save & Close

3. 具体例①:不動産|100万ドル以上で所有者にVIPタグ付与

前提: 物件モデルに価格フィールド(例:value)、所有者 Many2one(owner_id)、パートナー側にタグ(カテゴリ/タグフィールド)あり。

  • Trigger: On save
  • Apply on: 物件モデル(例:estate.property)、Match rules: value >= 1000000
  • Action #1:Update record
    • 更新先:owner_id.category_id(または owner_id.tag_ids
    • 操作:VIP追加by adding VIP
  • Action #2(任意):Update record
    • 更新先:agent_id(担当者)をユーザーにセット(「誰が更新したか」を保持したい場合)
  • Action #3(任意):Create activity(公開リマインド)
    • Activity type:To-do
    • Due date:3 days
    • User type: Dynamic User (based on record)
    • User field:x_studio_user_id(エージェントのテクニカル名
    • Title:Publish Listing

🔎 テクニカル名の確認開発者モードを有効化 → フィールドのツールチップで technical_name を確認。


4. 具体例②:カンバンから設定(Stageベースの自動化)

状況: カンバンViewでステージ移動をトリガーにする場合、Trigger が自動で Stage is set to にプリセットされる。

  • Trigger: Stage is set to
  • Action:Create activity
    • Activity type:To-do(または Email, Call, Meeting, Reminder, Upload Document, Grant Approval など)
    • Due date:3 days
    • User type:Dynamic User
    • User field:x_studio_user_id
    • Title:Publish Listing

5. 具体例③:販売注文→配送注文の自動作成(最短案)

目的: 受注確認(state = confirmed)で、**倉庫の出荷オペレーション(Delivery/Transfer)**を自動生成。

  • Trigger: On save(または Value updated: Stage is set to Confirmed 相当)
  • Apply on: sale.order(Match:state = sale か、confirmation_date が更新された、等)
  • Action:Create record
    • model: stock.picking
    • 倉庫・オペレーションタイプ・顧客・Moveラインなど必要フィールドをマッピング
    • 可能なら Execute existing actions(既存サーバーアクション)や Send webhook で外部WMSと連携
    • 注意: 出荷の詳細ロジックが絡むと、Studioだけでは厳しいケースあり(標準フロー活用推奨)

6. 設計の要点(コンサルの勘所)

  • Triggerは最小にOn save は汎用だが、不要な再発火を避けるため 条件(ドメイン)を厳密に
  • Apply on の条件が中核ドメインで「対象集合」を絞る(value >= 1000000 など)。
  • Actionは“加える/上書き”を明確に:タグは「追加」か「置換」かを間違えない。
  • ユーザー割当は Dynamic を活用:レコード上の担当フィールドをUser fieldで参照。
  • テクニカル名は必ず確認:開発者モードのツールチップでミスを防止。
  • SaaSの制約Execute codeSaaS不可。必要時は webhookexisting actions で代替。
  • 無限ループ回避:アクションで更新したフィールドが再度トリガーにならないよう、条件を工夫。
  • 監査性:自動付与・自動作成は、メモ/活動で残すと後から説明しやすい。

7. できあがりの動作(不動産の例)

  • 物件を保存(On save
  • 価格が >= 1,000,000所有者にVIPタグが自動付与
  • かつ 3日後期限の公開To-do が担当者に自動アサイン
  • (必要なら、更新ユーザーの追跡や通知も同時に実行)

8. 付録|チェックリスト(導入前後)

  • 対象モデル・フィールドのテクニカル名を控えた
  • TriggerApply on(条件) を最小に抑えた
  • Actions の順序と「追加/上書き」を確認した
  • Dynamic User の User field をテストした
  • 既存の自動化やボタンと二重実行になっていない
  • Sandboxで保存→想定どおり動作→本番に適用

Odoo Studio の テクニカル名 (Technical Name) とは、
Odoo内部でフィールドやモデルを一意に識別するために使うシステム上の名前です。
画面上の日本語・英語の「ラベル名」とは別物で、自動化・開発・API連携などで非常に重要な役割を持ちます。


1. テクニカル名とは

  • Odoo内部でフィールドやモデルを識別するための内部ID
  • 人間が見る「ラベル名(表示名)」とは異なる
  • Automations / Studio / XML / Python / APIで設定やコードを書くときは、必ずテクニカル名で指定

例:不動産モジュールで物件モデル(estate.property)の場合

種別ラベル名(画面表示)テクニカル名(内部名)
モデル物件estate.property
フィールド価格value
フィールド所有者owner_id
フィールド担当エージェントx_studio_user_id
フィールドタグtag_ids

2. テクニカル名の使い所

(1) StudioでのAutomations

  • User field など、どのフィールドを使うか設定する際に必要
  • 例)「担当者にToDoを自動作成」する場合:
    • User field = x_studio_user_id
    • → Dynamic User指定時に、ここを正しく指定しないと動かない

(2) 条件(ドメイン)設定

  • 「価格が100万ドル以上の物件にVIPタグを付与」する場合:
[("value", ">=", 1000000)]

ここで value がテクニカル名。

(3) QWeb / Pythonコードでの参照

  • QWebテンプレート:
<t t-esc="o.owner_id.name"/>
  • Pythonコード:
record.owner_id.name

3. テクニカル名の確認方法

  1. 開発者モードを有効化
    • 「設定」→「開発者モードを有効化」
  2. 対象フィールドにマウスオーバー
    • ツールチップにテクニカル名(field: value など)が表示される
  3. Studioを開いて確認
    • Studio内でフィールドを選択すると「テクニカル名」項目が表示される

4. コンサル向けベストプラクティス

  • ラベル名ではなくテクニカル名で仕様書を作成
    → 開発者とやりとりがスムーズになる
  • Automations設定時は必ずテクニカル名を確認
    → 誤ったフィールド指定を防ぐ
  • 開発ポータルでテクニカル名を管理する
    → モデル・フィールド・ビュー・ボタンの情報を一元管理しておくと、今後の拡張が楽


Comments

コメントを残す

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