| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Project (project)
• Discuss (mail) • Attendances (hr_attendance) • Employees (hr) |
| Community Apps Dependencies | Show |
| Lines of code | 4595 |
| Technical Name |
eh_hr_attendance_jobcost |
| License | LGPL-3 |
| Website | https://www.erpheritage.com.au/ |
| Versions | 16.0 17.0 18.0 19.0 |
Attendance Job Costing
Tag every face-kiosk punch to an analytic account, project, and task, right at the moment a worker clocks in.
Why this module
Attendance Job Costing
Costed where the hours are made
The job is chosen on the kiosk the moment a worker clocks in, not reconstructed from memory days later. After a successful face match the picker appears, the selection POSTs to the server, and the just-created hr.attendance row is updated in place. No after-the-fact spreadsheet allocation.
Workers only see jobs they are on
Each employee can carry an allowed analytic list, and the kiosk offers only that list as choices. An empty list falls back to active company analytic accounts. A company require-job-punch toggle forces the picker even when only one job is allowed, so a costing policy is enforced, not hoped for.
Plain fields, standard flows
The analytic account, project, and task are real indexed fields on hr.attendance, surfaced in the backend list and form. They feed the standard Odoo analytic and project machinery rather than a private ledger. LGPL-3 source on disk, no activation key, no phone-home.
Day in the life
A site foreman runs two jobs off one kiosk.
Morning at a construction site. A carpenter steps up to the kiosk, the camera matches her face, and an attendance row is created for check-in. Because she is rostered on two active jobs, the picker appears with exactly those two analytic accounts, the ones on her allowed list, and nothing else. She taps the tower-block fit-out, the choice POSTs to the server, the row is updated to that analytic account, and the success card bounces back to the welcome screen. A labourer on a single job and a company that has require-job-punch switched off would skip straight through on his default account. By payroll time, every attendance hour already carries its job tag, no reconstruction, no guesswork. The HR administrator can still open any row in the backend and set or correct the analytic account, project, and task by hand.
Edge cases
The cases most modules quietly ignore.
In the shipped code today, each one a place where a cheaper module silently does the wrong thing.
Both the options and the punch endpoints reject any call without a valid X-EH-Kiosk-Token that resolves to an active kiosk terminal, returning 401. An unregistered or deactivated device cannot read the job list or write to an attendance row.
The punch endpoint refuses to update an attendance whose employee company does not match the calling device company, returning 404. Job options likewise reject an employee_id that belongs to another company, so one site cannot punch hours onto another company's books.
If an employee has an allowed analytic list, the kiosk offers only that list, filtered to active accounts. Empty list means active company analytic accounts only. The list is capped at 200 rows so a large chart of accounts never floods the picker.
The company require-job-punch flag forces the picker after every match. With it off, the picker only appears when more than one job is offered, and a single-job or zero-option worker passes straight to the success screen on their default.
A model constraint rejects any attendance where the task does not belong to the selected project, whether the row is written from the kiosk or edited by hand in the backend. A mismatched task and project can never be saved.
The kiosk picker wraps the existing match fetch without editing the base kiosk shell. When a picker is pending it returns a synthetic non-match so the shell does not also flip to success, then re-arms the shell's own bounce-to-welcome timer so the screen clears exactly like the non-picker path.
What is inside
Built to do the job, end to end.
- Job fields on attendance. Indexed analytic account, project, and task many2one fields added to hr.attendance, with a constraint that the task must belong to the chosen project. Shown in the attendance list (analytic visible by default, project and task optional) and grouped under Job costing on the form.
- Per-employee defaults and allowed jobs. Default analytic account and default project per employee, plus an allowed analytic accounts many2many that scopes what the kiosk will offer that person. Surfaced under a Job costing section on the employee form.
- Company require-job-punch toggle. A res.company boolean, exposed in Settings via a related field, that forces the kiosk picker after every successful match instead of only when multiple jobs are offered.
- Kiosk picker and server endpoints. Front-end job picker loaded into the kiosk shell that fetches options and posts the selection, backed by a GET options endpoint and a POST punch endpoint that writes the analytic, project, and task onto the just-created attendance row and logs a kiosk event.
- Tests. Python tests cover field acceptance on attendance, employee default and allowed lists, the task-belongs-to-project constraint, the 401 on an unauthenticated options call, the write-through of an analytic account via the punch endpoint, and the 400 when attendance_id is missing.
Honest about the edges
What this does not do, so nothing surprises you.
- Extends the ERP Heritage face kiosk. It depends on eh_hr_attendance_base and eh_hr_face_kiosk, so the kiosk picker only works inside that kiosk shell. It is not a standalone job-costing engine for the stock attendance app.
- It tags attendance rows with analytic, project, and task. It does not itself post analytic ledger entries, create timesheets, or compute labour cost amounts. Those come from the standard Odoo analytic and project machinery that consumes the tag.
- The kiosk picker offers an analytic account; it does not let a worker pick a project or task at the kiosk. Project and task can be set by an HR administrator on the backend form, and the punch endpoint accepts them if supplied.
- There is no rate card, no cost-per-hour calculation, and no billing or invoicing here. It records which job the hours belong to, not what they are worth.
- Allowed-job scoping and the require-job-punch policy are enforced for the kiosk flow. A backend user with attendance write access can still set any analytic account directly on the form.
attendance job costing odoo 19, analytic account on attendance, project time tracking odoo community, construction labour cost tracking, field service time allocation, hr attendance analytic account, billable hours odoo, job costing kiosk punch, per employee allowed jobs, require job punch policy, face kiosk job picker, project task on attendance
Need this fitted to the way you work?
ERP Heritage delivers end to end Odoo work: Odoo Implementation, Customization and Development, Integration, Migration, Consultation, Support and Training. We help teams put this module into production, shape it to their process, and keep it running.
We work with businesses across Australia (Melbourne, Sydney, Brisbane, Perth, Adelaide, Canberra) and the Middle East (Dubai, Abu Dhabi, Riyadh, Jeddah, Doha, Kuwait City, Muscat). Start a conversation at erpheritage.com.au or email info@erpheritage.com.au.
Please log in to comment on this module