openapi: 3.1.0
info:
title: Management Accounting KPI + RCA Core API
version: 0.1.0
description: >
Core contract for management accounting KPIs with month-over-month (MoM)
deltas and hypothesis-ranked root-cause comments across Material, Process,
and Skill. This spec intentionally focuses on user-visible outputs and
stable domain semantics. Infrastructure and Cypher details are out of scope.
servers:
- url: /api
tags:
- name: Health
- name: KPI
- name: RCA
- name: Dictionary
- name: Status
# ------------------------------------------------------------
# Extension blocks to keep the whole design consistent
# ------------------------------------------------------------
x-scope:
concept: "KPI calculation + MoM + hypothesis-ranked RCA comments"
out_of_scope:
- "Full accounting valuation reproduction"
- "Enterprise-wide KPI coverage"
- "Auto-human evaluation decisions"
- "Complex ML scoring (PoC uses rules/thresholds)"
x-phases:
- id: P0
name: Docker bootstrap
goals:
- "Run Odoo + Postgres + Neo4j + API container in one network"
- "Verify base connectivity"
- id: P1
name: Ontology minimal set
goals:
- "Fix KPI hierarchy and dependency semantics"
- "Define enterprise-diff domains (org/step/skill/material/baselines)"
- id: P2
name: Data minimum requirements
goals:
- "Decide minimal Odoo sources per KPI"
- "Fix period granularity (monthly)"
- id: P3
name: Minimal ETL
goals:
- "Load dictionaries + relations first"
- "Load thin monthly aggregates"
- id: P4
name: KPI compute
goals:
- "Compute numeric KPIs reproducibly"
- "Persist results (DB or Neo4j)"
- id: P5
name: Hypothesis ranking (rule-based)
goals:
- "Rank material/process/skill hypotheses"
- "Generate MoM reason comments"
- id: P6
name: API visibility
goals:
- "Expose REST with Swagger (/docs)"
- "Optionally expose GraphQL with GraphiQL"
x-ontology:
# The ontology here is expressed as a pragmatic domain dictionary.
# This is not a formal OWL spec; it is a product-facing semantic contract.
entities:
- name: KPI
role: "Symptom/Outcome metrics and derived diagnosis anchors"
- name: Component
role: "Cost/Value structure elements composing KPI meaning"
- name: Driver
role: "Root-cause drivers grouped into Material, Process, Skill"
- name: OrgUnit
role: "Enterprise hierarchy units used for scope and allocation"
- name: CostCenter
role: "Accounting/controlling unit for cost attribution"
- name: Step
role: "Process/work step in manufacturing or service flow"
- name: Skill
role: "Capability required to execute Steps with quality/efficiency"
- name: Material
role: "Purchased/consumed input affecting cost and yield"
relations:
- "KPI USES Component"
- "Component DECOMPOSES_TO Component"
- "Component AFFECTED_BY Driver"
- "Step USES_MATERIAL Material"
- "Step REQUIRES_SKILL Skill"
- "OrgUnit/Role OWNS Step (enterprise-specific)"
- "CostCenter ROLLS_UP CostCenter (hierarchical allocation support)"
hypothesis_categories:
- MATERIAL
- PROCESS
- SKILL
x-kpi-hierarchy:
# PoC minimal set. These codes should match DB seeds.
symptom_kpis:
- code: GM_RATE
name: Gross Margin Rate
formula_hint: "(Sales - COGS) / Sales"
- code: COGS_RATE
name: COGS Rate
formula_hint: "COGS / Sales"
- code: INV_TURN
name: Inventory Turnover
formula_hint: "COGS / AvgInventory"
- code: INV_AGING
name: Inventory Aging
formula_hint: "Bucketized stock value/qty by age"
cause_split_kpis:
material:
- code: MAT_RATE
name: Material Cost Rate
- code: PP_INDEX_MOM
name: Purchase Price Index (MoM)
- code: SCRAP_RATE
name: Scrap Rate
process:
- code: PROC_RATE
name: Process Cost Rate
- code: LAB_EFF
name: Labor Efficiency (Std vs Actual)
- code: REWORK_RATE
name: Rework Rate
- code: DEFECT_RATE
name: Defect Rate / FPY
x-enterprise-diff:
# What customers must configure per company.
dictionaries:
- OrgUnit
- CostCenter
- Step
- Skill
- Material
baselines:
- StandardLaborHours
- StandardMaterialUsage
mappings:
- "Odoo products -> Material"
- "Odoo routings/workcenters -> Step (if available)"
- "Org/Role -> Step ownership (optional for PoC)"
x-data-model:
# PoC recommendation:
# Create these tables in the same PostgreSQL instance used by Odoo,
# but inside a dedicated schema (e.g., mgmt) to avoid collision.
target:
postgres:
instance: "odoo_db"
schema_recommended: "mgmt"
isolation_policy:
- "Do not modify Odoo core tables"
- "Read-only access to Odoo schemas"
- "Write only to mgmt schema"
tables:
- name: mgmt.kpi_def
kind: "model"
purpose: "KPI dictionary and hierarchy anchors"
key:
- kpi_code
columns:
- kpi_code
- name
- category # SYMPTOM|MATERIAL|PROCESS|SKILL
- unit
- formula_hint
- is_active
- created_at
- updated_at
- name: mgmt.component_def
kind: "model"
purpose: "Component dictionary for cost/value decomposition"
key:
- component_code
columns:
- component_code
- name
- parent_component_code # optional for tree
- created_at
- updated_at
- name: mgmt.kpi_component_map
kind: "relation"
purpose: "KPI USES Component"
key:
- kpi_code
- component_code
columns:
- kpi_code
- component_code
- weight_hint # optional
- name: mgmt.driver_def
kind: "model"
purpose: "Drivers for RCA scoring grouped by hypothesis category"
key:
- driver_code
columns:
- driver_code
- name
- hypothesis_category # MATERIAL|PROCESS|SKILL
- created_at
- updated_at
- name: mgmt.step_def
kind: "dictionary"
purpose: "Enterprise-specific process steps"
key:
- step_code
columns:
- step_code
- name
- org_key # optional
- created_at
- updated_at
- name: mgmt.skill_def
kind: "dictionary"
purpose: "Enterprise-specific skills"
key:
- skill_code
columns:
- skill_code
- name
- created_at
- updated_at
- name: mgmt.material_def
kind: "dictionary"
purpose: "Enterprise-specific material catalog (mapped from Odoo products)"
key:
- material_code
columns:
- material_code
- name
- odoo_product_id # optional linkage
- created_at
- updated_at
- name: mgmt.step_skill_map
kind: "relation"
purpose: "Step REQUIRES Skill"
key:
- step_code
- skill_code
columns:
- step_code
- skill_code
- required_level_hint
- name: mgmt.step_material_map
kind: "relation"
purpose: "Step USES Material"
key:
- step_code
- material_code
columns:
- step_code
- material_code
- usage_weight_hint
- name: mgmt.period_kpi_value
kind: "fact"
purpose: "Monthly KPI values by scope"
key:
- period
- kpi_code
- scope_type
- scope_key
columns:
- period # YYYY-MM
- kpi_code
- scope_type # COMPANY|ORG|COSTCENTER|STEP|MATERIAL|SKILL
- scope_key
- value
- currency
- created_at
- name: mgmt.period_kpi_mom
kind: "fact"
purpose: "MoM deltas and simple derived stats"
key:
- period
- kpi_code
- scope_type
- scope_key
columns:
- period
- kpi_code
- scope_type
- scope_key
- value_current
- value_prev
- delta_abs
- delta_pct
- created_at
- name: mgmt.period_rca_result
kind: "fact"
purpose: "Hypothesis-ranked RCA outputs used for comments"
key:
- period
- kpi_code
- scope_type
- scope_key
columns:
- period
- kpi_code
- scope_type
- scope_key
- top_hypothesis # MATERIAL|PROCESS|SKILL
- hypothesis_scores_json
- evidence_json # top materials/steps/skills
- comment_text
- created_at
# ------------------------------------------------------------
# Paths (REST)
# ------------------------------------------------------------
paths:
/health:
get:
tags: [Health]
summary: Health check
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
status: { type: string }
/status/summary:
get:
tags: [Status]
summary: Summary of latest computed periods
responses:
"200":
description: Summary
content:
application/json:
schema:
$ref: "#/components/schemas/StatusSummary"
/kpis:
get:
tags: [KPI]
summary: List KPI definitions (hierarchy-aware)
parameters:
- $ref: "#/components/parameters/activeOnly"
responses:
"200":
description: KPI definitions
content:
application/json:
schema:
type: object
properties:
items:
type: array
items: { $ref: "#/components/schemas/KpiDef" }
/kpis/values:
get:
tags: [KPI]
summary: Get KPI values for a period and scope
parameters:
- $ref: "#/components/parameters/period"
- $ref: "#/components/parameters/scopeType"
- $ref: "#/components/parameters/scopeKey"
responses:
"200":
description: KPI values
content:
application/json:
schema:
$ref: "#/components/schemas/PeriodKpiValueList"
/kpis/mom:
get:
tags: [KPI]
summary: Get month-over-month KPI deltas
parameters:
- $ref: "#/components/parameters/period"
- $ref: "#/components/parameters/scopeType"
- $ref: "#/components/parameters/scopeKey"
responses:
"200":
description: MoM results
content:
application/json:
schema:
$ref: "#/components/schemas/PeriodKpiMoMList"
/rca:
get:
tags: [RCA]
summary: Get hypothesis-ranked RCA results with comment
parameters:
- $ref: "#/components/parameters/period"
- $ref: "#/components/parameters/kpiCode"
- $ref: "#/components/parameters/scopeType"
- $ref: "#/components/parameters/scopeKey"
responses:
"200":
description: RCA result
content:
application/json:
schema:
$ref: "#/components/schemas/PeriodRcaResult"
/dictionary/steps:
get:
tags: [Dictionary]
summary: List enterprise steps
responses:
"200":
description: Steps
content:
application/json:
schema:
type: object
properties:
items:
type: array
items: { $ref: "#/components/schemas/StepDef" }
/dictionary/skills:
get:
tags: [Dictionary]
summary: List enterprise skills
responses:
"200":
description: Skills
content:
application/json:
schema:
type: object
properties:
items:
type: array
items: { $ref: "#/components/schemas/SkillDef" }
/dictionary/materials:
get:
tags: [Dictionary]
summary: List enterprise materials
responses:
"200":
description: Materials
content:
application/json:
schema:
type: object
properties:
items:
type: array
items: { $ref: "#/components/schemas/MaterialDef" }
# ------------------------------------------------------------
# Components
# ------------------------------------------------------------
components:
parameters:
period:
name: period
in: query
required: true
schema:
type: string
pattern: "^[0-9]{4}-[0-9]{2}$"
description: "Target period in YYYY-MM"
kpiCode:
name: kpi_code
in: query
required: true
schema:
type: string
scopeType:
name: scope_type
in: query
required: false
schema:
type: string
enum: [COMPANY, ORG, COSTCENTER, STEP, MATERIAL, SKILL]
description: "Scope type for aggregation"
scopeKey:
name: scope_key
in: query
required: false
schema:
type: string
description: "Scope key for aggregation (id/code)"
activeOnly:
name: active_only
in: query
required: false
schema:
type: boolean
default: true
schemas:
StatusSummary:
type: object
properties:
latest_period:
type: string
computed_kpis:
type: integer
computed_rca:
type: integer
KpiDef:
type: object
properties:
kpi_code: { type: string }
name: { type: string }
category:
type: string
enum: [SYMPTOM, MATERIAL, PROCESS, SKILL]
unit: { type: string }
formula_hint: { type: string }
is_active: { type: boolean }
StepDef:
type: object
properties:
step_code: { type: string }
name: { type: string }
org_key: { type: string }
SkillDef:
type: object
properties:
skill_code: { type: string }
name: { type: string }
MaterialDef:
type: object
properties:
material_code: { type: string }
name: { type: string }
odoo_product_id: { type: integer }
PeriodKpiValue:
type: object
properties:
period: { type: string }
kpi_code: { type: string }
scope_type: { type: string }
scope_key: { type: string }
value: { type: number }
currency: { type: string }
PeriodKpiValueList:
type: object
properties:
items:
type: array
items: { $ref: "#/components/schemas/PeriodKpiValue" }
PeriodKpiMoM:
type: object
properties:
period: { type: string }
kpi_code: { type: string }
scope_type: { type: string }
scope_key: { type: string }
value_current: { type: number }
value_prev: { type: number }
delta_abs: { type: number }
delta_pct: { type: number }
PeriodKpiMoMList:
type: object
properties:
items:
type: array
items: { $ref: "#/components/schemas/PeriodKpiMoM" }
HypothesisScore:
type: object
properties:
hypothesis:
type: string
enum: [MATERIAL, PROCESS, SKILL]
score:
type: number
evidence:
type: array
items:
type: string
PeriodRcaResult:
type: object
properties:
period: { type: string }
kpi_code: { type: string }
scope_type: { type: string }
scope_key: { type: string }
top_hypothesis:
type: string
enum: [MATERIAL, PROCESS, SKILL]
hypothesis_scores:
type: array
items: { $ref: "#/components/schemas/HypothesisScore" }
top_materials:
type: array
items: { type: string }
top_steps:
type: array
items: { type: string }
top_skills:
type: array
items: { type: string }
comment_text:
type: string
コメントを残す