Skip to content

Fix: missing meds when saving - LEGACY BUG#2261

Draft
LiamStanziani wants to merge 12 commits intohotfix/01232026from
bug/missing-meds-when-saving
Draft

Fix: missing meds when saving - LEGACY BUG#2261
LiamStanziani wants to merge 12 commits intohotfix/01232026from
bug/missing-meds-when-saving

Conversation

@LiamStanziani
Copy link
Collaborator

@LiamStanziani LiamStanziani commented Feb 3, 2026

In this PR, I have fixed:

  • Missing meds when saving the staged meds (happens with multiple Rx sessions overwriting each other)
  • General code upgrades (Encoding, error handling, bugs with stash reset when overwriting isn't an issue)

I have tested this by:

  • Creating medications, searching for medications, having multiple instances of Rx tabs open, Save & Print functionality

User description

Summary by Sourcery

Implement per-patient prescription session handling and multi-tab safety across the Rx workflow to keep medications tied to the correct patient and avoid losing staged prescriptions.

Bug Fixes:

  • Ensure prescriptions and drug lists remain associated with the correct patient when multiple Medications tabs or windows are open by keying Rx sessions per demographic.
  • Prevent loss of staged prescriptions after Save & Print by clearing the stash reliably on close and handling lightwindow closures safely.
  • Return clear 400 errors when an invalid or missing prescription identifier is submitted during drug updates, and improve logging for empty special instructions.

Enhancements:

  • Introduce RxSessionBean helpers and RxSessionFilter to manage per-patient Rx session beans while preserving legacy session key behavior.
  • Add a client-side rxSessionInterceptor that injects demographicNo into Rx-related AJAX calls, forms, and iframes for accurate server-side patient context.
  • Update Rx JSPs and messenger preview generation to propagate demographicNo through requests and use it for patient lookup instead of relying on shared session attributes.

Summary by cubic

Fixes medications disappearing or mixing between patients by adding per‑patient Rx session handling and multi‑tab safety across the prescription workflow. All Rx requests now carry demographicNo so the correct patient context is always used.

  • New Features

    • Added per‑patient RxSessionBean helpers (keyed by demographicNo) with legacy fallback.
    • Introduced RxSessionFilter to swap the correct patient bean per request; mapped for oscarRx pages, CaseManagementView.do, and messenger preview.
    • Added rxSessionInterceptor.js to add demographicNo to Prototype, jQuery, and fetch requests and Rx forms; updated Rx pages and iframes to include it.
  • Bug Fixes

    • Prescriptions and drug lists stay tied to the correct patient when multiple Medications tabs/windows are open.
    • Prevents lost prescriptions after Save & Print by clearing the stash on close (not when editing) and handling lightwindow closures safely.
    • Returns 400 with a clear message when an invalid or missing prescription identifier is submitted; simplified logging for empty special instructions.

Written for commit 13084e2. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Enhanced multi-tab prescription management—users can now simultaneously work with prescriptions for multiple patients across different browser tabs without experiencing session conflicts or data interference.
  • Bug Fixes

    • Improved error handling and validation throughout prescription workflows. Better error messages and graceful recovery for edge cases, including improved handling of missing or incomplete prescription data.

PR Type

Bug fix, Enhancement


Description

  • Implement per-patient RxSessionBean storage with demographicNo-based session keys to prevent medication loss across multiple patient tabs

  • Add RxSessionFilter servlet filter to swap correct patient bean per request, enabling multi-tab prescription safety

  • Introduce rxSessionInterceptor.js to inject demographicNo into AJAX calls, forms, and iframes for consistent patient context

  • Improve error handling in prescription workflows with clearer validation and error messages for missing/invalid data


Diagram Walkthrough

flowchart LR
  A["Request with demographicNo"] -->|RxSessionFilter| B["Lookup per-patient RxSessionBean"]
  B -->|Found| C["Swap into legacy session key"]
  B -->|Not found| D["Use fallback bean"]
  C -->|Available to| E["Actions & JSPs"]
  D -->|Available to| E
  F["rxSessionInterceptor.js"] -->|Injects demographicNo| G["AJAX calls & forms"]
  G -->|Propagates to| H["Server-side filter"]
  H -->|Ensures| I["Correct patient context"]
Loading

File Walkthrough

Relevant files
Enhancement
12 files
RxSessionBean.java
Add per-patient session key helpers and storage methods   
+81/-0   
RxSessionFilter.java
New servlet filter for multi-patient tab session isolation
+113/-0 
RxChoosePatient2Action.java
Update to use per-patient RxSessionBean retrieval               
+13/-4   
RxShowAllergy2Action.java
Refactor bean initialization with per-patient session support
+12/-12 
CaseManagementView2Action.java
Update RxSessionBean initialization with per-patient keys
+14/-4   
RxPrescriptionData.java
Simplify special instructions logging with clearer format
+4/-8     
rxSessionInterceptor.js
New AJAX interceptor to propagate demographicNo across requests
+108/-0 
ListDrugs.jsp
Enhance patient data retrieval with demographicNo parameter support
+28/-1   
SearchDrug3.jsp
Add rxSessionInterceptor and improve lightwindow close handling
+29/-7   
ViewScript.jsp
Include rxSessionInterceptor and add demographicNo to iframe/form
+11/-3   
ViewScript2.jsp
Add rxSessionInterceptor and implement stash clearing on close
+27/-3   
generatePreviewPDF.jsp
Update RxSessionBean initialization with per-patient session support
+12/-9   
Bug fix
1 files
RxWriteScript2Action.java
Add validation and error handling for missing prescriptions
+37/-4   
Configuration changes
1 files
web.xml
Register RxSessionFilter for Rx-related URL patterns         
+18/-1   
Formatting
1 files
struts.xml
Minor formatting adjustment to struts configuration           
+1/-0     

…eeded for fixes to this issue when reproducing the workflow
…ate as needed for fixes to this issue when reproducing the workflow"

This reverts commit 8d3b9b7.
…providers, will need to update to iron out bugs and less memory usage
…working fully, but works for most actions inside of the Medications module
…icNo and there was no per-patient session bean for the connected demographicNo, meaning a bean was never created in the first place
@LiamStanziani LiamStanziani self-assigned this Feb 3, 2026
@sourcery-ai
Copy link

sourcery-ai bot commented Feb 3, 2026

Reviewer's Guide

Implements per-patient Rx session management and multi-tab safety for prescriptions by keying RxSessionBean off demographicNo, introducing a servlet filter and client-side interceptor to propagate demographicNo across requests, and tightening error handling around the Rx stash and prescription updates.

Sequence diagram for multi-tab Rx request routing via RxSessionFilter and rxSessionInterceptor.js

sequenceDiagram
    actor Provider
    participant TabA as BrowserTab_A(demographicNo_101)
    participant TabB as BrowserTab_B(demographicNo_202)
    participant Interceptor as rxSessionInterceptor_js
    participant Filter as RxSessionFilter
    participant Session as HttpSession
    participant Bean101 as RxSessionBean_101
    participant Bean202 as RxSessionBean_202
    participant Action as Rx_action_or_JSP

    Provider->>TabA: Open Medications for patient 101
    TabA->>Interceptor: Load page scripts
    Interceptor-->>TabA: Attach demographicNo=101 to AJAX/forms

    Provider->>TabB: Open Medications for patient 202
    TabB->>Interceptor: Load page scripts
    Interceptor-->>TabB: Attach demographicNo=202 to AJAX/forms

    Note over TabA: Save prescription for patient 101
    TabA->>Filter: POST /oscarRx/... demographicNo=101
    Filter->>Session: lookup RxSessionBean_101 via RxSessionBean.getSessionKey
    alt bean exists
        Session-->>Filter: Bean101
    else create new
        Filter->>Session: create Bean101 and store under per-patient key
    end
    Filter->>Session: set legacy RxSessionBean = Bean101
    Filter->>Action: continue filterChain
    Action->>Session: getAttribute(RxSessionBean)
    Action-->>Action: work in context of patient 101

    Note over TabB: Save prescription for patient 202
    TabB->>Filter: POST /oscarRx/... demographicNo=202
    Filter->>Session: lookup RxSessionBean_202
    Session-->>Filter: Bean202
    Filter->>Session: set legacy RxSessionBean = Bean202
    Filter->>Action: continue filterChain
    Action->>Session: getAttribute(RxSessionBean)
    Action-->>Action: work in context of patient 202
Loading

Class diagram for updated RxSessionBean, RxSessionFilter, and RxWriteScript2Action

classDiagram

    class RxSessionBean {
        <<Serializable>>
        - Logger logger
        - static String SESSION_KEY_PREFIX
        - static String LEGACY_SESSION_KEY
        - String providerNo
        - int demographicNo
        + static String getSessionKey(int demographicNo)
        + static RxSessionBean getFromSession(HttpSession session, int demographicNo)
        + static RxSessionBean getFromSession(HttpServletRequest request, int demographicNo)
        + static void saveToSession(HttpSession session, RxSessionBean bean)
        + static void saveToSession(HttpServletRequest request, RxSessionBean bean)
        + String getProviderNo()
        + void setProviderNo(String providerNo)
        + int getDemographicNo()
        + void setDemographicNo(int demographicNo)
    }

    class RxSessionFilter {
        <<Filter>>
        - Logger logger
        - static String LEGACY_KEY
        + void init(FilterConfig filterConfig)
        + void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        + void destroy()
        - static int parseDemographicNo(String param)
    }

    class RxWriteScript2Action {
        + String updateDrug()
        + int getDemographicNo()
        + void setDemographicNo(String RHS)
    }

    class rxSessionInterceptor_js {
        <<JavaScript_module>>
        - String demoNo
        + void hasDemographicNo(params)
        + void addDemographicNo(params)
        + void processAllForms()
    }

    RxSessionFilter --> RxSessionBean : uses
    RxWriteScript2Action --> RxSessionBean : uses
    rxSessionInterceptor_js --> RxSessionFilter : supplies_demographicNo_param
    rxSessionInterceptor_js ..> RxSessionBean : indirect_support
Loading

File-Level Changes

Change Details Files
Add per-patient RxSessionBean helpers and server-side filter to select the correct patient context per request while preserving legacy session access.
  • Introduce session key helper methods and static get/save helpers on RxSessionBean, with legacy key fallback and migration.
  • Add RxSessionFilter servlet filter that selects the appropriate per-patient RxSessionBean based on demographicNo (from request or legacy bean) and maps it to the legacy session key.
  • Wire RxSessionFilter into web.xml for oscarRx endpoints, CaseManagementView.do, and messenger/generatePreviewPDF.jsp.
  • Update various actions (CaseManagementView2Action, RxShowAllergy2Action, RxChoosePatient2Action, messenger generatePreviewPDF.jsp) to use RxSessionBean.getFromSession/saveToSession instead of directly reading/writing the legacy session attribute.
src/main/java/ca/openosp/openo/prescript/pageUtil/RxSessionBean.java
src/main/java/ca/openosp/openo/prescript/pageUtil/RxSessionFilter.java
src/main/webapp/WEB-INF/web.xml
src/main/java/ca/openosp/openo/casemgmt/web/CaseManagementView2Action.java
src/main/java/ca/openosp/openo/prescript/pageUtil/RxShowAllergy2Action.java
src/main/java/ca/openosp/openo/prescript/pageUtil/RxChoosePatient2Action.java
src/main/webapp/messenger/generatePreviewPDF.jsp
Propagate demographicNo through Rx JSPs, forms, AJAX calls, and iframes, and add a client-side interceptor to enforce it for multi-tab safety.
  • Introduce rxSessionInterceptor.js to attach demographicNo to Prototype/jQuery AJAX calls and Rx-related form submissions, driven by a global currentDemographicNo variable.
  • Set currentDemographicNo from the RxSessionBean on key Rx JSPs (SearchDrug3.jsp, ViewScript.jsp, ViewScript2.jsp) and include rxSessionInterceptor.js.
  • Ensure demographicNo is passed as hidden form fields and query parameters in Rx workflows (e.g., searchDrug, clearPending, Preview/Preview2/ViewScript iframe URLs).
  • Update ListDrugs.jsp to prioritize demographicNo from request parameters, then RxSessionBean, then legacy session Patient attribute when resolving the patient context.
src/main/webapp/oscarRx/js/rxSessionInterceptor.js
src/main/webapp/oscarRx/SearchDrug3.jsp
src/main/webapp/oscarRx/ViewScript.jsp
src/main/webapp/oscarRx/ViewScript2.jsp
src/main/webapp/oscarRx/ListDrugs.jsp
Harden prescription stash handling and error reporting during update/print workflows and improve logging for special instructions.
  • In RxWriteScript2Action.updateDrug, validate randomId, return structured 400 errors when invalid or when the prescription is missing from the stash, and log clearer diagnostics.
  • Add client-side logic in SearchDrug3.jsp to track when the Rx lightwindow closes after Save & Print and clear the stash (deleteRx) in that case, to avoid lost-orphaned prescriptions.
  • In ViewScript2.jsp and ViewScript.jsp, ensure stash clearing endpoints are invoked when closing/returning, including a new resetStashAndClose helper.
  • Simplify RxPrescriptionData.Save() logging when special instructions are null or short, removing SQL-escaping-based logging and logging the effective length instead.
src/main/java/ca/openosp/openo/prescript/pageUtil/RxWriteScript2Action.java
src/main/webapp/oscarRx/SearchDrug3.jsp
src/main/webapp/oscarRx/ViewScript2.jsp
src/main/webapp/oscarRx/ViewScript.jsp
src/main/java/ca/openosp/openo/prescript/data/RxPrescriptionData.java

Assessment against linked issues

Issue Objective Addressed Explanation
#2237 Implement per-patient RxSessionBean handling (per-patient session key and corresponding usage updates) so multiple patients’ Rx pages can be open without overwriting each other or losing staged prescriptions.
#2237 Fix the NullPointerException in RxWriteScript2Action.updateDrug() by safely handling invalid or missing staged prescriptions (e.g., invalid randomId or stash entry not found) before accessing rx.getQuantity().
#2237 Correct the logic bug in RxShowAllergy2Action where the RxSessionBean reuse check used incorrect String comparison, ensuring the correct bean is used per patient/provider.

Possibly linked issues

  • #N/A: PR implements per-patient RxSessionBean keys, RxSessionFilter, and NPE handling that directly address the multi-tab prescription loss bug.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link

coderabbitai bot commented Feb 3, 2026

📝 Walkthrough

Walkthrough

This pull request refactors the prescription system's session management to support multi-patient/multi-tab scenarios by implementing per-demographic-number session keys instead of a single shared session attribute. A new servlet filter (RxSessionFilter) and utility methods in RxSessionBean manage dual-key storage for backward compatibility. Client-side changes propagate the demographicNo parameter through AJAX calls and forms via a new JavaScript interceptor. Several Java actions are updated to use the new session utilities, and multiple JSP pages are modified to pass and use demographicNo context. Additional defensive checks handle null prescriptions and validate parameters.

Changes

Cohort / File(s) Summary
Session Management Infrastructure
RxSessionBean.java, RxSessionFilter.java, web.xml
Added per-demographic session key utilities to RxSessionBean (getFromSession, saveToSession, getSessionKey) with fallback to legacy key for migration. Introduced new servlet filter RxSessionFilter to intercept requests, determine demographicNo, and populate legacy session key from per-patient bean. Registered filter in web.xml for /oscarRx/*, /CaseManagementView.do, and /messenger/generatePreviewPDF.jsp.
Java Action Classes
CaseManagementView2Action.java, RxChoosePatient2Action.java, RxShowAllergy2Action.java
Refactored bean retrieval to use RxSessionBean.getFromSession(request, demographicNoInt) and persistence via RxSessionBean.saveToSession(request, bean) instead of direct session attribute manipulation, enabling per-demographic bean storage.
Prescription Handling
RxPrescriptionData.java, RxWriteScript2Action.java
Simplified special field validation in RxPrescriptionData by removing escapeSql-based checks and using raw getSpecial() value. Updated RxWriteScript2Action.setDemographicNo signature from int to String with safe parsing, and added null-check with HTTP 400 error response for missing prescriptions in updateQty path.
Client-Side Session Support
rxSessionInterceptor.js, SearchDrug3.jsp, ViewScript.jsp, ViewScript2.jsp
Created new rxSessionInterceptor.js to auto-inject demographicNo parameter into AJAX requests (Prototype.js and jQuery) and form submissions. Updated JSP files to expose currentDemographicNo global variable, pass demographicNo in iframe URLs and form fields, and include the interceptor script.
Patient Data Retrieval
ListDrugs.jsp, generatePreviewPDF.jsp
Refactored patient data lookup to prefer demographicNo request parameter over session Patient, with fallback to session attribute. Updated to use RxSessionBean.getFromSession and saveToSession for per-demographic context management.
Configuration
struts.xml
Minor whitespace formatting change (blank line insertion).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • yingbull

Poem

🐰 Hops through sessions, patient by patient,
No more tangled tabs, the fix is quite latent,
Each demographic gets its own little space,
AJAX now knows its rightful place,
Filters and beans in harmony dance,
Multi-tab chaos? Not a chance! 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix: missing meds when saving - LEGACY BUG' accurately describes the core objective of the PR, which addresses a bug where medications were being lost or mixed between patients when saving prescriptions with multiple open tabs/windows.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bug/missing-meds-when-saving

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @LiamStanziani, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the prescription management module by introducing a comprehensive per-patient session handling mechanism. This architectural improvement addresses data integrity concerns and improves the user experience by allowing the simultaneous management of prescriptions for multiple patients across different browser tabs. The solution integrates both server-side session filtering and client-side JavaScript interception to ensure accurate and consistent data handling.

Highlights

  • Multi-Patient Prescription Tab Support: Implemented a robust per-patient session management system for prescription data, enabling users to open and manage prescriptions for multiple patients in separate browser tabs without data cross-contamination.
  • New RxSessionFilter for Context Management: Introduced a new servlet filter, RxSessionFilter, which dynamically manages the RxSessionBean in the HTTP session. This ensures that the correct patient's prescription data is always active for relevant requests, providing seamless context switching.
  • Enhanced RxSessionBean Functionality: Added static helper methods (getFromSession, saveToSession) to RxSessionBean for consistent and safe retrieval and storage of patient-specific prescription data. This includes logic for backward compatibility, migrating legacy session beans to the new per-patient key.
  • Client-Side AJAX and Form Interception: Developed a new JavaScript file, rxSessionInterceptor.js, which automatically injects the patient's demographic number into Prototype.js and jQuery AJAX requests, as well as relevant form submissions. This client-side change works in conjunction with the server-side filter to maintain patient context.
  • Improved Prescription Saving Robustness: Added a null check when retrieving prescriptions from the stash in RxWriteScript2Action.java. This prevents potential errors and provides better feedback to the user if a prescription is unexpectedly missing during the saving process.
Changelog
  • src/main/java/ca/openosp/openo/casemgmt/web/CaseManagementView2Action.java
    • Updated RxSessionBean initialization to utilize new per-patient session management methods (getFromSession, saveToSession).
  • src/main/java/ca/openosp/openo/prescript/data/RxPrescriptionData.java
    • Removed unused StringEscapeUtils import.
    • Refactored logging for 'drug special' values, removing SQL escaping and improving clarity.
  • src/main/java/ca/openosp/openo/prescript/pageUtil/RxChoosePatient2Action.java
    • Modified RxSessionBean setup to leverage per-patient session keys for multi-patient tab support.
  • src/main/java/ca/openosp/openo/prescript/pageUtil/RxSessionBean.java
    • Added static methods getSessionKey, getFromSession, and saveToSession for per-patient session management.
    • Implemented logic within getFromSession for migrating legacy session beans to the new per-patient key.
    • Added HttpServletRequest and HttpSession imports.
  • src/main/java/ca/openosp/openo/prescript/pageUtil/RxSessionFilter.java
    • Added new file: Implemented a servlet filter to manage RxSessionBean context per request, supporting multi-patient tabs.
  • src/main/java/ca/openosp/openo/prescript/pageUtil/RxShowAllergy2Action.java
    • Refactored RxSessionBean handling to utilize the new per-patient session methods, simplifying bean retrieval and creation logic.
  • src/main/java/ca/openosp/openo/prescript/pageUtil/RxWriteScript2Action.java
    • Added a null check for RxPrescriptionData.Prescription retrieval from the stash to prevent NullPointerException and provide client-side error feedback.
    • Updated setDemographicNo method signature to accept String and included robust parsing logic with error handling.
  • src/main/webapp/WEB-INF/struts.xml
    • Minor formatting change (added a blank line).
  • src/main/webapp/WEB-INF/web.xml
    • Registered the new RxSessionFilter and mapped it to relevant URL patterns (/oscarRx/*, /CaseManagementView.do, /messenger/generatePreviewPDF.jsp) to ensure proper application.
  • src/main/webapp/messenger/generatePreviewPDF.jsp
    • Updated RxSessionBean and patient data setup to use the new per-patient session methods for consistent context.
  • src/main/webapp/oscarRx/ListDrugs.jsp
    • Modified patient data retrieval logic to prioritize demographicNo from request parameters, falling back to the RxSessionBean and then legacy session attributes, to support multi-tab patient views.
  • src/main/webapp/oscarRx/SearchDrug3.jsp
    • Integrated rxSessionInterceptor.js for client-side demographic number injection into AJAX calls and forms.
    • Updated form input property attribute to name for demographicNo to ensure correct form submission.
    • Introduced clearStashOnLightwindowClose flag and handleLightwindowClose function to manage clearing the prescription stash when the lightwindow closes, particularly after 'Save & Print'.
    • Appended demographicNo to ViewScript2.jsp URLs for maintaining patient context.
  • src/main/webapp/oscarRx/ViewScript.jsp
    • Integrated rxSessionInterceptor.js for client-side demographic number injection.
    • Added demographicNo to the Preview.jsp iframe source and the clearPending form.
    • Modified the 'Back to Oscar' button's onClick event to call resetStash() before closing.
  • src/main/webapp/oscarRx/ViewScript2.jsp
    • Integrated rxSessionInterceptor.js for client-side demographic number injection.
    • Introduced resetStashAndClose() function to clear both the stash and re-Rx drug list, then close the window.
    • Added demographicNo to the Preview2.jsp iframe source and the clearPending form.
    • Modified the 'Back to Oscar' button's onClick event to use resetStashAndClose().
  • src/main/webapp/oscarRx/js/rxSessionInterceptor.js
    • Added new file: JavaScript utility to automatically add demographicNo to Prototype.js and jQuery AJAX requests, as well as forms, for Rx-related pages to ensure patient context is always passed.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant and well-designed enhancement for handling prescription sessions on a per-patient basis, which is crucial for supporting multi-tab workflows. The use of a servlet filter, session bean helpers with backward compatibility, and a client-side AJAX interceptor is a solid approach. My review has identified a recurring high-severity issue: several new code paths parse request parameters into integers without proper error handling for NumberFormatException. This could lead to unhandled exceptions and server errors. I have provided specific comments and code suggestions to address this by adding robust try-catch blocks.

@LiamStanziani
Copy link
Collaborator Author

@copilot review

Copy link

Copilot AI commented Feb 3, 2026

@LiamStanziani I've opened a new pull request, #2262, to work on those changes. Once the pull request is ready, I'll request review from you.

@LiamStanziani
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 3, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🤖 Fix all issues with AI agents
In `@src/main/java/ca/openosp/openo/casemgmt/web/CaseManagementView2Action.java`:
- Around line 458-467: The code currently calls Integer.parseInt(demoNo) without
handling NumberFormatException; wrap the parse in a safe guard (e.g., check
demoNo for null/empty and digits or use a try/catch) inside the method in
CaseManagementView2Action before calling
RxSessionBean.getFromSession/saveToSession; on parse failure, either use a
sensible default demographicNo (e.g., 0) or skip creating/saving the
RxSessionBean and log a warning with the invalid demoNo so the request does not
throw a 500. Ensure you still call RxSessionBean.setProviderNo and save only
when a valid demographicNo was obtained.

In `@src/main/java/ca/openosp/openo/prescript/data/RxPrescriptionData.java`:
- Around line 1650-1654: The code calls getSpecial() which may log raw PHI;
instead read the underlying field directly in RxPrescriptionData (avoid invoking
getSpecial()) or add a non-logging accessor (e.g., getSpecialRaw()) and use that
to compute length for the warning; ensure the log message only includes the
length (no content) and if you must log any sanitized value use Encode.forJava()
to prevent log injection.

In
`@src/main/java/ca/openosp/openo/prescript/pageUtil/RxChoosePatient2Action.java`:
- Around line 85-96: The code calls Integer.parseInt(this.getDemographicNo())
without validating the input, which will throw if getDemographicNo() is null or
not numeric; update the method in RxChoosePatient2Action to first check that
this.getDemographicNo() is non-null and matches a numeric pattern (or try
parsing inside a try/catch), and if invalid return the appropriate error view
("error.html" or "Logout"); then only call Integer.parseInt and proceed to use
RxSessionBean.getFromSession(request, demographicNoInt) and
RxSessionBean.saveToSession(request, bean) when the demographicNo is valid.

In `@src/main/java/ca/openosp/openo/prescript/pageUtil/RxSessionBean.java`:
- Around line 47-112: The public session helper methods (getSessionKey,
getFromSession(HttpSession,int), getFromSession(HttpServletRequest,int),
saveToSession(HttpSession,RxSessionBean),
saveToSession(HttpServletRequest,RxSessionBean)) need complete JavaDoc: add
explicit `@param` entries specifying types (e.g., "@param demographicNo int",
"@param session HttpSession", "@param request HttpServletRequest", "@param bean
RxSessionBean"), add precise `@return` tags (e.g., "@return RxSessionBean or null"
or "@return String" for getSessionKey), add `@throws` tags for any
unchecked/checked exceptions the methods may surface (or explicitly state none),
and add `@since` tags populated from git history for each public method; update
the JavaDoc blocks above each referenced method accordingly.

In `@src/main/java/ca/openosp/openo/prescript/pageUtil/RxSessionFilter.java`:
- Around line 36-95: Add comprehensive JavaDoc to the public class
RxSessionFilter and its public methods init(FilterConfig),
doFilter(ServletRequest, ServletResponse, FilterChain), and destroy(): document
each `@param` with exact types (e.g., `@param` filterConfig
javax.servlet.FilterConfig), include `@throws` tags for
ServletException/IOException where applicable, add `@since` tags using the
commit/date when the class was introduced, and include a brief description of
behavior and return type (where applicable — void) for each method; ensure the
class-level JavaDoc also lists the purpose and the LEGACY_KEY behavior
referenced in doFilter.
- Around line 81-83: The logger.warn in RxSessionFilter currently logs
demographicNo (PHI) and the raw request URI; remove demographicNo from the log
message and sanitize the URI before logging. Update the logging call in
RxSessionFilter (the logger.warn that references demographicNo and
request.getRequestURI()) to omit demographicNo entirely and pass the request URI
through a safe sanitizer such as Encode.forJava() (or your project's approved
sanitizer) before including it in the log message to prevent log injection and
PHI exposure. Ensure only non-PHI, sanitized values are logged and adjust the
log text accordingly.

In `@src/main/java/ca/openosp/openo/prescript/pageUtil/RxShowAllergy2Action.java`:
- Around line 175-190: The code in RxShowAllergy2Action currently uses
Integer.parseInt(demo_no) which can throw NumberFormatException; update the
handler to validate demo_no (e.g., check null/empty and digits or use try/catch
around Integer.parseInt) and on invalid input set the response status to
SC_BAD_REQUEST (or return "failure") instead of allowing a 500; preserve the
existing RxSessionBean creation and RxSessionBean.saveToSession logic when the
parse succeeds, and ensure any NumberFormatException branch skips bean creation
and exits early with the bad-request response.

In `@src/main/java/ca/openosp/openo/prescript/pageUtil/RxWriteScript2Action.java`:
- Around line 813-821: The logger.error call in RxWriteScript2Action logs the
user-supplied randomId directly; sanitize it using Encode.forJava(randomId) and
switch to parameterized logging (e.g., logger.error("Prescription not found in
stash for randomId: {}", Encode.forJava(randomId))) so the input is encoded and
not concatenated into the message; update the logger invocation around the
null-check for rx and add the necessary import for Encode.
- Around line 1563-1570: Add comprehensive JavaDoc to the public setter
setDemographicNo in class RxWriteScript2Action describing its purpose, include a
typed `@param` tag (e.g., `@param` RHS String - numeric string to parse into an
int), a `@since` tag with the appropriate git-history date, and document behavior
on invalid input (note that NumberFormatException is caught and the default
remains 0) — include an `@throws` tag only if the method is changed to propagate
exceptions; otherwise explicitly state in the JavaDoc that invalid numeric input
is swallowed and no exception is thrown.

In `@src/main/webapp/oscarRx/js/rxSessionInterceptor.js`:
- Around line 21-38: The demographic injection functions must handle FormData
and URLSearchParams explicitly: in addDemographicNo (and correspondingly
hasDemographicNo if needed) detect instances of FormData and URLSearchParams
(e.g., params instanceof FormData / params instanceof URLSearchParams) and use
their APIs instead of treating them as plain objects—call
formData.append('demographicNo', demoNo) for FormData and
urlSearchParams.set('demographicNo', demoNo) for URLSearchParams; preserve
existing behavior for strings (append as query param) and plain objects (assign
params.demographicNo = demoNo), and ensure hasDemographicNo checks FormData.has
and URLSearchParams.has or inspects string/query properly so injection isn't
skipped.

In `@src/main/webapp/oscarRx/SearchDrug3.jsp`:
- Around line 238-243: The demographicNo is being output unencoded in
JavaScript, HTML attributes, and URI contexts; add the import
org.owasp.encoder.Encode at the top and replace direct uses of
rxSessionBean.getDemographicNo() with context-appropriate encoding: wrap it with
Encode.forJavaScript(...) when assigning the JavaScript variable
currentDemographicNo, use Encode.forHtmlAttribute(...) where demographicNo is
inserted into hidden input value attributes, and use Encode.forUriComponent(...)
where demographicNo is concatenated into query parameter strings (search for
currentDemographicNo, the hidden input value usages, and the methods/builders
that append demographicNo to query URIs to update each).

In `@src/main/webapp/oscarRx/ViewScript.jsp`:
- Around line 322-325: The RxClearPendingForm POST is missing the CSRF token;
update the form named "RxClearPendingForm" (action "/oscarRx/clearPending.do")
to include a hidden input using the Spring CSRF parameters: add an input with
name="${_csrf.parameterName}" and value="${_csrf.token}" alongside the existing
hidden fields (e.g., near the demographicNo input) so the form submits the CSRF
token.
- Around line 171-176: Import the OWASP encoder by adding <%@ page
import="org.owasp.encoder.Encode" %> and then apply context-appropriate encoding
to demographicNo: replace the unencoded embed that sets the JavaScript variable
currentDemographicNo (variable currentDemographicNo in ViewScript.jsp) with
Encode.forJavaScript(bean.getDemographicNo()), encode the URL parameter usage
(the code that builds the src or query param around rxSessionInterceptor.js or
other URL at line ~317) with Encode.forUriComponent(bean.getDemographicNo()),
and encode any HTML attribute usage (the attribute at ~324) with
Encode.forHtmlAttribute(bean.getDemographicNo()); finally, add the missing CSRF
hidden input to the POST form (the form around line ~322) using <input
type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> so the form
includes the CSRF token.

In `@src/main/webapp/oscarRx/ViewScript2.jsp`:
- Around line 675-678: The RxClearPendingForm POST form (form name
"RxClearPendingForm", action "/oscarRx/clearPending.do") is missing a CSRF
token; add a hidden input using the application's CSRF attributes (e.g. <input
type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> or the
project's equivalent) inside the form (near the other hidden inputs like
"action" and "demographicNo") so the server can validate the token on
submission.
- Around line 238-243: currentDemographicNo is being output unencoded in
multiple contexts causing XSS/CSRF issues; replace all direct uses of
bean.getDemographicNo() with context-appropriate OWASP encoders: wrap the value
in Encode.forJavaScript() when assigning the JavaScript variable
currentDemographicNo (the script block that sets var currentDemographicNo), use
Encode.forUriComponent() when appending demographicNo into URLs (the places
building query params), and use Encode.forHtmlAttribute() when inserting
demographicNo into HTML attributes; update the other occurrences of
demographicNo (the remaining usages in this JSP) similarly to their contexts,
and add a hidden CSRF token input to the POST form (include the server-side CSRF
token variable provided by the app framework) so the form submits the CSRF
token.
🧹 Nitpick comments (4)
src/main/webapp/oscarRx/js/rxSessionInterceptor.js (1)

41-68: Use camelCase for local aliases.
Rename OriginalRequest/OriginalUpdater to camelCase to match JS naming conventions.

♻️ Suggested refactor
-        var OriginalRequest = Ajax.Request;
+        var originalRequest = Ajax.Request;
         Ajax.Request = function(url, options) {
@@
-            return new OriginalRequest(url, options);
+            return new originalRequest(url, options);
         };
-        Ajax.Request.prototype = OriginalRequest.prototype;
+        Ajax.Request.prototype = originalRequest.prototype;
@@
-        var OriginalUpdater = Ajax.Updater;
+        var originalUpdater = Ajax.Updater;
         Ajax.Updater = function(container, url, options) {
@@
-            return new OriginalUpdater(container, url, options);
+            return new originalUpdater(container, url, options);
         };
-        Ajax.Updater.prototype = OriginalUpdater.prototype;
+        Ajax.Updater.prototype = originalUpdater.prototype;
As per coding guidelines: **/*.{java,js,ts,tsx}: Use camelCase for variable and method names (e.g., `ticklerManager`, `isPatientActive()`, `getLastUpdateDate()`).
src/main/webapp/oscarRx/ListDrugs.jsp (1)

69-95: Move demographicNo parsing out of JSP scriptlets.
Consider resolving the demographic context in a controller and passing it via request attributes/JSTL to keep the view logic minimal.

As per coding guidelines: JSP views should use JSTL tags and avoid Java scriptlets - use request.getAttribute() for passing data.

src/main/webapp/messenger/generatePreviewPDF.jsp (2)

410-423: Rename Rxbean to camelCase rxBean.
Aligns with Java naming conventions.

♻️ Suggested refactor
-                                            RxSessionBean Rxbean = RxSessionBean.getFromSession(request.getSession(), demoNoInt);
+                                            RxSessionBean rxBean = RxSessionBean.getFromSession(request.getSession(), demoNoInt);
 
-                                            if (Rxbean == null) {
-                                                Rxbean = new RxSessionBean();
+                                            if (rxBean == null) {
+                                                rxBean = new RxSessionBean();
                                             }
@@
-                                            Rxbean.setProviderNo((String) request.getSession().getAttribute("user"));
-                                            Rxbean.setDemographicNo(demoNoInt);
+                                            rxBean.setProviderNo((String) request.getSession().getAttribute("user"));
+                                            rxBean.setDemographicNo(demoNoInt);
@@
-                                            RxSessionBean.saveToSession(request.getSession(), Rxbean);
+                                            RxSessionBean.saveToSession(request.getSession(), rxBean);
As per coding guidelines: **/*.{java,js,ts,tsx}: Use camelCase for variable and method names (e.g., `ticklerManager`, `isPatientActive()`, `getLastUpdateDate()`).

410-423: Move Rx session setup out of JSP scriptlets.
Prefer resolving RxSessionBean in a controller and passing it via request attributes/JSTL.

As per coding guidelines: JSP views should use JSTL tags and avoid Java scriptlets - use request.getAttribute() for passing data.

@LiamStanziani LiamStanziani marked this pull request as ready for review February 3, 2026 16:03
@qodo-code-review
Copy link

qodo-code-review bot commented Feb 3, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Authorization bypass

Description: The filter trusts the client-supplied demographicNo request parameter to select/swap the
active RxSessionBean without verifying the logged-in user's authorization for that
patient, which could enable an attacker to force context-switching to another patient's Rx
session and potentially access/modify another patient's prescription workflow if
downstream code relies on this bean for patient context.
RxSessionFilter.java [57-89]

Referred Code
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpSession session = request.getSession(false);

    if (session != null) {
        int demographicNo = parseDemographicNo(request.getParameter("demographicNo"));
        boolean usedFallback = false;

        if (demographicNo <= 0) {
            RxSessionBean currentBean = (RxSessionBean) session.getAttribute(LEGACY_KEY);
            if (currentBean != null) {
                demographicNo = currentBean.getDemographicNo();
                usedFallback = true;
            }
        }

        if (demographicNo > 0) {
            RxSessionBean perPatientBean = (RxSessionBean) session.getAttribute(
                    RxSessionBean.getSessionKey(demographicNo));


 ... (clipped 12 lines)
SQL injection

Description: The PR removes SQL escaping of special (previously
StringEscapeUtils.escapeSql(this.getSpecial())) and now uses the raw this.special, which
could reintroduce SQL injection risk if any downstream persistence/query code interpolates
this field into SQL rather than using parameterized statements.
RxPrescriptionData.java [1650-1657]

Referred Code
if (this.special == null || this.special.length() < 6) {
    logger.warn("drug special appears to be null or empty (length={})",
            this.special == null ? 0 : this.special.length());
}

DrugDao dao = SpringUtils.getBean(DrugDao.class);
Drug drug = new Drug();
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status:
Non-descriptive parameter: The new setter setDemographicNo(String RHS) uses the identifier RHS, which is not
self-documenting and obscures the meaning of the incoming demographic number value.

Referred Code
/**
 * Sets the demographic number from a String value, as provided by Struts2
 * parameter binding from the {@code demographicNo} request parameter.
 *
 * @param RHS String the demographic number to parse; ignored if null, empty, or non-numeric
 * @since 2026-01-30
 */
public void setDemographicNo(String RHS) {
    if (RHS != null && !RHS.isEmpty()) {
        try {
            this.demographicNo = Integer.parseInt(RHS);
        } catch (NumberFormatException e) {
            // Keep default value (0) if parse fails
        }
    }
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Swallowed parse error: The new setDemographicNo(String RHS) silently ignores non-numeric input without returning
an error or logging, which can lead to confusing downstream behavior with the default
demographicNo value.

Referred Code
public void setDemographicNo(String RHS) {
    if (RHS != null && !RHS.isEmpty()) {
        try {
            this.demographicNo = Integer.parseInt(RHS);
        } catch (NumberFormatException e) {
            // Keep default value (0) if parse fails
        }
    }

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Limited audit coverage: The new per-patient Rx session switching and stash-related behaviors do not add an
auditable record tying patient/session swaps or prescription-stash operations to a user
and outcome, making reconstruction of Rx context changes difficult.

Referred Code
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpSession session = request.getSession(false);

    if (session != null) {
        int demographicNo = parseDemographicNo(request.getParameter("demographicNo"));
        boolean usedFallback = false;

        if (demographicNo <= 0) {
            RxSessionBean currentBean = (RxSessionBean) session.getAttribute(LEGACY_KEY);
            if (currentBean != null) {
                demographicNo = currentBean.getDemographicNo();
                usedFallback = true;
            }
        }

        if (demographicNo > 0) {
            RxSessionBean perPatientBean = (RxSessionBean) session.getAttribute(
                    RxSessionBean.getSessionKey(demographicNo));


 ... (clipped 12 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Potentially sensitive identifier: New error logs include request-supplied identifiers like randomId (even though encoded),
and without knowing local policy on logging patient/Rx workflow identifiers it is unclear
if this meets the project's PII/PHI logging restrictions.

Referred Code
    logger.error("Invalid randomId parameter: {}", Encode.forJava(randomId));
    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
    ObjectNode errorResponse = objectMapper.createObjectNode();
    errorResponse.put("error", "Invalid prescription identifier.");
    response.getOutputStream().write(errorResponse.toString().getBytes());
    return null;
}
RxPrescriptionData.Prescription rx = bean.getStashItem2(randomIdInt);
if (rx == null) {
    logger.error("Prescription not found in stash for randomId: {}. " +
                 "Session may have been reset or prescription was not properly staged.",
                 Encode.forJava(randomId));
    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Trust boundary unclear: The filter now trusts the client-provided demographicNo to select and swap the per-patient
RxSessionBean into the legacy key, and the diff does not show any authorization/ownership
validation that the request is permitted to operate on that patient context.

Referred Code
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpSession session = request.getSession(false);

    if (session != null) {
        int demographicNo = parseDemographicNo(request.getParameter("demographicNo"));
        boolean usedFallback = false;

        if (demographicNo <= 0) {
            RxSessionBean currentBean = (RxSessionBean) session.getAttribute(LEGACY_KEY);
            if (currentBean != null) {
                demographicNo = currentBean.getDemographicNo();
                usedFallback = true;
            }
        }

        if (demographicNo > 0) {
            RxSessionBean perPatientBean = (RxSessionBean) session.getAttribute(
                    RxSessionBean.getSessionKey(demographicNo));


 ... (clipped 9 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In RxWriteScript2Action.updateDrug, when returning the JSON error responses you should explicitly set the response Content-Type (e.g., application/json with UTF‑8) and use a consistent charset when writing bytes instead of relying on defaults.
  • The heuristics in rxSessionInterceptor.js for adding demographicNo (string checks for 'Rx' in form action and only checking for 'demographicNo=' in raw strings) are quite broad and could affect unrelated forms or miss encoded params; consider tightening the action matching and using proper querystring parsing instead of substring checks.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In RxWriteScript2Action.updateDrug, when returning the JSON error responses you should explicitly set the response Content-Type (e.g., application/json with UTF‑8) and use a consistent charset when writing bytes instead of relying on defaults.
- The heuristics in rxSessionInterceptor.js for adding demographicNo (string checks for 'Rx' in form action and only checking for 'demographicNo=' in raw strings) are quite broad and could affect unrelated forms or miss encoded params; consider tightening the action matching and using proper querystring parsing instead of substring checks.

## Individual Comments

### Comment 1
<location> `src/main/webapp/oscarRx/ViewScript.jsp:384-387` </location>
<code_context>
                                             <td><span><input type=button
                                                              value="<fmt:setBundle basename="oscarResources"/><fmt:message key="ViewScript.msgBackToOscar"/>"
                                                              class="ControlPushButton" style="width: 200px"
-                                                             onClick="javascript:clearPending('close');"/></span></td>
+                                                             onClick="resetStash();javascript:clearPending('close');"/></span></td>
                                         </tr>

</code_context>

<issue_to_address>
**issue (bug_risk):** Calling resetStash() here will throw a JS error because it is not defined on this page.

On ViewScript2.jsp `resetStash()` is defined locally, but on ViewScript.jsp it isn’t present and there’s no script import that provides it. As written, clicking “Back to Oscar” will throw `ReferenceError: resetStash is not defined`. To keep the stash-clearing behavior, either define `resetStash` on this page, move it into a shared JS file, or reuse an existing function that already clears the stash.
</issue_to_address>

### Comment 2
<location> `src/main/java/ca/openosp/openo/prescript/pageUtil/RxWriteScript2Action.java:811-819` </location>
<code_context>
                 String randomId = request.getParameter("randomId");
-                RxPrescriptionData.Prescription rx = bean.getStashItem2(Integer.parseInt(randomId));
-                // get prescript from randomId
+                int randomIdInt;
+                try {
+                    randomIdInt = Integer.parseInt(randomId);
+                } catch (NumberFormatException e) {
+                    logger.error("Invalid randomId parameter: {}", Encode.forJava(randomId));
+                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                    ObjectNode errorResponse = objectMapper.createObjectNode();
+                    errorResponse.put("error", "Invalid prescription identifier.");
+                    response.getOutputStream().write(errorResponse.toString().getBytes());
+                    return null;
+                }
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Error JSON responses should set content type and charset to avoid client-side parsing issues.

In the invalid `randomId` (and similar `rx == null`) branches you write a JSON error body to the `ServletOutputStream` without setting a `Content-Type`. This can prevent some clients or intermediaries from treating it as JSON and may introduce charset issues. Please set `response.setContentType("application/json;charset=UTF-8");` before writing the body and encode with `getBytes(StandardCharsets.UTF_8)` (or use a writer).

Suggested implementation:

```java
                    logger.error("Invalid randomId parameter: {}", Encode.forJava(randomId));
                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                    ObjectNode errorResponse = objectMapper.createObjectNode();
                    errorResponse.put("error", "Invalid prescription identifier.");
                    response.setContentType("application/json;charset=UTF-8");
                    response.getOutputStream().write(errorResponse.toString().getBytes(StandardCharsets.UTF_8));
                    return null;

```

```java
                    logger.error("Prescription not found in stash for randomId: {}. " +
                                 "Session may have been reset or prescription was not properly staged.",
                                 Encode.forJava(randomId));
                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                    ObjectNode errorResponse = objectMapper.createObjectNode();
                    errorResponse.put("error", "Prescription not found. Please refresh and try again.");
                    response.setContentType("application/json;charset=UTF-8");
                    response.getOutputStream().write(errorResponse.toString().getBytes(StandardCharsets.UTF_8));
                    return null;

```

1. Ensure `java.nio.charset.StandardCharsets` is imported at the top of `RxWriteScript2Action.java`, e.g.:
   `import java.nio.charset.StandardCharsets;`
2. If there are other JSON error branches in this class that write directly to the response without setting a content type or charset, apply the same `setContentType("application/json;charset=UTF-8")` and `getBytes(StandardCharsets.UTF_8)` pattern there for consistency.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@qodo-code-review
Copy link

qodo-code-review bot commented Feb 3, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent using stale session data
Suggestion Impact:The commit adds logic to remove the legacy session attribute when demographicNo is explicitly provided but no per-patient bean exists, preventing cross-patient leakage from a stale legacy bean. The conditional structure differs from the suggestion but implements the core safeguard.

code diff:

@@ -77,7 +77,11 @@
                         RxSessionBean.getSessionKey(demographicNo));
                 if (perPatientBean != null) {
                     session.setAttribute(LEGACY_KEY, perPatientBean);
-                } else if (usedFallback) {
+                } else if (!usedFallback) {
+                    // demographicNo was explicitly provided but no per-patient bean exists.
+                    // Clear the legacy key to prevent cross-patient leakage from a stale bean.
+                    session.removeAttribute(LEGACY_KEY);
+                } else {

In RxSessionFilter, if a per-patient bean is not found for the requested
demographicNo, remove the legacy session bean to prevent using stale data from
another patient.

src/main/java/ca/openosp/openo/prescript/pageUtil/RxSessionFilter.java [75-85]

 if (demographicNo > 0) {
     RxSessionBean perPatientBean = (RxSessionBean) session.getAttribute(
             RxSessionBean.getSessionKey(demographicNo));
     if (perPatientBean != null) {
         session.setAttribute(LEGACY_KEY, perPatientBean);
-    } else if (usedFallback) {
-        logger.warn("RxSessionFilter: No demographicNo param and no per-patient bean " +
-                "found for {}. Using legacy bean as-is.",
-                Encode.forJava(request.getRequestURI()));
+    } else {
+        // No per-patient bean found for the requested demographic.
+        // Remove the legacy bean to prevent using a bean for the wrong patient.
+        session.removeAttribute(LEGACY_KEY);
+        if (usedFallback) {
+            logger.warn("RxSessionFilter: No demographicNo param and no per-patient bean " +
+                    "found for {}. Using legacy bean as-is.",
+                    Encode.forJava(request.getRequestURI()));
+        }
     }
 }

[Suggestion processed]

Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies a data cross-contamination bug in the new RxSessionFilter logic, where a stale session bean could be used for the wrong patient. Applying this fix is critical for the correctness of the multi-tab feature introduced in this PR.

Medium
General
Improve demographicNo existence check

In hasDemographicNo, use the .has() method to correctly check for the existence
of the demographicNo parameter in URLSearchParams and FormData objects.

src/main/webapp/oscarRx/js/rxSessionInterceptor.js [21-26]

 function hasDemographicNo(params) {
     if (!params) return false;
-    if (typeof params === 'string') return params.indexOf('demographicNo=') !== -1;
-    if (typeof params === 'object') return 'demographicNo' in params;
+    if (typeof params === 'string') {
+        return params.indexOf('demographicNo=') !== -1;
+    }
+    if (params instanceof URLSearchParams || params instanceof FormData) {
+        return params.has('demographicNo');
+    }
+    if (typeof params === 'object') {
+        return 'demographicNo' in params;
+    }
     return false;
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a bug in the new JavaScript interceptor where checking for the existence of demographicNo would fail for FormData and URLSearchParams objects. The fix is important for the correct functioning of the interceptor.

Medium
Enforce UTF-8 on JSON output
Suggestion Impact:The commit updated JSON error responses to encode bytes using StandardCharsets.UTF_8 and added an explicit UTF-8 JSON content type (application/json;charset=UTF-8), improving charset handling for the response output.

code diff:

+import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -814,9 +815,10 @@
                 } catch (NumberFormatException e) {
                     logger.error("Invalid randomId parameter: {}", Encode.forJava(randomId));
                     response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                    response.setContentType("application/json;charset=UTF-8");
                     ObjectNode errorResponse = objectMapper.createObjectNode();
                     errorResponse.put("error", "Invalid prescription identifier.");
-                    response.getOutputStream().write(errorResponse.toString().getBytes());
+                    response.getOutputStream().write(errorResponse.toString().getBytes(StandardCharsets.UTF_8));
                     return null;
                 }
                 RxPrescriptionData.Prescription rx = bean.getStashItem2(randomIdInt);
@@ -825,9 +827,10 @@
                                  "Session may have been reset or prescription was not properly staged.",
                                  Encode.forJava(randomId));
                     response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                    response.setContentType("application/json;charset=UTF-8");
                     ObjectNode errorResponse = objectMapper.createObjectNode();
                     errorResponse.put("error", "Prescription not found. Please refresh and try again.");
-                    response.getOutputStream().write(errorResponse.toString().getBytes());
+                    response.getOutputStream().write(errorResponse.toString().getBytes(StandardCharsets.UTF_8));
                     return null;

Explicitly set the character encoding to UTF-8 for the JSON error response and
use StandardCharsets.UTF_8 when converting the string to bytes.

src/main/java/ca/openosp/openo/prescript/pageUtil/RxWriteScript2Action.java [817-819]

 ObjectNode errorResponse = objectMapper.createObjectNode();
 errorResponse.put("error", "Invalid prescription identifier.");
-response.getOutputStream().write(errorResponse.toString().getBytes());
+response.setCharacterEncoding("UTF-8");
+byte[] jsonBytes = errorResponse.toString().getBytes(StandardCharsets.UTF_8);
+response.getOutputStream().write(jsonBytes);

[Suggestion processed]

Suggestion importance[1-10]: 5

__

Why: This is a valid code quality improvement that makes the application more robust by explicitly setting the character encoding to UTF-8 for JSON responses, preventing potential character display issues.

Low
Log invalid demographic number parameters

In the setDemographicNo method, log a warning when a NumberFormatException
occurs during parsing to improve debugging of malformed requests.

src/main/java/ca/openosp/openo/prescript/pageUtil/RxWriteScript2Action.java [1581-1589]

 public void setDemographicNo(String RHS) {
     if (RHS != null && !RHS.isEmpty()) {
         try {
             this.demographicNo = Integer.parseInt(RHS);
         } catch (NumberFormatException e) {
-            // Keep default value (0) if parse fails
+            // Keep default value (0) if parse fails, but log the issue.
+            logger.warn("Invalid non-numeric demographicNo parameter received: {}", Encode.forJava(RHS), e);
         }
     }
 }
  • Apply / Chat
Suggestion importance[1-10]: 4

__

Why: The suggestion improves code quality by adding logging for silently-handled exceptions, which aids in debugging. It's a good practice but has a moderate impact on the overall functionality.

Low
  • Update

@claude
Copy link

claude bot commented Feb 3, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 15 files

Confidence score: 3/5

  • Multi-tab sessions can submit the wrong patient ID because src/main/webapp/oscarRx/SearchDrug3.jsp still uses the legacy session patient object instead of the per-tab RxSessionBean demographicNo.
  • src/main/java/ca/openosp/openo/prescript/pageUtil/RxSessionFilter.java can leave a stale legacy bean when demographicNo is provided but no per-patient bean exists, risking cross-patient data bleed.
  • These are concrete, user-facing behaviors around patient context and navigation, so there’s some regression risk despite otherwise localized changes.
  • Pay close attention to src/main/webapp/oscarRx/SearchDrug3.jsp, src/main/java/ca/openosp/openo/prescript/pageUtil/RxSessionFilter.java, src/main/webapp/oscarRx/ViewScript.jsp - patient context isolation and client-side navigation errors.
Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/main/webapp/oscarRx/SearchDrug3.jsp">

<violation number="1" location="src/main/webapp/oscarRx/SearchDrug3.jsp:796">
P1: This hidden input still uses the session `patient` object instead of the per-tab RxSessionBean demographicNo, so multi-tab sessions can submit the wrong patient ID. Use `demoNo` (from RxSessionBean) to keep requests tied to the correct tab.</violation>
</file>

<file name="src/main/java/ca/openosp/openo/prescript/pageUtil/RxSessionFilter.java">

<violation number="1" location="src/main/java/ca/openosp/openo/prescript/pageUtil/RxSessionFilter.java:78">
P1: When a demographicNo is provided but no per-patient bean exists, the legacy session bean is left untouched, so downstream code can still read a stale bean from a different patient. Clear the legacy key (or set it to null) in this case to avoid cross-patient leakage.</violation>
</file>

<file name="src/main/webapp/oscarRx/ViewScript.jsp">

<violation number="1" location="src/main/webapp/oscarRx/ViewScript.jsp:392">
P2: resetStash() is not defined for this page, so clicking “Back to Oscar” will throw a ReferenceError and can block clearPending from executing. Either define/resetStash for this page or guard the call.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@LiamStanziani LiamStanziani requested a review from D3V41 February 4, 2026 20:17
@D3V41
Copy link
Collaborator

D3V41 commented Feb 4, 2026

@LiamStanziani Reviewed it. Looks good.

@LiamStanziani
Copy link
Collaborator Author

Thanks for the review!

@LiamStanziani LiamStanziani changed the title Fix: missing meds when saving Fix: missing meds when saving - LEGACY BUG Feb 6, 2026
yingbull pushed a commit to carlos-emr/carlos that referenced this pull request Feb 10, 2026
Implements isolated session management for the prescription (Rx) module
to prevent medication data cross-contamination when providers have
multiple patient tabs open simultaneously.

Core changes:
- Add static helpers to RxSessionBean (getFromSession/saveToSession) for
  per-patient session keying using RxSessionBean_<demographicNo>
- Create RxSessionFilter servlet filter to swap correct per-patient bean
  into legacy "RxSessionBean" session key per request
- Create rxSessionInterceptor.js to inject demographicNo into AJAX calls
  and form submissions for Prototype.js, jQuery, and native forms
- Update all Rx Action classes to use new per-patient session API
- Add demographicNo propagation to ViewScript/ViewScript2 iframes and forms
- Improve error handling in RxWriteScript2Action for missing prescriptions
- Update ListDrugs.jsp to resolve patient from request parameter first

Adapted from openo-beta/open-o PR #2261 by LiamStanziani.
Original commits: 8d3b9b7..be7f6576 (squashed, path/namespace translated)
Upstream PR: openo-beta/Open-O#2261

Changes from upstream:
- File paths translated: ca/openosp/openo/ -> io/github/carlos_emr/carlos/
- Package names translated: ca.openosp.openo. -> io.github.carlos_emr.carlos.
- RxPrescriptionData.java changes already present in CARLOS (skipped)
- struts.xml whitespace-only change skipped
- Adapted to existing CARLOS codebase divergences

https://claude.ai/code/session_01Jg6sucQ6C6PgndyCrCHqs1
yingbull pushed a commit to carlos-emr/carlos that referenced this pull request Feb 25, 2026
…contamination

Port of openo-beta/open-o PR #2261 ("Fix: missing meds when saving - LEGACY BUG")
originally authored by LiamStanziani.

When multiple prescription tabs are open simultaneously for different patients,
a single shared "RxSessionBean" session attribute causes medications to disappear
or mix between patients. This fix introduces per-patient session keys
(RxSessionBean_<demographicNo>) with a servlet filter that swaps the correct bean
into the legacy key per request, maintaining backward compatibility.

Changes:
- RxSessionBean: Add static getFromSession/saveToSession methods with per-patient keys
- RxSessionFilter: New servlet filter for per-request bean swapping (web.xml mapped)
- rxSessionInterceptor.js: AJAX interceptor injecting demographicNo into all Rx requests
- CaseManagementView2Action, RxChoosePatient2Action, RxShowAllergy2Action: Use new
  session methods instead of direct session attribute access
- RxWriteScript2Action: Add error handling for randomId parsing, String demographicNo setter
- ListDrugs.jsp: Multi-tab patient lookup from request parameter
- SearchDrug3.jsp, ViewScript.jsp, ViewScript2.jsp: Include interceptor, add resetStash,
  pass demographicNo through forms and iframes
- generatePreviewPDF.jsp: Use getFromSession/saveToSession

Original PR: openo-beta/Open-O#2261
Original Author: LiamStanziani

https://claude.ai/code/session_01D1MB9CvCDC4XaJGCXgxtB3
@LiamStanziani
Copy link
Collaborator Author

See latest comment on this PR for an explanation as to why this is being set back to draft: MagentaHealth#75

@LiamStanziani LiamStanziani marked this pull request as draft March 11, 2026 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Staged prescriptions lost when opening Rx related pages for multiple patients

3 participants