dodil/erp-brick-manufacturing
DODIL ERP brick: manufacturing
src/brick_manufacturing/application/process.yaml
# The process this brick serves: manufacturing version 1, SIGNED 2026-10-25 (erp-method signoff/manufacturing/). Copied from the spec's
# actors and workflows, step ids frozen (Task_<key>): tests/test_process.py holds it equal to the spec AND the BPMN it draws equal to
# the signed files' hashes. A change goes through `erp-compose change propose` and a new signature first, then here.
id: manufacturing
name: Production (bills of materials and production orders)
version: 1
actors:
- id: process_engineer
name: Process engineer / head roaster
does: writes bills of materials and their versions, routings and work centres, the planned yield
sees: every BOM, routing and work centre
permissions:
- mfg:bom:read
- mfg:bom:write
- mfg:bom:release
- mfg:routing:write
- mfg:work_centre:write
- mfg:order:read
- id: planner
name: Production planner
does: runs MRP-lite, firms planned orders, creates and releases production orders, enters forecasts
sees: every plan and order
permissions:
- mfg:bom:read
- mfg:plan:run
- mfg:plan:firm
- mfg:forecast:write
- mfg:order:read
- mfg:order:create
- mfg:order:release
- mfg:order:cancel
- id: operator
name: Roaster / production operator
does: issues components, records operation times, reports output, by-products and scrap with a reason, reverses their own wrong entries the
same day
sees: released orders of their work centres
permissions:
- mfg:bom:read
- mfg:order:read
- mfg:order:issue
- mfg:order:confirm
- mfg:order:report_output
- mfg:order:scrap
- mfg:order:reverse
- id: cost_accountant
name: Cost accountant
does: runs and approves cost roll-ups, releases standard costs to inventory, reviews and closes orders, explains variances
sees: every order's cost
permissions:
- mfg:bom:read
- mfg:order:read
- mfg:cost:run
- mfg:cost:approve
- mfg:order:complete
- mfg:order:close
- mfg:report:read
- mfg:migration:read
note: 'never approves a roll-up they ran, nor closes an order whose variance approval they asked for: email AND sub'
- id: quality
name: Quality / food safety lead
does: blocks a batch, runs a trace back to the ingredients and forward to the customers and shops
sees: every lot's genealogy
permissions:
- mfg:bom:read
- mfg:order:read
- mfg:trace:read
- id: owner
name: The owner (or whoever the owner names)
does: holds the controls (over-issue tolerance, release with shortages, variance approval threshold, AI step modes), signs the migration reconciliation
sees: everything
permissions:
- mfg:order:read
- mfg:report:read
- mfg:settings:controls
- mfg:migration:read
- mfg:migration:sign
note: anyone else holding mfg:settings:controls is a configuration error the access test refuses
- id: system
name: The system
does: runs MRP-lite on request or from the worker, explodes BOMs for pos and the planner, sends issues and receipts to inventory and postings
to gl from pending rows, marks plans, roll-ups and unreleased orders stale, derives genealogy, loads an Odoo migration
sees: everything, through its own service account
permissions:
- mfg:bom:explode
- mfg:plan:read_requirements
- mfg:plan:derive
- mfg:outbox:dispatch
- mfg:genealogy:derive
- mfg:number_series:claim
- mfg:migration:load
- mfg:migration:reconcile
note: exact grants to manufacturing's service client, never a wildcard; mfg:bom:explode is also granted to pos's client and mfg:plan:read_requirements
to inventory's; the client holds product:product:read, inv:availability:read, inv:valuation:read, inv:stock:read, gl:journal:post, gl:cost_centre:read,
gl:period:read
workflows:
- entity: bom_version
states:
- draft
- released
- obsolete
terminal:
- obsolete
transitions:
- key: write_bom
name: Write a BOM version
from: none
to: draft
by: process_engineer
gate: quantities convert exactly; no cycle (graph walk); components stocked or phantom
- key: release_bom
name: Release the version
from: draft
to: released
by: process_engineer
gate: valid_from after today; no overlap with another released version of the BOM
effect: cost roll-ups and unreleased orders using the BOM marked stale (MFG-18)
- key: obsolete_bom
name: Make it obsolete
from: released
to: obsolete
by: process_engineer
requires: valid_to
gate: no unreleased order or firmed plan names it after valid_to
- entity: production_order
states:
- created
- released
- in_progress
- completed
- closed
- cancelled
terminal:
- closed
- cancelled
transitions:
- key: create_order
name: Create the order
from: none
to: created
by:
- planner
- system
gate: a released BOM and routing valid on the due date
effect: production_order_id claimed from the company's yearly series
- key: release_order
name: Release the order
from: created
to: released
by: planner
gate: components available (or release_with_shortage); BOM and routing valid on the release date
effect: BOM and routing copied onto the order and frozen
- key: issue_components
name: Issue components
from:
- released
- in_progress
to: in_progress
by: operator
gate: within over_issue_tolerance; the lot available
effect: inventory issue from a pending row (MFG-11)
- key: confirm_operation
name: Confirm an operation
from:
- released
- in_progress
to: in_progress
by: operator
effect: activity journal from a pending row (MFG-12)
- key: report_output
name: Report output
from:
- released
- in_progress
to: in_progress
by: operator
gate: at most 110% of plan unless confirmed
effect: inventory receipt of the output lot and by-products (MFG-13)
- key: reverse_entry
name: Reverse a wrong entry
from: in_progress
to: in_progress
by: operator
requires: reason
gate: the entry not reversed already; the operator's own entry on the same business date, else a planner
effect: a mirror movement with its own ref (…:reverse)
- key: complete_order
name: Technically complete
from: in_progress
to: completed
by:
- operator
- cost_accountant
effect: no further issues or outputs; costs may still arrive
- key: close_order
name: Close and settle
from: completed
to: closed
by: cost_accountant
gate: every pending outbox row of the order sent; a variance above variance_approval_threshold approved by another person (email AND sub)
effect: scrap and variances posted, WIP 0.00 (MFG-15)
- key: cancel_order
name: Cancel
from:
- created
- released
to: cancelled
by: planner
requires: reason
gate: no movement or confirmation recorded
readiness:
- transition: release_order
gate: components_available
reason: 'short: named with quantities'
fix: wait for the receipt, or the owner allows release with shortages
- transition: release_order
gate: bom_valid
reason: no released BOM version valid on the date
fix: release a version
- transition: close_order
gate: outbox_empty
reason: 'movements not yet confirmed by inventory or gl: named'
fix: wait, or retry the failed ones
- transition: close_order
gate: variance_approved
reason: the variance {amount} is above {threshold}
fix: another cost accountant approves it
note: a completed order never returns to in_progress; a cost that arrives after close is a post-close settlement (settle:n)
- entity: planned_order
states:
- proposed
- firmed
- converted
- superseded
terminal:
- converted
- superseded
transitions:
- key: propose_planned_order
name: Proposed by MRP-lite
from: none
to: proposed
by: system
- key: firm_planned_order
name: Firm
from: proposed
to: firmed
by: planner
effect: kept by later runs
- key: convert_planned_order
name: Convert to a production order
from:
- proposed
- firmed
to: converted
by: planner
effect: production order created (MFG-09)
- key: supersede_planned_order
name: Superseded by a later run
from: proposed
to: superseded
by: system
- entity: cost_rollup
states:
- computed
- approved
- released
- stale
terminal:
- released
- stale
transitions:
- key: run_rollup
name: Run the roll-up
from: none
to: computed
by: cost_accountant
gate: every component has a standard price on the effective date
- key: approve_rollup
name: Approve
from: computed
to: approved
by: cost_accountant
approval: true
gate: the approver did not run it (email AND sub)
- key: release_rollup
name: Release to inventory
from: approved
to: released
by: cost_accountant
gate: not stale; effective date after today
effect: standard prices handed to inventory from pending rows
- key: mark_rollup_stale
name: Stale
from:
- computed
- approved
to: stale
by: system
gate: an input changed (MFG-18)
readiness:
- transition: approve_rollup
gate: second_person
reason: you ran this roll-up
fix: ask another cost accountant
- entity: mfg_migration_run
states:
- loaded
- reconciled
- signed
- discarded
terminal:
- signed
- discarded
transitions:
- key: load_mfg_from_odoo
name: Load Odoo's manufacturing
from: none
to: loaded
by: system
gate: product and inventory migrations signed
- key: reconcile_mfg_migration
name: Reconcile against Odoo
from: loaded
to: reconciled
by: system
- key: sign_mfg_reconciliation
name: Sign the reconciliation
from: reconciled
to: signed
by: owner
approval: true
gate: every check equal or explained
- key: discard_mfg_migration
name: Discard the run
from:
- loaded
- reconciled
to: discarded
by: owner
requires: reason