Conversation
Frappe seeds a Desktop Icon per public workspace only once, at creation time, and never re-copies the workspace's roles or icon after that. Every tile ended up System Manager only, so field staff landed on /desk with nothing to click. CAD's workspace also had no icon set when its tile was seeded, so it kept rendering blank even after cad.json got one later. Replaces the old single shared 'App' tile (which only ever led to Frappe's 'Icon is not correctly configured' dead end) with syncing each workspace's own per-role tile on every migrate, and fixes the workspace lookup itself: it was filtering on Workspace.app, which is only as good as whatever app happened to be active in the Desk UI when someone created the record by hand, wrong for most of these. module is what each workspace's own fixture actually sets.
Every card/box on the Doctor, Nurse, My Schedule, Staff Onboarding and New Schedule pages referenced var(--radius-md) / --radius / --radius-full, none of which exist as Frappe design tokens — the real names are --border-radius-md / --border-radius / --border-radius-full. An unset custom property resolves to nothing, so border-radius was computing to its default (0) everywhere it was used; every one of these boxes has been rendering square-cornered instead of rounded.
New record_vitals endpoint + a dialog on the nurse queue card: height, weight, temperature, pulse, SpO2 and blood pressure, each optional but at least one required, all rejected if zero or negative. BMI is recalculated from height/weight whenever both are present. Only allowed while the patient is actually with the nurse (Awaiting Test or Awaiting Medicine) — matches the same session-ownership gate as the rest of this page.
… letter order_test, prescribe_medicine and complete_encounter now all accept chief complaint, past history and allergy history, saved onto the encounter regardless of which action closes out the visit. complete_encounter also takes an optional referral (where to, who to, why, priority) and creates a real Referral record when given one — both where and why are required together, so a half-filled referral can't be saved. New Bandhu Referral Letter print format plus get_referral_letter_html, which crosses the same permission boundary get_patient_card_html already does on the CAD page: Referral is System Manager only in DocType permissions, and load_owned_encounter is what proves the caller may see this particular patient before the render runs on their behalf.
…ails The shared patient-details dialog (Doctor and Nurse queue cards) now shows this visit's own vitals instead of only what the patient carried in from registration, plus Pulse/SpO2/Blood Pressure and Chief Complaint/Past History/Allergy History sections. A visit's own reading wins over registration data whenever the nurse has recorded one, since that's what's clinically true right now. Doctor and Nurse also get direct read/print/report permission on Patient Encounter (permlevel 0) so this no longer has to go through ignore_permissions to render. Bumped session_ui.js's cache-busting ?v= — app_include_js raw file paths aren't auto-versioned, so without this browsers keep serving the old file after a change.
New Schedule only ever covered recurring camps (Weekly/Fortnightly/ Monthly) — starting a single ad hoc session still meant the full 20-field Bandhu Clinic Session Desk form. New Session is a one-screen form instead: Clinic autofills Project/Vehicle the same way the schedule wizard does, a clash warning reuses find_assignment_clashes against the one date instead of a generated list, and Create just inserts a Planned session with no session_schedule — that blank field is what marks it as ad hoc rather than schedule-owned, so it can't collide with the nightly generator's idempotency key. require_scheduling_access, practitioners_by_role, association_maps and clock_value move out of new_schedule.py into the shared session_schedule module so both pages use one implementation instead of two copies that would drift apart.
…or data Registration form: Sex/Country/Native State/Occupation render as quick-tap tab groups instead of free-text inputs, so a typo can no longer crash registration with a Link-validation error the way native state and sector used to. Full Name, Native State, Sex and the Age/DOB pair are marked as actually required, matching what register_patient enforces server-side — Age/DOB gets a plain-language note instead of a star on either field, since only one of the two is required, not both. DOB estimate from age alone: was today's exact month/day minus N years, which reads as a real recorded birthday. Now Jan 1 of the calculated birth year, so it's honestly an estimate rather than a fabricated date — matters for migrant workers who know their age but not their birthday. Native District: was a free-text field wired to a native <datalist> positioned by the browser with no CSS hook available to fix it. Now a plain select next to the state, disabled until a state is picked, populated from state_districts.py — matching how the state and country 'Other' pickers already work. Only 6 of India's 36 states/UTs ever had a district list; the other 30 returned nothing at all, in a state field that already accepted 36 states via 'Other'. Filled in the rest — general knowledge of India's administrative divisions, not an official register, so worth a spot-check against the Census of India before relying on it, especially for Madhya Pradesh/Andhra Pradesh/Telangana/Rajasthan which have all had district boundaries officially reorganised in the last few years. Sectors gets the same is_major_sector split the state master already had: five CMID-specified sectors as quick taps (two renamed in place via rename_doc, not delete+recreate, so any patient already linked carries forward), the rest reachable only from the full Desk picker. Full Name, Native District and ABHA ID each rendered alone in a row of the form's 2-column grid, stuck at half width with the other column empty next to them — now span the full row like the tab groups already did.
Went through every workspace shortcut and checked its label against what it actually links to and what data is really behind it: - Doctor's 'Patient Queue' pointed at Patient Encounter, not the real Patient Queue doctype the Bandhu workspace's own shortcut of the same name correctly uses. - Bandhu's 'Vehicle log' linked to the Vehicle master, not a log — the real logs (Usage, Refuel) sit right beside it. Relabelled to 'Vehicles'. - Bandhu's 'Start Consultation' created a bare Patient Encounter, bypassing sync_to_queue, the session link and the workflow-state machine entirely. Now opens the CAD page, the real entry point. - Bandhu's 'Staff Log' and Nurse's 'Medicine Movement'/'Medicine Dispense' pointed at doctypes with zero rows and no code anywhere writing to them — removed. - Pharmacist's 'Medicines' showed all 34 Items in the system, 29 of them not medicines at all. Now a URL shortcut to /app/item?item_group= Drug, which a DocType-type shortcut's stats_filter can't do (that only drives the badge count, not what the list opens to). - Inventory's three shortcuts (Asset/Asset Movement/Asset Repair) are ERPNext fixed-asset accounting, unrelated to a mobile clinic's actual medicine/vehicle/equipment inventory, all zero rows. Removed. - Admin's 'Roles' badge filter referenced a Helpline Staff role that doesn't exist in this system. Dropped from the filter. - Trailing-space label typos on Admin's 'Site' and (already fixed separately) Bandhu's old 'Start Clinic Session'.
Filtered on encounter_date = today only, so a patient still waiting or with the doctor counted as 'seen' the moment they registered. Added custom_workflow_state = Completed, matching the filter the CAD page's own 'Completed Patients' quick list already uses correctly.
Two long explanatory comments removed at the user's request. The page-width/page-height landmine they documented (non-standard CSS properties Chrome's PDF generator reads directly, real card size verified by rendering to PDF and measuring the output) stays recorded in this app's own project notes instead of inline.
restrict_other_app_desktop_icons filtered on Desktop Icon.app != 'bandhu_app' to decide which icons belong to other apps and should be locked to System Manager. Every one of this app's own 9 icons has app blank — the exact same landmine sync_bandhu_desktop_icons already had to work around on Workspace.app — so this function was classifying CAD/Doctor/Nurse/Admin/ Bandhu/Dashboard/Inventory/Pharmacist/Referrals as foreign and locking all nine back to System Manager only. Both functions run in the same after_migrate hook, in sequence, so this undid sync_bandhu_desktop_icons's fix immediately after it ran, every single time — not caught in the earlier commit because a role was only ever spot-checked once via frappe.get_doc right after running sync alone, never after both hooks ran together, and not through an actual browser session as that role. Now filters on link_to against this app's own workspace names (via Workspace.module, the same reliable check the sibling function already uses) instead of the icon's own app field. Verified live as Nurse, CAD and Doctor (each now shows exactly their own tile) and as Administrator (unchanged, still sees the full grid) — not just re-read from the database this time.
CI builds a real fresh site (bench new-site + install-app, no setup wizard), which has zero Gender, Appointment Type, Indian State and Sectors records, plus no Clinic/Site/Unit/Bandhu Projects/Item for a dozen test classes to grab. All of it only ever worked on bandhuapp.local because that dev site carries leftover data none of it was ever actually seeded by. - seed_default_genders, seed_default_appointment_type: new, wired into after_install and after_migrate. "Walk-In" is a hardcoded Property Setter default on Patient Encounter, so every encounter this app creates needs it to exist. - seed_indian_states now also covers Kerala and the 6 major migrant-source states, not just the other 29 -- also never seeded by app code, just manually added on the dev site once. - new baseline_test_fixtures.py, wired into every test class that assumed Clinic/Site/Unit/Bandhu Projects/Item already existed. - fixed test_doctor_form.py: its tearDown calls frappe.db.rollback() after every test, which was also wiping the baseline setUpClass had just created since nothing had committed in between. - test_nurse_form.py's hardcoded _Test Stock Item doesn't exist on a fresh site either, switched to the shared baseline Item. 209/209 tests pass, ruff and semgrep clean.
…cons Adds four SVG icons and syncs them onto each Desktop Icon. Clears the shared desktop icon cache when an icon changes.
Sends a realtime message when a camp changes and every open board re-reads its own queues. The refresh is delayed half a second, skipped for the person who made the change, and held back while a dialog is open. It never freezes the screen. Refresh moved into the page header as an icon, next to My Schedule.
Warns when the patient looks already registered, matching on ABHA, then mobile, then name and date of birth, and offers to queue the one it found. Search needs two characters, says when the list was cut short, and the row action is outline instead of filled. A camp with no LSG number can no longer issue a Clinic ID. Today's Queue reads the visits instead of the queue table, and shows how long each patient has waited.
Clearing a clinical note now saves. Rejects the same medicine twice on one visit, and negative days or quantity. The prescribe dialog opens on the medicines table with a row ready to type into, and the notes moved into a collapsed History section underneath. Order Tests and Mark Complete follow the same shape. A recorded allergy shows as a banner on the room card and at the top of the prescribe dialog. Adds a name and Clinic ID filter to the board from eight patients. Both print endpoints check the Doctor role.
Every ordered test needs a result before the patient goes back to the doctor, and Not Done is now one of the options. The dispense list starts unticked and asks first if nothing is ticked. Shows the frequency, days and quantity the doctor wrote. Vitals have upper limits and blood pressure needs both numbers. Each row shows the ordered test or medicine and how long the patient has been in the camp, and a line under the camp bar shows where everyone is.
Grants the report permission both roles were missing, which a number card needs on top of read. Removes the Patients Seen Today tile, which needs a permission those roles do not have. Hides the last-edited time Frappe prints under every row of a quick list, which read as the camp date. All three reports refuse a period longer than a year.
The nightly job writes an error log when an enabled schedule is still behind after a run.
Mobile is now Mobile Number, Company Name is Name of Company, Occupation / Sector is Sector of Employment, Chief Complaint is Patient Complaints, and Clinical Notes is Observations and Notes on Examination. Labels only, no field was renamed. Patient had two fields both labelled Clinic ID and one was empty on every patient. The empty one is removed, including from the form's field order. A visit now records its location, so LSG, district and state fill in by themselves. A patch backfills the visits that already exist.
The header now uses the same width as the page content, so the title and the buttons share the same edges as the page. Button icons match Desk at 12px with a 6px gap. Adds the missing styles for the allergy banner and for the badge the session bar has always emitted.
Covers the duplicate patient check, the short search, the capped result list, blank and Not Done test results, impossible vitals, half a blood pressure, duplicate medicines, negative amounts, clearing a note, and a camp with no LSG number. Every fixture camp now has a unit, since a camp without one can no longer issue a Clinic ID.
Eight screenshots taken from the running app as the real CAD, doctor, nurse and programme manager users.
dadsena01
force-pushed
the
fix/clinic-boards-realtime-and-cmid-naming
branch
from
September 8, 2026 06:15
cf47964 to
c951eb9
Compare
Contributor
Author
|
Replaced by #25 with a tidied history and description. |
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.
Sits on top of #23, so the diff carries those commits too until that one merges.
This carries on from #23. It covers the three clinic boards, the dashboard, and a pass over field names against what CMID actually asked for in their scope document.
The screenshots below are from the running app, taken as the real CAD, doctor, nurse and programme manager users, so you can see the changes without setting up a camp yourself.
Boards update on their own
The CAD, doctor and nurse boards only changed when somebody pressed refresh, so staff worked off a stale queue for most of a camp. A camp change now reaches every open board and each one re-reads its own queues. The message carries the camp name and who made the change, nothing about the patient, because it goes to every logged-in user on the site.
The refresh waits half a second, is skipped for the person who made the change, and is held back while a dialog is open so nobody loses what they are typing. It never freezes the screen.
Things that were quietly wrong
Nothing stopped the same person being registered twice. Every duplicate burns a Clinic ID off the yearly series, prints a second card and splits that person's history in two, and none of it can be undone once the card is in their hand. There were already two such pairs in the data. Registration now checks ABHA, then mobile, then name and date of birth, and offers to queue the person it found. It warns rather than blocks, because brothers share one mobile and two people can have the same name and birth year.
Clearing a clinical note was silently ignored. The doctor endpoints only saved a note if it had text in it, so deleting a wrongly recorded allergy did nothing and the old allergy stayed on the record.
Nothing caught a repeated medicine, so the same drug could go on a prescription twice and be dispensed as a double dose.
A patient could go back to the doctor with no test result at all, marked reviewed and carrying no answer. Every ordered test now needs a result, and Not Done was added so a nurse who cannot run a test can say so.
The dispense list had every medicine ticked in advance, so tapping straight through recorded the whole prescription as handed over. That is the row the donor-fund and stock reporting will count.
Vitals had no upper limit, so an SpO2 of 150 or a pulse of 9999 saved fine.
Every Clinic ID issued so far said unknown location. The fallback was meant for a patient registered with no camp at all, but it had become the normal path, because only three of twenty-two locations had an LSG number. Registration now refuses a camp whose LSG has no number and names the record an admin has to fill.
The dashboard was broken for the people it was built for. A director or programme manager got a blocking permission error. A number card runs through the report engine, so read on the doctype is not enough. Both roles already had read, so nothing new is exposed. It had only ever been checked as Administrator, who skips every check.
Field names against the CMID scope
Mobile is now Mobile Number, Company Name is Name of Company, Occupation / Sector is Sector of Employment, Chief Complaint is Patient Complaints, and Clinical Notes is Observations and Notes on Examination. Labels only, no field name was renamed, because the Clinic IDs and every report point at the old names.
Patient had two fields both labelled Clinic ID. One holds all 33 issued IDs, the other was empty on every patient, so anything picking a column by label got the blank one half the time. The empty one is gone.
LSG, district and state on a visit all copy from a location link that nothing ever set, so three fields the scope asks the software to fill by itself were empty on all 33 visits. One line at registration fixes it, and a patch backfills 19 of 33. The other 14 sit on old demo camps that have no site at all.
Screenshots
CAD board after a search, showing the result count, the Clear button and the waiting time on each queue row:
The warning before a duplicate registration:
The registration form. The chosen answer in each row is one shade darker instead of a solid button, so the only filled button on the form is the one that submits it:
Doctor board with the allergy banner, the patient filter, and Call next patient as the one filled button:
The prescribe dialog. Medicines first with a row ready to type into, the allergy at the top, and the notes collapsed underneath:
Nurse board with the camp summary line, the ordered test or medicine on each row, and how long each patient has been in the camp:
The dispense dialog, now showing the dose the doctor wrote, with nothing ticked in advance:
Dashboard opened by a programme manager, with no permission error:
Testing
245 tests pass. New ones cover the duplicate check, the short search, the capped result list, blank and Not Done test results, impossible vitals, half a blood pressure, duplicate medicines, negative amounts, clearing a note, and a camp with no LSG number.
Every fixture camp now has a unit, since a camp without one can no longer issue a Clinic ID. Ten test modules were building camps that had none, and the suite passed anyway, which is a fair measure of how invisible that was.
Also checked by hand in the running app at 1280px and 420px as each of the real users.
Still open, needs CMID
The tick lists in the scope have no options listed anywhere, so Diagnosis category, Services provided and Other interventions are not built. Occupation is a separate field in their table and we only store the sector. Five of the eight reports do not exist yet, and the session and clinic reports are missing the age group, sex and native state breakdowns their own descriptions ask for. Pharmacist, Helpline Staff and Admin Officer roles do not exist, and neither does the helpline follow-up module.
The real LSG numbers are also still outstanding. The dev site is running on numbers we made up, and these end up printed on every patient card.