Repository navigation
[18.0][MIG] ni_patient: Migration to 18.0 - #154
Merged
Merged
Conversation
5 tasks done
wasud
force-pushed
the
18.0-mig-p1-patient
branch
from
October 14, 2026 01:02
7ee134d to
021552d
Compare
wasud
force-pushed
the
18.0-mig-p1-patient
branch
2 times, most recently
from
October 14, 2026 01:39
e123b96 to
6053884
Compare
4 tasks done
[FIX] ni_patient: lazy error [IMP] ni_patient: add 'user_id' for encounter_participant [IMP] ni_patient: Add chief complaint [IMP] ni_patient: Remove participant page [IMP] ni_patient: No duplicate encounter [IMP] ni_patient: use `color_picker` [IMP] ni_patient: lock state for CC [IMP] ni_patient: better CC
[IMP] ni_patient: rename `ni.encounter.cls` to `ni.encounter.class` [IMP] ni_patient: check datetime instead of date on every 'ni.patient.res' [IMP] ni_patient: priority value as char and show as badge on list view [MIG] ni_patient: cascade on delete Encounter [FIX] ni_patient: migration error [IMP] ni_patient_birthdate_est: `Medical` category
[IMP] ni_patient: implement create in batch [IMP] ni_patient: fix type
…d model -fix typo of priority's routine -discharge wizard call action_close() on encounter
[IMP] ni_patient: set name attribute for oe_chatter (xml) [IMP] ni_patient: location history as server action
[IMP] ni_comminity_care: Area Manager [ADD] ni_community_care_attendance [IMP] ni_patient: add Patient Archive Wizard
* [IMP] ni_patient,ni_community_care: update ni_patient_view_search * [IMP] ni_community_care: update filter my_area_employees * [IMP] ni_community_care: update field my_area
- Fix _safe_get_parent() returning partner_id instead of parent_id - Fix action_abort() using in operator on string instead of = match - Fix action_revoked() filtering wrong state (in-progress doesn't exist on requests); now correctly targets draft/active records - Add state field with color decorations to event and request tree views - Add Timeline menu section under Encounter app (Events + Requests) - Add tests for workflow event/request creation, replacement tracking, action_resource, and garbage_collect behavior Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Took 20 minutes
Took 23 minutes
Add ni.workflow.line (_auto=False SQL UNION view) merging ni.workflow.event and ni.workflow.request into a single patient timeline. Add kanban + tree + search views for all three models with consistent badge/avatar styling and a shared Thai translation update. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Took 1 hour 23 minutes Took 18 minutes Took 6 minutes
…ife#97) - detect-secrets hook scans every commit for passwords, tokens, API keys - .secrets.baseline captures known false positives (CI postgres/test creds) - CLAUDE.md: explicit rule — no hardcoded credentials, use env vars
…ght cutoff (nirun-life#103) cron_auto_close() computed the "today" cutoff from fields.Datetime.now(), which is UTC-naive, so the midnight-offset day boundary was always UTC midnight regardless of the encounter's company. For companies running ahead of UTC (e.g. Thailand, UTC+7), an encounter that had already been open for a full local day could stay open for up to 7 more extra hours, then close abruptly whenever the UTC date happened to roll over - explaining reports that the cron "sometimes works, sometimes doesn't". Derive the day boundary from each encounter's company timezone instead (res.company.resource_calendar_id.tz, falling back to UTC), grouping candidates per company since a class isn't necessarily scoped to one. Fixes nirun-life#102. Co-authored-by: Nutcha <nutcha.noonmomo@gmail.com>
…'s event/request id WorkflowMixin._to_workflow() wrote the parent record's own id into ni.workflow.event.parent_id / ni.workflow.request.parent_id, but those fields reference other ni.workflow.event/request rows, not the source model's own id. This either raised a foreign key violation or silently linked to an unrelated workflow row, depending on whether the id happened to collide with an existing workflow row. Surfaced via ni_observation: an observation type with a computed parent (e.g. Systolic/Diastolic Blood Pressure -> Blood Pressure) hit this as soon as both child values were saved, since ni.observation.sheet links the children to the newly computed parent via child_ids, which re-writes each child's parent_id and re-triggers the workflow mirror write. Fixes nirun-life#109. Also fixes ni_device's test_requires_supported_observation_type, which tried to clear a device's observation_type_ids via its definition + recompute, but _compute_from_definition only ever fills a device's observation_type_ids from a non-empty definition and never clears it.
…irun-life#123) ## Why The departure wizard was reachable from exactly one view: the community care "simp" patient form, the only one carrying `js_class="ni_patient_form"`. Archive from anywhere else — the base form, any patient list — wrote `active=False` directly and lost the reason, date and note the wizard exists to collect. ## What - `js_class="ni_patient_form"` on the base patient form, `js_class="ni_patient_list"` on the base tree - New `PatientListController` so a multi-selection archives through the wizard too - Wizard takes `patient_ids` instead of `patient_id` and writes one reason set to every selected patient - One patient still renders as the big heading; several render as tags on a normal field label - `toggle_active` / `no_wizard` removed — nothing ever read `no_wizard`, and `write(active=False)` does the same work ## Deploy order Ships with nirun-life/nirun-dop#13, which imports `PatientListController` from this branch. **This repo must go out first** or the community care patient list fails to register its view type. ## Testing - `ni_patient/tests/test_ni_patient_archive_wizard.py` covers both the single and multi cases - Full `ni_patient` suite on a scratch DB: `0 failed, 0 error(s) of 20 tests` - Verified server-side: rendered arch carries both `js_class` values, `default_patient_ids` normalises to `Command.SET`, and the Thai field label resolves to `ผู้รับบริการ` - Not yet clicked through in a browser — worth one pass on the list and the form --------- Co-authored-by: Nutcha <nutcha.noonmomo@gmail.com>
…e#124) `ni.location._name_search` matched on `display_name`, which is neither stored nor given a search method. The ORM logs `Non-stored field ... cannot be searched` and quietly turns that leaf into `TRUE`, so every location autocomplete — and every `('<m2o to a location>', 'ilike', ...)` leaf — returned the whole table. Typing a community name into the house map's search bar brought back all five communities and, through them, every house in the database. ### What changes The override goes away. `_rec_names_search = ["name", "alias"]` states the same thing as a class attribute, and core's `_name_search` (`odoo/models.py:1633-1666`) does the rest — the same aggregator on `NEGATIVE_TERM_OPERATORS`, the same leaf shape, plus dotted paths, `convert_to_write` with a `ValueError` skip, and a warning rather than a `[None]` leaf when `_rec_name` is missing. Net -25 lines. ### Scope Nothing in the codebase calls `name_search` directly, so the change is confined to autocompletes and `ilike` leaves, which now filter instead of returning everything. Opening a dropdown without typing still lists all locations — core's `not (name == '' and operator in ('like', 'ilike'))` guard skips the domain entirely. No model inheriting `ni.location` overrides `_name_search`, so nothing resolves `super()` through the deleted method. `ni.location.commu` declares its own `_rec_names_search = ["name", "code"]`; it needs `"alias"` added there if community aliases should stay matchable. ### Tests `ni_patient/tests/test_ni_location_name_search.py`, two cases: `ilike` filters instead of matching everything, and `alias` is still matched. Both scope their `name_search` to the records they create — unscoped, the pre-fix `TRUE` leaf returns an arbitrary 100 rows that need not include the others, and the assertion would pass against the bug it guards. Verified they fail against the original bug — `_rec_names_search = ["display_name", "alias"]` reproduces it: ``` FAIL: test_ilike_filters_instead_of_matching_everything AssertionError: ni.location(22, 23, 24) != ni.location(22,) FAIL: test_alias_is_still_matched AssertionError: ni.location(22, 23, 24) != ni.location(23,) ``` `ni_patient` suite: 20 tests, 0 failed. --------- Co-authored-by: Nutcha <nutcha.noonmomo@gmail.com>
ni.encounter previously only received references from other resources' workflow events, never producing its own. Since its state selection is incompatible with ni.workflow.event.mixin, write to ni.workflow.event directly instead of inheriting the mixin, so start/discharge/cancel transitions show up in the patient timeline. Took 12 minutes
Encounter form gains a "Workflow Timeline" action-menu entry, alongside Location/Participant History, opening ni.workflow.line filtered to that encounter. Also default-groups the generic timeline by date, adds a company filter to its searchpanel, and drops the unused static/src/views/*.xml asset glob (no such templates exist). th.po gains the reference line the new action's name field needs to pick up the existing translation, and fills the previously blank encounter start-date validation message. Took 17 minutes
`default=fields.Datetime.now()` (and, in ni_reception, the stdlib `fields.datetime.now()`) is evaluated once when the model class is defined, freezing every record's default to that one moment instead of the actual creation time. Pass the bare callable so it evaluates per record. Affected: ni_observation (observation wizard), ni_document_ref, ni_medication (suggest wizard), ni_reception, and ni_patient's ni.workflow.event.mixin (shared by every clinical event model: procedures, immunizations, medication dispenses, etc).
Odoo's view validator warns on every <i class="fa ..."> and icon button without a title, since screen readers have nothing to announce. The titles name what the icon stands for (Occurrence, Patient, Encounter, Location, ...) so they double as tooltips. Took 4 minutes
wasud
force-pushed
the
18.0-mig-p1-patient
branch
from
October 16, 2026 02:27
6053884 to
5878f23
Compare
5 tasks done
piruin
reviewed
Oct 19, 2026
piruin
left a comment
Contributor
There was a problem hiding this comment.
Review
No blockers.
Minor
ni.encounter.nameis now labelled "Encounter Name", but the field holds the encounter number. That label shows in list headers, search and exports. Consider swapping, e.g. keep "Encounter No." onnameand giveidentifiera distinct label.- The new English labels are unique, but some of their Thai translations are still identical. The same-label warning only checks English, so Thai users will see two identical entries in the filter, group-by and export menus:
- "Encounter Name" and "Current Encounter" are both
รหัสรับบริการ. - "Active Encounters" and "Active Encounter Count" are both
ข้อมูลรับบริการ. - "Show Participant" and "Current Participant" are both
ผู้ดูแล.
- "Encounter Name" and "Current Encounter" are both
- The PR body says
location_history_countis labelled "Location History", but the code says "Location Moves". #155 also lists that rename as a 19.0 change.
Checked, OK
_has_cycle()raisesodoo.exceptions.ValidationError,has_access("read")is used in the portal controller, and the cron fields are removed.- Stored and non-stored computes are split, and the
browse(rec._get_encounter_active_ids())call gets ids, so it's fine. _search_display_nameORs with core's search instead of replacing it.- The duplicate-patient test asserts
IntegrityErrorundermute_logger. - CI is green, and the commit series ends with
[IMP] pre-commit auto fixes+[MIG].
wasud
force-pushed
the
18.0-mig-p1-patient
branch
from
October 19, 2026 05:09
5878f23 to
1b80cde
Compare
Contributor
Author
|
Thanks. Fixed in the
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Decisions to review
Labels (checklog): Odoo 18 warns when two fields share a label, and
checklog-odoofails CI on warnings. Our own fields get distinct labels:ni.encounter:namekeeps "Encounter No." andidentifierbecomes "Encounter Identifier" (the form header still reads "Encounter No."),location_history_count"Location Moves",participant_id"Current Participant",show_chief_complaint/show_participant"Show …".ni.patient.encounter_id"Current Encounter".res.partner.patient"Is a Patient".ni.encounter.class.sequence_id"Identifier Sequence".ni.location.encounter_active_*"Active Encounter(s)…".The Thai translations of these labels are distinct too: รหัสรับบริการ / เลขที่รับบริการ / รหัสรับบริการปัจจุบัน, รายการ/จำนวนรับบริการปัจจุบัน, ผู้ดูแลปัจจุบัน / แสดงผู้ดูแล, แสดงอาการสำคัญ, จำนวนการย้ายสถานที่. The one clash that isn't ours (
res.usersbirthdate frompartner_agevshr'sbirthday, both "Date of Birth") gets a precise ignore line inchecklog-odoo.cfg.Partner search:
res.partnersearch now ORs core's_search_display_namewith exact phone/mobile/identification number. Up to 17 it replaced core's partner name search; now email/ref/VAT also match, as [MIG 19.0][P1-2] ni_patient #137 recommends.What changed
ni_patientreplayed from the 17.0 PR branch, then[MIG] ni_patient: Migration to 18.0.tree→listandview_mode; removedmigrations/17.0.1.0.0(OCA keeps only the current version's scripts).<chatter/>in the patient and encounter forms.card(highlight_color,menutemplate withkanban_color_picker, footer).aside/main.idinstead ofactive_id._has_cycle()withodoo.exceptions.ValidationError(location, encounter class).has_access("read")in the portal controller.numbercall/doall._sql_constraints(_()at import time warns in 18).ni.patientencounter count/start,res.partner.patient_id,ni.locationactive encounters.ni.encounter.display_age: keeps showing the age at encounter start, through its own_compute_display_age(partner_age split that compute in [18.0][MIG] ni_period, ni_identifier, ni_coding, partner_age, partner_gender, partner_religion, uom_alias: Migration to 18.0 #148).ni.encounter._check_birthdate: disabled; birthdate is a read-only related field validated on the patient./** @odoo-module **/dropped.IntegrityErrorundermute_logger("odoo.sql_db")(its expected SQL error would fail checklog).oe_structureid in the summary report; README andth.poupdated.Why
Part of #137 (P1-2, epic #131).
Migration notes
cardcannot evaluate Python%int-att-src(it renderedNaN), so it usest-attf-src.active_idfails view validation.How tested
/my/homeentry and/my/patients.pre-commit run -aclean.Documentation
README.mdupdated (partner search, computed fields)Notes for reviewer
odoo-module-migrate17 rewrote Thai text in XML (comments,string,placeholder) as&#NNNN;entities, and prettier then split one of them with spaces. The Thai text is back as UTF-8.invisible="1"on a list field only hides the cells, so the column header still shows. Hidden list fields now usecolumn_invisible="1". Found in the browser check ofni_observation([MIG 19.0][P1-4] ni_observation #139).