qa2#21442
Conversation
…tity
When a bill item's available stock was less than the requested quantity,
the insufficient-stock error message navigated through a minimal ItemBatch
object (built from DTO data without the Item relationship populated) to
retrieve the item name, causing a NullPointerException:
bi.getPharmaceuticalBillItem().getItemBatch().getItem().getName()
^^^^^^^^^^^ null
Root cause: createBillItemFromStockDTO() constructs a minimal ItemBatch
from bulk-query DTO data (id, batchNo, rates, expiry) but never calls
batch.setItem(...), leaving the item field null.
Fix: use bi.getItem().getName() instead — BillItem.item is always set
correctly via newBillItem.setItem(referenceItem.getItem()) during bill
item creation and is updated by replaceSelectedSubstitute() when a
substitute is chosen.
Diagnosed from server log: NullPointerException at
TransferIssueForRequestsController.settle(TransferIssueForRequestsController.java:422)
Closes #20972
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ent NPE The previous fix replaced getItemBatch().getItem().getName() with bi.getItem().getName(), but bi.getItem() or getName() can also be null (e.g. legacy data, Vmpp/Vmp items). Remove the item name reference entirely and use a safe generic message instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two independent bugs allowed a transfer issue to proceed even when actual DB stock was insufficient: 1. Stale in-memory stock check The validation loop compared bi.getPharmaceuticalBillItem().getStock() .getStock() against qty, but that Stock object is constructed at page-load time by createBillItemFromStockDTO() with the available qty captured from the bulk DTO query. Any stock movement that occurs after the page loads (retail sale, concurrent issue) is invisible to this check. Fix: replace with a live DB lookup via StockFacade.find() for every item immediately before saveBill() is called, so the check always reflects the true current stock level. 2. Negated qty passed to isStockAvailable() In the post-save processing loop, qty is negated at line 831 before isStockAvailable() is called at line 856. The internal check (qty > fetchedStock.getStock()) evaluates as e.g. -10 > 2 → false, causing the guard to always return true regardless of actual stock. Fix: pass Math.abs(tmpPh.getQty()) so the comparison uses the magnitude of the quantity. Also made the error messages null-safe (bi.getItem() null guard) and added a clearer message telling the user to refresh when live stock check fails, since the cause is stale page state. Closes #20972 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…data
When a transfer issue is settled, the PharmaceuticalBillItem carries a
minimal ItemBatch built from a DTO — only id, batchNo, and rates are set;
the item relationship is null. Both addToStock(pbi, qty, Staff) and
deductFromStock(pbi, qty, Staff) create a new Staff Stock row when none
exists, and they attempt to populate that row's metadata from the batch:
ItemBatch ib = pharmaceuticalBillItem.getItemBatch();
Item i = ib != null ? ib.getItem() : null;
if (i != null) { /* set itemName, barcode, longCode, dateOfExpire, retailsaleRate */ }
else { s.setItemName("UNKNOWN"); s.setBarcode(""); s.setLongCode(0L); }
Because the DTO batch has no item loaded, i is always null, so every Staff
Stock row created through a transfer issue is stored with:
- ITEM_NAME = "UNKNOWN"
- BARCODE = ""
- LONGCODE = 0
- DATEOFEXPIRE = null
- RETAILSALE_RATE = 0.0 (skipped in the else branch)
This does not break stock quantity movement — the JPQL lookup key is
(itemBatch, staff) by ID — but it corrupts the metadata fields used by
stock valuation and expiry reports for in-transit staff stock.
Fix: before accessing ib.getItem(), check whether the item relation is
unloaded (null) while the batch has a valid DB id. If so, call
itemBatchFacade.find(ib.getId()) to obtain the full entity. The loaded
entity is used only for metadata population; the FK already stored on
the Stock row is unchanged. The guard (getItem() == null && getId() != null)
ensures no extra query is issued when the full entity is already loaded
(normal receive-side path where ItemBatch is eagerly fetched from DB).
Applied to both:
- addToStock(PharmaceuticalBillItem, double qty, Staff)
- deductFromStock(PharmaceuticalBillItem, double qty, Staff)
Closes #20972
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…l optional Adds "GRN Return - Approval Required" and "Direct Purchase Return - Approval Required" config keys (default true, preserving current two-stage Finalize -> Approve behaviour). When disabled, finalizeRequest() runs the same stock-update, payment creation, and completed-marking logic that previously only ran in approve() (extracted into a shared completeApproval() method), so the return is completed immediately on Finalize without a separate approval step. Adds a "Config" button + dialog on procurement_index.xhtml (PharmacyReturnApprovalConfigController) so these settings are configurable from the UI instead of requiring direct ConfigOption edits. Part of #21404
…on Patient Deposit The single-payment composite patient_deposit_with_paid_value.xhtml carried a <p:ajax> copied from the multi-payment variant whose update targeted balanceDisplay via p:resolveFirstComponentWithId. That component only exists in multiple_payment_methods.xhtml, so on inward_bill_payment.xhtml the lookup returned null, the update expression collapsed to ":", and PrimeFaces threw ComponentNotFoundException while rendering txtMpPd. The listener also referenced a controller attribute the inward callers never pass. Removed the offending p:ajax. The amount still commits via the non-ajax Pay button submit, matching the Cash field on the same page. Part of #21408
… payment The Inward Bill Payment Patient Deposit panel showed "Patient not selected" because the patient was never set on InwardPaymentController when navigating to the payment page. navigateDoctorPayment() now sets the patient from the encounter so the deposit balance is available. InwardPaymentController.setPatient() now resets the patient deposit totalValue to 0.0 when loading the deposit, so the amount field starts at 0.00 instead of carrying a stale value. Part of #21408
…yment-fix fix(inward): Patient Deposit payment ComponentNotFoundException and missing deposit details (#21408)
…idation hardening - Persist PharmaceuticalBillItem.stock in approveDirectPurchaseDraft(); the stock link was set in memory but never written to DB, so Direct Purchase Returns against draft-approved bills failed with "Stock information not available for item" - Sync phi.qty/freeQty with auto-corrected fd quantities in GRN and Direct Purchase return validateStockAvailability() so a re-finalize after auto-correction approves the corrected quantity, not the stale oversized one - Allow zero-quantity return lines with no Stock record to pass validation instead of blocking the whole return - Add "Direct Purchase - Approval Required" toggle to the Procurement Return Configuration dialog (maps to existing config key "Use Save Finalize Approve Workflow for Direct Purchase") - Switch finalized Direct Purchase drafts to read-only print preview; approval is done only from the Approve Direct Purchase list Part of #21404 Refs #21266 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…save (CodeRabbit, PR #21411)
fix(pharmacy): optional procurement approval workflows + direct purchase stock-link and return validation fixes
…t defaultCommand race The page-level p:defaultCommand target="btnAdd" made Enter anywhere in the form fire the item Add button. In the item autocomplete (acItem, forceSelection=true) this raced the itemSelect AJAX commit, so Enter could click Add while currentBillItem.item was still null server-side, producing "Please select an item" despite a visually selected item. The same race silently dropped typed values in the qty/rate/discount fields, whose recalculation listeners fire on blur, which defaultCommand bypasses. Guards applied (same pattern as pharmacy_transfer_request.xhtml): - acItem, acExpense, distributor and reference-company autocompletes: Enter is swallowed unless the suggestion panel is open, in which case PrimeFaces handles the selection only - txtQty, txtFreeQty, txtPrate, txtLineDiscountRate, txtRetailRate, batch no: Enter blurs the field first (committing the blur AJAX recalculation), then clicks Add after 350 ms - expense value/description: Enter clicks Add Expense (full-submit button, no blur race) instead of the unrelated item Add button Closes #21415 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-guard fix(pharmacy): guard Enter key on direct purchase entry fields against defaultCommand race
…zero-qty PBIs A double-submit race on the purchase order request page (Enter-key defaultCommand firing btnSave while another save was in flight) persisted the same in-memory BillItem twice, creating duplicate rows that share one BillItemFinanceDetails with only one of them owning the PharmaceuticalBillItem. Removing the visible duplicate could retire the row holding the real PBI; the survivor then got a lazily created all-zero PBI, and PO approval/GRN (which reads pbi.qty) showed qty 0 and blocked the GRN (coop POR/COOP/MP/26/000965, Maxgalin 50mg). Server side (PurchaseOrderRequestController): - saveRequestWithoutMessage() and finalizeRequest() are now synchronized (same pattern as TransferIssueController.settle()) so concurrent saves from one session serialize; the second save sees the assigned id and edits instead of creating a duplicate - resyncPharmaceuticalBillItemIfEmpty() runs for every line in saveBillComponent()/finalizeBillComponent(): a live line whose PBI has zero qty and free qty while its finance details hold a real quantity is rebuilt via calculateLineValues(), healing already-corrupted lines at the next save or finalize Page (pharmacy_purhcase_order_request.xhtml), same guard pattern as direct purchase (#21415): - Qty, Free Qty and P Price inputs in the items table swallow Enter and blur instead, committing the blur-AJAX recalculation rather than firing a full-page Save mid-edit - Supplier and item autocompletes swallow Enter unless the suggestion panel is open, so PrimeFaces handles only the selection Closes #21417 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pbi-guard fix(pharmacy): prevent duplicate PO request bill items and self-heal zero-qty PBIs
chore: remove unwanted tracked files and prevent future inclusion Delete unwanted tracked files from the repository: - .codex-backup/ — Codex temp backup files (2 files) - lib/unknown/binary/ — unused EclipseLink SNAPSHOT JARs (8 files) - 4 .class files accidentally committed in src/main/java/ - clinical_favourite_item_by_weight.xhtml.backup Add .gitignore patterns for *.class, *.backup, .codex-backup/, and lib/unknown/ to prevent these from being tracked again. @
chore: move db/ migration README to developer_docs, remove one-time SQL scripts Move the BigDecimal migration strategy documentation to developer_docs/database/ where it belongs as reference knowledge. Remove the one-time SQL migration scripts and production hotfix SQL from db/ — these are historical artifacts with no enduring value. Add db/ to .gitignore to prevent future migration scripts from being committed to the wrong location. @
chore: move healthcare-erp-architect agent to .claude/agents/, remove github-issues/ The agent file was buried under github-issues/.claude/agents/ where it could not be discovered by Claude Code. Move it to the proper location and gitignore the github-issues/ scratch workspace directory. @
chore: remove wiki-docs, sql-scripts, pm, temp root files; preserve one user doc Delete from repository: - wiki-docs/ — 43 files, wiki now lives in ../hmis.wiki sibling repo - sql-scripts/ — 2 one-time ALTER TABLE scripts - pm/ — 1 project management CSV - 4 root temp files: session transcript, 3 stale fix-summary MDs Move Patient-Deposit-Department-Level-Management.md from user_documentation/ to developer_docs/. Add gitignore patterns for all removed directories and root files. @
chore: add ad-hoc artifacts and node_modules to .gitignore
Add department-wise sale & issue consolidated view with BHT query fixes - Add DepartmentSaleIssueDTO for consolidated retail/wholesale/inpatient by department in pharmacy item history - Add createDepartmentSaleIssueDto() method with per-category JPQL queries using PharmaceuticalBillItem.qty for correct sign handling - Add "Dept. Sale & Issue" block and tab to history.xhtml, gated behind config keys with default true (visible to all institutions by default) - Fix calDepartmentBhtIssue() to use pharmaceuticalBillItem.qty instead of BillItem.qty for correct stock-aware quantity (#21430) - Add missing cancellation and return bill type atomics to createInstitutionBhtIssue() (#21431) - Guard all findAggregates() results against null (NPE on query failure) Closes #21428 Closes #21430 Closes #21431 Co-Authored-By: Claude <noreply@anthropic.com> @
fix(pharmacy): query from PharmaceuticalBillItem, revert BHT issue sign - Change createDepartmentSaleIssueDto() JPQL to query FROM PharmaceuticalBillItem pbi instead of BillItem i — ensures every row has a PBI and uses pbi.qty directly without implicit joins - Revert sign of PBI qty in createInstitutionBhtIssue() when building DepartmentSale objects — PBI qty is negative for outgoing issues, the BHT Issue tab/block needs positive display quantities (Codex review on PR #21439) Co-Authored-By: Claude <noreply@anthropic.com> @
Add department-wise pharmacy sale & issue consolidated view, fix BHT issue queries
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5c71ed4eef
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!webUserController.hasPrivilege("PharmacyGrnApprove") | ||
| && !webUserController.hasPrivilege("PharmacyDirectPurchaseApprove")) { |
There was a problem hiding this comment.
Require the matching privilege for direct-purchase settings
When a user has only PharmacyGrnApprove, this OR guard lets the request continue and saveConfig() still writes the Direct Purchase Return and Direct Purchase approval-workflow keys below. That allows a GRN approver to disable direct-purchase approval requirements without PharmacyDirectPurchaseApprove; gate each setting by its matching privilege or require a broader/admin privilege for the combined dialog.
Useful? React with 👍 / 👎.
Raw '&' in the config key string inside an EL expression caused an XML parse error (FaceletException) on any page that includes history.xhtml. Escaped as '&' to comply with XHTML requirements.
…e lines deductFromStock() already does an atomic UPDATE...WHERE s.stock >= :qty check (the #20138 TOCTOU-safe guard) and returns false if insufficient, handled by the existing else branch. The userStockController.isStockAvailable() check ran after the BillItem/PharmaceuticalBillItem were already persisted, and on failure only zeroed tmpQty and continued - leaving a saved transfer issue line with no corresponding stock movement. UserStockContainer reservations are not populated in this flow, so the check added no real protection beyond the live stock check already done earlier in settle(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ceuticalBillItem createEmptyBillItem() (used in generateBillComponent when an item has no available stock) adds a BillItem with pharmaceuticalBillItem left unset. Both the live stock validation loop and the zero-qty filter in settle() dereferenced getPharmaceuticalBillItem() unconditionally, NPEing whenever such an item was present in the transfer issue. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix(pharmacy): prevent NPEs and over-issuing in transfer settlement
…from clinical queue When an OPD prescription is written in Prescriptions Given on the EMR visit page (emr/opd_visit.xhtml) and the user clicks Pharmacy Bill on the clinical queue (clinical/clinical_queue.xhtml), the prescribed medicines now load automatically onto the retail sale bill - matching the inward flow where admission prescriptions are converted to pharmacy dispensing notes. Root cause: PracticeBookingController.issuePharmacyBill() loaded the encounter medicines and iterated them with an empty loop body - the conversion to bill items was never implemented. Only the patient, referring doctor and prescription text (as a comment) reached the retail sale page. Changes: - PharmacySaleController.addBillItemsFromEncounterMedicines(): converts each VisitMedicine prescription to a bill item. Resolves dispensable item and quantity via PrescriptionToItemService (qty defaults to 1 when the prescription is incomplete so the pharmacist adjusts it on the page), auto-picks an earliest-expiry (FEFO) in-date stock batch from the logged-in user's department, resolving VMP/VTM prescriptions to candidate AMPs via PharmacyBean.resolveAmps(). - Items are added through the existing addBillItemSingleItem() so all retail-sale safeguards apply unchanged: expiry check, stock quantity check, duplicate batch check, UserStock locking and allergy check. - Dose, frequency, duration and comment are carried onto the bill item's prescription so bill descriptions and drug label printing work as in the inward discharge issue flow. - Medicines that cannot be auto-added (no stock, conversion failure, duplicate batch) are reported in a visible warning, never silently dropped. - PracticeBookingController.issuePharmacyBill(): replaced the empty placeholder loop with a call to the new conversion method. Mirrors PharmacySaleBhtController.prepareDischargeIssueFromPrescriptions (issue #21334) for the OPD retail sale path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ility Rework clinical/clinical_queue.xhtml to follow the project UI and accessibility-first guidelines: - Replace the h:panelGrid filter layout with a responsive Bootstrap row/col grid; labels bound to inputs via p:outputLabel for=... - Add stable ids to every actionable control (btnProcess, btnVisit, btnMarkPresent, btnMarkAbsent, btnEditVisit, btnViewVisit, btnOpdBill, btnPharmacyBill) and descriptive title attributes that include the patient name and queue number, so Playwright/automation can target individual rows. - Cap both speciality and doctor autocompletes with maxResults="20" and add placeholders. - Date/doctor filter changes now re-render the whole queue form (render=":queueForm") instead of a single resolved component id. - DataTables: add widgetVar and rowKey, pagination (25 rows, bottom, hidden when not needed), emptyMessage texts, and a row highlight for absent patients in the To Complete tab. - To Complete tab gains Age, Phone and a Waiting/Absent status badge column; both tab titles show live session counts. - Consistent icon set and button styling (success/info/danger/warning variants) across row actions; growl shows message details. JSF-only change - no Java modifications. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…o favourite medicines API - Add GET /clinical/favourite_medicines/entities/diagnoses to search ClinicalEntity records (Disease_or_Syndrome) for use as forItemName - Support type=FavouriteMedicine (default) / type=FavouriteDiagnosis on create, search, and by-id endpoints - Resolve forItemName to a diagnosis for FavouriteDiagnosis templates, with "did you mean" suggestions on mismatch - Register the new endpoint and capability in CapabilityStatementResource and the AI chat module listing Closes #21452
…ourite diagnosis/medicine, and channel report cashier summaries - FavouriteMedicineApiService: add fromKg/toKg weight range fields to templates; make age range optional when weight range is provided; validate weight ranges - PatientEncounterController: add method 4 (no age/weight filter) fallback so favourites load even when patient DOB/weight aren't recorded; validate a medicine is selected before 'Add Favourite'; fall back to FavouriteDiagnosis template directly when no separate FavouriteMedicine config exists - PastPatientEncounterController: detect legacy prescriptions persisted with null or VisitDocument type via DocumentTemplateType.Prescription, default new prescriptions to VisitPrescription type - PracticeBookingController: separate prescriptionHtml (rich text) from plain comment via htmlToPlainText(); clear stale prescription when switching encounters - ChannelReportTempController: add fetchBillsTotal, fetchCashiers, fetchUserRows, fetchDateRangeRows with WebUser/agency support for cashier-based channel reporting - FavouriteMedicineApi: serialize fromKg/toKg in API responses - PharmacyBean: fallback VMP lookup via direct VMP.VTM_ID when VirtualProductIngredient join table is unpopulated - Privileges/UserPrivilageController: add PharmacyItemNameEdit privilege - pharmacy_bill_retail_sale.xhtml: add toggleable prescription preview panel rendering prescriptionHtml - lab_vmp/vtm, store_atm/vtm XHTML: gate Add/Edit/Save buttons behind PharmacyItemNameEdit privilege - opd_visit.xhtml: process acMedicine + dose/frequency/duration fields when adding a favourite, so the current selection is captured Co-Authored-By: Claude <noreply@anthropic.com>
PR validation failed because local JNDI names (jdbc/coop, jdbc/rhAuditDS)
were committed in the branch history instead of the CI/CD placeholders
(${JDBC_DATASOURCE}, ${JDBC_AUDIT_DATASOURCE}).
Co-Authored-By: Claude <noreply@anthropic.com>
- ChannelReportTempController.java: guard b.getClass() calls at lines 251-259 behind a null check — consistent with the existing null guard at line 280 - clinical_queue.xhtml: add onclick=this.disabled=true on btnOpdBill and btnPharmacyBill to prevent accidental double-issue - clinical_queue.xhtml: bind pharmacySaleController.patient from bsc.bill.patient (selected row) rather than the stale practiceBookingController.opdVisit.patient Co-Authored-By: Claude <noreply@anthropic.com>
fix: discharge notification navigation, removal, filters, real-time push, and UI display - Navigate to admission profile when clicking discharge notifications (PatientRoom/PatientEncounter) - Allow removal of PatientRoom/PatientEncounter-based notifications - Fix NPE in cancel filter for discharge notifications (clear + filter methods) - Fix today filter date comparison (use midnight truncation instead of equals) - Add WebSocket listener to user_notifications.xhtml for real-time notification list updates - Add Room Discharge and Patient Discharge type badges to notification list Closes #21389 Closes #21390 Closes #21391 Co-Authored-By: Claude <noreply@anthropic.com> @
…ork filters, add clear/restore - removeUserNotification: PatientEncounter-based (clinical/final discharge) notifications silently returned without removing; now removable. Null-safe department check (avoids NPE on unmatched bill types) and sets retiredAt/retirer. - Filters reworked: previously pruned the already-loaded 20-item list in memory (compounding, not reversible, contradictory checkboxes). Now a fresh JPQL query per filter: Today Only, Seen (All/Unseen/Seen), Status (All/Pending/Completed), Cancelled Bills Only. - Clear button now clears exactly what is listed (filter first, then clear), with explicit confirmation and cleared count; previously it only acted on checked criteria and did nothing when none were checked. - New Show Cleared filter with per-row Restore button so cleared notifications remain retrievable; Clear is guarded while viewing cleared items. - Push refresh and row removal refill via the filter-aware query so active filters are preserved; filters reset when navigating to the page. Refs #21389 #21390 #21391 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…m guard
Replace `onclick="this.disabled=true;"` on btnOpdBill and btnPharmacyBill
with `onclick="if (!confirm('…')) return false;"` per the codebase UI
guidelines (developer_docs/ui/comprehensive-ui-guidelines.md §358–363).
Disabling the button synchronously in onclick before the form POST fires
excludes the button's name/value from the request, potentially breaking
JSF action dispatch on ajax=false PrimeFaces command buttons.
Co-Authored-By: Claude <noreply@anthropic.com>
feat(clinical): favourite diagnosis API — weight range support, no-filter fallbacks, and channel report summaries
- navigateToCurrentNotificationRequest: clicking Go now marks the notification retired (seen=true, retired=true, retireComments="Viewed") so it disappears from the active list and appears under Show Cleared. Also adds null guard on un.getNotification() to prevent NPE. - NotificationController: guard pr.getRoomFacilityCharge() before calling getName() to prevent NPE when a PatientRoom has no facility charge set. - user_notifications.xhtml: add timeZone="Asia/Colombo" to f:convertDateTime for consistent timestamp display regardless of JVM default timezone. Refs #21389 #21390 #21391 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fixes-21389-21390-21391 fix(notifications): restore discharge notification navigation, delete, and filter fixes
…pt, and audit logging (Closes #21257) - Fix unresponsive Discharge/Cancel buttons on inward_patient_room_details.xhtml by switching from ajax=false to AJAX with process="@this" update="@Form" - Fix patientRoom vs pR bug in RoomChangeController.dischargeWithCurrentTime() where indexOf used the List field instead of the method parameter - Add rendered toggle between Discharge and Cancel buttons based on room discharge state - Add success/error messages after discharge and cancel actions - Add current room listing to admission_profile.xhtml Room Management panel showing active PatientRoom/GuardianRoom with admitted time and View link - Add inward_room_discharge_receipt.xhtml receipt page with Print, Print Previous, and Inpatient Dashboard navigation - Add JS confirmation dialogs (p:confirm) for discharge and cancel actions - Add audit event logging (AuditEvent) for room discharge and cancel via AuditEventApplicationController - Fix cancel to properly clear dischargedAt field Co-Authored-By: Claude <noreply@anthropic.com>
…anagement actions (Closes #21257) - Add privilege checks to all Room Management buttons on admission_profile.xhtml (InwardRoomRoomChange, InwardRoomGurdianRoomChange, InwardRoomRoomOccupency) - Add new "Add New Room" and "Add Guardian Room" buttons to dashboard - Add audit event logging (AuditEvent) to all room operations: admitRoom, change, addNewRoom, changeGurdianRoom, discharge, remove, removeRoom, removeGuardianRoom - Add notification support for room admit, room change, and discharge cancel via new trigger types INWARD_PATIENT_ROOM_ADDED and INWARD_PATIENT_ROOM_CHANGED - Extend NotificationController with createInwardRoomAdmitNotifications, createInwardRoomChangeNotifications, createInwardRoomDischargeCancelNotifications Co-Authored-By: Claude <noreply@anthropic.com>
When the pharmacist clicks Issue on a BHT request, items are now automatically resolved to concrete AMPs with available stock instead of falling back to a null-stock placeholder that required full manual substitution. - Replace the empty Amp/Vmp/Ampp/Vmpp if-blocks with PharmacyBean.resolveAmps() which traverses the full VTM→VPI→VMP→AMP / ATM→AMP hierarchy - Tiered candidate selection: exact requested AMP first, then same-strength sibling AMP, then any available AMP under the same VMP/VTM - Strength-adjusted quantity for different-strength substitutes: ceil(issuableQty × requestedStrength / substituteStrength); null strength on either side is treated as equivalent (ratio = 1) - Add @transient fields autoSubstituted and requestedItemName to BillItem to carry substitution state through to the UI without a schema change - Visual markers on the issue page for auto-substituted rows: yellow background, orange exchange icon with tooltip, italic sub-line showing "Auto-sub: <original> → <dispensed>" - Null-stock placeholder rows still appear for items with no stock at all so the pharmacist can manually substitute via the existing dialog Closes #16132 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ceipt page
- Replace non-existent nameWithInitials with name() on WebUser
- Replace non-existent Person.nameOfAgeAndGender with Person.ageAsString
- Remove broken #{now} EL expression, use loggedUser.name for printed-by
- Remove redundant footer printed-by line
Co-Authored-By: Claude <noreply@anthropic.com>
…ement, timezone, button IDs - Fix duplicate audit event logging: set status/duration before single logAuditEvent call - Remove h:head script block outside ui:composition (would not render); inline window.print() - Add timeZone="Asia/Colombo" to all f:convertDateTime in receipt page and dashboard - Rename "Print Previous" to "Print Copy" since previous retrieval not implemented - Add stable id attributes to room-management buttons (btnAddNewRoom, btnAddGuardianRoom, btnRoomDetails, btnRoomChange, btnGuardianRoomChange) - Add JsfUtil error message for failed active room query instead of silent swallow Co-Authored-By: Claude <noreply@anthropic.com>
- Include VMP siblings in candidate list when requested item is an AMP so same-brand-out-of-stock falls through to substitute tiers instead of immediately producing a null-stock placeholder - Update prepareSubstitute() to use pharmacyBean.resolveAmps() instead of the instanceof Amp guard — manual substitute dialog now works for VMP/VTM placeholder rows too - Add escape="false" to the Font Awesome icon h:outputText so the glyph renders correctly instead of as escaped entity text Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ry failure - Use existing logger with Level.SEVERE and admission ID context - Surface recoverable error via JsfUtil.addErrorMessage instead of silent return Co-Authored-By: Claude <noreply@anthropic.com>
…tion Co-Authored-By: Claude <noreply@anthropic.com>
The substitute selection loop previously picked the first same-strength or fallback AMP with any available stock, which depended on the DB order from findAmpsForVmp() and could select a later-expiring brand ahead of an earlier-expiring one. getStockByQty returns batches ORDER BY dateOfExpire, so the first entry in the returned list is always the earliest-expiring batch for that AMP. The loop now tracks the earliest expiry per tier and replaces the current best when a candidate's first batch expires sooner, preserving FIFO across substitute brands. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-improvements feat(pharmacy): auto-convert BHT request items to AMPs on issue
No description provided.