OBPIH-6365 Add validation on quantity field#4651
Conversation
9b2e651 to
64e0826
Compare
64e0826 to
6b23e06
Compare
jmiranda
left a comment
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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'] |
There was a problem hiding this comment.
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'] |
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Did a quick search and discovered that maybe this doesn't matter.
https://stackoverflow.com/questions/24158889/apache-commons-lang-2-vs-3
| expirationDate(nullable: true) | ||
| binLocation(nullable: true) | ||
| quantity(nullable: false, min: 0) | ||
| quantity(nullable: false, validator: { val, obj -> |
There was a problem hiding this comment.
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]
}
})
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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
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. |
|
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. |
kchelstowski
left a comment
There was a problem hiding this comment.
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'] |
There was a problem hiding this comment.
fyi: in Grails 3 we don't need to declare a transient anymore. The getter is enough.
There was a problem hiding this comment.
I think I prefer transients over setters but we can discuss.
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
have you made sure it is not used anywhere with the getList() version?
There was a problem hiding this comment.
Yep I am sure, It was me who introduced this variable in the last PR
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 Integerin constructor when assigningthis.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
getQuantityAsNumberso 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