Skip to content

fix: per-patient RxSessionBean isolation to prevent medication cross-…#479

Draft
yingbull wants to merge 1 commit into
developfrom
claude/port-pr-2261-carlos-U997n
Draft

fix: per-patient RxSessionBean isolation to prevent medication cross-…#479
yingbull wants to merge 1 commit into
developfrom
claude/port-pr-2261-carlos-U997n

Conversation

@yingbull

@yingbull yingbull commented Feb 25, 2026

Copy link
Copy Markdown
Collaborator

Summary by Sourcery

Implement per-patient Rx session isolation to support multiple concurrent patient medication tabs without cross-contamination, while maintaining backward compatibility with existing session usage.

Bug Fixes:

  • Prevent prescriptions and medication actions from leaking between patients when multiple Medications tabs are open by keying RxSessionBean per demographic and ensuring correct bean selection on each request.
  • Handle invalid or missing identifiers (demographicNo, randomId) more safely in prescription flows, returning clear errors instead of failing silently or misapplying actions.

Enhancements:

  • Introduce RxSessionFilter and a client-side rxSessionInterceptor to automatically attach demographic context to Rx-related requests and align legacy session access with per-patient beans.
  • Update Rx-related actions and JSPs to consistently derive patient context from demographicNo parameters and RxSessionBean helpers instead of relying on a single shared session bean or Patient attribute.
  • Improve stash management and cleanup behavior around prescription preview/printing flows to avoid stale or orphaned prescription state.
  • Enhance RxSessionBean with helper methods and documentation for managing per-patient session keys and retrieval.

Documentation:

  • Add class-level and method-level documentation explaining the new per-patient Rx session isolation model and its interaction with legacy session keys.

…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
Copilot AI review requested due to automatic review settings February 25, 2026 18:23
@coderabbitai

coderabbitai Bot commented Feb 25, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/port-pr-2261-carlos-U997n

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
Copy Markdown
Contributor

Summary of Changes

Hello @yingbull, 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 introduces a significant architectural improvement to the prescription (Rx) module by implementing per-patient session isolation for RxSessionBean instances. This change directly addresses the problem of medication data cross-contamination that could occur when users had multiple patient medication tabs open. By centralizing session bean management, introducing a servlet filter to handle context switching, and enhancing client-side requests to include patient identifiers, the system now ensures that all prescription-related operations are correctly scoped to the intended patient, thereby improving data integrity and user experience.

Highlights

  • Per-Patient RxSessionBean Isolation: Implemented a mechanism to isolate RxSessionBean instances per patient, preventing data cross-contamination when multiple patient medication tabs are open simultaneously. Each patient's bean is now stored under a unique session key, RxSessionBean_<demographicNo>.
  • Enhanced RxSessionBean Management: The RxSessionBean class was extended with static helper methods (getFromSession, saveToSession, getSessionKey) to centralize and standardize the retrieval and storage of per-patient session beans, while also maintaining backward compatibility with the legacy RxSessionBean key.
  • Introduction of RxSessionFilter: A new RxSessionFilter servlet filter was added to dynamically manage the RxSessionBean context. This filter intercepts Rx-related requests, extracts the demographicNo from parameters, and ensures the correct per-patient RxSessionBean is available under the legacy session key for existing code.
  • Client-Side AJAX and Form Interception: A new JavaScript file, rxSessionInterceptor.js, was introduced to automatically inject the demographicNo into all Prototype.js and jQuery AJAX calls, as well as relevant form submissions. This ensures that the patient context is consistently passed from the client to the server for proper session management.
  • Refactored Action and JSP Files: Multiple Java action classes and JSP files were updated to utilize the new RxSessionBean management methods, incorporate robust demographicNo parsing, include the new client-side interceptor, and pass demographicNo explicitly in URLs and hidden form fields to support the per-patient isolation.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/main/java/io/github/carlos_emr/carlos/casemgmt/web/CaseManagementView2Action.java
    • Updated RxSessionBean setup logic to use per-patient isolation via getFromSession and saveToSession methods, including error handling for invalid demographic numbers.
  • src/main/java/io/github/carlos_emr/carlos/prescript/pageUtil/RxChoosePatient2Action.java
    • Modified RxSessionBean initialization to retrieve or create per-patient instances using the new static helper methods and added error handling for demographic number parsing.
  • src/main/java/io/github/carlos_emr/carlos/prescript/pageUtil/RxSessionBean.java
    • Added HttpServletRequest and HttpSession imports.
    • Introduced static constants SESSION_KEY_PREFIX and LEGACY_SESSION_KEY.
    • Implemented static helper methods getSessionKey, getFromSession (for both HttpSession and HttpServletRequest), and saveToSession (for both HttpSession and HttpServletRequest) to manage per-patient RxSessionBean instances.
    • Updated the setDemographicNo method to accept a String parameter, parsing it to an int and handling NumberFormatException.
  • src/main/java/io/github/carlos_emr/carlos/prescript/pageUtil/RxSessionFilter.java
    • Added a new servlet filter to manage RxSessionBean context.
    • Implemented init, doFilter, and destroy methods for the filter.
    • The doFilter method extracts demographicNo from request parameters, retrieves the corresponding per-patient RxSessionBean, and sets it as the legacy RxSessionBean attribute in the session.
  • src/main/java/io/github/carlos_emr/carlos/prescript/pageUtil/RxShowAllergy2Action.java
    • Refactored RxSessionBean retrieval and saving to use the new getFromSession and saveToSession methods, including error handling for demographic number parsing.
  • src/main/java/io/github/carlos_emr/carlos/prescript/pageUtil/RxWriteScript2Action.java
    • Added StandardCharsets import.
    • Improved error handling for parsing randomId when updating drug quantities, providing specific JSON error responses for invalid or not-found prescriptions.
    • Updated the setDemographicNo method to accept a String parameter, parsing it and handling NumberFormatException.
  • src/main/webapp/WEB-INF/web.xml
    • Registered the new RxSessionFilter with mappings for /oscarRx/*, /CaseManagementView.do, and /messenger/generatePreviewPDF.jsp.
  • src/main/webapp/messenger/generatePreviewPDF.jsp
    • Updated RxSessionBean setup to use getFromSession and saveToSession with demographic_no parameter, removing redundant demographic setting logic.
  • src/main/webapp/oscarRx/ListDrugs.jsp
    • Modified patient data retrieval logic to prioritize demographicNo from request parameters for multi-tab support, falling back to the bean's demographic number, and finally to the session's 'Patient' attribute.
  • src/main/webapp/oscarRx/SearchDrug3.jsp
    • Added import for org.owasp.encoder.Encode.
    • Included rxSessionInterceptor.js and initialized currentDemographicNo for client-side interception.
    • Updated hidden input fields for demographicNo to use name attribute and Encode.forHtmlAttribute.
    • Introduced clearStashOnLightwindowClose flag and handleLightwindowClose function to manage prescription stash clearing.
    • Modified popForm2 to pass demographicNo in URL parameters for ViewScript2.jsp.
  • src/main/webapp/oscarRx/ViewScript.jsp
    • Added import for org.owasp.encoder.Encode.
    • Included rxSessionInterceptor.js and initialized currentDemographicNo.
    • Added a resetStash JavaScript function.
    • Updated the iframe source for Preview.jsp to include demographicNo as a URL parameter.
    • Added a hidden demographicNo input to the clearPending.do form.
    • Integrated resetStash() into the onClick event for the 'Back to Oscar' button.
  • src/main/webapp/oscarRx/ViewScript2.jsp
    • Included rxSessionInterceptor.js and initialized currentDemographicNo.
    • Added a resetStashAndClose JavaScript function to clear stash and close the window.
    • Updated the iframe source for Preview2.jsp to include demographicNo as a URL parameter.
    • Added a hidden demographicNo input to the clearPending.do form.
    • Modified the 'Back to Oscar' button's onClick event to call resetStashAndClose().
  • src/main/webapp/oscarRx/js/rxSessionInterceptor.js
    • Added a new JavaScript file to intercept Prototype.js and jQuery AJAX calls, and form submissions.
    • Automatically appends demographicNo to request parameters or form data if not already present, ensuring patient context is passed to the server.
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.

@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

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

Copy link
Copy Markdown
Contributor

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 crucial fix for preventing medication data cross-contamination between different patient tabs. It achieves this by isolating the RxSessionBean on a per-patient basis in the HTTP session, using a unique key derived from the patient's demographic number.

The implementation is robust, employing a combination of a new Servlet Filter (RxSessionFilter) to transparently manage session state for legacy code, and a JavaScript AJAX interceptor (rxSessionInterceptor.js) to ensure patient context is passed in client-side requests. The changes are propagated consistently across various Actions and JSPs.

The code is well-structured and includes important enhancements for error handling, particularly in RxWriteScript2Action. My review includes a couple of suggestions to further improve maintainability and debuggability by reducing code duplication and adding logging for suppressed exceptions. Overall, this is a high-quality and important contribution to the stability of the application.

Comment on lines +817 to +839
try {
randomIdInt = Integer.parseInt(randomId);
} 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(StandardCharsets.UTF_8));
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);
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(StandardCharsets.UTF_8));
return null;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The error handling logic for invalid randomId and for a null prescription object is duplicated. The code for setting the HTTP status, content type, and writing the JSON error response is nearly identical in both the catch block and the if (rx == null) block. This duplication can be avoided by extracting the response logic into a private helper method. This will make the code more maintainable and reduce redundancy.

Consider adding a helper method like this to the class:

private String sendErrorResponse(String errorMessage) throws IOException {
    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
    response.setContentType("application/json;charset=UTF-8");
    ObjectNode errorResponse = objectMapper.createObjectNode();
    errorResponse.put("error", errorMessage);
    response.getOutputStream().write(errorResponse.toString().getBytes(StandardCharsets.UTF_8));
    return null;
}

Then you can simplify the updateDrug method as shown in the suggestion.

                int randomIdInt;
                try {
                    randomIdInt = Integer.parseInt(randomId);
                } catch (NumberFormatException e) {
                    logger.error("Invalid randomId parameter: {}", Encode.forJava(randomId));
                    return sendErrorResponse("Invalid prescription identifier.");
                }
                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));
                    return sendErrorResponse("Prescription not found. Please refresh and try again.");
                }

Comment on lines +1592 to +1594
} catch (NumberFormatException e) {
// Keep default value (0) if parse fails
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Swallowing exceptions without logging can make debugging difficult. While the intent here may be to ignore invalid input for demographicNo, it would be beneficial to log the NumberFormatException at a DEBUG or WARN level. This would provide visibility into malformed requests without cluttering logs in production if set to a higher level.

            } catch (NumberFormatException e) {
                // Keep default value (0) if parse fails
                logger.debug("Could not parse demographicNo '{}'. Keeping previous value.", RHS, e);
            }

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR implements per-patient session isolation for the prescription (Rx) module to prevent medication data cross-contamination when multiple patient medication tabs are open simultaneously. The solution uses demographicNo-keyed session storage (RxSessionBean_<demographicNo>) with a servlet filter that swaps the correct bean before each request, maintaining backward compatibility with legacy code that reads the single RxSessionBean session attribute.

Changes:

  • Introduced RxSessionFilter to swap per-patient RxSessionBeans based on demographicNo request parameters
  • Added client-side AJAX interceptor (rxSessionInterceptor.js) to inject demographicNo into all Rx-related requests
  • Enhanced RxSessionBean with static methods for per-patient session management (getFromSession/saveToSession)
  • Updated key Rx actions and JSPs to use new session management pattern
  • Added defensive error handling in RxWriteScript2Action for missing prescriptions in stash

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
RxSessionFilter.java NEW: Servlet filter that swaps per-patient RxSessionBean into legacy session key before each Rx request
RxSessionBean.java Added static methods (getFromSession/saveToSession) for per-patient session storage with dual-key strategy
rxSessionInterceptor.js NEW: Client-side interceptor adds demographicNo to Prototype.js, jQuery AJAX calls, and Rx forms
web.xml Registered RxSessionFilter for /oscarRx/*, /CaseManagementView.do, and generatePreviewPDF.jsp
RxWriteScript2Action.java Added error handling for missing prescriptions and String-based demographicNo setter for Struts2
RxShowAllergy2Action.java Migrated to use RxSessionBean.getFromSession/saveToSession pattern
RxChoosePatient2Action.java Migrated to use RxSessionBean.getFromSession/saveToSession pattern
CaseManagementView2Action.java Migrated to use RxSessionBean.getFromSession/saveToSession pattern
ViewScript2.jsp Added rxSessionInterceptor.js, encoded demographicNo in iframe src and hidden form field
ViewScript.jsp Added rxSessionInterceptor.js, encoded demographicNo in iframe src and hidden form field
SearchDrug3.jsp Added rxSessionInterceptor.js, proper encoding for demographicNo in forms and JavaScript
ListDrugs.jsp Enhanced to retrieve patient data by demographicNo parameter with proper fallback logic
generatePreviewPDF.jsp Updated to use per-patient RxSessionBean.getFromSession/saveToSession pattern

<% if (bean.getStashSize() > 0) { %>
<iframe id='preview' name='preview' width=420px height=890px
src="oscarRx/Preview2.jsp?scriptId=<%=bean.getStashItem(0).getScript_no()%>&rePrint=<%=reprint%>&pharmacyId=<%=request.getParameter("pharmacyId")%>"
src="oscarRx/Preview2.jsp?scriptId=<%=bean.getStashItem(0).getScript_no()%>&rePrint=<%=reprint%>&pharmacyId=<%=request.getParameter("pharmacyId")%>&demographicNo=<%=Encode.forUriComponent(Integer.toString(bean.getDemographicNo()))%>"

Copilot AI Feb 25, 2026

Copy link

Choose a reason for hiding this comment

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

The src attribute for the iframe embeds request.getParameter("pharmacyId") directly inside a quoted HTML attribute without any HTML encoding, which allows an attacker who controls pharmacyId to inject malicious markup or JavaScript by including a quote and additional attributes or event handlers. For example, a crafted pharmacyId containing " onload="alert(1) would break out of the URL and execute script in the clinician’s browser. Ensure pharmacyId is treated as untrusted input by HTML-encoding it for attribute context (or strictly validating and using a trusted numeric ID value) before inserting it into the page.

Suggested change
src="oscarRx/Preview2.jsp?scriptId=<%=bean.getStashItem(0).getScript_no()%>&rePrint=<%=reprint%>&pharmacyId=<%=request.getParameter("pharmacyId")%>&demographicNo=<%=Encode.forUriComponent(Integer.toString(bean.getDemographicNo()))%>"
src="oscarRx/Preview2.jsp?scriptId=<%=bean.getStashItem(0).getScript_no()%>&rePrint=<%=reprint%>&pharmacyId=<%=Encode.forUriComponent(request.getParameter("pharmacyId"))%>&demographicNo=<%=Encode.forUriComponent(Integer.toString(bean.getDemographicNo()))%>"

Copilot uses AI. Check for mistakes.
@sourcery-ai

sourcery-ai Bot commented Feb 25, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Implements per-patient RxSessionBean isolation and multi-tab safety across the Rx workflow by introducing a session filter, a JS interceptor, and consistently propagating demographicNo through server and client code, while adding defensive checks and small UX fixes around stash management and prescription updates.

Sequence diagram for Rx request with per-patient session isolation

sequenceDiagram
    actor User
    participant Browser as Browser_rxSessionInterceptor
    participant Server as ServletContainer
    participant Filter as RxSessionFilter
    participant Action as RxAction_or_JSP
    participant Session as HttpSession

    User->>Browser: Trigger Rx action (e.g. save, preview)
    note over Browser: rxSessionInterceptor.js adds demographicNo
    Browser->>Server: HTTP request with demographicNo

    Server->>Filter: doFilter(req,res,chain)
    Filter->>Session: getAttribute(RxSessionBean)
    Filter->>Filter: parseDemographicNo(demographicNo)
    alt demographicNo > 0
        Filter->>Session: getAttribute(RxSessionBean_<demographicNo>)
        alt per-patient bean exists
            Filter->>Session: setAttribute(RxSessionBean, perPatientBean)
        else explicit demographicNo but no bean
            Filter->>Session: removeAttribute(RxSessionBean)
        end
    else demographicNo missing
        Filter->>Session: use legacy RxSessionBean as fallback
    end
    Filter-->>Server: chain.doFilter()

    Server->>Action: dispatch (execute/view/JSP)
    Action->>Session: getAttribute(RxSessionBean)
    note right of Action: Now bound to the correct patient

    Action-->>Browser: Response (HTML/JSON)
Loading

Class diagram for per-patient RxSessionBean isolation and filter

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

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

    class RxShowAllergy2Action {
        + String execute()
        - void reorder(HttpServletRequest request)
    }

    class RxChoosePatient2Action {
        + String execute()
        + String getDemographicNo()
    }

    class CaseManagementView2Action {
        + String view()
    }

    class HttpSession
    class HttpServletRequest

    RxSessionFilter --> RxSessionBean : uses getSessionKey
    RxSessionFilter --> HttpSession : accesses attributes
    RxSessionFilter --> HttpServletRequest : reads parameters

    RxShowAllergy2Action --> RxSessionBean : getFromSession(HttpSession,int)
    RxShowAllergy2Action --> RxSessionBean : saveToSession(HttpSession,RxSessionBean)

    RxChoosePatient2Action --> RxSessionBean : getFromSession(HttpServletRequest,int)
    RxChoosePatient2Action --> RxSessionBean : saveToSession(HttpServletRequest,RxSessionBean)

    CaseManagementView2Action --> RxSessionBean : getFromSession(HttpServletRequest,int)
    CaseManagementView2Action --> RxSessionBean : saveToSession(HttpServletRequest,RxSessionBean)
Loading

File-Level Changes

Change Details Files
Introduce per-patient RxSessionBean session management with backward-compatible access helpers.
  • Added per-patient session key prefix and legacy key constants.
  • Implemented helper methods to generate per-patient keys, retrieve beans from HttpSession/HttpServletRequest with legacy fallback and promotion, and save beans under both per-patient and legacy keys.
  • Documented RxSessionBean behavior and its role in per-patient isolation.
src/main/java/io/github/carlos_emr/carlos/prescript/pageUtil/RxSessionBean.java
Add RxSessionFilter to select the correct per-patient RxSessionBean for each request based on demographicNo.
  • Implemented RxSessionFilter that parses demographicNo (and demographic_no) from requests, resolves the corresponding per-patient RxSessionBean, and swaps it into the legacy RxSessionBean session attribute.
  • Added logic to fall back to existing legacy bean demographic when no parameter is present, and to clear legacy key when an explicit demographicNo has no corresponding per-patient bean to avoid cross-patient leakage.
  • Registered RxSessionFilter in web.xml for /oscarRx/*, CaseManagementView.do, and messenger/generatePreviewPDF.jsp.
src/main/java/io/github/carlos_emr/carlos/prescript/pageUtil/RxSessionFilter.java
src/main/webapp/WEB-INF/web.xml
Add client-side rxSessionInterceptor to propagate demographicNo in AJAX calls and Rx-related forms.
  • Created rxSessionInterceptor.js to inject demographicNo into Prototype.js Ajax.Request/Updater calls, jQuery AJAX calls, and Rx-related forms that lack a demographicNo field.
  • Guarded interceptor with currentDemographicNo global variable and wired initialization on DOMContentLoaded.
  • Integrated the interceptor script and currentDemographicNo variable into SearchDrug3.jsp, ViewScript2.jsp, and ViewScript.jsp.
src/main/webapp/oscarRx/js/rxSessionInterceptor.js
src/main/webapp/oscarRx/SearchDrug3.jsp
src/main/webapp/oscarRx/ViewScript2.jsp
src/main/webapp/oscarRx/ViewScript.jsp
Ensure demographicNo flows correctly through Rx UI, iframe URLs, and server actions to support multi-patient tabs.
  • Updated hidden demographicNo fields in SearchDrug3.jsp and clearPending forms in ViewScript/ViewScript2 to use encoded values and consistent name attributes.
  • Appended demographicNo query parameters to ViewScript2.jsp and Preview/Preview2 iframe URLs so downstream JSPs and actions receive patient context.
  • Modified JavaScript functions (Discontinue2, RePrescribeLongTerm, popForm2) to use the correct demoNo/currentDemographicNo variables rather than patient.getDemographicNo().
src/main/webapp/oscarRx/SearchDrug3.jsp
src/main/webapp/oscarRx/ViewScript2.jsp
src/main/webapp/oscarRx/ViewScript.jsp
Refactor server-side actions to use per-patient RxSessionBean helpers and safer demographicNo handling.
  • Changed RxShowAllergy2Action, CaseManagementView2Action, RxChoosePatient2Action, and generatePreviewPDF.jsp to resolve RxSessionBean via RxSessionBean.getFromSession and persist via saveToSession, instead of directly using the legacy session key.
  • Added robust parsing/validation of demographicNo/demographic_no with logging and appropriate error handling, and ensured provider/demographic context is set once the bean is resolved.
  • Updated generatePreviewPDF.jsp to throw a ServletException on invalid demographic_no and to always set providerNo/demographicNo before saving the bean.
src/main/java/io/github/carlos_emr/carlos/prescript/pageUtil/RxShowAllergy2Action.java
src/main/java/io/github/carlos_emr/carlos/casemgmt/web/CaseManagementView2Action.java
src/main/java/io/github/carlos_emr/carlos/prescript/pageUtil/RxChoosePatient2Action.java
src/main/webapp/messenger/generatePreviewPDF.jsp
Harden Rx actions around stash handling, randomId parsing, and demographicNo binding.
  • In RxWriteScript2Action.updateDrug, validated randomId as an integer, returned structured JSON errors and HTTP 400 for invalid IDs or missing prescriptions in the stash, and logged using encoded values.
  • In RxWriteScript2Action, changed setDemographicNo to accept a String parameter for Struts2 binding, safely parsing to int and ignoring invalid input.
  • Adjusted Rx UI scripts (SearchDrug3.jsp and ViewScript2.jsp) to add/reset stash-clearing logic when closing lightwindows, including resetStash, clearStashOnLightwindowClose flag, and resetStashAndClose in ViewScript2.
src/main/java/io/github/carlos_emr/carlos/prescript/pageUtil/RxWriteScript2Action.java
src/main/webapp/oscarRx/SearchDrug3.jsp
src/main/webapp/oscarRx/ViewScript2.jsp
Use server-side patient lookup by demographicNo for ListDrugs.jsp to avoid stale session Patient objects.
  • Changed ListDrugs.jsp to prefer demographicNo from the request parameter, falling back to the RxSessionBean demographicNo and finally to the legacy Patient session attribute.
  • Used RxPatientData.getPatient with LoggedInInfo and demographicNo instead of relying solely on session-stored Patient.
  • Added defensive parsing of demographicNo with a silent fallback on parse failure.
src/main/webapp/oscarRx/ListDrugs.jsp

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

@yingbull

Copy link
Copy Markdown
Collaborator Author

Opened #1826 capturing design-pattern observations from unmerged upstream work by @chitrankd on the same underlying problem (Open-O branch enhancement/rx_code_optimisation, commits 9ca1aa869b + 14b2629735).

The observations sketch a two-phase architectural follow-up — inner-class extraction, then moving Patient out of HttpSession entirely — that would compose with the per-demographic keying this PR introduces. Not a cherry-pick (pre-migration org.oscarehr.* paths, 4,600+ commits behind upstream), just design-pattern notes for a later refactor sequence once #479 lands.

Full rationale and suggested sequencing in #1826.

@yingbull yingbull added this to the may-2026-release milestone May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants