feat(jobs): add fp.job.step.timelog for granular timer tracking
Each button_start opens a fresh timelog row; button_finish closes the open row and recomputes step.duration_actual as the sum of all interval durations. Replicates Odoo MRP's mrp.workorder.time_ids granularity natively (no mrp dep). Schema: step_id (M2O cascade), user_id, date_started, date_finished, duration_minutes (computed, stored). ACLs: operator get create permission on timelogs because button_start creates them. Tests: test_start_creates_timelog (asserts the log row exists, date_finished is False, user_id is the current user) and test_finish_closes_timelog (asserts log gets date_finished, has a non-negative duration, and step.duration_actual matches). Manifest 19.0.8.5.1 -> 19.0.8.6.0. Part of: native job model migration (spec 2026-04-25) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -120,3 +120,40 @@ class TestFpJobStepStateMachine(TransactionCase):
|
||||
# Force recompute via invalidate (Odoo recomputes on next read).
|
||||
step.invalidate_recordset(['cost_total'])
|
||||
self.assertEqual(step.cost_total, 60.0)
|
||||
|
||||
|
||||
class TestFpJobStepTimeLog(TransactionCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.partner = self.env['res.partner'].create({'name': 'Cust'})
|
||||
self.product = self.env['product.product'].create({'name': 'Widget'})
|
||||
self.wc = self.env['fp.work.centre'].create({
|
||||
'name': 'WC', 'code': 'WC', 'kind': 'wet_line',
|
||||
})
|
||||
self.job = self.env['fp.job'].create({
|
||||
'partner_id': self.partner.id,
|
||||
'product_id': self.product.id,
|
||||
'qty': 1.0,
|
||||
})
|
||||
self.step = self.env['fp.job.step'].create({
|
||||
'job_id': self.job.id,
|
||||
'name': 'S',
|
||||
'sequence': 10,
|
||||
'work_centre_id': self.wc.id,
|
||||
'state': 'ready',
|
||||
})
|
||||
|
||||
def test_start_creates_timelog(self):
|
||||
self.step.button_start()
|
||||
self.assertEqual(len(self.step.time_log_ids), 1)
|
||||
self.assertFalse(self.step.time_log_ids[0].date_finished)
|
||||
self.assertEqual(self.step.time_log_ids[0].user_id, self.env.user)
|
||||
|
||||
def test_finish_closes_timelog(self):
|
||||
self.step.button_start()
|
||||
self.step.button_finish()
|
||||
log = self.step.time_log_ids[0]
|
||||
self.assertTrue(log.date_finished)
|
||||
self.assertGreaterEqual(log.duration_minutes, 0.0)
|
||||
# duration_actual on the step should match the sum of timelog durations
|
||||
self.assertEqual(self.step.duration_actual, log.duration_minutes)
|
||||
|
||||
Reference in New Issue
Block a user