- Added 'Discharge' button in sidebar under In-Patient & Nursing Care section
- Created search_patient() and search_patient_ajax() controller methods
- Implemented AJAX-based patient search by IPD Code, UHID, Name, or Mobile
- Added ipd_discharge/search_patient.php view with dynamic results table
- Search results show patient details, bed info, doctor assignments, and discharge status
- Added 'Create Discharge' and 'Print Discharge' action buttons for each patient
- Integrated DataTables for sorting and pagination
- Fixed database column names (patient_master.gender, age, age_in_month)
- Added routes for search page and AJAX endpoint
- Add 'Discharge Summary' section to buildAutoDischargeContent with department, doctors, and dates so {{DISCHARGE_SUMMARY}} token extracts properly
- Improve getDischargeDepartmentName to support dept_id, department_id fields and check both hc_department and ipd_department tables
- Improve getDischargeDoctorNames to fallback to doctor_id, doc_id, consultant_id if doc_list is empty
- Update DISCHARGE_TEMPLATE_TOKENS.md to clarify what {{DISCHARGE_SUMMARY}} contains
- Clear cached content for IPD 1 to regenerate with new logic
Fixes issue where {{DISCHARGE_SUMMARY}}, {{DOCTOR_NAMES}}, {{DEPARTMENT}} tokens were not working (replaced with empty values)
New placeholders added:
1. PATIENT_PHONE - Patient's phone number from patient_master.mphone1 or ipd_master.P_mobile1
Example: 9876543210
2. INSURANCE_COMPANY - Insurance company name from ipd_master.ins_short_name or ins_company_name
Example: Star Health Insurance
Default: 'Direct' if no insurance
3. DOCTOR_NAMES / DOCTOR_NAME - Comma-separated list of treating doctors with specialties
Example: Dr. Rajesh Kumar [General Medicine], Dr. Priya Sharma [Cardiology]
Reads from ipd_master.doc_list field and joins with doctor_master and med_spec tables
Changes:
- Added getDischargeDoctorNames() helper function
- Updated buildDischargeSummaryTokenVars() to include three new tokens
- Updated DISCHARGE_TEMPLATE_GUIDE.md with new token documentation
- Added example template showing usage of new tokens
- Updated CI3 to CI4 migration mapping table
These tokens match the data shown in the auto-generated patient summary table
and allow templates to display this information in custom layouts.
OLD CI3 BEHAVIOR (correct):
- Patient demographic table was IN the template file
- Content variables contained ONLY clinical sections
- Template controlled entire layout
NEW CI4 FIX (matching CI3):
- Patient table removed from buildAutoDischargeContent completely
- Added {{PATIENT_INFO_TABLE}} token for templates to use if needed
- Auto-generated content now contains ONLY clinical sections
- Blank template = blank output (no patient table forced)
- Templates can add patient table using token or individual tokens
Changes:
- Removed includeSummaryTable parameter from buildAutoDischargeContent
- Removed templateHasDemographicTokens helper (no longer needed)
- Added PATIENT_INFO_TABLE token in buildDischargeTemplateTokenVars
- Simplified show_discharge and preview_discharge_report (no template detection)
- Added comment explaining CI3 compatibility
This matches the old CI3 chamunda_discharge_template_html.php pattern where:
- Template had hardcoded patient table structure
- Variables like \, \ were injected
- Content did NOT include patient demographics by default
- Modified buildAutoDischargeContent to accept includeSummaryTable parameter
- Added templateHasDemographicTokens helper to detect if template has patient info tokens
- Updated show_discharge and preview_discharge_report to check template before auto-generation
- When template has {{PATIENT_NAME}}, {{UHID}}, etc., skip auto-summary table
- Added clear_discharge_content.php script to clear cached content for regeneration
- Solves issue where blank templates still showed patient information table
- Updated all controllers: Opd, Diagnosis, Billing\Charges, Billing\Ipd, Storestock, AbdmGateway
- freeserif provides better Unicode coverage and more readable font weight
- Ipd_discharge and DoctorDocument already used good fonts (freeserif/freesans)
- Added comprehensive font configuration documentation
FEATURE: Quick-add functionality for Course/Treatment master terms
NEW COMPONENTS:
1. Course master table: ipd_discharge_course_master
- term_name, term_code, icd_code, is_active
- Auto-creates on first use
2. Controller endpoints in Ipd_discharge.php:
- course_master_lookup() - AJAX autocomplete search
- course_master_list() - Full list with filters
- course_master_save() - Create/update records
- course_master_delete() - Remove records
3. Routes added to Config/Routes.php:
- Ipd_discharge/course_master_lookup (GET)
- Ipd_discharge/course_master_list (GET)
- Ipd_discharge/course_master_save (POST)
- Ipd_discharge/course_master_delete (POST)
VIEW CHANGES (discharge_create.php):
- Course input now uses input-group layout
- Green 'Save in Master' button added
- Custom dropdown for autocomplete with code badges
- Hidden field: new_course_master_id
- JavaScript: initCourseAutocomplete() function
- Integrated with existing quickAddTermModal
QUICK-ADD MODAL ENHANCEMENT:
- Now supports 3 types: surgery, procedure, course
- Dynamic endpoint selection based on type
- Course type uses course_master_save endpoint
- Auto-populates course input and master_id on save
AUTOCOMPLETE FEATURES:
- Type 2+ characters → searches course_master table
- Dropdown shows: Name + SNOMED/ICD code badges
- Arrow key navigation (Up/Down/Enter/Escape)
- Click or Enter to select
- Hover highlighting with visual feedback
- 300ms debounce for performance
WORKFLOW:
1. Type course/treatment name
2. See dropdown with existing terms (if any)
3. Click 'Save in Master' to add new term
4. Modal opens with name pre-filled
5. Enter SNOMED/ICD codes (optional)
6. Save → Auto-populates input + master_id
7. Click '+ADD Row' to add to discharge
BENEFITS:
- Consistent UI across Surgery/Procedure/Course sections
- Fast term entry without leaving form
- Master library builds during workflow
- ABDM compliance with code support
- Reusable terms across discharges
BUGFIX: Database error when saving editable complaint fields
PROBLEM: After adding X-Requested-With header, the request reached
the controller but threw 'Database error' when trying to update the
complaint field.
ROOT CAUSE: The ipd_discharge_complaint table does not have an
'updated_at' column. The update query was trying to set:
- comp_report or comp_remark (correct)
- updated_at (does not exist) ← DATABASE ERROR
INVESTIGATION: Checked how complaint rows are inserted elsewhere in
the codebase (line 4117-4130). The table has 'update_by' column for
tracking changes, not 'updated_at'.
SOLUTION:
1. Removed hardcoded 'updated_at' => date(...) from update data
2. Only update the target field (comp_report or comp_remark)
3. Conditionally set 'update_by' if column exists (audit trail)
4. Use session full_name for user tracking
Now editable complaint fields save successfully without database errors.
BUGFIX: Event handlers were only attached on initial page load
PROBLEM: After implementing editable complaint fields, the inputs
wouldn't become editable when adding new complaints via the Smart
Complaints Picker. The event handlers were only attached once on
page load, not after the section was dynamically reloaded.
ROOT CAUSE: When clicking Add or AI Draft buttons, the form submits
and the section-complaints area is reloaded from server. The new
complaint rows had the input fields but no event handlers attached.
SOLUTION:
1. Moved event handler attachment logic into initComplaintTools()
function as new attachComplaintFieldHandlers() helper
2. This function is automatically called after section reload
3. Clone-and-replace pattern to prevent duplicate event handlers
4. Fixed table name: ipd_discharge_complaint (not ipd_discharge_comp)
FLOW:
- Add complaint → section reloads → initComplaintTools() runs →
attachComplaintFieldHandlers() attaches blur/keydown handlers →
editable fields work
Now clicking on complaint name/remark inputs works for both:
- Existing complaints (on page load)
- Newly added complaints (after section reload)
FEATURE: Inline editing for discharge complaint table
PROBLEM: In IPD discharge form, complaint names and remarks were
read-only after adding. Unlike OPD consult form where you can edit
complaint names directly in the table.
SOLUTION:
1. VIEW Changes (discharge_create.php):
- Changed complaint name and remark columns from static <td> to editable <input>
- Added data attributes for complaint ID and original values
- Added CSS classes: discharge-complaint-name-input, discharge-complaint-remark-input
2. JAVASCRIPT Changes:
- Added saveComplaintField() function for AJAX updates
- Event handlers on blur to save changes
- Enter key support to blur and save
- Visual feedback: green border on success, red on error
- Auto-updates original value after successful save
3. CONTROLLER Changes (Ipd_discharge.php):
- Added update_complaint_field() method for AJAX endpoint
- Updates ipd_discharge_comp table (comp_report or comp_remark)
- Returns JSON success/error response
BENEFITS:
- Same user experience as OPD consult form
- Edit complaints/remarks without page reload
- Immediate feedback on save success/failure
- Type correction and updates without removing and re-adding rows
Reduced sizes:
- Table width: 100% to 60%
- Debug text: 10px to 8px
- Triangle: 18px to 12px
- Number boxes: 14px to 10px with 3px padding
- Border: 3px to 2px for selected
- Labels: uniform 7px font
- Reduced all margins and spacing
Still maintains clear B&W visibility with triangle, gray background, and bold border.
PROBLEM: Emojis don't render in PDF (showing as empty boxes)
SOLUTION:
- Use down-pointing triangle (▼) using HTML entity ▼
- This Unicode character renders properly in mPDF/PDF generators
- Darker gray background (#cccccc) for better B&W visibility
- Bold 3px border on selected box
- Larger font and padding for selected number
Visual indicators that work in B&W printing:
- Triangle (▼) above selected level
- Dark gray shading on selected box
- Bold border on selected box
- Bold text for selected label
PROBLEM: Color coding not visible in black & white printers
SOLUTION:
- Added emoji indicators: 😊😐😣😖😭 for pain levels 0-4
- Changed colors to black/white/gray for B&W compatibility
- Emoji shows above selected pain level (larger size)
- Bold 3px border on selected box (visible in B&W)
- Light gray background on selected box
Now pain scale is clear even in monochrome printing!
PROBLEM: Static pains_scale_0-4.png files all look identical (just gradient)
so users can't tell which pain level is selected.
SOLUTION: Comment out PNG file checking code to force use of dynamic HTML
with arrow indicator, colored backgrounds, and bold borders.
Now {{painscale_img}} will show:
- Debug line: 'Pain Level: X - Label'
- Down arrow (▼) above selected level
- Colored background (green to red) on selected box
- Bold 3px border on selected box
- Different pain values will have visibly different displays
- Add 'Pain Level: X - Label' text above pain scale for debugging
- Helps identify if pain_value is being read correctly from database
- Fix comparison to use strict equality check for ===
- User can now see which pain level is actually being rendered
- Change checkmark to down arrow (▼) for better visibility in PDFs
- Increase arrow size to 20px and make it bold
- Use white background for non-selected boxes for stronger contrast
- Add thick black border (3px) on selected level for emphasis
- Selected box shows colored background with white text
- Increase selected label text size and make it bold black
- Clear visual distinction between selected (value 2 vs 4) levels now
- Add fallback to 'logo.png' when H_logo is not set in database or constants
- Prevents {{H_logo}} placeholder from appearing as literal text in print/PDF
- Resolves 400 Bad Request error when browser tries to load unreplaced placeholder
- Ensures prescription templates always have a valid logo path
- Add checkmark (✓) above the selected pain intensity level
- Use color gradient from green (No Pain) to red (Worst) for better visual clarity
- Highlight selected level with colored background and white text
- Thicker border on selected level for better emphasis
- Bold label text for selected intensity
- Makes pain scale selection immediately visible in printed/PDF prescriptions
- Add billing.patient.edit-name-anytime permission to AuthGroups
- Update patient edit time restriction from 12 hours to 24 hours
- Modify Person_Edit_V to check for edit-name-anytime permission
- Admin can now edit patient name field (input_name) even after 24-hour window
Modified {{investigation}} to show both:
1. Textarea content (advise_investigation_notes) if present
2. Comma-separated test list from opd_prescription_investigation table
Both sources combined with newline separator when both exist.
{{Tadvise_test_list}} still available separately for backward compatibility.
Example combined output:
USG WHOLE ABDOMEN
CECT ABDOMEN
PAC
COMPLETE CARDIAC CHECK UP
PAP SMEAR
CBC (Complete Blood Count), ECG (Electrocardiogram), HBA1C, HBSAG, HCV, HIV I & II (Elisa), Liver Function Test (LFT), PT/INR, Renal Function Test (RFT), Urine Routine & Microscopy, X-Ray Chest (PA View)
Changed placeholder behavior to match user requirements:
- {{investigation}} = textarea content (advise_investigation_notes) only
- {{Tadvise_test_list}} = comma-separated test names from table
Removed HTML bullet list formatting. Now outputs plain comma-separated text.
Example output:
CBC (Complete Blood Count), ECG (Electrocardiogram), HBA1C, HBSAG, HCV,
HIV I & II (Elisa), Liver Function Test (LFT), PT/INR, Renal Function Test (RFT),
Urine Routine & Microscopy, X-Ray Chest (PA View)
Both placeholders are independent and can show content simultaneously.
When opd_prescription_investigation table is empty but investigation
textarea has content, parse textarea content (split by newlines) to
populate the test list.
This handles cases where tests are entered as free-text in the
"Advise Investigation" textarea instead of selecting from master list.
Parsing logic:
- Try rx_investigations table first
- If empty, split investigation textarea by newlines
- Filter out empty lines and 'null' values
- Build HTML bullet list from either source
Example textarea content:
USG WHOLE ABDOMEN
CECT ABDOMEN
PAC
COMPLETE CARDIAC CHECK UP
PAP SMEAR
Will render as:
• USG WHOLE ABDOMEN
• CECT ABDOMEN
• PAC
• COMPLETE CARDIAC CHECK UP
• PAP SMEAR
Added try-catch around mPDF WriteHTML to catch rendering errors.
Changed debug_html mode to output as text/html for better preview.
Logs mPDF errors and returns user-friendly error message.
This will help diagnose blank page issues with {{Tadvise_test_list}} placeholder.
Previous implementation only built test list when investigation textarea
was empty, preventing {{Tadvise_test_list}} from showing when textarea
had content.
Changes:
- Separated test list building from textarea population logic
- {{Tadvise_test_list}} now always populates when rx_investigations exists
- {{investigation}} still gets comma-separated fallback when textarea empty
- Both placeholders now work independently
Test list HTML output format:
<ul style="margin:3px 0 0 16px;padding:0;list-style-type:disc;">
<li>CBC (Complete Blood Count)</li>
<li>ECG (Electrocardiogram)</li>
<li>HBA1C</li>
</ul>
Created new OPD template placeholder to display investigation test names
as a formatted HTML list, separate from the textarea {{investigation}}.
Changes:
- app/Controllers/Opd.php:
* Added $rxInvestigationTestList variable in buildLegacyDashboardTemplateVars()
* Builds HTML <ul> list from rx_investigations test names
* Added 'Tadvise_test_list' token to return array
- app/Views/billing/opd_template_builder.php:
* Added 'Tadvise_test_list' to $opdConsultSet placeholder list
* Now appears in "OPD Consult" group in template builder
Usage in OPD templates:
- {{investigation}} = comma-separated test names (for textarea)
- {{Tadvise_test_list}} = formatted HTML bullet list of test names (for print/display)
Example output:
<ul style="margin:3px 0 0 16px;padding:0;list-style-type:disc;">
<li>CBC (Complete Blood Count)</li>
<li>USG WHOLE ABDOMEN</li>
<li>ECG (Electrocardiogram)</li>
</ul>
Root cause: 50k+ invoices had insurance_case_id='0', and MySQL string comparison '0'='C26060004997' matched (both convert to numeric 0).
Fix: Exclude NULL/empty/'0' insurance_case_id before comparison in:
- getOrganizationCaseInvoiceRows() union query
- updateOrgTotals() aggregate queries
Result: Case C26060004997 now returns 9 rows instead of 56,786 (memory crash fixed)
- FhirR4Builder: rewrite buildLabReportBundle() with DiagnosticReportLab
profile, structured Observation valueQuantity/valueString, reference
ranges, interpretation codes, Binary+DocumentReference for HTML report,
Encounter resource, HFR-based bundle identifier system
- FhirR4Builder: rewrite buildDischargeSummaryBundle() with optional
Practitioner/Organization params, structured ABDM sections (chief
complaints 422843007, medical history 1003642006, investigations
721981007, procedures 1003640003, medications 721912009, follow-up
736271009), HFR-based bundle identifier system
- AbdmGateway: shareIpdDischargeBundle() now builds a real
DischargeSummaryRecord FHIR bundle and posts via pushRecord()
(POST /v3/records/push) instead of the stale shareIpdDischargeBundle()
- AbdmGateway: shareDiagnosisReportBundle() now builds a real
DiagnosticReportRecord FHIR bundle and posts via pushRecord()
instead of the stale shareDiagnosisReportBundle()
- AbdmGateway: add getHospitalProfileForFhir() and mapLabTypeToTitle()
helpers; fix pre-existing undefined \ bug in
getActiveConsentRecord()
- Add ABDM_FHIR reference package, AbdmBridgeLog controller/view,
bridge record migration, debug scripts and misc UI/settings updates
- POST Opd_prescription/fhir_bundle_submit endpoint:
loads stored FHIR bundle, fetches patient/OPD/doctor context,
calls EAtriaBridgeConnector::pushRecord to push to bridge
- Standalone preview page (/Opd_prescription/fhir_bundle_preview/N):
added Submit to ABDM Bridge button with CSRF-safe AJAX + inline
success/error alert
- FHIR modal in task_board.php: added Submit to ABDM button next
to Copy JSON; JS reads _fhirOpdId/_fhirSessionId and posts to
fhir_bundle_submit with CSRF token refresh
- Add separate Save Settings button below User Settings card
- Create myProfileSaveSettings controller method for dedicated settings save
- Settings save endpoint: POST /my-profile/save-settings
- Validates sidebar_auto_hide_seconds (0-60 range)
- Provides immediate feedback: success/error message display
- Separate from profile photo and password updates
- Message auto-hides after 2 seconds on success
- Create user_settings table to store individual user preferences
- UserSettings model for get/set/delete user setting values
- Home::myProfile loads user settings with hospital defaults fallback
- Home::myProfileSave persists user settings to DB
- my_profile.php adds Sidebar Auto-Hide Delay field (0-60 seconds)
- welcome_message.php checks user settings first, then hospital default
- Setting value 0 = auto-hide disabled; blank = use hospital default
- Extensible table design for future user settings (color theme, etc)
- Safe delete for empty IPD charge groups with CSRF-safe AJAX
- Migration: add sort_order to ipd_item_type (backfilled)
- Sort order UI on create/edit/list views; persist in controller/model
- Up/Down swap buttons on group list (AJAX swap endpoint + CSRF refresh)
- Fix 500 on edit screen: restore missing getItemTypeById() in ItemIpdModel
- IpdBillingModel: propagate sort_order ordering to ipd-charges and bill-details tabs
- Billing IPD Charges tab: Bedside Clinical/Nursing Charge and Doctor Visit Charge panels now collapsible, start collapsed
- Add OPD Validity (Days) field in Doctor add/edit forms\n- Save/update doctor_master.opd_valid_no_days from Doctor module\n- Use hospital_setting fallback keys when doctor validity is blank/zero\n- Apply fallback consistently in OPD showfee, print expiry, and running_opd lookup window\n- Supports hospital-wide defaults while preserving doctor-specific overrides
OpdModel.php:
- running_opd_id lookup now uses doctor's configured opd_valid_no_days
instead of hardcoded 4 days (resolveRunningLookupDate helper)
- Doctors with validity > 5 days now correctly link running OPD back
to the original paid OPD even on days 5..N of the window
Opd.php (buildOpdLetterPrintData):
- For running OPD entries (running_opd=1 or opd_fee_type=3), expiry
display now shows "OPD Start Date: [paid visit] | Valid Upto: [paid
visit + validity]" using old_opd record — mirrors Opd_print_1 legacy
behavior and prevents validity window from advancing with each visit
- Make docedit_load/doc_input_list/create_doc flows tolerant when doc_format_sub.active is missing\n- Guard master/sub insert/update payloads by existing DB columns to avoid SQL 500 on old dumps\n- Avoid hard dependency on created_at/updated_at/default_print_type print margin columns\n- Keep behavior unchanged on upgraded schemas while restoring compatibility with legacy production data
- Add paper print settings to IPD templates (page size/custom size/margins)\n- Add Header HTML, Footer HTML, and Style (CSS) support\n- Update IPD PDF renderer to honor template paper settings and header/footer\n- Extend IPD placeholders with logo/QR support: {{H_logo}}, {{H_logo_abs}}, {{hospital_logo_html}}, {{qr_content}}, {{qr_code_html}}\n- Improve token rendering with robust curly placeholder parser for body/header/footer\n- Keep legacy sticker forms aligned with custom label dimensions and migrate old default rows
Root cause: the fallback lookup from opd_med_master used
$labelField = $masterSaltField ?? $masterGenericField
which preferred salt_name (the Substitute / other brand names field) over
genericname (the actual Generic Name / composition). If a prescription row's
inline genericname was empty, the Substitute string was written into it and
then printed as \"Salt/Generic\" in the {{medical}} block.
Fixes applied in three places:
1. Opd.php buildOpdLetterPrintData - master lookup now prefers genericname
2. Opd.php buildOpdLetterPrintData second loop - removed inline salt_name
fallback that overwrote genericname with Substitute value
3. Opd.php buildLegacyDashboardTemplateVars - removed salt_name from the
generic resolution chain used when rendering the medicine table HTML
4. Opd_prescription.php medicine list function - same preferGenericname fix
and removed inline salt_name -> genericname overwrite"
- Queue No (Opd.php::autoCreateOpdQueue, Opd_prescription.php::opd_prescription_save
and ::ensurePrescriptionSession): changed MAX(queue_no) query to JOIN through
opd_master.doc_id instead of trusting opd_prescription.doc_id, ensuring queue
numbering is strictly per-doctor per-day regardless of stale doc_id values in
opd_prescription rows.
- OPD appointment list: replaced auto-reload-on-digest-change with a manual
'Refresh' button. When the 8s digest poll detects new data the button turns
yellow with a 'New' badge; the page only reloads when the user explicitly clicks
it, preventing disruptive mid-session reloads."
Captures:
- Storage key being used
- Payload length
- Which candidate keys are checked
- Persistence verification results
Remove this commit after testing confirms the fix works
- Shortened template-scoped paper setting keys to avoid s_name length limits in legacy schemas
- Added legacy key fallback so existing saved settings still load
- Updated rename/delete/reset flows to handle both new and legacy key aliases safely
- Restored corrupted buildOpdLetterPrintData method that broke class structure
This fixes the 'Unable to save OPD paper print settings' error when saving new templates
- add new permissions:
template.opd_print, template.pathology_print, template.diagnosis_print,
template.document_print, template.ipd_document
- Template controller now accepts new permission keys (with backward-compatible fallbacks)
- IPD Document Master now has independent permission key (template.ipd_document)
- Template index tile visibility updated to use new granular keys
- OPD print template builder now honors template.opd_print permission
- preserve existing behavior via legacy permission fallbacks to avoid role breakage
- new rx_group_delete(int rxId) method in Opd_prescription controller
- deletes linked medicines first (opd_prescrption_prescribed_template), then group row
- POST route: Opd_prescription/rx_group_delete/(:num)
- Delete button (red) added to each row in Rx-Group list table
- confirm dialog before delete (shows group name)
- reloads list after delete; clears form if deleted group was loaded
- consult form medicine Dose/When/Freq changed to dropdowns from dose masters
- added Where dropdown in consult medicine editor and table display
- consult now loads options from Opd_prescription/rx_group_dose_masters
- medicine add/update payload now includes dosage_where
- medicine list now returns dosage_where_label for readable table output
- edit mode supports existing values not present in current master set
- quick chips now map into select options (with safe fallback)
- sttProxy now returns detailed upload error info (code, error string, php limits)
- sttProxy accepts fallback upload field name 'file' in addition to 'audio'
- reject empty audio payloads with explicit message
- client: guard against very short/empty blobs before POST to /stt-proxy
Production runs HTTPS; browser blocks http://139.59.13.39:8000 (mixed content).
Solution: CI4 proxy endpoints forward audio/health check server-side (HTTP→HTTP).
- Home::sttProxy() POST stt-proxy - forwards audio to STT server
- Home::sttProxyHealth() GET stt-proxy/health - pings STT and returns {ok:bool}
- JS: auto-detect window.location.protocol; use proxy URLs on HTTPS,
direct IP URLs on HTTP (local/dev)
- checkComplaintsSttServer: parse {ok:bool} from proxy health response
- Add complaints Speech-to-Text (browser + server STT fallback) with interim preview
- Add Medical Mic (server STT) for examination, diagnosis, provisional diagnosis fields
- Add speech-test route + Home::speechTest() view for STT testing
- OPD prescription: resolve dosage/when/frequency labels from lookup tables
- OPD prescription: show generic/salt name below medicine name in print
- OPD prescription: translate complaints/diagnosis/advice/investigation to Hindi for patient
- Add translateToLocalPatientText() helper with English->Hindi medical term map
- Add 'Remove All' medicines button (btn_clear_medicine)
- Add template search box in Load Template modal
- Fix investigation batch add: dedup + guard against concurrent add
- Fix single investigation add: check duplicate before adding
- Fix rx status text overflow: ellipsis + title tooltip
- Remove unused FHIR History and Back to Invoice buttons from Rx header
- .gitignore: exclude public/uploads/* and writable/ayushman/*
- Redact AIzaSy Gemini key from clinical_audit_trail entries in seed JSON
- Add default never-export denylist to exportMasterSeedData() so that
clinical_audit_trail, lab_log, bridge_sync_queue and session tables are
always skipped when setup.seed.export_tables is not configured.
This prevents API keys set via the admin UI from leaking into git via
the audit trail even after being deleted from hospital_setting.
- Add migration 2026-03-18-000028_CreateCityAutoUTable.php to create
the missing city_auto_u table (city, district, state columns) used
by PatientModel::getCitySuggestions() — resolves 500 on /billing/patient/city
- Fix HospitalProfile::save() to check directory creation success before
moving uploaded logo files; returns clean JSON error instead of
throwing HTTPException when public/assets/images/ is not writable