-
-
Notifications
You must be signed in to change notification settings - Fork 467
OBPIH-7626 Enable destination field on send page #5702
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/inbound-refactor
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request enables the destination field to be editable on the inbound stock movement send page and refactors form data structures in both create and send pages. The changes allow users to select different destination locations instead of being restricted to the current location.
Key Changes:
- Added
locationTypeCodeparameter todebounceLocationsFetchutility function to filter locations by type - Enabled destination field editing on the send page with async location fetching filtered by DEPOT type
- Restructured hook return values to use nested objects for better organization
- Removed destination-matching validation logic that previously restricted editing
- Reordered backend location filtering logic to prioritize
locationTypeCodeover direction filtering
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
src/js/utils/option-utils.jsx |
Added locationTypeCode parameter to debounceLocationsFetch for type-based filtering |
src/js/hooks/inboundV2/send/useInboundSendForm.js |
Enabled destination field editing, added destination location fetching, restructured return value, removed matchesDestination checks |
src/js/hooks/inboundV2/create/useInboundCreateForm.js |
Added debounced location/people fetch functions, restructured return value, improved form reset logic |
src/js/components/stock-movement-wizard/inboundV2/sections/send/InboundSendFormHeader.jsx |
Removed destination-matching checks from button disabled states, replaced with isDispatched checks |
src/js/components/stock-movement-wizard/inboundV2/sections/send/InboundSendForm.jsx |
Connected destination field to async location fetching, removed disabled states from form fields, simplified button logic |
src/js/components/stock-movement-wizard/inboundV2/sections/create/InboundCreate.jsx |
Refactored to use restructured hook return values, removed hasErrors from destination field |
grails-app/services/org/pih/warehouse/core/LocationService.groovy |
Reordered filtering logic to check locationTypeCode before INBOUND direction |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ? { | ||
| id: data.origin.id, | ||
| name: data.origin.name, | ||
| label: `${data.origin.name} [${data.origin.locationType?.description ?? ''}]`, |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code uses optional chaining data.origin.locationType?.description but doesn't check if data.origin itself could be null/undefined before accessing locationType. While this won't cause an error with the existing null check data.origin ? { ... } : null, it's inconsistent with the pattern used for data.destination and could be misleading. Consider using consistent null-safety patterns throughout.
| label: `${data.origin.name} [${data.origin.locationType?.description ?? ''}]`, | |
| label: `${data.origin.name} [${data.origin?.locationType?.description ?? ''}]`, |
| }, | ||
| data: { | ||
| stockLists, | ||
| origin, |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hook returns origin in the data object but not destination, even though destination is watched at line 86. If any consumer needs to react to destination changes (similar to how origin is used), they won't have access to it. Consider whether destination should also be included in the return value for consistency and potential future use.
| origin, | |
| origin, | |
| destination, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the moment, we don’t need the destination outside this hook
| } = getValues(); | ||
|
|
||
| return { | ||
| destination: { id: destination.id }, |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential null pointer exception: The code accesses destination.id without null checking. If the destination field is cleared by the user (since it's now editable), this will throw a runtime error when getShipmentPayload is called. Add validation to ensure destination is not null before accessing its properties.
| destination: { id: destination.id }, | |
| destination: destination ? { id: destination.id } : null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we always trigger validation, before getShipmentPayload is called
|
|
||
| useEffect(() => { | ||
| if (currentLocation) { | ||
| if (currentLocation && !destination?.id) { |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition has been changed from if (currentLocation) to if (currentLocation && !destination?.id). This could lead to the destination not being set to currentLocation on subsequent renders if the destination field gets cleared. The original logic ensured the destination was always set when currentLocation exists. Consider whether this behavior change is intentional and whether it could cause issues with form initialization.
| }), | ||
| origin: { id: values.origin.id }, | ||
| destination: { id: currentLocation.id }, | ||
| destination: { id: values.destination.id }, |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The destination field validation schema is optional/nullable, but the form submission at line 98 assumes values.destination.id exists. This could cause a runtime error if the destination is null or undefined when the form is submitted. Consider adding proper null checking in the onSubmitStockMovementDetails function or making the destination field required in the validation schema.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to check whether destination.id exists because destination is now required in the zod schema. This function is called on onSubmit, so we check validation before reaching this point.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## feature/inbound-refactor #5702 +/- ##
==========================================================
Coverage ? 9.73%
Complexity ? 1325
==========================================================
Files ? 717
Lines ? 45805
Branches ? 10932
==========================================================
Hits ? 4458
Misses ? 40655
Partials ? 692 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| if (direction == StockMovementDirection.INBOUND.name()) { | ||
| return locations.findAll { | ||
| it.locationType.locationTypeCode == LocationTypeCode.SUPPLIER | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not sure if this is the best solution and I hope this don't break anything, but I had to change the order of this if to the following:
if (params.locationTypeCode) {
LocationTypeCode locationTypeCode = params.locationTypeCode as LocationTypeCode
return locations.findAll { it.locationType.locationTypeCode == locationTypeCode }
}
Previously, when we called the API with StockMovementDirection.INBOUND, we always received only suppliers.
We still want to return only suppliers for the origin select, but for the destination select we want to return only depots.
That’s why I decided to pass locationTypeCode to params. If locationTypeCode is provided, we filter locations by this code instead of using the default behavior (which returns suppliers for StockMovementDirection.INBOUND).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So before your change, if direction is inbound and you specify locationTypeCode, then it'd still only return suppliers. After your change, that scenario would return whatever locationTypeCode you specified.
But your change in option-utils.jsx means we were never specifying locationTypeCode for the call before, right? So I suspect this is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another way you could do it is keep the code where it was before and do:
if (direction == StockMovementDirection.INBOUND.name()) {
LocationTypeCode code = (params.locationTypeCode as LocationTypeCode) ?: LocationTypeCode.SUPPLIER
return locations.findAll {
it.locationType.locationTypeCode == code
}
}
but the way you're doing it is fine. I think to make it truly "better" we'd need to refactor this whole method a bit so I don't mind you keeping it a small change for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So before your change, if direction is inbound and you specify locationTypeCode, then it'd still only return suppliers. After your change, that scenario would return whatever locationTypeCode you specified.
But your change in option-utils.jsx means we were never specifying locationTypeCode for the call before, right? So I suspect this is fine.
@ewaterman From the frontend side nothing should break, because we weren't sending locationTypeCode before.
I just wanted to mention it here, because it’s possible I missed something on the backend side where this logic is used, so I preferred to be careful.
kchelstowski
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how was the field disabled before? I can't see anything in the code responsible for "enabling" it now, and I see a lot of other "ad hoc" changes. Could you describe what is exactly fixed in this PR?
| LocationTypeCode code = (params.locationTypeCode as LocationTypeCode) ?: LocationTypeCode.SUPPLIER | ||
| return locations.findAll { | ||
| it.locationType.locationTypeCode == LocationTypeCode.SUPPLIER | ||
| it.locationType.locationTypeCode == code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the context of that change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| }); | ||
| } | ||
| }, [currentLocation?.id]); | ||
| }, [currentLocation?.id, destination]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
destination if an object, you should include id in the dependency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch!
|
@kchelstowski Manon also wanted that if the location is changed during inbound and the selected destination (it was always that destination which was our current location during creating inbound) is not equal to the current location, all buttons should be blocked and the "Exit" button should be shown. This behavior was controlled by the matchesDestination boolean. That's why I have now removed the matchesDestination boolean. The third change was related to the location selection. Previously, in the old inbound and this inbound before these changes, both the origin and destination selects only showed locations with locationTypeCode = SUPPLIER. Now, we want the destination select to include only depots in the options, as specified in the ticket: “- All depots should be selectable as destination.” The origin select, however, still has the same options as before. Additionally, I did some minor code cleanup, mainly in the Create step, which wasn’t required for this ticket. Initially, as you can see in the ticket, I was supposed to enable the destination select also on the Create step. However, after discussing with Kasia, we decided it would be better to leave this select disabled. Before that, I made some code cleanup in this step, which is why you can see some changes there and I didn't remove that. I hope that explained it clearly 😄 Let me know if you have any questions. |
✨ Description of Change
Link to GitHub issue or Jira ticket:
https://pihemr.atlassian.net/browse/OBPIH-7626
Description:
📷 Screenshots & Recordings (optional)