Repository navigation
[16.0][FIX] Pre-existing bugs found during the 19.0 migration scan - #166
Conversation
There was a problem hiding this comment.
Review
The fixes are correct. Superseded by the follow-up review: F5 needs a data-fix script and an upgrade test before merge.
Data left behind by the old bugs (decide before cutover)
- F4, decision: no automatic reactivation; support reviews the list by hand. Since
b25bb607(2023-12-20), "Error Entry" archived the patient, and through_inheritsthat also hid all of the patient's encounters. This PR stops new cases, but existing databases still have patients archived this way.- Patients archived through the archive wizard always have a
state_reason_id(the wizard requires it), so the likely victims are archived patients with nostate_reason_idand at least one encounter inentered-in-error. - Please provide a read-only query (e.g. in
.tools/) that lists them for support:SELECT p.id, p.identifier FROM ni_patient p WHERE p.active IS NOT TRUE AND p.state_reason_id IS NULL AND EXISTS ( SELECT 1 FROM ni_encounter e WHERE e.patient_id = p.id AND e.state = 'entered-in-error' )
- Support reactivates the confirmed ones from the UI, so the change is tracked in the chatter. The list holds patient identifiers: share it only through support's own channel, never in an issue or PR.
- Add the step to the P6 cutover runbook, before the OpenUpgrade run.
- Patients archived through the archive wizard always have a
- F5: records created from code have a stored
period_start_dateequal to the day they were created. A data-fix script with an upgrade test is required; see the follow-up review.
Minor
.tools/migration_sample_data_p1.py:191: the comment says the mixin's default wins over the compute, and the code setsperiod_start_dateitself to work around that. After F5 the comment is wrong and the workaround isn't needed.- The F5 test should move into its own file next to the new upgrade test; see the follow-up review.
- #6 (
compute_sudo): no confirmation needed.response_countis deprecated, and its replacement will be migrated into 16.0.
Checked, OK
check_origin_date:- It now raises
ValidationErrorinstead of a plain string. - It handles an empty
period_start. - It compares the transfer date with the start date in the user's timezone (
context_timestamp). - The Bangkok test covers the case where the UTC date and the local date differ.
- It now raises
- The "Until" filter now uses
period_end. - The
survey_subjectbutton comparesactivewithFalse, not the string'False'. _compute_observationreads each row's own record (rec.id), and the new test reads several rows at once.ondelete="set null"matches the existing FK, and thecompute_sudotypo is fixed.- The deleted
partner_agetest file was never imported, andtest_res_partner_age.pyhas the same six tests. - Removing the
period_start_datedefault doesn't change the form:period_startstill defaults to now.ni_conditionalready setdefault=Noneitself. - There is one
[FIX]commit per module, with patch versions bumped, sooca-portcan carry them forward. CI is green.
piruin
left a comment
There was a problem hiding this comment.
Changes requested: F5 needs a data fix and an upgrade test
The ni_period fix stops new wrong values, but existing databases still store period_start_date = the day the record was created for every record created from code (imports, RPC, bulk wizards) since the default was added. Please ship the cleanup with the version bump.
Script: ni_period/migrations/16.0.0.1.1/end-migration.py
- Use an
end-script, notpost-.ni_periodhas no dependencies, so atpost-time none of the models that use the mixin are loaded yet. - Loop over the stored models that inherit the mixin:
env.registry.descendants(["ni.period.mixin"], "_inherit").- Skip
_auto = Falsemodels (e.g.ni.condition.latest). - Skip models that define their own
period_start_date.ni.conditionalready haddefault=None, so the bug never affected it.
- Skip
- Fix only the rows the bug wrote:
period_mode = 'datetime',period_start IS NOT NULLandperiod_start_date <> period_start::date;- and
period_start_dateis the creation day.context_todayused the creating user's timezone, so allowcreate_date::date± 1 day.
- Leave date-mode rows alone. There,
_inverse_period_start_datedoes not touchperiod_startwhen the user edits the date, so a mismatch can be intended. - Set
period_start_date = period_start::date, the same UTC date_compute_period_start_dateproduces, and log the number of rows fixed per model.
Tests
- Put them in
ni_perioditself, using a test model loaded withodoo_test_helper.FakeModelLoader(it's already intest-requirements.txt). Fall back toni_patientonly if that doesn't work. - Move the create test (
test_period_start_date_follows_period_start_on_create) out oftest_ni_encounter_entered_in_error.pyand into this file. - Upgrade test: corrupt rows with SQL, run the script, then check the result.
- The migrations folder name contains dots, so load the script with
importlib.util.spec_from_file_locationand callmigrate(env.cr, "16.0.0.1.0"). - Cover four kinds of row:
- a datetime-mode row with
period_start_date= the creation day is fixed; - a date-mode row with a deliberate mismatch is left alone;
- a row that is already correct is left alone;
- a model that defines its own field is skipped.
- a datetime-mode row with
- The migrations folder name contains dots, so load the script with
Rollout
- OCA keeps only the current version's scripts, so this exists on 16.0 only. Production must run
-u ni_periodon 16.0 with this version before the OpenUpgrade 16→17 step. Please add that to the P6 cutover runbook. - Take a
migration_snapshot.pysnapshot before and after that update, so the change in the data is recorded.
|
#167 (the same |
period_start_date is a stored computed field with a default of today. On create, a default counts as a given value, so the compute never runs: records created from code (import, RPC, bulk wizards) with only period_start got today as their start date, and an encounter could then fail the end-date check when it was closed. Drop the default. period_start already defaults to now, so a new record in the form still starts today. Existing databases still hold the wrong dates, so the version bump ships an end-migration script that repairs them. It runs at end- time because ni_period has no dependencies, and goes through every stored model on the mixin, skipping SQL views and models that declare their own period_start_date (ni.condition already had default=None). It only fixes datetime-mode rows whose start date differs from period_start and equals their creation day (+/- 1 day for the creating user's timezone); date-mode rows are left alone, since there a mismatch can be intended. Each row gets period_start::date, as the compute does, and the number of fixed rows is logged per model. The tests use a fake model on the mixin: the create case, and the script on a fixed row, a date-mode row, a correct row and a model with its own field. Production must run `-u ni_period` on 16.0 with this version before the OpenUpgrade 16 -> 17 step: OCA keeps only the current version's scripts. Refs: nirun-life#135
tests/test_res_partner.py was not imported in tests/__init__.py, so it never ran. It is a near copy of test_res_partner_age.py (the same cases, two assertions in a different order), so there is nothing to merge. Refs: nirun-life#135
The button's invisible condition compared active with the string 'False', which never matches a boolean, so the button also showed on archived surveys. Compare with False. Refs: nirun-life#135
…ondelete ni.observation.report._compute_observation browsed self.id inside the loop, so reading observation_id or the history of several latest rows at once (a list, an export) raised "Expected singleton". Use rec.id. The test reads two rows of the patient's latest observations together. ni.observation.category.parent_id had ondelete="", which is not a valid value. Set "set null", Odoo's default for a nullable Many2one and what the column already used, so the database does not change. Refs: nirun-life#135
response_count on ni.patient and ni.encounter passed sudo_compute=True, which is not a field parameter and was ignored. Use compute_sudo=True, as intended. The counts now include completed responses the user cannot read through record rules, the same as the other stored response computes of these models. Refs: nirun-life#135
…ntry check_origin_date raised the translated message itself, a plain string, so setting a transfer date without a transfer source failed with "exceptions must derive from BaseException" instead of a validation error. It also compared the transfer date (a Date) with the encounter start (a Datetime), which fails with a TypeError on every encounter that has both, and on encounters without a start. Raise ValidationError, and compare the transfer date with the start date in the user's timezone, skipping the check when the encounter has no start. The encounter search filter "Until" filtered on period_start; use period_end. action_entered_in_error() wrote active=False. ni.encounter has no active field of its own and inherits ni.patient.active through _inherits, so marking an encounter as an error entry archived the patient. The state alone already marks the encounter inactive (ENCOUNTER_INACTIVE_STATE). Tests cover the transfer date and Error Entry. Refs: nirun-life#135
…orkaround Add a read-only query that lists the patients "Error Entry" may have archived by mistake (archived, no state reason, an entered-in-error encounter). Support reviews the list and reactivates the confirmed patients from the UI; nothing is reactivated automatically. The output holds patient identifiers, so it stays in support's own channel. The pilot sample data no longer sets period_start_date by hand: since the ni_period fix the compute gives the date of period_start on create. Refs: nirun-life#135
|
Thanks for the review. Pushed (head F5, data fix and upgrade test (in
F4, Error Entry victims (new commit
Minor
Rollout
Tests: 101 pass on a fresh 16.0 database ( |
Closes #135
Summary (TH)
แก้บั๊กที่มีอยู่แล้วบน 16.0 ซึ่งเจอระหว่างสแกนเพื่อ migrate ไป 19.0 (7 ข้อใน #135) และบั๊กอีก 2 ข้อที่ pilot ใน #141 เจอ (finding 4 และ 5) แยก commit
[FIX]ทีละ module เพื่อให้ forward-port ไป 17/18/19 ได้Changes
ni_patientcheck_origin_dateraisesValidationError(was a plain string), and compares the transfer date with the start date in the user's timezone (was Date vs Datetime, aTypeError)ni_patientperiod_endsurvey_subject('active', '=', False), not the string'False'ni_observation_compute_observationusesrec.id, so several latest rows can be read togetherni_observationni.observation.category.parent_id:ondelete="set null"(was""). The column already usesON DELETE SET NULL, so the database does not changeni_questionnairecompute_sudo=True(the typosudo_computewas ignored) onresponse_countpartner_agetests/test_res_partner.py: never imported, and a near copy oftest_res_partner_age.pyni_patientni.encounterhas noactiveof its own and wroteni.patient.activethrough_inherits. The state already marks the encounter inactiveni_periodperiod_start_datehas no default any more, so a record created from code gets the date of itsperiod_start, not todayni_periodmigrations/16.0.0.1.1/end-migration.pyrepairs the start dates the old default wrote: datetime-mode rows whoseperiod_start_datediffers fromperiod_startand equals the creation day (± 1 day). Date-mode rows, SQL views and models with their ownperiod_start_dateare skipped.toolserror_entry_archived_patients.sql: read-only list of the patients Error Entry may have archived, for support to review and reactivate from the UI. The output holds patient identifiers and stays in support's own channel.toolsmigration_sample_data_p1.pyno longer setsperiod_start_dateby handVersions bumped (patch):
ni_patient16.0.0.4.1,ni_period16.0.0.1.1,survey_subject16.0.0.3.2,ni_observation16.0.0.6.2,ni_questionnaire16.0.0.5.1.Decisions to review
response_countnow counts every completed response of the patient/encounter, also those a user cannot read through record rules. This is what the code meant, and matches the other response computes on these models (compute_sudo=True). The list the stat button opens still applies the record rules, so a restricted user can see a count larger than the list.activefrom the write is the minimal fix. Givingni.encounterits ownactivefield would also stop the encounters of archived patients from being hidden by default, which is a larger change..tools/error_entry_archived_patients.sqland reactivates the confirmed patients from the UI, so the change is tracked in the chatter.ni_period, on a fake model loaded withodoo_test_helper.FakeModelLoader: the create case, and the upgrade script on a fixed row, a date-mode row, a correct row and a model with its own field.ni_practitioner'sperformer_id) is fixed with theni_practitionermigration, as planned in [MIG 19.0][P1-6] Data migration pilot: OpenUpgrade 16→17→18→19 on Phase 1 modules #141.How tested
ni_period, partner_age, ni_patient, survey_subject, ni_observation, ni_questionnaire: 0 failed, 0 errors of 101 tests.ni_period16.0.0.1.0 with corrupted rows;-u ni_periodfixed 3ni.encounterand 2ni.encounter.participantrows and left the date-mode row alone.pre-commitclean on the changed files.Rollout
OCA keeps only the current version's migration scripts, so the F5 data fix exists on 16.0 only. Before the OpenUpgrade 16→17 step, production must:
-u ni_periodon 16.0 with this version, with amigration_snapshot.pysnapshot before and after.Both steps go into the P6 cutover runbook.
Forward-port
After merge, forward-port to 17.0/18.0/19.0 with
oca-port(or replay into the open P1 stacks #153–#161 when they rebase). The 17/18/19 replays of thecheck_origin_datefix (row 1) are ready locally. #167 (the same fix on its own) was closed as a duplicate, so this PR stays the only 16.0 source for it.