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