Skip to content

Commit 902da18

Browse files
committed
fix(validation): alternative price and currency must be both present or both left empty on a series sale form.
Fix #678
1 parent 4cfa56e commit 902da18

File tree

6 files changed

+131
-0
lines changed

6 files changed

+131
-0
lines changed

src/main/java/ru/mystamps/web/feature/series/importing/ImportSeriesSalesForm.java

+2
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@ public class ImportSeriesSalesForm implements AddSeriesSalesDto {
5454
// They are no-op methods because we don't support all values during series import.
5555
//
5656

57+
// For future: altPrice is required when altCurrency is specified
5758
@Override
5859
public BigDecimal getAltPrice() {
5960
return null;
6061
}
6162

63+
// For future: altCurrency is required when altPrice is specified
6264
@Override
6365
public Currency getAltCurrency() {
6466
return null;

src/main/java/ru/mystamps/web/feature/series/sale/AddSeriesSalesForm.java

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.hibernate.validator.constraints.URL;
2323
import org.springframework.format.annotation.DateTimeFormat;
2424
import ru.mystamps.web.dao.dto.Currency;
25+
import ru.mystamps.web.support.beanvalidation.BothOrNoneRequired;
2526
import ru.mystamps.web.support.beanvalidation.FieldsMismatch;
2627
import ru.mystamps.web.support.beanvalidation.Group;
2728
import ru.mystamps.web.validation.ValidationRules;
@@ -34,6 +35,7 @@
3435

3536
// @todo #503 Add integration test to ensure that seller and buyer are different
3637
// @todo #504 Add integration test to ensure that price and alternative price are different
38+
// @todo #678 Add integration tests for price and alternative price: both or none are required
3739
@Getter
3840
@Setter
3941
@FieldsMismatch.List({
@@ -44,6 +46,11 @@
4446
first = "currency", second = "altCurrency", message = "{currencies.prices.match}"
4547
)
4648
})
49+
@BothOrNoneRequired(
50+
first = "altPrice",
51+
second = "altCurrency",
52+
message = "{altprice.altcurrency.both-required}"
53+
)
4754
public class AddSeriesSalesForm implements AddSeriesSalesDto {
4855

4956
@DateTimeFormat(pattern = "dd.MM.yyyy")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (C) 2009-2019 Slava Semushin <[email protected]>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17+
*/
18+
package ru.mystamps.web.support.beanvalidation;
19+
20+
import javax.validation.Constraint;
21+
import javax.validation.Payload;
22+
import java.lang.annotation.Documented;
23+
import java.lang.annotation.Retention;
24+
import java.lang.annotation.Target;
25+
26+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
27+
import static java.lang.annotation.ElementType.TYPE;
28+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
29+
30+
@Target({ TYPE, ANNOTATION_TYPE })
31+
@Retention(RUNTIME)
32+
@Constraint(validatedBy = BothOrNoneRequiredValidator.class)
33+
@Documented
34+
public @interface BothOrNoneRequired {
35+
String message() default "{ru.mystamps.web.support.beanvalidation.BothOrNoneRequired.message}";
36+
Class<?>[] groups() default {};
37+
Class<? extends Payload>[] payload() default {};
38+
39+
String first();
40+
String second();
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright (C) 2009-2019 Slava Semushin <[email protected]>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17+
*/
18+
package ru.mystamps.web.support.beanvalidation;
19+
20+
import org.apache.commons.lang3.StringUtils;
21+
import org.springframework.beans.BeanWrapperImpl;
22+
import org.springframework.beans.PropertyAccessor;
23+
24+
import javax.validation.ConstraintValidator;
25+
import javax.validation.ConstraintValidatorContext;
26+
import java.util.Objects;
27+
28+
public class BothOrNoneRequiredValidator
29+
implements ConstraintValidator<BothOrNoneRequired, Object> {
30+
31+
private String firstFieldName;
32+
private String secondFieldName;
33+
34+
@Override
35+
public void initialize(BothOrNoneRequired annotation) {
36+
firstFieldName = annotation.first();
37+
secondFieldName = annotation.second();
38+
}
39+
40+
@Override
41+
public boolean isValid(Object value, ConstraintValidatorContext ctx) {
42+
43+
if (value == null) {
44+
return true;
45+
}
46+
47+
PropertyAccessor bean = new BeanWrapperImpl(value);
48+
49+
Object firstField = bean.getPropertyValue(firstFieldName);
50+
Object secondField = bean.getPropertyValue(secondFieldName);
51+
52+
String firstFieldValue = Objects.toString(firstField, null);
53+
String secondFieldValue = Objects.toString(secondField, null);
54+
55+
boolean firstIsEmpty = StringUtils.isEmpty(firstFieldValue);
56+
boolean secondIsEmpty = StringUtils.isEmpty(secondFieldValue);
57+
58+
if (firstIsEmpty && secondIsEmpty) {
59+
return true;
60+
}
61+
62+
if (!firstIsEmpty && !secondIsEmpty) {
63+
return true;
64+
}
65+
66+
// bind error message to 2nd field
67+
ConstraintViolationUtils.recreate(
68+
ctx,
69+
secondFieldName,
70+
ctx.getDefaultConstraintMessageTemplate()
71+
);
72+
73+
return false;
74+
}
75+
76+
}
77+

src/main/resources/ru/mystamps/i18n/ValidationMessages.properties

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ javax.validation.constraints.Max.message = Value must be less than or equal to {
55
org.hibernate.validator.constraints.NotEmpty.message = Value must not be empty
66
org.hibernate.validator.constraints.URL.message = Value must be a valid URL
77

8+
ru.mystamps.web.support.beanvalidation.BothOrNoneRequired.message = {first} and {second} fields must be empty or non-empty, specifying only one of them makes no-sense
89
ru.mystamps.web.support.beanvalidation.DenyValues.message = Invalid value
910
ru.mystamps.web.support.beanvalidation.FieldsMismatch.message = Field '{second}' must mismatch '{first}'
1011
ru.mystamps.web.support.beanvalidation.FieldsMatch.message = Field '{second}' must match '{first}'
@@ -50,6 +51,7 @@ password.mismatch = Password mismatch
5051
password.login.match = Password and login must be different
5152
seller.buyer.match = Seller and buyer must be different
5253
currencies.prices.match = Price and alternative price must be in a different currencies
54+
altprice.altcurrency.both-required = Alternative price and currency must be specified or left empty, specifying only one of them makes no-sense
5355

5456
name.invalid = Name must consist only letters, hyphen or spaces
5557

src/main/resources/ru/mystamps/i18n/ValidationMessages_ru.properties

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ javax.validation.constraints.Max.message = Значение должно быт
55
org.hibernate.validator.constraints.NotEmpty.message = Поле обязательно для заполнения
66
org.hibernate.validator.constraints.URL.message = Значение должно быть корректным URL
77

8+
ru.mystamps.web.support.beanvalidation.BothOrNoneRequired.message = Оба поля {first} и {second} должны быть заполнены либо пустыми. Указание только одного из них не имеет смысла
89
ru.mystamps.web.support.beanvalidation.DenyValues.message = Неправильное значение
910
ru.mystamps.web.support.beanvalidation.FieldsMismatch.message = Поле '{second}' не должно совпадать с '{first}'
1011
ru.mystamps.web.support.beanvalidation.FieldsMatch.message = Поле '{second}' должно совпадать с '{first}'
@@ -50,6 +51,7 @@ password.mismatch = Пароли не совпадают
5051
password.login.match = Пароль и логин не должны совпадать
5152
seller.buyer.match = Продавец и покупатель не должны совпадать
5253
currencies.prices.match = Цена и альтернативная цена должны быть в различных валютах
54+
altprice.altcurrency.both-required = Альтернативная цена и валюта должны быть указаны или оставлены пустыми, указание только одного поля не имеет смысла
5355

5456
name.invalid = Имя может состоять только из букв, дефиса или пробелов
5557

0 commit comments

Comments
 (0)