Skip to content

OBPIH-6365 Add validation on quantity field#4651

Merged
awalkowiak merged 2 commits intodevelopfrom
OBPIH-6365-after-review-fixes
Jun 7, 2024
Merged

OBPIH-6365 Add validation on quantity field#4651
awalkowiak merged 2 commits intodevelopfrom
OBPIH-6365-after-review-fixes

Conversation

@drodzewicz
Copy link
Collaborator

This PR was created to fix some points mentioned in #4650

As you can see it's not so straightforward to add a validation of type (not a number) on a quantity, let me explain.

So the case is that a user uploads a file with a potential "invalid" quantity number.
We want our API to process this data and return to the user all of the errors that come up in the File.

Now if we set quantity as a type Integer, we lose the ability to even get to the custom validators because it will throw an exception something along the lines of Can't cast String to an Integer in constructor when assigning this.quantity = quantity, which is why I changed the type of the quantity so we can assign the quantity and then our validator can validate the value.

Additionally, I have added a getQuantityAsNumber so we don't have to cast String to Integer every time we want to use this value, not sure if you guys will support this solution.

At the moment this is the best I could come up with so if you guys have any ideas then let me know

@drodzewicz drodzewicz self-assigned this Jun 5, 2024
@drodzewicz drodzewicz requested a review from kchelstowski June 5, 2024 11:56
@drodzewicz drodzewicz force-pushed the OBPIH-6365-after-review-fixes branch from 9b2e651 to 64e0826 Compare June 5, 2024 11:58
@drodzewicz drodzewicz force-pushed the OBPIH-6365-after-review-fixes branch from 64e0826 to 6b23e06 Compare June 5, 2024 12:58
Copy link
Member

@jmiranda jmiranda left a comment

Choose a reason for hiding this comment

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

Let me know what you think. Also, if you've moved on I can work on the request changes as part of OBPIH-6331.

}
return true
})
quantityAsNumber(nullable: true)
Copy link
Member

Choose a reason for hiding this comment

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

Does this actually work?

Copy link
Member

Choose a reason for hiding this comment

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

More to the point, is this necessary to resolve some other issue? This feels like it might be related to the fact that the getQuantityAsNumber() getter should probably have a property defined in the transients list.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it was necessary because getQuantityAsNumber() returned a null and without it I got an error that getQuantityAsNumber cant be a nul value.
And as Kacper mentioned earlier, we don't have to define a transient for this getter

return ['notANumber.error']
}
if (Integer.parseInt(val) < 0) {
return ['negative.error']
Copy link
Member

Choose a reason for hiding this comment

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

Nice. I hadn't thought of that case.

quantity(nullable: false, min: 0)
quantity(nullable: false, validator: { val, obj ->
if (!NumberUtils.isNumber(val)) {
return ['notANumber.error']
Copy link
Member

Choose a reason for hiding this comment

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

Let's go with invalidNumber.error. I'll try to remember to make this change in OBPIH-6331.

binLocation(nullable: true)
quantity(nullable: false, min: 0)
quantity(nullable: false, validator: { val, obj ->
if (!NumberUtils.isNumber(val)) {
Copy link
Member

Choose a reason for hiding this comment

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

I don't know the answer to this ... but make sure we're using the right class from the right library here. I remember there being two Apache Commons lang dependencies (lang, lang3).

Copy link
Member

Choose a reason for hiding this comment

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

expirationDate(nullable: true)
binLocation(nullable: true)
quantity(nullable: false, min: 0)
quantity(nullable: false, validator: { val, obj ->
Copy link
Member

Choose a reason for hiding this comment

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

Could we do this instead?

    Integer quantity

    quantity(nullable: false, validator: { val, obj ->
        if (!(val instanceof Integer)) {
            return ['invalid.error', val]
        }
        if (Integer.parseInt(val) < 0) {
            return ['negative.error', val]
        } 
    })

Copy link
Member

Choose a reason for hiding this comment

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

I see what you mean now regarding this comment.

Now if we set quantity as a type Integer, we lose the ability to even get to the custom validators because it will throw an exception something along the lines of Can't cast String to an Integer in constructor when assigning this.quantity = quantity, which is why I changed the type of the quantity so we can assign the quantity and then our validator can validate the value.

Copy link
Member

Choose a reason for hiding this comment

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

So i think what you have is the right approach for now.

We could/should explore if there's a way to do some of the parsing / validation logic in the CSVMapReader to avoid these kinds of workarounds. Or use a different library.

But that's out of scope for this ticket.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This definitely sounds like something we should discuss in the design of importers ticket (OBID idea garden ticket)

}

Integer getQuantityAsNumber() {
return NumberUtils.isNumber(this.quantity) ? Integer.parseInt(this.quantity) : null
Copy link
Member

Choose a reason for hiding this comment

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

If we can define quantity as an Interger and handle the validation like I proposed then we should get rid of these methods.

importPickCommand.quantity.negative.error=Invalid quantity: "{2}". Quantity must not be a negative number.
importPickCommand.requisitionItem.notFound.error=Requisition item with id: {0} not found
importPickCommand.availableItem.notFound.error=The stock entry you selected: lot "{0}" and bin location "{1}" does not exist. Please review pick.
importPickCommand.availableItem.notAvailable.error=The stock entry you selected: lot "{0}" and bin location "{1}" is not available. Please review pick.
Copy link
Member

Choose a reason for hiding this comment

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

In order to keep things consistent, let's change this to "unavailable" as well.

…ed quantity should be transient to avoid setters; fixed error code
@jmiranda
Copy link
Member

jmiranda commented Jun 6, 2024

Additionally, I have added a getQuantityAsNumber so we don't have to cast String to Integer every time we want to use this value, not sure if you guys will support this solution.

At the moment this is the best I could come up with so if you guys have any ideas then let me know

I pushed a commit that reverses those quantity mappings, so that the ugly property name is assigned the raw string field (String quantityAsText) rather than the field we'd like to expose (Integer quantity).

Otherwise, the PR looks good.

@jmiranda
Copy link
Member

jmiranda commented Jun 7, 2024

If/when this gets merged, please use a merge commit and NOT "squash and merge". I recreated my branch for OBPIH-6331 and I'm basing it off this branch.

Copy link
Collaborator

@kchelstowski kchelstowski left a comment

Choose a reason for hiding this comment

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

as far I as I understand the context, I'm fine with your solution, but for me, getting an exception "cannot cast ...." would be self-explanatory that I've done something wrong with the number.

Integer quantity
String quantityAsText

static transients = ['quantity']
Copy link
Collaborator

Choose a reason for hiding this comment

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

fyi: in Grails 3 we don't need to declare a transient anymore. The getter is enough.

Copy link
Member

Choose a reason for hiding this comment

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

I think I prefer transients over setters but we can discuss.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We don't need a setter neither. Since Grails 2.0 we can declare a getter without having to introduce a transient field:
https://docs.grails.org/6.1.2/ref/Domain%20Classes/transients.html

As of Grails 2.0 if there is only a getter or only a setter method, you don’t need to declare the property name of the method in the transients list. Only typed fields and get/set pairs that form a property but shouldn’t be persisted need to go in the transients list.

AVAILABLE, PICKED, RECALLED, HOLD, NOT_AVAILABLE

static getList() {
static list() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

have you made sure it is not used anywhere with the getList() version?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yep I am sure, It was me who introduced this variable in the last PR

@awalkowiak awalkowiak merged commit 4e1b82f into develop Jun 7, 2024
@awalkowiak awalkowiak deleted the OBPIH-6365-after-review-fixes branch June 7, 2024 14:37
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.

4 participants