Skip to content

fix(change_request_v2): enforce reason-document uniqueness with models.Constraint - #395

Merged
gonzalesedwin1123 merged 4 commits into
19.0from
fix/394-reason-document-constraint
Aug 5, 2026
Merged

fix(change_request_v2): enforce reason-document uniqueness with models.Constraint#395
gonzalesedwin1123 merged 4 commits into
19.0from
fix/394-reason-document-constraint

Conversation

@gonzalesedwin1123

Copy link
Copy Markdown
Member

Fixes #394.

What

spp.cr.type.reason.document declared its (cr_type_id, reason) uniqueness rule with the legacy _sql_constraints attribute, which Odoo 19 ignores entirely — the registry logs "Model attribute '_sql_constraints' is no longer supported" on every load and the constraint is never created. Duplicate per-reason document rules could be saved silently since 19.0.3.0.0 (release 2026.07). Full analysis in #394.

Changes

  • spp_change_request_v2 (19.0.3.1.019.0.3.1.1):
    • Re-declare the rule as models.Constraint (same derived PG constraint name, spp_cr_type_reason_document_reason_uniq; no rename hazard since the legacy constraint never existed in any DB).
    • migrations/19.0.3.1.1/pre-migrate.py: deduplicate existing rules before the constraint applies — keeps the lowest-id rule per (cr_type_id, reason) (matching which rule _get_effective_required_document_ids() used at runtime) and logs each removal. Required because Odoo downgrades a failed ADD CONSTRAINT during upgrade to a schema warning, which would leave the constraint silently missing exactly on the databases that have duplicates.
    • Tests (test_reason_document_constraint.py, following the spp_programs/tests/test_sql_constraints.py pattern): constraint exists in pg_constraint, duplicate insert raises IntegrityError, different reasons / different types still allowed, legacy attribute is gone. Written first, TDD: 3 of 5 failed on the unfixed code.
  • scripts/lint/check_odoo19.py: new odoo19.sql_constraints error flagging any class-body _sql_constraints assignment with the models.Constraint replacement hint — nothing in CI caught this holdout (the repo is otherwise fully migrated, 76 models.Constraint usages).

Verification

  • ./spp t spp_change_request_v2: 0 failed, 0 error(s) of 330 tests; the registry _sql_constraints warning no longer appears in the log.
  • TDD failing-first run (pre-fix): 3 failed, 0 error(s) of 5 tests — proving the constraint was absent and duplicates were accepted.
  • Migration SQL exercised against a scratch Postgres 15: 7 rows / 3 duplicates → duplicates removed keeping lowest ids, ADD CONSTRAINT then succeeds.
  • Lint rule verified to fire on the pre-fix code (change_request_type.py:604 reported as error) and to pass on the fixed file; ./spp lint, ruff, ruff-format clean on changed files.

…s.Constraint (#394)

The (cr_type_id, reason) rule on spp.cr.type.reason.document was declared
via the legacy _sql_constraints attribute, which Odoo 19 ignores — the
registry only logs a warning and the constraint is never created, so
duplicate rules could be saved silently since 19.0.3.0.0 (release 2026.07).

Re-declare it as models.Constraint (same derived PG constraint name,
spp_cr_type_reason_document_reason_uniq). A pre-migration removes duplicate
rules first (keeping the lowest-id rule per pair, matching which rule
_get_effective_required_document_ids() applied), because a failed ADD
CONSTRAINT during upgrade is downgraded by Odoo to a schema warning and the
constraint would silently stay missing.
Nothing in CI caught the legacy attribute — the repo is otherwise fully
migrated to models.Constraint (76 usages), and this one holdout shipped
unnoticed. Report any class-body _sql_constraints assignment as an error
with the models.Constraint replacement hint.
@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.18%. Comparing base (0f9241f) to head (565e6f2).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             19.0     #395   +/-   ##
=======================================
  Coverage   70.17%   70.18%           
=======================================
  Files         205      205           
  Lines       17751    17756    +5     
=======================================
+ Hits        12457    12462    +5     
  Misses       5294     5294           
Flag Coverage Δ
spp_api_v2_change_request 66.53% <ø> (ø)
spp_base_common 91.07% <ø> (ø)
spp_change_request_v2 77.83% <100.00%> (ø)
spp_cr_type_assign_program 92.07% <ø> (ø)
spp_dci_demo 94.28% <ø> (ø)
spp_farmer_registry_cr 61.24% <ø> (ø)
spp_farmer_registry_demo 61.06% <ø> (ø)
spp_mis_demo_v2 70.38% <ø> (ø)
spp_programs 65.27% <ø> (ø)
spp_registry 87.22% <ø> (+0.07%) ⬆️
spp_security 69.56% <ø> (ø)
spp_starter_sp_mis 86.66% <ø> (ø)
spp_studio_change_requests 84.85% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...pp_change_request_v2/models/change_request_type.py 69.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…#394)

With three or more copies of the same (cr_type_id, reason), the logging
join matched a duplicate against every lower-id copy, emitting repeated
warning lines for one deletion. SELECT DISTINCT and order by id. Found
while exercising the upgrade end-to-end in the local Docker image.

@emjay0921 emjay0921 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — approve (author to merge).

Correct root-cause fix: _sql_constraintsmodels.Constraint, matching the pattern already used 74× across the repo. Verified points:

  • Complete repo-wide — after this PR the only real _sql_constraints class attribute left is the one removed here; the new odoo19.sql_constraints lint rule enforces it going forward and pre-commit passing green confirms zero holdouts.
  • Migration is carefulpre-migrate.py dedups before ADD CONSTRAINT, keeps the lowest-id rule per (cr_type_id, reason) (matching _get_effective_required_document_ids()'s runtime behaviour), guards the pre-19.0.3.0.0 'table doesn't exist yet' case, and logs each removal — sidestepping Odoo's silent downgrade of a failed constraint-add.
  • Tests are solid — 5 TDD tests (constraint in pg_constraint, duplicate raw-INSERT raises IntegrityError via the savepoint+mute_logger pattern, different reasons/types allowed, legacy attribute gone).
  • Full changelog/version discipline (HISTORY.md, README.rst, index.html, manifest 19.0.3.1.0 → .1.1); all 32 CI checks green incl. the module's 330-test suite.

Minor non-blocking note: the new lint rule's doc_link (docs/principles/odoo19-compatibility.md#sql-constraints) points at a doc that doesn't exist in the repo — but every existing rule in check_odoo19.py uses that same path, so it's a pre-existing convention, not introduced here.

@gonzalesedwin1123
gonzalesedwin1123 merged commit 208d975 into 19.0 Aug 5, 2026
34 checks passed
@gonzalesedwin1123
gonzalesedwin1123 deleted the fix/394-reason-document-constraint branch August 5, 2026 06:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spp_change_request_v2: spp.cr.type.reason.document uses legacy _sql_constraints — uniqueness silently unenforced on Odoo 19

2 participants