hr_timesheet_sheet* fixes: back to list after remove a timesheet. bulk unfreeze confirmed and pending sheets
This commit is contained in:
parent
8b0d0bb6cc
commit
0323b6672e
11 changed files with 354 additions and 34 deletions
|
|
@ -4,11 +4,12 @@
|
|||
import os
|
||||
from unittest.mock import patch
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from lxml import etree
|
||||
|
||||
from odoo.exceptions import AccessError, UserError
|
||||
from odoo.modules.module import get_manifest, get_module_path
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.tests.common import Form, TransactionCase
|
||||
|
||||
MODULE = "hr_timesheet_sheet_no_review"
|
||||
REVIEW_ACTIONS = [
|
||||
|
|
@ -285,7 +286,62 @@ class TestHrTimesheetSheetNoReview(TransactionCase):
|
|||
"%s must only install technical records" % MODULE,
|
||||
)
|
||||
|
||||
def test_wizard_reports_pending_sheets(self):
|
||||
# The figures reach the interface
|
||||
|
||||
def test_wizard_reports_pending_sheets_in_the_interface(self):
|
||||
"""The path the user walks: default_get() + onchange(), no create().
|
||||
|
||||
Opening an action with ``target="new"`` never creates the record: the
|
||||
client builds a virtual one and the server decides what to compute from
|
||||
the dependency graph. A compute with no ``@api.depends`` is skipped and
|
||||
the dialog reports zero on a database full of frozen sheets.
|
||||
"""
|
||||
form = Form(self.wizard_model)
|
||||
self.assertEqual(form.sheet_count, self.baseline["sheets"] + 1)
|
||||
self.assertEqual(form.timesheet_count, self.baseline["timesheets"] + 1)
|
||||
self.assertAlmostEqual(
|
||||
form.timesheet_hours, self.baseline["hours"] + 3.0, places=2
|
||||
)
|
||||
self.assertNotIn("Nothing to unfreeze", form.summary_html)
|
||||
self.assertIn(self.sheet, form.sheet_ids)
|
||||
|
||||
def test_wizard_reports_pending_sheets_through_raw_onchange(self):
|
||||
"""Same flow, called the way the web client calls it.
|
||||
|
||||
``Form`` is a faithful client, but it is still test code. This drives
|
||||
``onchange()`` directly with the field list of the real view, so the
|
||||
test also fails if the view stops exposing ``sheet_ids`` and
|
||||
``default_get()`` is therefore never asked for it.
|
||||
"""
|
||||
arch = self._arch(
|
||||
"hr_timesheet_sheet_no_review.hr_timesheet_sheet_unfreeze_wizard_form",
|
||||
"form",
|
||||
model=self.wizard_model._name,
|
||||
)
|
||||
names = [node.get("name") for node in arch.xpath("//field")]
|
||||
self.assertIn("sheet_ids", names)
|
||||
values = self.wizard_model.onchange({}, [], {name: "1" for name in names})[
|
||||
"value"
|
||||
]
|
||||
self.assertEqual(values["sheet_count"], self.baseline["sheets"] + 1)
|
||||
self.assertEqual(values["timesheet_count"], self.baseline["timesheets"] + 1)
|
||||
self.assertAlmostEqual(
|
||||
values["timesheet_hours"], self.baseline["hours"] + 3.0, places=2
|
||||
)
|
||||
self.assertNotIn("Nothing to unfreeze", values["summary_html"])
|
||||
|
||||
def test_wizard_figures_depend_on_a_real_field(self):
|
||||
"""Guard: the figures must keep a dependency to be computed at all."""
|
||||
for name in ("sheet_count", "timesheet_count", "timesheet_hours"):
|
||||
field = self.wizard_model._fields[name]
|
||||
self.assertEqual(
|
||||
self.env.registry.field_depends[field],
|
||||
("sheet_ids",),
|
||||
"%s must depend on sheet_ids or it is never computed in the UI" % name,
|
||||
)
|
||||
|
||||
def test_wizard_reports_pending_sheets_from_code(self):
|
||||
"""``create({})`` is the shell path, used by the diagnosis probe."""
|
||||
wizard = self.wizard_model.create({})
|
||||
self.assertEqual(wizard.sheet_count, self.baseline["sheets"] + 1)
|
||||
self.assertEqual(wizard.timesheet_count, self.baseline["timesheets"] + 1)
|
||||
|
|
@ -296,17 +352,22 @@ class TestHrTimesheetSheetNoReview(TransactionCase):
|
|||
|
||||
def test_wizard_reports_nothing_to_unfreeze(self):
|
||||
self.sheet_model._unfreeze_sheets()
|
||||
wizard = self.wizard_model.create({})
|
||||
self.assertEqual(wizard.sheet_count, 0)
|
||||
self.assertEqual(wizard.timesheet_count, 0)
|
||||
self.assertEqual(wizard.timesheet_hours, 0.0)
|
||||
form = Form(self.wizard_model)
|
||||
self.assertEqual(form.sheet_count, 0)
|
||||
self.assertEqual(form.timesheet_count, 0)
|
||||
self.assertEqual(form.timesheet_hours, 0.0)
|
||||
self.assertEqual(len(form.sheet_ids), 0)
|
||||
self.assertIn("Nothing to unfreeze", form.summary_html)
|
||||
wizard = form.save()
|
||||
self.assertIn("Nothing to unfreeze", wizard.summary_html)
|
||||
|
||||
# Unfreezing
|
||||
|
||||
def test_unfreeze_releases_sheets(self):
|
||||
self.assertEqual(self.sheet.state, "confirm")
|
||||
wizard = self.wizard_model.create({})
|
||||
expected = wizard.sheet_count
|
||||
result = wizard.action_unfreeze()
|
||||
form = Form(self.wizard_model)
|
||||
expected = form.sheet_count
|
||||
result = form.save().action_unfreeze()
|
||||
self.assertEqual(self.sheet.state, "draft")
|
||||
self.assertFalse(self.sheet.reviewer_id)
|
||||
self.assertEqual(
|
||||
|
|
@ -314,24 +375,105 @@ class TestHrTimesheetSheetNoReview(TransactionCase):
|
|||
)
|
||||
self.assertIn(str(expected), result["params"]["message"])
|
||||
|
||||
def test_unfreeze_only_releases_the_sheets_it_announced(self):
|
||||
"""The confirmation must count what the warning announced.
|
||||
|
||||
The wizard is built, and only then another sheet is frozen. That sheet
|
||||
was not on screen, so it is not released and it is not counted.
|
||||
"""
|
||||
wizard = Form(self.wizard_model).save()
|
||||
announced = wizard.sheet_count
|
||||
|
||||
late_sheet = self.sheet_model.create(
|
||||
{
|
||||
"employee_id": self.employee.id,
|
||||
"company_id": self.company.id,
|
||||
"date_start": self.sheet.date_start + relativedelta(months=1),
|
||||
"date_end": self.sheet.date_end + relativedelta(months=1),
|
||||
}
|
||||
)
|
||||
late_sheet.action_timesheet_confirm()
|
||||
|
||||
result = wizard.action_unfreeze()
|
||||
self.assertIn(str(announced), result["params"]["message"])
|
||||
self.assertEqual(self.sheet.state, "draft")
|
||||
self.assertEqual(late_sheet.state, "confirm")
|
||||
|
||||
def test_unfreeze_ignores_sheets_outside_the_domain(self):
|
||||
"""Operating on a given recordset must not relax the domain.
|
||||
|
||||
``sheet_ids`` comes from the client and can be tampered with. The
|
||||
sheets that ``_unfreeze_domain()`` keeps out stay out, whatever the
|
||||
wizard is told to release.
|
||||
"""
|
||||
draft_sheet = self.sheet_model.create(
|
||||
{
|
||||
"employee_id": self.employee.id,
|
||||
"company_id": self.company.id,
|
||||
"date_start": self.sheet.date_start + relativedelta(months=2),
|
||||
"date_end": self.sheet.date_end + relativedelta(months=2),
|
||||
}
|
||||
)
|
||||
self.assertEqual(draft_sheet.state, "draft")
|
||||
kept_id = self.sheet.id
|
||||
with patch.object(
|
||||
type(self.sheet_model),
|
||||
"_unfreeze_domain",
|
||||
lambda records: [
|
||||
("state", "in", ("confirm", "done")),
|
||||
("id", "!=", kept_id),
|
||||
],
|
||||
):
|
||||
self.assertNotIn(self.sheet, Form(self.wizard_model).sheet_ids)
|
||||
wizard = self.wizard_model.create(
|
||||
{"sheet_ids": [(6, 0, (self.sheet + draft_sheet).ids)]}
|
||||
)
|
||||
result = wizard.action_unfreeze()
|
||||
self.assertEqual(self.sheet.state, "confirm")
|
||||
self.assertEqual(draft_sheet.state, "draft")
|
||||
self.assertEqual(result["params"]["type"], "info")
|
||||
|
||||
def test_unfreeze_is_idempotent(self):
|
||||
self.assertTrue(self.sheet_model._unfreeze_sheets())
|
||||
self.assertEqual(self.sheet_model._unfreeze_sheets(), 0)
|
||||
wizard = self.wizard_model.create({})
|
||||
result = wizard.action_unfreeze()
|
||||
result = Form(self.wizard_model).save().action_unfreeze()
|
||||
self.assertEqual(
|
||||
self.sheet_model.search_count([("state", "in", ("confirm", "done"))]), 0
|
||||
)
|
||||
self.assertEqual(result["params"]["type"], "info")
|
||||
self.assertEqual(self.sheet.state, "draft")
|
||||
|
||||
def test_unfreeze_twice_from_the_interface_is_idempotent(self):
|
||||
first = Form(self.wizard_model).save()
|
||||
second = Form(self.wizard_model).save()
|
||||
self.assertTrue(first.action_unfreeze()["params"]["type"] == "success")
|
||||
result = second.action_unfreeze()
|
||||
self.assertEqual(result["params"]["type"], "info")
|
||||
self.assertEqual(self.sheet.state, "draft")
|
||||
self.assertFalse(self.sheet.reviewer_id)
|
||||
|
||||
def test_unfrozen_lines_are_editable(self):
|
||||
with self.assertRaises(UserError):
|
||||
self.line.with_user(self.user).write({"unit_amount": 5.0})
|
||||
self.wizard_model.create({}).action_unfreeze()
|
||||
Form(self.wizard_model).save().action_unfreeze()
|
||||
self.line.with_user(self.user).write({"unit_amount": 5.0})
|
||||
self.assertEqual(self.line.unit_amount, 5.0)
|
||||
|
||||
def test_unfreeze_sheets_hook_keeps_its_signature(self):
|
||||
"""``_unfreeze_sheets()`` is public: calling it with no argument must
|
||||
keep releasing every frozen sheet."""
|
||||
self.assertEqual(
|
||||
self.sheet_model._unfreeze_sheets(), self.baseline["sheets"] + 1
|
||||
)
|
||||
self.assertEqual(self.sheet.state, "draft")
|
||||
|
||||
def test_unfreeze_sheets_accepts_a_recordset(self):
|
||||
self.assertEqual(self.sheet_model._unfreeze_sheets(self.sheet), 1)
|
||||
self.assertEqual(self.sheet.state, "draft")
|
||||
self.assertEqual(self.sheet_model._unfreeze_sheets(self.sheet), 0)
|
||||
no_sheet = self.sheet_model.browse()
|
||||
self.assertEqual(self.sheet_model._unfreeze_sheets(no_sheet), 0)
|
||||
|
||||
def test_unfreeze_domain_hook(self):
|
||||
self.assertEqual(
|
||||
self.sheet_model._unfreeze_domain(),
|
||||
|
|
@ -355,4 +497,6 @@ class TestHrTimesheetSheetNoReview(TransactionCase):
|
|||
def test_wizard_is_restricted_to_managers(self):
|
||||
with self.assertRaises(AccessError):
|
||||
self.wizard_model.with_user(self.user).create({})
|
||||
self.assertTrue(self.wizard_model.with_user(self.manager).create({}))
|
||||
manager_form = Form(self.wizard_model.with_user(self.manager))
|
||||
self.assertTrue(manager_form.sheet_count)
|
||||
self.assertTrue(manager_form.save())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue