dodil/erp-brick-time-off

DODIL ERP brick: time off

README.md

brick-time-off - Time off & absence

Holiday, sickness and family leave in one place: everyone sees what they have left, asks for days off in two clicks, and their manager approves with the team calendar in front of them. Bank holidays follow where people work, part-timers get their fair share, and payroll and timesheets see exactly the days that were approved.

A brick on erp-platform (erp-platform>=0.9.13,<0.10), built from the signed time-off module spec (erp-method, version 1). 0.2.0 (slices A and B): the leave request (ask, approve or reject by hr's derived manager, withdraw, ask and confirm a cancellation), the balance (entitlement derived from the policy and hr, plus an append-only ledger, closure days and the year end's carry, lapse and expiry), hr's leaver intake, sickness (recorded, extended, confirmed; fit notes stored as objects; the facts payroll needs for statutory sick pay), family leave with its statutory facts, manual adjustments that need a second person, company closure days, the owner's controls, leave types and policies as dated rows (a policy below 5.6 weeks is refused), what payroll reads (GET /absences, the balance of a leaver) and the Odoo migration (load, reconcile, sign, discard). Not built: the team calendar and overlap warning, accrual from approved hours, reminders (AGENTS.md lists them, and the gaps found in the spec).

src/brick_time_off/
  domain/          the rules, plain Python: request.py (state machine, who decides), calendar.py (cost, leave year, entitlement, balance, closures, year end),
                   sickness.py, adjustment.py, evidence.py, odoo.py (the mapping's arithmetic), values.py
  application/     the use cases - each declared once, served as an API route AND an MCP tool; process.yaml = the signed workflows
  infrastructure/  the tables and repositories (tables.py), hr behind one adapter (seams.py), the evidence store (objects.py), the Odoo reader port (odoo.py)
  schema.sql, migrations/   the tables; 00002 seeds the leave types and the UK annual-leave policies; 00003-00006 add sickness, closures and adjustments, the
                   migration, family leave
  roles.yaml       the default roles
tests/             stories, invariants, doors, seams, statement budget, process pin, schema, composition, sickness, year end, closures and adjustments,
                   settings, family leave, migration (Odoo is a fake behind its port), the permission matrix (hr is an in-memory fake checked against hr's signed spec)

Run it.

pip install -e '.[test]'
ERP_METHOD_DIR=../erp-method pytest -q       # no bucket needed; needs an erp-method checkout (the signed spec and BPMN)
BUCKET=my-bucket erp-migrate --schema src/brick_time_off/schema.sql --brick time-off=0.2.0
BUCKET=my-bucket HR_BASE_URL=... uvicorn brick_time_off.app:app     # API at /docs, agents at /mcp, the process at GET /process/bpmn

Fit notes need an evidence store (OBJECT_ENDPOINT, BUCKET, OBJECT_ACCESS_KEY, OBJECT_SECRET_KEY; a missing one is a 503 for the fit-note use cases only) and the Odoo migration an Odoo reader (ODOO_URL, ODOO_USER, ODOO_PASSWORD, read-only; a missing one is a 409 for the migration only).

hr is required (a person, their pattern, public holidays and manager come from it). Business time follows business_timezone (default Europe/London).