/** * 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'] ); } } } } } } } } Engineering-to-Order Odoo Projectモジュールと製造モジュールの連携 – Raqqa

Engineering-to-Order Odoo Projectモジュールと製造モジュールの連携

flowchart TD
  A[販売見積作成<br>Quotations(EtO Product & EtO Service)] --> B[販売受注確定<br>Sales Order Confirmed]
  B --> C[プロジェクト作成(EtO Serviceに基づく)<br>Project with Task]
  C --> D[設計作業を進行<br>Project: Task管理 & Timesheet入力]
  D --> E[設計完了]
  E --> F[製造オーダー作成]
  F --> G[製造実行 & 完了<br>MRP Module]
  G --> H[請求書発行<br>Invoice Created]
  H --> I[支払処理]
  I --> J[プロジェクト完了]

  style A fill:#f9f,stroke:#333,stroke-width:1px
  style J fill:#bbf,stroke:#333,stroke-width:1px

OdooにおけるEngineering-to-Order(EtO)のフロー

  1. ① Quotation作成
    • 販売モジュールで見積を作成。
    • EtO Product(製品)とEtO Service(サービス)を選択。
    • 上記は予め設定されているとする
  2. ② EtO Service → プロジェクトタスクへ連携
    • サービスは「タスクの生成」をトリガーに設定可能(プロダクトの販売時にタスク作成)。
    • タスクは自動生成でも、あらかじめプロジェクトひな型を作っておくことが多い。
    • タスクはプロジェクトモジュールで設定する
  3. ③ 製造オーダーの作成
    • EtO製品に MTO(受注生産) + Manufacturing ルートを設定していれば自動でMOが作成される。
    • ⚠️ あらかじめルート設定が必要。販売と製造の接続設定が必須。
  4. ④ 請求書作成
    • 通常どおり「Create Invoice」で請求書を発行。
  5. ⑤ タスクへ従業員をアサイン
    • プロジェクトモジュールで該当タスクに担当者を追加。
  6. ⑥ 従業員が時間記録
    • タスクにタイムシートを記入することで、進捗管理とコスト算出。
  7. ⑦ 作業完了・支払処理
    • 作業が終わればタスクを完了。
    • 請求と支払処理は会計モジュールを使用

🔧「ルート」を設定すると何ができるか?

「ルート(Route)」とは、製品がどのように供給されるか(補充・調達・製造など)を決定する手段です。

たとえば、次のようなことが可能になります:

ルート名意味・動作
受注補充(MTO)SO(Sales Order)が作成されたときに補充(仕入・製造)を即時にトリガー
購入在庫が不足すると、自動的に仕入先にRFQ(見積依頼)を発行
製造在庫が足りないと自動的に製造オーダー(MO)を起動
ドロップシッピング顧客に直送。仕入先→顧客への出荷指示を出す
外注(Subcontracting)部品を供給して外注先で製造させ、納品を受ける構成

🔁「ルート」の指定で変化する点

項目変更内容例
補充フローの自動化MTOでSO作成時に自動でMOやPOが発行される
在庫拠点(場所)の移動仮想ロケーション間(例:Customer Location → Supplier Location)で移動が起きる
ワークフロー表示の自動変更「ルート」に応じて製品フローの図(矢印付きのロジスティクス図)が更新される
調達ルール(Procurement Rule)商品がどのような条件で再発注されるかがルールとして適用される

🧭「図をみる」のフロー図とは?

プロダクト画面の「図をみる(View diagram)」リンクで表示される図は、以下の内容をビジュアルに可視化したものです:

目的:

  • 製品がどこから来て、どこに行くのか
  • 誰(パートナー/仕入先)が関係するのか
  • どのようなロケーションが使われるのか
  • MTO/製造などの動的なルートにより、どんな動きがあるのか

表示されるノード例:

ノード名内容
Partner / Vendor顧客、仕入先など外部取引先
WH/Input, WH/Stock入荷ロケーション、倉庫在庫
Virtual Locations/Production製造工程を表す仮想ロケーション
Virtual Locations/Loss廃棄や紛失を表すロケーション
Customer Location顧客への配送先

→ これらが矢印でつながっており、商品がどのルートで流れていくのかが一目でわかる仕組みです。


🔁 まとめ:業務影響のイメージ

シナリオルートで設定するもの動作例
製品を受注生産したい受注補充 + 製造見積承認 → 製造オーダー発生
製品が売れたら仕入したい受注補充 + 購入見積承認 → RFQ自動生成
製品を仕入先から直送ドロップシッピングSO作成 → 仕入先から顧客へ直送フロー生成(在庫経由せず)


Comments

コメントを残す

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