/** * 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'] ); } } } } } } } } 管理会計 × Neo4j ダッシュボード分析メニュー(メモ用) – Raqqa

管理会計 × Neo4j ダッシュボード分析メニュー(メモ用)

管理会計ダッシュボードで実装予定の Neo4j 分析項目候補を整理しておくメモです。
それぞれ:

  • 概略(何をしたいのか)
  • モデルのイメージ(ノード/リレーション)
  • モデルクエリ例(Cypher)

をまとめています。


① 製品別コストの因果分解(Causal Cost Breakdown)

■ 概略

特定の製品ライン(または製品)について、

  • どの 部門/工程/スキル/設備/外注
  • どれだけ原価に寄与しているか

因果チェーン付きでブレイクダウンする分析。
単なる「費目別原価」ではなく、原因ノード側からの寄与度ランキングまで見えるのがポイント。

■ モデルイメージ

  • (:ProductLine)
  • (:CostItem) … 人件費・減価償却・外注費など
  • (:Department) / (:Process) / (:Skill) / (:Machine) など原因側ノード
  • リレーション例:
  • (cause)-[:ALLOCATES {amount: …}]->(pl:ProductLine)

■ モデルクエリ例

製品ラインごとの原因別コスト寄与ランキング

“`cypher
:param pl_id => ‘PL100’;

MATCH (pl:ProductLine {id:$pl_id})<- [rel:ALLOCATES]-(cause)
RETURN
cause.id AS cause_id,
labels(cause) AS cause_labels,
sum(rel.amount) AS cost_contribution
ORDER BY cost_contribution DESC;
原因ノードから製品までのパス可視化(説明用)

cypher
:param pl_id => ‘PL100’;
:param cause_id => ‘D03’; // 例: 溶接課など

MATCH path = (cause {id:$cause_id})-[:ALLOCATES*1..3]->(pl:ProductLine {id:$pl_id})
RETURN path;


② 部門別コスト配賦の説明可能モデル(Explainable ABC)
■ 概略
部門原価(部門コスト)が どういうロジックで他部門/製品へ配賦されているか を、「配賦率の根拠」とともに説明する分析。

部門 → 部門(一次配賦)

部門 → 製品ライン(二次配賦)

を ドライバ(工数・件数・スキル時間など)で説明可能な形にする。

■ モデルイメージ
(:Department {base_cost: …})

(:ProductLine)

(:Activity) / (:Skill) / (:Driver)(配賦の根拠)

リレーション例:

(dept_from)-[:ALLOCATE_TO {rate: …, driver:’hours’}]->(dept_to)

(dept)-[:ALLOCATE_TO_PRODUCT {rate: …}]->(pl)

■ モデルクエリ例
部門間配賦額の算出

cypher
:param from_dept => ‘D06’; // 生産技術
:param total_cost => 1000000; // D06 の配賦対象コスト

MATCH (:Department {id:$from_dept})-[r:ALLOCATE_TO]->(d:Department)
RETURN
d.id AS to_dept,
r.driver AS driver_type,
r.rate,
$total_cost * r.rate AS allocated_cost;
部門コスト + 配賦流入分 = 最終部門負担額

cypher
MATCH (d:Department)
OPTIONAL MATCH (src:Department)-[r:ALLOCATE_TO]->(d)
WITH
d,
coalesce(d.base_cost, 0) AS own_cost,
sum(coalesce(src.base_cost,0) * r.rate) AS allocated_in
RETURN
d.id AS dept_id,
own_cost + allocated_in AS total_dept_cost;


③ 工程・スキル・人のボトルネックランキング(TOC)
■ 概略
TOC(制約理論)的な視点で、

どの 工程/スキル/人 が

生産能力・コスト・リードタイムの ボトルネック になっているか

を、グラフの依存構造+工数・負荷からランキングする分析。

■ モデルイメージ
(:Process) / (:Operation) / (:WorkCenter)

(:Skill) / (:Person)

(:ProductLine)

リレーション例:

(pl)-[:REQUIRES_PROCESS]->(proc)

(person)-[:CAN_RUN]->(proc)

(proc)-[:USES_SKILL {hours: …}]->(skill)

■ モデルクエリ例
スキル別の負荷集約(ボトルネック候補スキル)

cypher


MATCH (p:Person)-[u:HAS_SKILL]->(s:Skill)
WITH s, sum(u.hours) AS total_hours
RETURN
s.id AS skill_id,
total_hours
ORDER BY total_hours DESC;
プロセスノードの「依存度」(何本のラインから参照されているか)

cypher

MATCH (pl:ProductLine)-[:REQUIRES_PROCESS]->(pr:Process)
WITH pr, count(DISTINCT pl) AS used_by_lines
RETURN
pr.id AS process_id,
used_by_lines
ORDER BY used_by_lines DESC;


④ 退職インパクト & 属人性(Bus Factor)分析
■ 概略
特定の人/スキルを失ったときに、

どの工程/ライン/部門が影響を受けるか

Bus factor(その人しかできない仕事の多さ)がどれくらいか

を測る分析。

■ モデルイメージ
(:Person) / (:Skill) / (:Process) / (:ProductLine)

リレーション例:

(p:Person)-[:HAS_SKILL]->(s:Skill)

(s)-[:REQUIRED_BY]->(proc)

(proc)-[:PART_OF]->(pl)

■ モデルクエリ例
特定の人がいなくなった場合の影響ライン一覧

cypher

:param person_id => ‘P04’;

MATCH path = (p:Person {id:$person_id})-[:HAS_SKILL]->(:Skill)
-[:REQUIRED_BY*1..2]->(pl:ProductLine)
RETURN pl.id AS impacted_line, path;
Bus Factor 指標:特定スキルを扱える人数

cypher

MATCH (s:Skill)<-[:HAS_SKILL]-(p:Person)
WITH s, count(p) AS num_people
RETURN
s.id AS skill_id,
num_people,
(num_people = 1) AS is_single_point_risk
ORDER BY num_people ASC;


⑤ 原価異常の Root Cause Ranking(F-score)
■ 概略
「赤字ライン/コスト異常ライン」を symptoms として、

依存グラフをさかのぼり、共通原因候補ノードを抽出

precision / recall / F-score で原因らしさをスコアリング

最も“それっぽい原因”をランキング

する分析。

■ モデルイメージ
(:Element) を汎用ノードとして扱い、

実際には Department / Process / Skill / Machine / Vendor / ProductLine などが含まれる

[:DEPENDS_ON] により因果関係・依存関係を表現

■ モデルクエリ例(F-score root cause)
cypher

:param symptoms => [‘PL100′,’PL200′,’PL300’]; // 異常なラインID など

MATCH (e:Element)-[:DEPENDS_ON0..]->(x) WHERE e.id IN $symptoms AND NOT (x)-[:DEPENDS_ON]->() WITH x, collect(DISTINCT e.id) AS explains_faults WHERE size(explains_faults) > 1 WITH x AS candidate_root, explains_faults MATCH (candidate_root)<-[:DEPENDS_ON0..]-(n)
WITH candidate_root,
explains_faults,
collect(DISTINCT n.id) AS potential_max_impact
WHERE size($symptoms) > 0 AND size(potential_max_impact) > 0
WITH candidate_root,
explains_faults,
potential_max_impact,
toFloat(size(explains_faults)) / size($symptoms) AS precision,
toFloat(size([id IN potential_max_impact WHERE id IN $symptoms])) /
size(potential_max_impact) AS recall
RETURN
candidate_root.id AS candidate_root_id,
explains_faults,
precision,
recall,
(2 * precision * recall) / (precision + recall) AS fscore
ORDER BY fscore DESC;


⑥ 外注/設備のリスク集中分析(SPOF)
■ 概略
外注先/設備/工程などが、

どれだけ多くのライン・製品・部門から依存されているか

代替手段(RED ノード)があるかどうか

をチェックして 単一障害点 (SPOF) を検出 する分析。

■ モデルイメージ
(:Vendor) / (:Machine) / (:Process) などを (:Element) としてもよい

[:DEPENDS_ON] で「このラインはこの外注に依存」などを表現

[:RED] or mode:’RED’ の DEPENDS_ON で冗長性を表現

■ モデルクエリ例
SPOF 候補の抽出(多くに参照され、冗長性なし)

cypher

MATCH (n:Element)
OPTIONAL MATCH (n)-[:DEPENDS_ON {mode:’RED’}]->(alt)
WITH n, count(alt) AS redundant_count, size((n)<-[:DEPENDS_ON]-()) AS in_degree WHERE in_degree >= 5 AND redundant_count = 0
RETURN
n.id AS spof_candidate,
in_degree,
redundant_count;


⑦ KPI 連動因果グラフ(利益・不良率の原因構造)
■ 概略
KPI(利益・売上・原価率・不良率など)と、
部門/工程/スキル/外注などのノードを因果線で結び、

KPI 悪化(例:利益減少/不良率上昇)の原因となるノード群

逆に改善余地の大きいノード群

を、パスとして可視化する分析。

■ モデルイメージ
(:KPI {id:’Profit’}), (:KPI {id:’DefectRate’})

各種ノード(Department, Process, Skill, Machine …)

[:AFFECTS {weight: …}] リレーションで影響度を表現

■ モデルクエリ例
特定 KPI に影響を与える原因候補の列挙

cypher

:param kpi_id => ‘Profit’;

MATCH path = (cause)-[:AFFECTS*1..3]->(kpi:KPI {id:$kpi_id})
RETURN
cause.id AS cause_id,
path
LIMIT 50;
KPI に対する原因ノードの影響度ランキング

cypher

:param kpi_id => ‘Profit’;

MATCH (cause)-[r:AFFECTS]->(kpi:KPI {id:$kpi_id})
RETURN
cause.id AS cause_id,
sum(r.weight) AS total_influence
ORDER BY total_influence DESC;


⑧ 生産ラインの最適化ナビゲーション(改善提案 AI)
■ 概略
ナレッジグラフ上に、

生産ライン構造(工程・設備・人・スキル)

コスト/リードタイム/不良率

改善オプション(増員・外注・工程変更など)

を載せておき、
「どこをどう変えれば KPI が良くなりそうか」 を
AI/ルールベースクエリでナビゲートする分析。

※ dev-portal / NLQ と組み合わせると自然文対応も可能。

■ モデルイメージ
(:ProductLine) / (:Process) / (:Machine) / (:Person) / (:Skill)

(:ImprovementOption) …「工程の分割」「外注比率UP」「設備増設」など

[:CAN_IMPROVE {impact_on_kpi: …}] で改善余地を表現

■ モデルクエリ例
ラインごとの改善候補一覧

cypher

:param pl_id => ‘PL100’;

MATCH (pl:ProductLine {id:$pl_id})-[:HAS_PROCESS]->(pr:Process)
MATCH (pr)-[rel:CAN_IMPROVE]->(opt:ImprovementOption)
RETURN
opt.id AS option_id,
opt.description AS option_desc,
rel.impact_on_kpi AS estimated_effect
ORDER BY rel.impact_on_kpi DESC;
「ボトルネック工程 × 改善効果」でソートする例

cypher

MATCH (pr:Process)<-[:HAS_PROCESS]-(pl:ProductLine) MATCH (pr)-[ci:CAN_IMPROVE]->(opt:ImprovementOption)
WITH pr, opt, ci,
size((pr)<-[:REQUIRES_PROCESS]-(:ProductLine)) AS used_by_lines
RETURN
pr.id AS process_id,
opt.id AS option_id,
ci.impact_on_kpi,
used_by_lines,
ci.impact_on_kpi * used_by_lines AS priority_score
ORDER BY priority_score DESC;


Comments

コメントを残す

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