Skip to content

perf(pharmacy): optimize transfer request item add — DTOs, single rate query, AJAX#21089

Open
buddhika75 wants to merge 1 commit into
developmentfrom
21062-transfer-request-dto-optimization
Open

perf(pharmacy): optimize transfer request item add — DTOs, single rate query, AJAX#21089
buddhika75 wants to merge 1 commit into
developmentfrom
21062-transfer-request-dto-optimization

Conversation

@buddhika75
Copy link
Copy Markdown
Member

@buddhika75 buddhika75 commented May 31, 2026

Summary

  • Replace full Item entity loads in the transfer request autocomplete with lightweight ItemDTO projections (4 typed constructor queries per keystroke instead of 1 full entity query)
  • Replace 3 separate entity-loading rate calls on item select with a single BillItemFinanceDetails scalar query via new ItemRatesDTO
  • Change the Add Item button from ajax=false (full page reload) to AJAX with targeted component updates
  • Eliminate N+1 on the edit path by adding JOIN FETCH bi.item and LEFT JOIN FETCH bi.billItemFinanceDetails to fetchBillItems
  • Fix brittle bi.item.class eq 'class com.divudi...' comparison that silently broke for Hibernate proxies in edit mode (now uses class.simpleName, and correctly handles Vmpp which was missing)
  • Cache three department-type display lists (previously recomputed from config lookups on every render)
  • Fetch institution eagerly in recentToDepartments query to prevent lazy load in processTransferRequest

New files

File Purpose
core/data/dto/ItemRatesDTO.java Holds purchase/retail/cost from one query
core/converter/ItemDtoConverter.java CDI converter for autocomplete DTO round-trip

DB calls reduced per item-add cycle

Event Before After
Per autocomplete keystroke 1 × full entity query (up to 30 objects) 4 × DTO projection queries
On item select 3 × full BillItem entity loads for rates 1 × itemFacade.find + 1 × scalar rates query
Add Item click Full page reload AJAX — updates itemList + requestTotals only
Edit path bill items load 1 + N lazy SELECTs (N = item count) 1 query with JOIN FETCH

Navigation path for QA testing

  1. Pharmacy → Disbursement → Transfer Request
  2. Select a target department
  3. Type at least 3 characters in the Item field — verify the autocomplete shows Name / Code / Type / Pack Size columns correctly for Amp, Ampp, Vmp, and Vmpp items
  4. Select an item — verify rates populate in the rate/value fields (if enabled via config)
  5. Enter a quantity and click Add Item — verify the item appears in the table without a full page reload, pack size column shows correctly
  6. Navigate to an existing saved request via the finalize list and click Edit — verify all items display correctly with correct pack sizes (this exercises the fetchBillItems JOIN FETCH path)
  7. Remove an item from the table — verify totals update correctly

Related issues created

Closes #21062

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com

Summary by CodeRabbit

  • New Features

    • Enhanced item autocomplete with improved rate lookups for transfer requests.
    • Added automatic pack-size detection and calculation based on item type during rate population.
  • Improvements

    • Optimized item rate population with more efficient data loading.
    • Updated item-selection workflow with AJAX-based form updates for improved responsiveness.

…e query, AJAX add

Replace full Item entity loads in pharmacy_transfer_request.xhtml with
lightweight DTOs and a single-query rate lookup, eliminating the main
sources of delay when adding items to a transfer request.

Changes:
- ItemDTO (search): add dblValue, itemTypeName, rateItemId fields +
  new constructor for autocomplete DTO queries
- ItemRatesDTO (new): holds purchase/retail/cost rates from one query
- ItemDtoConverter (new): CDI converter encodes DTO as id:type:dblValue:rateItemId
- ItemController: add completeAmpAmppVmpVmppItemDtosForRequestingDepartment
  returning List<ItemDTO> via 4 typed constructor queries (replaces full
  entity load per keystroke)
- PharmacyBean: add getLastRatesForItem replacing 3 separate entity-loading
  rate calls with a single BillItemFinanceDetails scalar query
- TransferRequestController: autocomplete now uses ItemDTO + typed proxy;
  populateRatesOnItemSelect reduced from 3 entity loads to 1 entity load +
  1 scalar query; addItem reuses cached rates; fetchBillItems adds
  JOIN FETCH bi.item and LEFT JOIN FETCH bi.billItemFinanceDetails to
  eliminate N+1 on edit path; recentToDepartments query fetches institution
  eagerly; three display type list getters are now lazily cached
- XHTML: autocomplete bound to currentItemDto with itemDtoConverter;
  Add Item button changed from ajax=false (full page reload) to AJAX with
  targeted updates; pack-size column uses class.simpleName instead of
  brittle full class string comparison (also fixes Vmpp display)

Closes #21062

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 31, 2026

Review Change Stack

Walkthrough

This PR refactors transfer request item selection to use Data Transfer Objects (DTOs) for autocomplete and rate lookups. It extends ItemDTO with pricing metadata, introduces ItemRatesDTO for rates, adds a JSF converter, provides backend DTO-based queries, refactors TransferRequestController to integrate these DTOs with rate population, and updates the UI to use DTO serialization with optimized AJAX form submission.

Changes

Transfer Request DTO-driven Item Selection and Rates

Layer / File(s) Summary
DTO Models and Serialization
src/main/java/com/divudi/core/data/dto/search/ItemDTO.java, src/main/java/com/divudi/core/data/dto/ItemRatesDTO.java, src/main/java/com/divudi/core/converter/ItemDtoConverter.java
ItemDTO extended with dblValue, itemTypeName, and rateItemId fields; new ItemRatesDTO encapsulates purchase/retail/cost rates; ItemDtoConverter provides JSF bidirectional serialization for colon-delimited DTO exchange.
Backend Query Services
src/main/java/com/divudi/bean/common/ItemController.java, src/main/java/com/divudi/ejb/PharmacyBean.java
ItemController.completeAmpAmppVmpVmppItemDtosForRequestingDepartment executes four lightweight DTO constructor queries, aggregates, sorts, and returns matched ItemDTO rows; PharmacyBean.getLastRatesForItem fetches most recent rates via single BillItemFinanceDetails join (or fallback lookups when costing disabled) and returns ItemRatesDTO.
TransferRequestController DTO Integration
src/main/java/com/divudi/bean/pharmacy/TransferRequestController.java
Controller state adds currentItemDto, currentItemRates, and cached department-type label lists; autocomplete delegated to ItemController returning ItemDTO; item addition sources rates from currentItemRates with fallback to last-rate lookups; populateRatesOnItemSelect refactored to load rates once, compute pack sizing from DTO type name, and update both PharmaceuticalBillItem and BillItemFinanceDetails; fetchBillItems optimized with join-fetch; recent-department query updated to fetch distinct Department with institution join-fetch; recreate/department-change handlers clear DTOs and label caches.
UI Layer: Autocomplete and Form Submission
src/main/webapp/pharmacy/pharmacy_transfer_request.xhtml
Autocomplete binds to currentItemDto with itemDtoConverter; Add Item button changed to AJAX submit with explicit update targets for form inputs, item list, and totals; Pack Size columns compute from dblValue (or item class.simpleName for Ampp/Vmpp) with numeric default 1 via EL instead of class-name-based rendered blocks.

Sequence Diagram(s)

Included in layer diagram above.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #20995 — Implements the same entity→DTO autocomplete pattern (controller autocomplete methods, JSF converter, DTO field extensions, and re-fetch-on-select rate population) for Stock autocomplete migration, demonstrating a consistent DTO-driven refactoring approach across different item types in the system.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main optimization effort: replacing full Item entities with DTOs, introducing a single-query rate fetch, and switching to AJAX for Add Item functionality.
Linked Issues check ✅ Passed The changes directly address issue #21062 by eliminating N+1 queries via DTOs and lightweight constructor queries, replacing multi-step rate loads with a single scalar query, and switching Add Item to AJAX for reduced latency.
Out of Scope Changes check ✅ Passed All changes are in scope: ItemDTO/ItemRatesDTO/ItemDtoConverter support the DTO optimization goal, controller updates implement the rate-query consolidation, fetchBillItems JOIN FETCH eliminates N+1 on edit, and XHTML updates enable AJAX behavior.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 21062-transfer-request-dto-optimization

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.42.3)
src/main/java/com/divudi/bean/common/ItemController.java

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.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 47c7acbb60

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

action="#{transferRequestController.addItem}"
ajax="false"/>
process="@form"
update="itemAutoComplete txtQty txtLineGrossRate txtLineNetValue itemList requestTotals focusItem"/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Don't update rate inputs when they are not rendered

When the rate/value columns are disabled (the config defaults to false, or the user lacks StockRequestViewRates), txtLineGrossRate and txtLineNetValue are inside rendered panel groups and are not present in the JSF component tree. This new AJAX add action always updates those IDs, so clicking Add Item in that common mode will fail component resolution instead of adding the item; update a stable wrapper or only target these inputs when they are rendered.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/webapp/pharmacy/pharmacy_transfer_request.xhtml (2)

176-178: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove txtLineCostRate from the AJAX update targets (src/main/webapp/pharmacy/pharmacy_transfer_request.xhtml:176-178)

txtLineCostRate is referenced in the p:ajax update list, but no component with id="txtLineCostRate" exists in this view, so the AJAX update target can’t be resolved.

Suggested fix
                                             <p:ajax event="itemSelect"
                                                     listener="#{transferRequestController.populateRatesOnItemSelect}"
-                                                    update="txtLineCostRate txtLineGrossRate txtLineNetValue requestTotals focusQty selDepartmentType"/>
+                                                    update="txtLineGrossRate txtLineNetValue requestTotals focusQty selDepartmentType"/>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/webapp/pharmacy/pharmacy_transfer_request.xhtml` around lines 176 -
178, Remove the non-existent AJAX update target by editing the p:ajax in
pharmacy_transfer_request.xhtml: locate the p:ajax with listener
"#{transferRequestController.populateRatesOnItemSelect}" and remove
"txtLineCostRate" from its update list so the remaining update targets (e.g.,
txtLineGrossRate, txtLineNetValue, requestTotals, focusQty, selDepartmentType)
are valid.

211-217: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Narrow the AJAX process scope for “Add Item”. (src/main/webapp/pharmacy/pharmacy_transfer_request.xhtml:211-217)

process="@Form" causes the whole <h:form> (including the editable p:dataTable id="itemList" rows) to be decoded/validated on every add, so latency will scale with row count.

<p:commandButton
    value="Add Item"
    icon="fas fa-plus"
    class="ui-button-success w-100"
    action="#{transferRequestController.addItem}"
    process="`@form`"
    update="itemAutoComplete txtQty txtLineGrossRate txtLineNetValue itemList requestTotals focusItem"/>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/webapp/pharmacy/pharmacy_transfer_request.xhtml` around lines 211 -
217, The Add Item button currently uses process="`@form`" which decodes/validates
the entire h:form (including the editable p:dataTable id="itemList"); change the
p:commandButton's process attribute to only submit the specific input components
needed for addItem (for example use "`@this` itemAutoComplete txtQty
txtLineGrossRate txtLineNetValue" or similar) and remove "`@form`" so the
controller method addItem and validations only run for those inputs while
leaving update="itemList requestTotals focusItem" unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/webapp/pharmacy/pharmacy_transfer_request.xhtml`:
- Around line 171-174: The Pack Size column is showing vt.dblValue for any
positive number; update the EL in the h:outputText so dblValue is used only when
the item type is Ampp or Vmpp. Replace the current condition "(vt.dblValue ne
null and vt.dblValue gt 0) ? vt.dblValue : 1" with a compound check that also
ensures the DTO's item-type field (e.g., vt.itemType or the actual property
holding the item type) is 'Ampp' or 'Vmpp' before using vt.dblValue, otherwise
default to 1; keep the <f:convertNumber> formatting unchanged.

---

Outside diff comments:
In `@src/main/webapp/pharmacy/pharmacy_transfer_request.xhtml`:
- Around line 176-178: Remove the non-existent AJAX update target by editing the
p:ajax in pharmacy_transfer_request.xhtml: locate the p:ajax with listener
"#{transferRequestController.populateRatesOnItemSelect}" and remove
"txtLineCostRate" from its update list so the remaining update targets (e.g.,
txtLineGrossRate, txtLineNetValue, requestTotals, focusQty, selDepartmentType)
are valid.
- Around line 211-217: The Add Item button currently uses process="`@form`" which
decodes/validates the entire h:form (including the editable p:dataTable
id="itemList"); change the p:commandButton's process attribute to only submit
the specific input components needed for addItem (for example use "`@this`
itemAutoComplete txtQty txtLineGrossRate txtLineNetValue" or similar) and remove
"`@form`" so the controller method addItem and validations only run for those
inputs while leaving update="itemList requestTotals focusItem" unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5b552135-72c7-4aca-8a5c-90e31c302b8d

📥 Commits

Reviewing files that changed from the base of the PR and between fefeaa1 and 47c7acb.

📒 Files selected for processing (7)
  • src/main/java/com/divudi/bean/common/ItemController.java
  • src/main/java/com/divudi/bean/pharmacy/TransferRequestController.java
  • src/main/java/com/divudi/core/converter/ItemDtoConverter.java
  • src/main/java/com/divudi/core/data/dto/ItemRatesDTO.java
  • src/main/java/com/divudi/core/data/dto/search/ItemDTO.java
  • src/main/java/com/divudi/ejb/PharmacyBean.java
  • src/main/webapp/pharmacy/pharmacy_transfer_request.xhtml

Comment on lines +171 to +174
<p:column headerText="Pack Size" style="padding: 6px;">
<h:outputText value="#{(vt.dblValue ne null and vt.dblValue gt 0) ? vt.dblValue : 1}">
<f:convertNumber pattern="0.#" />
</h:outputText>
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Gate the autocomplete pack-size column by item type.

This now shows vt.dblValue for any DTO with a positive numeric value, so non-pack rows can render a misleading “Pack Size”. Match the bill-items table behavior and only use dblValue for Ampp/Vmpp, otherwise default to 1.

Suggested fix
-                                            <p:column headerText="Pack Size" style="padding: 6px;">
-                                                <h:outputText value="#{(vt.dblValue ne null and vt.dblValue gt 0) ? vt.dblValue : 1}">
+                                            <p:column headerText="Pack Size" style="padding: 6px;">
+                                                <h:outputText value="#{((vt.itemTypeName eq 'Ampp' or vt.itemTypeName eq 'Vmpp') and vt.dblValue ne null and vt.dblValue gt 0) ? vt.dblValue : 1}">
                                                     <f:convertNumber pattern="0.#" />
                                                 </h:outputText>
                                             </p:column>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p:column headerText="Pack Size" style="padding: 6px;">
<h:outputText value="#{(vt.dblValue ne null and vt.dblValue gt 0) ? vt.dblValue : 1}">
<f:convertNumber pattern="0.#" />
</h:outputText>
<p:column headerText="Pack Size" style="padding: 6px;">
<h:outputText value="#{((vt.itemTypeName eq 'Ampp' or vt.itemTypeName eq 'Vmpp') and vt.dblValue ne null and vt.dblValue gt 0) ? vt.dblValue : 1}">
<f:convertNumber pattern="0.#" />
</h:outputText>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/webapp/pharmacy/pharmacy_transfer_request.xhtml` around lines 171 -
174, The Pack Size column is showing vt.dblValue for any positive number; update
the EL in the h:outputText so dblValue is used only when the item type is Ampp
or Vmpp. Replace the current condition "(vt.dblValue ne null and vt.dblValue gt
0) ? vt.dblValue : 1" with a compound check that also ensures the DTO's
item-type field (e.g., vt.itemType or the actual property holding the item type)
is 'Ampp' or 'Vmpp' before using vt.dblValue, otherwise default to 1; keep the
<f:convertNumber> formatting unchanged.

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.

Delay When Adding Items to Transfer Request

1 participant