/** * 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 ワークフローとNeo4j組み合わせ – Raqqa

Odoo ワークフローとNeo4j組み合わせ

はい、その割り切りはかなり有力です。
少なくとも、あなたの今の構想には合っています。

結論から言うと、基本方針はこうです。

業務ワークフローは自作
Odoo は最終反映先・計算先・台帳先

この分担のほうが、たぶんあなたには向いています。


なぜそのほうが良いか

Odoo の標準ワークフローや各アプリ内の状態遷移は、もちろん便利です。
ただ、あなたが今やりたいのは単なる承認ではなく、

  • 部署 × 役職
  • ownership
  • 組織変更
  • SQL 制御
  • Neo4j 正本
  • 独自ポータル
  • 将来の説明可能性

までまとめて扱うことですよね。

そうなると、Odoo 側のワークフローに寄せるほど、逆にこうなりやすいです。

  • Odoo の都合に設計が引っ張られる
  • 承認ロジックが Odoo 内に閉じる
  • Neo4j 正本と二重管理になる
  • 画面もロジックも Odoo 依存が強くなる
  • あとで変えにくい

なので、あなたのケースでは

承認・申請・差戻し・次承認者決定は自作
Odoo は結果を受けて必要データを更新するだけ

のほうが筋がいいです。


どう分けるのがよいか

自作側で持つもの

  • 申請状態
  • 承認経路
  • 誰が次に承認できるか
  • ownership
  • 部署・役職
  • 差戻し履歴
  • 監査ログ
  • 表示制御
  • SQL 制御

Odoo に任せるもの

  • 会計転記
  • 在庫反映
  • 請求・支払など標準業務処理
  • マスタ更新
  • 必要最小限の台帳保持

つまり、

DevPortal/Neo4j = workflow brain
Odoo = business engine / ledger engine

です。


この方式の一番良い点

ワークフローの主語が Odoo ではなく、あなたの業務モデルになることです。

たとえば稟議なら、Odoo の都合で考えるのではなく、

  • 誰の申請か
  • どの部署案件か
  • いくらか
  • 誰が承認すべきか
  • 今どの状態か

で完結できます。

その結果として、
最後に

  • Odoo の注文を作る
  • 支払依頼を作る
  • 仕訳を切る
  • ステータスだけ更新する

で済みます。

このほうが自然です。


ただし、全部を最後まで貯めて「最後に一括反映」だけにするのも危ない

ここは少し注意です。

「最後にデータだけ更新」という考え方は良いのですが、
全部を最後の1回だけで反映すると、Odoo 側で途中状態が見えなくなります。

なので実務的には、2段に分けるのがよいです。

1. 業務進行中

自作側が正本

  • draft
  • submitted
  • manager_approved
  • director_approved
  • rejected

2. 確定時

Odoo に反映

  • 伝票作成
  • 注文確定
  • 会計計上
  • 支払依頼登録

必要なら途中でも最小限の mirror はしていいです。

たとえば

  • Odoo に reference id だけ作る
  • 最終承認後に正式レコード化する
  • 途中状態は自作側だけ持つ

などです。


向いているもの/向いていないもの

自作に向いている

  • 申請
  • 稟議
  • 承認
  • 差戻し
  • 所有・責任範囲
  • 組織ベースの閲覧制御
  • 独自 UI
  • 監査ログ

Odoo に任せたほうがよい

  • 会計仕訳
  • 在庫引当
  • 販売・購買の標準確定処理
  • Odoo標準帳票に乗る処理

私ならこうします

方針

ワークフロー本体は自作

  • Neo4j で定義
  • DevPortal で実行
  • Postgres に履歴保存

Odoo は確定処理だけ担当

  • 最終承認後に create/write
  • 必要なら status sync
  • 仕訳や受発注など標準ロジック利用

一番しっくりくる整理

あなたの構想を一文で言うとこうです。

Odoo を“業務画面付きERP”として使うのではなく、
“標準業務エンジン”として使い、
業務ワークフロー・権限・所有・承認は外で持つ。

この整理ならかなりぶれません。


なので答えは

はい、基本はその方向でよいと思います。
Odoo のワークフローに寄せず、自作して、最後に必要データだけ Odoo を更新する
そのほうが、Neo4j 正本・ownership・部署役職ベース権限というあなたの設計思想に合っています。

ただし、
Odoo 側に何を“最後に更新する対象”として残すか は最初に決めたほうがいいです。

次はそこを整理すると進みます。
「Odoo に残すべきデータ」と「外で完結させるべきデータ」 を切り分けましょう。

では、そのまま設計の土台にできる形で、Neo4j を正本にした承認ワークフロー設計をまとめます。
目的は次の3つです。

  1. Odoo に依存しない承認基盤を作る
  2. Neo4j の組織構造・権限制約と自然につなげる
  3. 将来 Odoo Importer / Adapter で反映先を増やせるようにする

1. 全体思想

承認ワークフローは、Odoo の state 遷移や mail.activity を中心に組むのではなく、Neo4j 上の関係定義として持ちます。

役割分担はこうです。

  • Neo4j
    • 組織構造
    • 権限構造
    • 承認フロー定義
    • 承認条件
    • 承認履歴の主索引
  • UI
    • 申請
    • 差分表示
    • 承認・却下・差戻し
    • フロー定義編集
    • 履歴表示
  • Execution Engine
    • 承認済みリクエストを実行
    • 反映先は Adapter に委譲
  • Adapter
    • Odoo Adapter
    • SQL Adapter
    • API Adapter
    • Neo4j Adapter

この構成なら、承認の定義とUIは Odoo 非依存で伸ばせます。


2. 基本アーキテクチャ

処理の流れはこの形です。

Step 1. Request 作成

ユーザーが「こう変えたい」を送る

Step 2. Policy Resolve

Neo4j から、そのユーザーが

  • 申請できるか
  • 即時実行か
  • 承認必要か
  • どのフローを使うか

を判定する

Step 3. Approval Instance 生成

適用フローを固定し、承認ステップ列を作る

Step 4. UI で承認

承認者が各 step を処理する

Step 5. Execution

最終承認後に Adapter 経由で反映する

Step 6. Audit

申請〜承認〜実行結果を監査ログ化する


3. ノード設計

まずはノードです。
最小構成で十分使える形に絞ります。

3-1. 組織・権限系

User

{
  "user_id": "u10",
  "name": "Kenji",
  "email": "kenji@example.com",
  "active": true
}

Role

{
  "role_key": "sales_manager",
  "name": "Sales Manager"
}

OrgUnit

{
  "org_key": "sales_east",
  "name": "Sales East",
  "org_type": "department"
}

Company

{
  "company_id": "c001",
  "name": "Demo Company"
}

3-2. リソース・操作系

Resource

{
  "resource_key": "crm.lead",
  "table_name": "fact_opportunity",
  "resource_type": "model"
}

Action

{
  "action_key": "write",
  "name": "Write"
}

もしくはより細かく

{
  "action_key": "update_stage",
  "base_action": "write",
  "name": "Update Opportunity Stage"
}

3-3. 承認定義系

ApprovalFlow

{
  "flow_key": "opp_update_standard",
  "name": "Opportunity Standard Update Flow",
  "active": true,
  "version": 1
}

ApprovalStep

{
  "step_key": "opp_update_standard_step_1",
  "step_order": 1,
  "step_type": "role_approval",
  "approval_mode": "any_one",
  "required_count": 1
}

step_type の例:

  • role_approval
  • org_manager_approval
  • specific_user_approval
  • auto_approval
  • policy_check

ApprovalCondition

{
  "condition_key": "amount_over_1m",
  "condition_type": "expr",
  "expr_lang": "jsonlogic",
  "expr": {
    ">=": [ { "var": "payload.amount" }, 1000000 ]
  }
}

ここは後で DSL を固定します。

3-4. 実行・履歴系

Request

{
  "request_id": "req_20260405_0001",
  "status": "pending",
  "resource_key": "crm.lead",
  "action_key": "write",
  "created_at": "2026-04-05T10:00:00Z",
  "requested_by": "u10"
}

ApprovalInstance

{
  "instance_id": "ai_20260405_0001",
  "flow_key": "opp_update_standard",
  "status": "in_progress",
  "started_at": "2026-04-05T10:01:00Z"
}

ApprovalDecision

{
  "decision_id": "dec_0001",
  "decision": "approved",
  "comment": "OK",
  "decided_at": "2026-04-05T10:10:00Z"
}

ExecutionJob

{
  "job_id": "job_20260405_0001",
  "adapter_type": "odoo",
  "status": "queued",
  "created_at": "2026-04-05T10:11:00Z"
}

4. リレーション設計

次に関係です。

4-1. 組織・権限

  • (u:User)-[:HAS_ROLE]->(r:Role)
  • (u:User)-[:BELONGS_TO]->(o:OrgUnit)
  • (o:OrgUnit)-[:PARENT_OF]->(child:OrgUnit)
  • (u:User)-[:BELONGS_TO_COMPANY]->(c:Company)

4-2. ポリシー・操作

  • (r:Role)-[:CAN_REQUEST]->(a:Action)
  • (a:Action)-[:ON_RESOURCE]->(res:Resource)
  • (a:Action)-[:REQUIRES_FLOW]->(f:ApprovalFlow)

必要なら

  • (r:Role)-[:CAN_APPROVE]->(f:ApprovalFlow)

4-3. フロー定義

  • (f:ApprovalFlow)-[:HAS_STEP {order:1}]->(s1:ApprovalStep)
  • (f:ApprovalFlow)-[:HAS_STEP {order:2}]->(s2:ApprovalStep)
  • (f:ApprovalFlow)-[:HAS_CONDITION]->(cond:ApprovalCondition)

step の承認者候補はこう結びます。

  • (s:ApprovalStep)-[:APPROVED_BY_ROLE]->(r:Role)
  • (s:ApprovalStep)-[:APPROVED_BY_USER]->(u:User)
  • (s:ApprovalStep)-[:APPROVED_BY_ORG_MANAGER]->(o:OrgUnit)

4-4. 申請・承認インスタンス

  • (req:Request)-[:REQUESTED_BY]->(u:User)
  • (req:Request)-[:USES_FLOW]->(f:ApprovalFlow)
  • (req:Request)-[:HAS_APPROVAL_INSTANCE]->(ai:ApprovalInstance)
  • (ai:ApprovalInstance)-[:HAS_STEP_INSTANCE]->(si:ApprovalStepInstance)
  • (si:ApprovalStepInstance)-[:BASED_ON]->(s:ApprovalStep)
  • (si:ApprovalStepInstance)-[:DECIDED_BY]->(u:User)
  • (si:ApprovalStepInstance)-[:HAS_DECISION]->(d:ApprovalDecision)
  • (req:Request)-[:EXECUTED_AS]->(job:ExecutionJob)

5. 実務で使いやすい status 設計

Request.status

  • draft
  • pending
  • in_approval
  • approved
  • rejected
  • returned
  • executing
  • executed
  • execution_failed
  • cancelled

ApprovalInstance.status

  • in_progress
  • approved
  • rejected
  • returned
  • cancelled

StepInstance.status

  • waiting
  • current
  • approved
  • rejected
  • skipped
  • returned

ExecutionJob.status

  • queued
  • running
  • succeeded
  • failed

6. Request の中身

承認の実体は Request です。
ここに「何をどう変えたいか」を保持します。

おすすめは payload を分けることです。

{
  "request_id": "req_20260405_0001",
  "resource_key": "crm.lead",
  "action_key": "write",
  "target_ids": ["op0262"],
  "change_set": {
    "before": {
      "stage": "proposal",
      "memo": null
    },
    "after": {
      "stage": "won",
      "memo": "受注確定"
    }
  },
  "context": {
    "company_id": "c001",
    "org_key": "sales_east",
    "requested_by": "u10"
  },
  "reason": "受注確定のため",
  "status": "pending"
}

ポイントは before/after を持つことです。
UI で差分表示しやすく、監査にも強いです。


7. 承認条件 DSL

ここはあとで重要になります。
まずは複雑にしすぎず、JSON DSL にした方が良いです。

7-1. シンプル条件

{
  "all": [
    { "field": "resource_key", "op": "eq", "value": "crm.lead" },
    { "field": "action_key", "op": "eq", "value": "write" }
  ]
}

7-2. 金額条件

{
  "all": [
    { "field": "change_set.after.amount", "op": "gte", "value": 1000000 }
  ]
}

7-3. 組織条件

{
  "all": [
    { "field": "context.org_key", "op": "in_descendants_of", "value": "sales_head" }
  ]
}

7-4. 列変更条件

{
  "any": [
    { "field": "changed_fields", "op": "contains", "value": "margin_rate" },
    { "field": "changed_fields", "op": "contains", "value": "internal_score" }
  ]
}

この DSL を ApprovalFlow や ApprovalStep に紐づけます。


8. 承認者解決ルール

承認者を毎回固定 user で持つと組織変更に弱いです。
なので原則は Role / OrgUnit から解決 です。

8-1. Role ベース

「sales_manager の誰か1人」

8-2. Org manager ベース

「申請者の所属部門の manager」

8-3. 上位組織 manager

「申請者の所属部門の親部門長」

8-4. 固定 user

例外的にシステム管理者や法務責任者

推奨

基本は

  • APPROVED_BY_ROLE
  • APPROVED_BY_ORG_MANAGER

この2つを主軸にするのが強いです。


9. フロー例

例1: 軽微な案件更新

  • 対象: crm.lead
  • action: write
  • 条件: changed_fieldsmemo, next_action のみ
  • 承認: 不要 or 自動承認

例2: ステージ更新

  • 対象: crm.lead
  • action: write
  • 条件: stage を変更
  • 承認: 所属部門 manager 1名

例3: 金額変更

  • 対象: crm.lead
  • action: write
  • 条件: amount >= 1000000
  • 承認:
    • step1: 所属 manager
    • step2: 部長

例4: 顧客マスタ変更

  • 対象: res.partner
  • action: write
  • 条件: changed_fieldscompany_registry, payment_terms
  • 承認:
    • step1: 営業責任者
    • step2: 管理部

10. UI 要件

UI は軽くで良いとのことですが、設計上必要な画面はあります。

10-1. 申請画面

  • resource
  • target
  • 変更前後差分
  • 申請理由
  • 想定承認フローのプレビュー

10-2. 承認待ち一覧

  • 自分が今処理すべき step
  • 優先度
  • resource
  • 申請者
  • 差分要約

10-3. 承認詳細

  • before / after diff
  • 申請理由
  • フロー全体図
  • 現在 step
  • approve / reject / return

10-4. フロー定義画面

  • flow 作成
  • step 並び替え
  • 条件編集
  • 承認者ロジック編集

11. 実行 Adapter 設計

ここが Odoo 非依存拡張の鍵です。

共通インターフェース

class ExecutionAdapter:
    def validate(self, request): ...
    def dry_run(self, request): ...
    def execute(self, request): ...
    def rollback_hint(self, request): ...

11-1. Odoo Adapter

  • ORM / XML-RPC / JSON-RPC / 専用API
  • モデルごとの write/create
  • Odoo 業務ロジックを活かす

11-2. SQL Adapter

  • 制約付き SQL 実行
  • Odoo 非依存テーブル向け
  • 影響件数制限
  • column 制限

11-3. API Adapter

  • 外部SaaS や社内API

推奨

Odoo に書く時は、長期的には Odoo Adapter 優先 がよいです。
承認は Odoo 非依存でよいですが、反映は Odoo のロジックを使った方が安全です。


12. 監査設計

これはかなり大事です。
承認ワークフローは監査が価値になります。

最低限残すものはこれです。

  • 誰が申請したか
  • いつ申請したか
  • 何を変えようとしたか
  • 承認フローは何か
  • 誰がいつ承認/却下したか
  • 実行結果はどうだったか
  • 失敗時のエラー内容
  • 実際に反映された target_id

おすすめは、Neo4j に索引的に持ちつつ、詳細は別の RDB テーブルに逃がしてもよいです。

例:

  • approval_request_log
  • approval_decision_log
  • execution_job_log

13. Importer との関係

将来 Odoo Importer を作る前提なら、承認定義も Odoo 互換を意識しておくとよいです。

ただし、承認そのものは Odoo 標準に無理に寄せなくてよいです。
寄せるべきはこの部分だけです。

  • Role / Group
  • Resource / Model
  • Action / CRUD
  • Row scope
  • Field scope

承認フローは Neo4j 独自でよく、必要なら Odoo には結果だけ渡せば十分です。


14. 最初の実装範囲

いきなり全部は広いので、最初はここまでで十分です。

Phase A

  • User / Role / OrgUnit / Resource / Action
  • ApprovalFlow / ApprovalStep
  • Request / ApprovalInstance / StepInstance
  • approve / reject / return API

Phase B

  • 条件 DSL
  • 承認者自動解決
  • フロープレビュー
  • 監査ログ

Phase C

  • Execution Adapter
  • Odoo Adapter
  • dry_run
  • 実行結果反映

Phase D

  • フロー編集UI
  • バージョン管理
  • 履歴比較
  • 申請テンプレート

15. 一番重要な設計原則

最後に、ぶれないように原則だけ固定します。

原則1

承認定義は Neo4j が正本

原則2

承認と実行は分離
承認されたからといって即 DB 更新ロジックを直書きしない

原則3

Request は before/after 差分を持つ
監査・UI・再実行のため

原則4

承認者は user 直指定より Role / OrgUnit 解決を優先
組織変更に強くするため

原則5

反映先は Adapter 化
Odoo 依存を閉じ込めるため


16. あなた向けの最終形

あなたの全体構成に合わせて言い切ると、こうです。

  • Neo4j
    • 組織
    • 権限
    • 承認フロー
    • 条件
    • 履歴索引
  • UI
    • 申請
    • 承認
    • フロー編集
    • 履歴参照
  • Execution Engine
    • 承認済み request を adapter に渡す
  • Odoo
    • 受け先の1つ
    • ワークフロー定義の正本ではない

この形なら、Odoo を超えて拡張できます。

次は、これをさらに実装寄りにして、
Neo4j の具体 Cypher スキーマ案
Request / Approval API の JSON 契約
を、そのまま使える形で出すのがよいです。

はい、かなりの部分は Odoo 画面で確認できます。
なので、最初から Neo4j 側に権限可視化UIを全部作らなくても、確認用UIは Odoo をかなり流用可能です。

ただし、全部がそのまま見やすく確認できるわけではありません。
結論だけ先に言うと、こうです。

  • Group: Odoo 画面で確認しやすい
  • ACL: Odoo 画面で確認できる
  • Record Rule: Odoo 画面で確認できる
  • User と Group の紐付け: Odoo 画面で確認できる
  • Menu / View groups: ある程度確認できる
  • Field groups: 技術者向け画面なら確認できるが、少し見づらい
  • 「このユーザーには最終的に何が見えるか」の一覧: 標準では弱い

つまり、定義そのものは Odoo で見えるが、
ユーザー目線の effective permission 一覧は標準だと見づらい、という整理です。
Chapter 11 でも、group / ACL / record rule / field groups / groups on views がそれぞれ別レイヤで定義される前提になっていて、確認も同じく分散します。


1. Odoo で確認できるもの

A. Group

Importer で res.groups を作れば、Odoo のユーザー・権限系画面で見えます。
ここでは少なくとも次が確認できます。

  • group 名
  • implied group
  • どのユーザーが属しているか
  • アクセス権の関連

これは比較的見やすいです。
Neo4j から作った group を neo.group_sales_user みたいな体系で入れておけば、管理画面で追えます。


B. ACL

ir.model.access に入れた model 単位 CRUD は、技術者向け画面で確認できます。

見える内容はおおむね次です。

  • 対象 model
  • 対象 group
  • read / create / write / delete

つまり、「この group はこの model に何ができるか」は Odoo 画面で見られます。
これはあなたの言う「テーブル相当の権限確認」に当たります。


C. Record Rule

ir.rule に入れれば、これも Odoo で確認できます。

見える内容は例えば次です。

  • rule 名
  • model
  • group
  • domain_force
  • active
  • read/write/create/delete の適用

つまり「どの行に制限がかかるか」の定義は見えます。
なので、Neo4j から Importer で入れた row 制約は、Odoo 側でも確認可能です。


D. User と Group の紐付け

res.users に group を付けたなら、ユーザー画面から確認できます。

  • このユーザーがどの group を持つか
  • implied により何が入っているか

ここも実務ではかなり使います。
「Kenji が sales_manager だから sales_user も持つ」などの確認はしやすいです。


E. Menu / View groups

menu や view に group 制御を入れた場合も、技術者モードなら追えます。
ただし、ACL や rule ほど一覧性は高くありません。

確認できるのは

  • この menu はどの group に見せるか
  • この view 要素はどの group か

です。
UI を隠す制御の確認には使えます。


F. Field groups

これは確認できますが、少し面倒です。
field 自体に groups が付いていれば、開発者向け画面やモデル定義側で追えます。

ただし、標準画面で

  • model ごとに
  • field ごとに
  • group がどう付いているか

を一覧で気持ちよく見るのは弱いです。

つまり 「存在確認」はできるが、「運用確認UI」としては弱い です。


2. Odoo 標準で弱いところ

ここが大事です。
Importer で流したものが Odoo に入っていても、ユーザー目線の最終結果は標準ではやや追いにくいです。

たとえば、あなたが本当に見たいのは多分こういうものです。

  • ユーザー A は crm.lead を読めるか
  • ユーザー A はどの行まで見えるか
  • ユーザー A は margin_rate を見えるか
  • ユーザー A がこの menu を開けるか

これは Odoo では、定義が分散しているので、標準で一画面にまとまりません。

理由は単純で、最終権限は

  • group
  • implied group
  • ACL
  • record rule
  • field groups
  • menu/view groups

の合成結果だからです。

つまり Odoo で見えるのは主に 定義 であって、
合成後の effective permission ではありません。


3. なので実務的なおすすめ

答えは「Odoo だけで十分か」「Neo4j 側にも描画が必要か」の二択ではなく、
最初は Odoo を確認UIとして使い、足りないところだけ補助表示する が一番現実的です。

おすすめ方針

まず Odoo に任せるもの

  • group 一覧
  • user-group 紐付け
  • model ACL
  • record rule
  • menu/view groups

これは Odoo で確認できます。

補助が欲しいもの

  • field groups 一覧
  • ある user に対する最終可視範囲
  • Neo4j 定義との対応表
  • Import 差分の確認

この部分だけを Neo4j 側、または Portal 側で軽く持つとよいです。


4. 一番コスパがよい方法

あなたが「Neo4j に描画させるのは結構手間」と言うなら、最初は Odoo を確認用に使う前提で問題ありません。

ただし、1つだけ追加するとかなり楽になります。
それは Importer が同時に “権限サマリ表” を出力すること です。

例えば Importer 実行時に、Odoo へ流し込むだけでなく、別途こんな CSV/JSON を出します。

{
  "groups": [
    {
      "group_xmlid": "neo.group_sales_manager",
      "implies": ["neo.group_sales_user"],
      "models": [
        {
          "model": "crm.lead",
          "read": true,
          "create": true,
          "write": true,
          "delete": false,
          "record_rules": [
            "company_id = user.company_id",
            "team_id in user.team_ids"
          ],
          "restricted_fields": [
            "margin_rate"
          ]
        }
      ]
    }
  ]
}

これがあると、

  • 詳細確認は Odoo
  • 全体俯瞰は Importer サマリ

で十分回ります。
Neo4j に本格的な権限UIを作らなくてもかなり楽です。


5. 実際にどう見えるかのイメージ

Importer 後、Odoo では次のように追えます。

管理者が見る流れ

1. Group 画面

「sales user」「sales manager」がある
→ implied group も見える

2. User 画面

Kenji に sales_manager が付いている
→ 実質 sales_user も持つ

3. Access Rights 画面

crm.lead に対して sales_manager は read/create/write
sales_user は read only

4. Record Rules 画面

sales_user / sales_manager に company_id = user.company_idteam_id in user.team_ids

5. モデル/フィールド確認

margin_rate に manager group が付いている

これでかなり追えます。


6. Odoo だけで十分か

運用初期はかなり十分です。

特にあなたが今ほしいのは

  • Importer で流し込んだ内容を確認したい
  • Neo4j の権限定義と Odoo の定義がずれていないか見たい

ということなので、これは Odoo 画面でかなり対応できます。

ただし将来的に次をやりたくなるなら、補助UIが欲しくなります。

  • 「ユーザー X の最終権限を一画面で見たい」
  • 「Neo4j 定義との差分を見たい」
  • 「この field はなぜ見えないか説明したい」
  • 「ある行が見えない理由を説明したい」

ここは Odoo 標準では弱いです。


7. なので最終判断

結論としてはこうです。

すぐにやるなら

Odoo に表示させるで十分です。
Neo4j 側に重い描画UIを急いで作る必要はありません。

ただし

Odoo で見えるのは主に 定義単位 です。
ユーザー最終結果の可視化 は弱いです。

一番良い落とし所

  • 権限定義の確認は Odoo
  • Importer が権限サマリ JSON/CSV を出す
  • 後で必要なら Neo4j/Portal 側に effective permission 画面を足す

これがコスパが高いです。


一言で答えると

はい、Importer で流した権限設定の多くは Odoo 画面で確認できます。
特に group、ACL、record rule、user-group 紐付けは確認しやすいです。
なので最初は Odoo を確認UIとして使う方針で問題ありません。
不足するのは「ユーザー最終権限の見える化」なので、そこだけ Importer のサマリ出力で補うのが現実的です。

必要なら次に、
「Odoo でどの画面で何を確認するか」の確認マップ を、管理者向けチェックリストとして整理します。


Comments

コメントを残す

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