From a8c9bc9249a0ed96636d7a7b1f19df03295e2f14 Mon Sep 17 00:00:00 2001 From: Red Date: Fri, 24 Jul 2026 19:37:50 +0800 Subject: [PATCH] fix(spp_registry): drop non-writeable @constrains('age') load warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `age` is a non-stored compute derived from `birthdate`, so `@api.constrains("age")` never fired — Odoo only runs constrains on stored writes — and it emitted this warning at every registry load: method res.partner._check_age_is_integer: @constrains parameter 'age' is not writeable The check was also redundant: `age` is fully derived from `birthdate` and can never carry user-supplied input, so `_check_age_is_integer` is removed as dead code. The `age` field, its `_compute_calc_age` compute, and `compute_age_from_dates` are unchanged, so every computed `age` value behaves exactly as before. Existing creation regression tests are kept; the stale constraint docstring is updated to record the removal. Signed-off-by: Red --- spp_registry/models/individual.py | 6 ----- spp_registry/tests/test_individual_name.py | 28 ++++++++++++---------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/spp_registry/models/individual.py b/spp_registry/models/individual.py index da2101a62..87a0681e3 100644 --- a/spp_registry/models/individual.py +++ b/spp_registry/models/individual.py @@ -75,12 +75,6 @@ def _compute_calc_age(self): for line in self: line.age = self.compute_age_from_dates(line.birthdate) - @api.constrains("age") - def _check_age_is_integer(self): - for record in self: - if record.age and not record.age.isdigit(): - raise ValidationError(_("Age must be a valid integer.")) - def compute_age_from_dates(self, partner_dob): now = datetime.strptime(str(fields.Datetime.now())[:10], "%Y-%m-%d") if partner_dob: diff --git a/spp_registry/tests/test_individual_name.py b/spp_registry/tests/test_individual_name.py index 4a6f5eda5..f958b06b6 100644 --- a/spp_registry/tests/test_individual_name.py +++ b/spp_registry/tests/test_individual_name.py @@ -279,21 +279,25 @@ def test_age_populated_from_birthdate(self): @tagged("post_install", "-at_install") class TestCheckAgeIsInteger(RegistryCommon): - """``_check_age_is_integer`` — constraint on the ``age`` field. - - Note: ``age`` is a NON-stored compute, so ``@api.constrains("age")`` - only fires when ``age`` is read after a dependency change. In - practice, creating an individual with no birthdate yields - ``age = "No Birthdate!"`` (not isdigit), but the constraint does NOT - fire because Odoo's constrains hooks only trigger on stored writes. - - These tests pin the current observable behavior. If the constraint - is ever made to fire (e.g., by storing the compute), the second test - will need to flip to ``assertRaises(ValidationError)``. + """Creation behavior around the computed ``age`` field. + + ``age`` is a NON-stored compute derived from ``birthdate`` + (``str(years)`` when set, else ``"No Birthdate!"``), so it can never + carry a user-supplied non-integer. The former ``@api.constrains("age")`` + ``_check_age_is_integer`` guard therefore never fired (Odoo's constrains + hooks only trigger on stored writes) and only emitted the registry-load + warning ``@constrains parameter 'age' is not writeable`` — it was removed + as dead code. + + These tests pin the observable behavior the removal must preserve: + an individual can be created with or without a birthdate. They also + guard against a naive re-introduction (e.g. constraining on + ``birthdate`` or storing the compute), which would make the + birthdate-less case raise on ``age == "No Birthdate!"``. """ def test_individual_without_birthdate_can_be_created(self): - """Despite age=='No Birthdate!' (not isdigit), no error.""" + """age=='No Birthdate!' (not isdigit) must not block creation.""" rec = self.Partner.create({"name": "Ageless", "is_registrant": True}) self.assertTrue(rec.id)