/** * 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'] ); } } } } } } } } 設計内容の反映 – Raqqa

設計内容の反映

5177/Bundleデータは 「業務として何が必要か」 は表現できていますが、5176で設計判断として扱うには、まだ次が弱いです。

1. NFF は標準機能のどの追加利用・設定・ビューなのか
2. CusField はどのモデルに、どの型で、どんな条件で必要なのか
3. ロジックは何をトリガーに、どの条件で、何を更新するのか
4. 標準機能で代替できるなら、どこまで代替するのか
5. コード生成できる条件まで落ちているか

なので、BusinessStream解決ベースには、NFF / CusField / LogicCandidate を自然文ではなく、半構造化して持つべきです。


追加すべきデータ層

今の考え方に、次の3種類を追加するのが良いです。

BusinessStream Resolution Base
  ├─ confirmed_business_context
  ├─ field_standard_overlay_slots
  ├─ nff_slots
  ├─ custom_field_slots
  ├─ logic_slots
  ├─ gap_resolution_slots
  └─ implementation_readiness

ポイントは、NFF・CusField・Logicを同じ「実装候補」扱いにしないことです。

それぞれ性質が違います。

種別意味実装の姿
Standard Field標準フィールドを使うOdoo標準項目を参照
NFF標準機能を使うために必要な標準設定・標準ビュー・標準連携・標準機能上の見せ方設定、保存フィルタ、ビュー拡張、標準モデル利用
CusField標準にない業務状態・理由・分類を持つx_ field、selection、boolean、many2oneなど
Logic条件判定・自動更新・警告・状態遷移computed field、onchange、constraint、server action、scheduled action、button actionなど

NFFの定義を明確にした方がいいです

NFFは「New Feature Field」ではなく、今回の文脈ではこう定義した方が自然です。

NFF = 標準機能を使う場合に追加で設計・設定・表示すべき機能要素

つまり、CusFieldとは違います。

例:

nff_slots:
  - nff_key: nff::sales.delivery_allocation::delivery_allocation_view
    type: standard_view_or_filter
    label: 出荷・引当・納期回答一覧
    base_model: sale.order.line
    uses_standard_models:
      - sale.order.line
      - stock.move
      - stock.picking
    uses_standard_fields:
      - sale.order.line.product_id
      - sale.order.line.product_uom_qty
      - sale.order.line.qty_delivered
      - stock.picking.scheduled_date
      - stock.picking.state
    purpose: >
      標準の受注明細・出荷情報を組み合わせて、出荷予定・引当・納期回答の確認画面を作る。
    implementation_type: saved_filter_or_view_extension
    standard_dependency:
      - Sales Order
      - Inventory Delivery
      - Stock Reservation
    decision_status: pending

これは「標準フィールドを使うが、業務画面・一覧・確認導線としては新しく作る」ものです。
つまり、コード生成対象になる可能性はあるが、カスタム業務ロジックではない


CusFieldは「なぜ標準では不足するか」まで必須

CusFieldは、必ず次を持たせた方がいいです。

custom_field_slots:
  - field_key: cusfield::sale.order.line::x_delivery_answer_status
    model: sale.order.line
    field_name: x_delivery_answer_status
    label_ja: 納期回答状態
    field_type: selection
    selection_values:
      - value: not_answered
        label_ja: 未回答
      - value: answered
        label_ja: 回答済み
      - value: need_reanswer
        label_ja: 再回答必要
    business_meaning: >
      顧客に対する納期回答の状態を明細単位で管理する。
    why_standard_is_not_enough: >
      Odoo標準の scheduled_date や commitment_date は予定日を表すが、
      顧客へ回答済みか、再回答が必要かという業務状態は表せない。
    standard_alternatives_checked:
      - model: sale.order
        field: commitment_date
        result: insufficient
        reason: 受注単位であり、明細単位の回答状態ではない
      - model: stock.picking
        field: scheduled_date
        result: insufficient
        reason: 出荷予定日であり、顧客回答状態ではない
    required_by:
      - business_stream_id: BS-SALES-001
      - gap_key: gap::delivery_answer_status
    decision_status: pending
    implementation_readiness: needs_selection_values

ここまで書かせると、無駄なCusFieldをかなり抑制できます。

重要なのは、CusFieldを作る条件です。

CusFieldを作ってよい条件:
- 標準フィールドでは意味が違う
- 標準フィールドでは粒度が違う
- 標準フィールドでは履歴・理由・判断状態を保持できない
- 標準機能の状態と混ぜると矛盾する

Logicは自然文ではなく「条件・トリガー・結果」で持つ

ここが一番重要です。
ロジックは自然文のままだと、あとで実装できません。

最低限、こう分解した方がいいです。

logic_slots:
  - logic_key: logic::sales.delivery_allocation::delivery_answer_status_update
    label: 納期回答状態の更新
    logic_type: state_update
    implementation_candidate:
      - onchange
      - server_action
      - button_action
    trigger:
      type: user_action
      event: delivery_answer_confirmed
      source_model: sale.order.line
    conditions:
      all:
        - field: sale.order.line.x_delivery_answer_status
          operator: in
          value:
            - not_answered
            - need_reanswer
        - field: stock.picking.scheduled_date
          operator: is_not_null
    actions:
      - type: update_field
        model: sale.order.line
        field: x_delivery_answer_status
        value: answered
      - type: write_log
        model: sale.order.line
        message: 納期回答済みに更新
    exceptions:
      - condition:
          field: stock.picking.scheduled_date
          operator: is_null
        handling: block_or_warn
        message: 出荷予定日が未設定のため、納期回答済みにできません。
    affected_models:
      - sale.order.line
      - stock.picking
    required_fields:
      - sale.order.line.x_delivery_answer_status
      - stock.picking.scheduled_date
    decision_status: pending
    implementation_level: level_2
    codegen_readiness: needs_trigger_confirmation

この形にすると、あとで実装タイプを判定できます。


Logicの種類をenum化した方がいいです

ロジックは最初から分類しておくと良いです。

logic_type:
  - computed_value
  - state_update
  - validation_constraint
  - warning
  - auto_assignment
  - synchronization
  - aggregation
  - scheduled_check
  - button_action
  - report_filter

これにより、Odooでどの実装になるかが見えます。

logic_typeOdoo実装候補
computed_valuecompute field
state_updatebutton / server action
validation_constraintPython constraint
warningonchange / constraint / activity
auto_assignmentautomated action / server action
synchronizationwrite override / server action
aggregationcomputed field / SQL view / report model
scheduled_checkcron
button_actionmodel method + button
report_filtersaved filter / search view

BusinessStream単位の最終構造

BusinessStreamごとに、こういう構造にするとよいです。

business_stream_resolution_base:
  business_stream_id: BS-SALES-001
  label: 指定出荷日・在庫引当・納期回答・出荷調整をつなげる

  confirmed_business_context:
    purpose: ...
    input_summary: []
    process_summary: []
    output_summary: []
    main_model_candidates: []
    related_model_candidates: []
    gaps: []
    questions: []

  standard_field_overlay_slots:
    description: フィールドデータで人間が採用した標準フィールドを差し込む場所
    adopted_standard_fields: []
    rejected_standard_fields: []

  nff_slots:
    - nff_key: ...
      type: standard_view_or_filter
      base_model: ...
      uses_standard_models: []
      uses_standard_fields: []
      purpose: ...
      decision_status: pending

  custom_field_slots:
    - field_key: ...
      model: ...
      field_name: ...
      field_type: ...
      business_meaning: ...
      why_standard_is_not_enough: ...
      decision_status: pending

  logic_slots:
    - logic_key: ...
      logic_type: ...
      trigger: {}
      conditions: {}
      actions: []
      exceptions: []
      required_fields: []
      implementation_candidate: []
      codegen_readiness: ...

  resolution_effect:
    resolves_gaps: []
    creates_domain_guide_patch: []
    creates_ontology_delta: []
    affects_5176_nodes: []

これなら、自然文の「標準でいけそう」「追加が必要そう」ではなく、設計判断として扱えるデータになります。


Cypherに入れるならノードも分ける

5176で見るなら、グラフ側もこう分けた方がよいです。

(:BusinessStream)
(:BusinessBundle)
(:ConfirmedBusinessContext)
(:StandardFieldOverlaySlot)
(:NFFSlot)
(:CustomFieldSlot)
(:LogicSlot)
(:LogicCondition)
(:LogicAction)
(:Gap)
(:OpenQuestion)
(:ResolutionEffect)

関係はこうです。

(BusinessStream)-[:HAS_NFF_SLOT]->(NFFSlot)
(BusinessStream)-[:HAS_CUSTOM_FIELD_SLOT]->(CustomFieldSlot)
(BusinessStream)-[:HAS_LOGIC_SLOT]->(LogicSlot)

(NFFSlot)-[:USES_STANDARD_MODEL]->(OdooModel)
(NFFSlot)-[:USES_STANDARD_FIELD]->(OdooField)

(CustomFieldSlot)-[:EXTENDS_MODEL]->(OdooModel)
(CustomFieldSlot)-[:REQUIRES_BECAUSE_OF]->(Gap)

(LogicSlot)-[:HAS_CONDITION]->(LogicCondition)
(LogicSlot)-[:HAS_ACTION]->(LogicAction)
(LogicSlot)-[:REQUIRES_FIELD]->(CustomFieldSlot)
(LogicSlot)-[:USES_STANDARD_FIELD]->(OdooField)

(ResolutionEffect)-[:RESOLVES_GAP]->(Gap)
(ResolutionEffect)-[:AFFECTS_5176_NODE]->(...)

これで、5176上では、

このBusinessStreamを解決するには、
標準フィールド採用だけでなく、
NFFが2つ、CusFieldが1つ、Logicが1つ必要

という見え方にできます。


DomainGuide / Ontology Deltaへの分け方

ここも整理できます。

DomainGuideに入れるもの

DomainGuideは、標準やカスタムをどう業務で使うかです。

domain_guide_patch:
  business_stream_id: BS-SALES-001
  standard_usage:
    - stock.picking.scheduled_date は出荷予定日として使う
    - sale.order.line.qty_delivered は納品済数量として使う
  nff_usage:
    - 出荷・引当・納期回答一覧は sale.order.line を基点に作る
  logic_usage:
    - 納期回答済みの判断は x_delivery_answer_status で管理し、出荷予定日が未設定なら回答済みにしない

Ontology Deltaに入れるもの

Ontology Deltaは、標準にない追加だけです。

ontology_delta:
  business_stream_id: BS-SALES-001
  custom_fields:
    - model: sale.order.line
      field: x_delivery_answer_status
      type: selection
      meaning: 顧客への納期回答状態
  custom_logic:
    - logic_key: logic::sales.delivery_allocation::delivery_answer_status_update
      type: state_update
      required_fields:
        - sale.order.line.x_delivery_answer_status

Cypherに入れるもの

Cypherはレビュー・同期・可視化用なので、全部入れてよいです。

BusinessStream
NFFSlot
CustomFieldSlot
LogicSlot
Gap
ResolutionEffect

実装条件の見える化

「実際にどう実装されるのか」を見るためには、各候補に implementation_condition を持たせるのが良いです。

implementation_condition:
  required_when:
    - 顧客への納期回答状態を明細単位で管理する
    - 回答済み/再回答必要を検索・一覧表示したい
  not_required_when:
    - 出荷予定日のみで運用できる
    - 納期回答履歴を管理しない
  requires_customer_decision:
    - 納期回答状態の選択肢
    - 再回答必要になる条件
  implementation_blocker:
    - 状態遷移条件が未確定

これを入れると、コンサル/オフショアが自然文で曖昧に書いたものを、実装可能な単位にできます。


人間編集テンプレも変えるべきです

人間が編集するテンプレは、こうするのがよいです。

business_stream_id: BS-SALES-001

standard_field_overlay:
  adopted_standard_fields:
    - stock.picking.scheduled_date
    - stock.picking.state
    - sale.order.line.qty_delivered

nff_decisions:
  - nff_key: nff::sales.delivery_allocation::delivery_allocation_view
    decision: accepted
    implementation_type: view_extension
    notes: 受注明細を基点に一覧化する

custom_field_decisions:
  - field_key: cusfield::sale.order.line::x_delivery_answer_status
    decision: accepted
    field_type: selection
    selection_values:
      - not_answered
      - answered
      - need_reanswer
    reason_standard_is_not_enough: 標準日付項目では回答状態を持てないため

logic_decisions:
  - logic_key: logic::sales.delivery_allocation::delivery_answer_status_update
    decision: needs_review
    implementation_type: button_action
    trigger_confirmed: false
    missing_decisions:
      - 再回答必要になる条件
      - 回答済みにする操作タイミング

これなら、入力は少ないまま、実装条件まで残せます。


次の抽出フェーズの目的を修正するなら

次フェーズは、単なるBusinessStream Resolution Baseではなく、こうした方がよいです。

P5-5177-to-5176 BusinessStream Resolution & Implementation Slots

目的:

Bundle / BusinessStreamデータから、前段階で確定している業務文脈を抽出し、
フィールドデータで人間が採用した標準フィールドを差し込めるようにし、
さらに NFF / CusField / Logic を実装候補スロットとして構造化する。

成果物はこうです。

business_stream_resolution_implementation_slots/
  json/
    01_business_stream_base.json
    02_standard_field_overlay_slots.json
    03_nff_slots.json
    04_custom_field_slots.json
    05_logic_slots.json
    06_gap_resolution_slots.json
    07_5176_sync_map.json
    08_human_edit_template.json
    09_implementation_readiness_report.json

  cypher/
    01_business_stream_resolution_graph.cypher
    02_nff_custom_logic_slots.cypher
    03_5176_resolution_links.cypher

  domainguide/
    01_domain_guide_patch_template.json

  ontology_delta/
    01_ontology_delta_candidate_slots.json

結論

はい。
NFF、CusField、Logicも必ず反映した方がいいです。

今の自然文だけだと、5176で「解決済み」と言えても、実装に進むとまた曖昧になります。

なので次は、BusinessStreamごとに、

標準フィールド採用
NFF
CusField
Logic
Gap解決
実装条件

を分けて持つべきです。

特にロジックは、

trigger
conditions
actions
exceptions
required_fields
implementation_type
codegen_readiness

まで持たせる。

この形にすれば、5176で「何が解決したか」だけでなく、その解決が実装可能なレベルまで落ちているかも判断できるようになります。


Comments

コメントを残す

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