dodil/erp-brick-expenses
DODIL ERP brick: expenses
src/brick_expenses/application/process.yaml
# The brick's process — module.yaml's workflows, copied (pattern.md §15). BRICK.declare_process serves
# it as BPMN 2.0 at GET /process/bpmn; it is identical to `erp-compose bpmn --from module.yaml`.
# A transition's key (or its derived <entity>_<from>_to_<to>) is frozen once signed: it is the BPMN id.
id: expenses
name: Expenses
version: 1
actors:
- id: employee
name: Anyone who spends money for work
- id: manager
name: Line manager (derived from hr)
- id: finance
name: Expenses accountant
- id: owner
name: The owner (or whoever the owner names)
- id: system
name: The system
workflows:
- entity: expense_report
states:
- draft
- submitted
- approved
- posted
- paid
- returned
- rejected
- cancelled
- post_failed
terminal:
- paid
- returned
- rejected
- cancelled
transitions:
- key: create_report
name: Start a claim
from: none
to: draft
by: employee
gate: the claimant employed on the date (hr)
effect: report_id claimed from the company's yearly series
- key: read_receipt
name: Read the receipt (AI, suggest)
from: draft
to: draft
by: system
gate: the employee's consent recorded; the step's mode suggest
effect: suggested fields marked until confirmed (EXP-01)
- key: submit_report
name: Submit the claim
from: draft
to: submitted
by: employee
gate: lines ready; receipts present; no block rule broken; card transactions of the period resolved; no stale line
effect: approval chain derived from hr and fixed; lines frozen
- key: approve_level
name: Approve one level
from: submitted
to: submitted
by:
- manager
- finance
approval: true
gate: the approver is the level's approver or delegate and is not the claimant (email AND sub); the revision is the current
one
effect: the level recorded; on the last level the claim moves to approved
- key: complete_approval
name: Last level approved
from: submitted
to: approved
by: system
gate: every level of the revision approved
- key: return_report
name: Return with a question
from: submitted
to: returned
by:
- manager
- finance
requires: reason
effect: a draft revision n+1 created with the lines copied; rev n approvals void
- key: reject_report
name: Reject
from: submitted
to: rejected
by:
- manager
- finance
requires: reason
- key: cancel_report
name: Cancel a draft
from: draft
to: cancelled
by: employee
- key: post_report
name: Post the claim
from:
- approved
- post_failed
to: posted
by: system
gate: the gl period of the approval date open
effect: one journal (EXP-12); a payout requested from a pending row (EXP-13)
- key: fail_report_post
name: Posting refused
from: approved
to: post_failed
by: system
effect: gl's gate and reason kept
- key: confirm_paid
name: Paid
from: posted
to: paid
by: system
gate: payments confirmed the payout
readiness:
- transition: submit_report
gate: lines_ready
reason: 'lines with fields not confirmed: named'
fix: confirm or correct each suggested field
- transition: submit_report
gate: receipts_present
reason: 'lines above {receipt_required_above} with no receipt: named'
fix: add the receipt, or a signed missing-receipt declaration where the policy allows
- transition: submit_report
gate: policy_blocks
reason: 'a block rule is broken: named with its row'
fix: remove or change the line
- transition: submit_report
gate: card_resolved
reason: 'card transactions of the period not matched or marked personal: named'
fix: 'My expenses: match them'
- transition: submit_report
gate: not_stale
reason: 'lines computed from rules that changed: named'
fix: recompute (one click)
- transition: approve_level
gate: second_person
reason: you are the claimant
fix: the claim goes to the next approver up
- transition: post_report
gate: period_open
reason: the gl period is closed
fix: finance posts it in the next open period
note: returned is terminal for its revision; the claim continues as revision n+1, so no state is ever re-entered
- entity: card_txn
states:
- imported
- matched
- personal
- recovered
terminal:
- recovered
transitions:
- key: import_card_txn
name: Imported from the feed
from: none
to: imported
by: system
gate: the feed's total equals its lines; the card has a holder
- key: match_card_txn
name: Match to a claim line
from: imported
to: matched
by: employee
gate: the line unmatched; the same currency billed
effect: the line's amount becomes the billed amount
- key: mark_personal
name: Mark as personal
from: imported
to: personal
by: employee
effect: recovery journal posted (EXP-14)
- key: recover_personal
name: Recovered
from: personal
to: recovered
by: system
gate: netted from a payout or repaid in payments (one claim row decides)
- entity: exp_migration_run
states:
- loaded
- reconciled
- signed
- discarded
terminal:
- signed
- discarded
transitions:
- key: load_exp_from_odoo
name: Load Odoo's expenses
from: none
to: loaded
by: system
gate: hr, business-partner and gl migrations signed
- key: reconcile_exp_migration
name: Reconcile against Odoo
from: loaded
to: reconciled
by: system
- key: sign_exp_reconciliation
name: Sign the reconciliation
from: reconciled
to: signed
by: owner
approval: true
gate: every check equal or explained
- key: discard_exp_migration
name: Discard the run
from:
- loaded
- reconciled
to: discarded
by: owner
requires: reason