Skip to content
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

OBPIH-6331 Inferring bin locations in pick import #4675

Merged
merged 18 commits into from
Jun 26, 2024

Conversation

jmiranda
Copy link
Member

@jmiranda jmiranda commented Jun 16, 2024

Rebased version.

Comment on lines +1285 to +1286
String lotNumberName = data.lotNumber ?: g.message(code: "default.noLotNumber.label", default: Constants.DEFAULT_LOT_NUMBER)
String binLocationName = data.binLocation ?: g.message(code: "default.noBinLocation.label", default: Constants.DEFAULT_BIN_LOCATION_NAME)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This was probably an issue that came after rebase but it looks like g is not defined in this scope so we need to add

ApplicationTagLib g = grailsApplication.mainContext.getBean(ApplicationTagLib.class)

in the scope or move this same code from the previous if (!availableItem) in a higher block/scope (move it out of the if block)

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah this was my fault. I think I moved it into the wrong block. Fixed!

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't expect you to change this but I'd love for us to have a dto (or whatever) package that we can put our request objects in instead of sticking them alongside the controllers. Something for us to think about in the future

}

// FIXME If location.name is not unique then we need to do some error handling
return locations.find { it.name.equalsIgnoreCase(name) }
Copy link
Collaborator

Choose a reason for hiding this comment

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

if this is an internal location, you don't need to do an isInternalLocation() check?

@@ -38,6 +39,10 @@ class ImportPickCommand implements Validateable {
})
}

boolean getIsDefaultBinLocation() {
return !binLocation || binLocation?.equalsIgnoreCase(Constants.DEFAULT_BIN_LOCATION_NAME)
Copy link
Collaborator

Choose a reason for hiding this comment

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

You don't need the ? if you're doing the null check first.

Suggested change
return !binLocation || binLocation?.equalsIgnoreCase(Constants.DEFAULT_BIN_LOCATION_NAME)
return !binLocation || binLocation.equalsIgnoreCase(Constants.DEFAULT_BIN_LOCATION_NAME)


stockMovementService.validatePicklistListImport(stockMovement, pickPageItems, importedLines)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This might be a larger discussion but I'll note it here.

What's the advantage of using these command objects vs just passing the objects that a method needs like we had before? What problem is it solving for us?

We used something similar at my previous job (we called them Context objects) but were encouraged to only use them in really complex flows that need to maintain a certain state across classes.

I get that it simplifies the args down to a single object, but if we're not careful I can see a world where we're stuck with dozens of command objects that only do one specific thing.

void validatePicklistListImport(StockMovement stockMovement, List<PickPageItem> pickPageItems, List<ImportPickCommand> picklistItems) {
Map<String, List> picklistItemsGroupedByRequisitionItem = picklistItems.groupBy { it.id }
/**
* This method needs to be broken down into multiple smaller methods.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree 😆

Copy link
Collaborator

@EWaterman EWaterman Jun 17, 2024

Choose a reason for hiding this comment

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

I think you have a comment in the controller class that is alluding to this, but you could have a PicklistImportValidator (that extends some generic ImportValidator) that wraps all this logic and returns a PicklistImportValidationResult object (that extends some generic ValidationResult).

This is a really complex validation step so I don't know exactly what that would look like, but you could break it down further into smaller validators if you really wanted to and just bubble up the ValidationResult.

    PicklistImportValidationResult result = picklistImportValidator.validate()
    if (!result.isValid()) {
        result.errors()....
    }

And in the PicklistImportValidator:

    PicklistImportValidationResult validate() {
       PicklistImportValidationResult result = new()
        ...
        ValidationResult xResult = someMoreSpecificValidator.validate()
        result.addAll(xResult.errors())
    }

Again, this is super complex so I don't know how well that would work, but as is, this feels like it really needs to be broken down somehow. The method is pretty unwieldy.

Comment on lines +478 to +491
return InventoryItem.createCriteria().get {
and {
eq("product", this)
if (lotNumber) {
eq("lotNumber", lotNumber)
} else {
or {
isNull("lotNumber")
eq("lotNumber", "")
}
}
}
} as InventoryItem
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is an issue I mentioned on the tech-huddle channel but I guess it will be best if I left a comment here under the PR to have some code reference

There are some cases that this chunck on code will result in an Exception.
createCriteria().get will throw an exception when it encounters more than one result found by given criteria, which is what I have encountered.

running the following query will return a list of products that will cause this issue.

select ii.product_id, count(ii.id) from inventory_item ii 
where ii.lot_number is NULL or ii.lot_number = ''
group by ii.product_id
HAVING count(ii.id) > 1
ORDER BY count(*) desc;

basically after inspecting these inventory items I have noticed that there are some items that have lotNumber: NULL and some that lotNumber: ""

InventoryItem has a following GORM validation set on lotNumber

lotNumber(nullable: true, unique: ['product'], maxSize: 255)

so there should be unique lotNumber per product.
As for DB constraints I don't see any fro inventory_item table lot_number column

Comment on lines +1568 to +1571
PicklistItem picklistItem = picklist.picklistItems.find {
it.inventoryItem?.id == availableItem.inventoryItem?.id &&
it.binLocation?.id == availableItem.binLocation?.id
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This picklistItem lookup might not work as you expect it to.
I encountered simillar case for picklist item update.
The issue is that before this lookup we call clearPicklist(pickPageItem.requisitionItem)
which will delete PicklistItems so in the ned we are always looking for Picklistitems that do not exist anymore.

So we either can remove this lookup line and ignore "updating picklist Items" or rethink the clearPicklist.

Currently we don't have a case anywhere where we update existing picklistItem (at least I don't think so).
The way we handle update for picklistItems is we remove them and recreate as a new one.

@awalkowiak awalkowiak merged commit a9b850d into develop Jun 26, 2024
5 checks passed
@awalkowiak awalkowiak deleted the OBPIH-6331-part-deux-rebase-develop branch June 26, 2024 08:57
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.

None yet

5 participants