dodil/erp-brick-timesheets
DODIL ERP brick: timesheets
src/brick_timesheets/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: timesheets
name: Timesheets
version: 1
actors:
- id: employee
name: Anyone who records time
- id: manager
name: Line manager (derived from hr)
- id: project_lead
name: Project lead (from projects)
- id: finance
name: Finance / the biller
- id: owner
name: The owner (or whoever the owner names)
- id: system
name: The system
workflows:
- entity: timesheet
states:
- open
- submitted
- approved
- returned
- superseded
terminal:
- returned
- superseded
transitions:
- key: open_timesheet
name: Open the week
from: none
to: open
by:
- employee
- system
gate: employed on a day of the week (hr)
effect: sheet_id <person_id>:<iso week>, revision 1; expected minutes from hr; approved absences as read-only lines
- key: submit_timesheet
name: Submit the week
from: open
to: submitted
by: employee
gate: hours_covered; lines bookable on their days; cost centres open; not_stale
effect: lines frozen; the route fixed (approval_route in force on the week's Monday); the approver derived from hr on
the Monday
- key: approve_project_lines
name: Approve the project lines
from: submitted
to: submitted
by: project_lead
approval: true
gate: approval_route project_lead_then_manager; the lead leads the line's project and is not the sheet's person (email
AND sub)
effect: the line approvals recorded
- key: approve_timesheet
name: Approve the week
from: submitted
to: approved
by:
- manager
- finance
approval: true
gate: the approver is the sheet's approver or a delegate and not its person (email AND sub); every project line approved
when the route asks; not_stale
effect: 'after commit, from pending rows: the labour journal (TS-11), deliveries (TS-12), billing claims (TS-13); payroll
and time-off can read the hours'
- key: return_timesheet
name: Return with a question
from: submitted
to: returned
by:
- manager
- project_lead
- finance
requires: reason
effect: revision n+1 opens with the lines copied; rev n approvals void
- key: reopen_timesheet
name: Reopen an approved week for a correction
from: approved
to: superseded
by: finance
requires: reason
gate: the reopener is neither the sheet's person nor its approver (email AND sub)
effect: revision n+1 opens with the lines copied; on its approval revision n's journal is reversed and every downstream
effect moves by the difference
readiness:
- transition: submit_timesheet
gate: hours_covered
reason: '{date}: {booked} of {expected} hours'
fix: book the missing hours, or ask for the absence in time off
- transition: submit_timesheet
gate: bookable
reason: '{project} is not bookable on {date}: {why}'
fix: book to another project or item
- transition: submit_timesheet
gate: cost_centre_open
reason: cost centre {cost_centre} is closed (gl)
fix: book to an open cost centre
- transition: approve_timesheet
gate: second_person
reason: this is your own week
fix: it goes to your manager
- transition: approve_timesheet
gate: project_lines_approved
reason: lines on {project} wait for {lead}
fix: the lead approves them, or returns the week
- transition: approve_timesheet
gate: not_stale
reason: 'lines changed under the week: named'
fix: return the week for a correction
note: returned and superseded are terminal for their revision; the week continues as revision n+1, so no state is ever re-entered
- entity: billing_run
states:
- draft
- released
- held
- cancelled
terminal:
- released
- cancelled
transitions:
- key: create_time_billing_run
name: Gather the month's billable hours
from: none
to: draft
by: system
gate: the project's terms bill via ar; approved billable lines not claimed by another run
effect: run_id tsbill:<project_id>:<period_id>; a ts_billing_claim row per line (plain INSERT)
- key: release_time_billing_run
name: Release to invoicing
from:
- draft
- held
to: released
by: finance
gate: the total not negative; the releaser is not a person whose hours are in it (email AND sub)
effect: ar POST /invoices/raise from a pending row after commit
- key: hold_time_billing_run
name: Hold the run
from: draft
to: held
by: finance
requires: reason
- key: cancel_time_billing_run
name: Cancel the run
from:
- draft
- held
to: cancelled
by: finance
requires: reason
effect: the claims released, so the lines fall into the next run
- entity: ts_migration_run
states:
- loaded
- reconciled
- signed
- discarded
terminal:
- signed
- discarded
transitions:
- key: load_ts_from_odoo
name: Load Odoo's timesheets
from: none
to: loaded
by: system
gate: hr's and projects' migrations signed
- key: reconcile_ts_migration
name: Reconcile against Odoo
from: loaded
to: reconciled
by: system
- key: sign_ts_migration
name: Sign the reconciliation
from: reconciled
to: signed
by: owner
approval: true
gate: every check equal or explained
- key: discard_ts_migration
name: Discard the run
from:
- loaded
- reconciled
to: discarded
by: owner
requires: reason