/** * 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の元データ(モデル/テーブル) → 集計粒度 → 前提(会計科目/原価要素のマッピング)」 – Raqqa

「指標 → 構成要素 → Odooの元データ(モデル/テーブル) → 集計粒度 → 前提(会計科目/原価要素のマッピング)」

在庫などトランザクションベース分析まで含めて「Odooデータとの関連表」と「実装の要点」をまとめたものです。

0) まず決めるべき“共通前提”

生産性系KPIは「付加価値」「人件費」「売上高」の定義次第で数字が変わるので、ここを Odooの勘定科目/原価要素に紐づけて固定するのが最初の仕事です。

付加価値の代表的な定義(候補)

一般に以下のような複数流派があります。どれにするかを KPIメタに明示するのがおすすめ。

  • 粗付加価値(簡易)
    粗付加価値 = 売上高 - 外部購入費
    外部購入費 = 材料費/外注費/仕入/間接購入など
  • 付加価値(利益加算型)
    付加価値 = 営業利益 + 人件費 + 減価償却費 (+ 支払利息 など)

Odoo上では、どちらも
**「特定の会計科目グループの合算」**で実装できます。


1) 生産性分析:指標とOdooデータの対応

売上高付加価値率

定義
売上高付加価値率(%) = (付加価値 / 売上高) × 100

Odoo構成要素と典型ソース

  • 売上高
    • 会計ベース(推奨)
      • account_move(仕訳/請求)
      • account_move_line(科目別の明細)
    • 受注ベース(補助)
      • sale_order, sale_order_line
  • 付加価値
    • 会計ベースで「付加価値計算用の科目セット」を合算
    • もしくは
      売上 - 外部購入費 の “外部購入費科目セット” を定義

集計粒度

  • 月次 / 部門 / 製品カテゴリ / プロジェクト
    analytic_account / 分析タグを使うと綺麗)

実装メモ

  • まず dev-portal に
    • 売上高科目グループ
    • 外部購入費グループ
    • 人件費グループ
    • 減価償却費グループ
      “KPI用マスタ”として定義しておくと後工程が爆速です。

労働分配率

定義
労働分配率(%) = (人件費 / 粗付加価値額) × 100

Odoo構成要素と典型ソース

  • 人件費
    • Odoo Payrollを使う場合
      • hr_payslip, hr_payslip_line
    • 使っていない場合(現実的に多い)
      • 会計側の給与/法定福利費科目
        • account_move_line
  • 粗付加価値額
    • 売上 - 外部購入費 型が一番扱いやすい

注意点

  • Payroll未導入なら
    **“人件費=特定勘定科目合計”**として割り切るのが正解。
  • 部門別労働分配率をやるなら
    分析勘定/タグ運用が鍵。

労働生産性

定義
労働生産性 = 付加価値 / 平均従業員数

Odoo構成要素と典型ソース

  • 付加価値:指標20と同じ
  • 平均従業員数
    • hr_employee
    • hr_contract(在籍/雇用形態の判定補助)
    • 月次平均は
      “期中の在籍スナップショット” を作るのが実務的

実装メモ

  • OdooのHRを使うなら
    • 月末時点人数
    • 期中平均((期首+期末)/2)
    • or 月次の頭数平均
      を KPI設定で選べるように。

2) 在庫・トランザクションベース分析の対応

「在庫分析」は 数量KPI金額KPI で参照先が微妙に違います。

主要Odoo在庫データ

  • 在庫数量の実体
    • stock_quant
  • 在庫の移動履歴
    • stock_move, stock_move_line
  • 在庫評価(金額)
    • stock_valuation_layer
    • 会計連携がある場合は account_move_line と突合
  • 商品マスタ
    • product_product, product_template
  • 製造
    • mrp_production, mrp_workorder

代表的な在庫KPI例とOdoo対応

  • 在庫回転率
    • 例:
      在庫回転率 = 売上原価 / 平均在庫
    • 売上原価
      • account_move_line(COGS科目群)
      • or stock_valuation_layer の出庫評価
    • 平均在庫
      • stock_valuation_layer の月次在庫評価残高
      • or stock_quant × 標準原価
  • 在庫回転日数
    • 在庫回転日数 = 365 / 在庫回転率
  • 在庫年齢(Aging)
    • 各ロット/入庫日を起点に
      • stock_move_line(lot/serialがある場合)
      • stock_move
    • 期間別(0-30/31-90/91-180/181+)で滞留を可視化
  • 欠品率/サービスレベル
    • 受注未充足やバックオーダーの判定
      • sale_order_line + 出荷状況
      • stock_picking / stock_move 状態
  • ABC分析
    • 消費/売上への寄与で分類
      • sale_order_line
      • stock_move(出庫量)
    • 製品カテゴリ/倉庫/ロケーション単位で集計

3) 「指標⇄Odooデータ」の最小マッピング表(雛形)

あなたのシステムにそのまま登録できるよう、KPIメタの雛形として書くとこんな構造が綺麗です。

KPIメタに持たせたい項目

  • kpi_code(例: P20)
  • kpi_name
  • definition
  • formula_ast(NLQ生成にも使える)
  • components[]
    • component_name(売上高/人件費/付加価値…)
    • source_type(account/hr/stock/sale/mrp)
    • odoo_models[]
    • account_group_refs[](会計系はここが最重要)
    • filters(posted_only, company_id, analytic, warehouse…)
  • grain(month, dept, product_category…)

4) Neo4jでの整理イメージ(最小)

**KPIとOdoo実体を“知識グラフ化”**すると、
「なぜこの数字が動いたか」を説明するルート探索ができるのが強みです。

  • (KPI)-[:USES_COMPONENT]->(Component)
  • (Component)-[:SOURCED_FROM]->(OdooModel)
  • (Component)-[:MAPPED_TO_ACCOUNT_GROUP]->(AccountGroup)
  • (OdooModel)-[:HAS_FIELD]->(OdooField)
    ※ dev-portalのメタを流し込む

そしてNLQ側で

  • ユーザー質問 → KPI候補同定
  • KPIのComponentから
    • 使うべきテーブル/フィルタ/分析軸を自動復元
  • SQL生成の used_tables やガードにも連動

という流れが作れます。


5) 実務で詰まりやすいポイント(先に潰す)

1) 付加価値/外部購入費の科目定義

ここが曖昧だと全指標が揺れます。
必ず “KPI用科目グループ” を作って固定

2) 人件費と従業員数の整合

  • Payroll未導入の会社は多い
    → 会計ベースで割り切る
  • 平均従業員数は
    月次スナップショットテーブルを用意するのが現実解

3) 在庫評価の方式差

  • 標準原価 / 平均原価 / FIFO などで数字が変わる
    → Odoo設定をメタ化してKPI説明に含める

6) 次の一手

  1. KPI辞書(まずは生産性3つ+在庫基本3つ)をDB化
    • entity 例:portal_kpi を追加するか
      既存のメタ管理の流儀に合わせて拡張
  2. “科目グループ/原価要素グループ” を定義
    • 売上、COGS、人件費、減価償却、外部購入費
  3. dev-portalに
    • KPI ↔ Odooモデル/フィールド ↔ 科目グループ
      の関係を登録
  4. ChromaにKPI辞書をパッケージ
    • NLQの文脈取得で
      KPI定義・前提・必要テーブルを引けるように
  5. Neo4jに
    • KPIノード + Componentノード + Odooメタを投入
    • “変動要因探索” のデモができる

まとめ

  • 生産性KPIは **会計×HR×(必要なら製造/在庫)**の合流。
  • 成否の8割は
    付加価値/外部購入費/人件費の“科目マッピング固定”
  • 在庫は
    stock_quant(数量)× stock_valuation_layer(評価)× account_move_line(COGS)
    の三点セットで整理。
  • これを KPI辞書としてdev-portalに収めてChroma/Neo4jに展開すると、
    nlq-devが“説明可能な管理会計”に一段進化します。

次にやれること

  • KPI辞書のDDL案
  • dev-portalのentity設計(portal_kpi / portal_kpi_component)
  • Neo4jのノード/リレーション設計
  • /chroma/package 対応のドキュメントテンプレ
  • サンプルSQL(Odoo標準テーブル想定)


Comments

コメントを残す

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