dodil/erp-brick-hr

src/brick_hr/domain/standards.py

"""Where a playbook and its steps sit in the APQC Process Classification Framework (PCF), so a BPMN reader — an
auditor, a process-mining tool, a buyer comparing with their SAP or Oracle map — can join on an id they already use.

Every id below is from the APQC PCF Cross-Industry v8.0 (generated 2026-02-25; the workbook named in
erp-bench standards/processes.yaml), verified by id AND name; nothing is guessed. A step with no clear mapping
carries none. ©2026 APQC. ALL RIGHTS RESERVED. This Process Classification Framework® ("PCF") is the copyrighted
intellectual property of APQC; it is used here under APQC's licence, which requires this notice on every copy.
"""
from typing import Optional

# direction → the process it implements
PROCESS_PCF = {
    "onboarding": ("10469", "Manage employee onboarding"),          # 7.3.1
    "offboarding": ("21710", "Manage offboarding"),                 # 7.6.2.3
    "change": ("10524", "Manage and maintain employee data"),       # 7.7.3
    "conversion": ("10524", "Manage and maintain employee data"),   # 7.7.3
}

# a capability (family) → the activity it performs
CAPABILITY_PCF = {
    "identity": ("20758", "Manage IT user directory"),                        # 8.3.8.2
    "groups": ("20759", "Manage IT user authorization"),                      # 8.3.8.3
    "chat": ("20759", "Manage IT user authorization"),                        # 8.3.8.3
    "contracts": ("11052", "Negotiate and document agreements/contracts"),    # 12.4.9
}

# a step kind → the activity (the starter's own form keeps the employee record)
KIND_PCF = {"intake": ("10524", "Manage and maintain employee data")}         # 7.7.3

# a designed step key (localisation packs) → the activity, where neither its capability nor its kind says it
KEY_PCF = {
    "payroll_setup": ("10497", "Administer Payroll"),                         # 7.5.4
    "final_pay": ("10497", "Administer Payroll"),                             # 7.5.4
    "offer_outplacement": ("10516", "Develop and implement employee outplacement"),  # 7.6.2.4
    "pension_notice": ("10514", "Manage retirement"),                          # 7.6.2.1
    "knowledge_handover": ("10514", "Manage retirement"),                      # 7.6.2.1
    "long_service_recognition": ("10514", "Manage retirement"),                # 7.6.2.1
}


def process_pcf(direction: str) -> Optional[tuple]:
    return PROCESS_PCF.get(direction)


def step_pcf(key: str, kind: str, capability: Optional[str]) -> Optional[tuple]:
    """(pcf_id, name) for one step, or None when the PCF has no activity that is clearly this one."""
    if key in KEY_PCF:
        return KEY_PCF[key]
    if capability:
        return CAPABILITY_PCF.get(capability.split(".")[0])
    return KIND_PCF.get(kind)


__all__ = ["process_pcf", "step_pcf", "PROCESS_PCF"]