This is an automated email from the ASF dual-hosted git repository.
taskain pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new b27653c05 [FINERACT-1965] Change date resolvement from SMART to STRICT
b27653c05 is described below
commit b27653c055d185997a85133799de6208aa4d0404
Author: taskain7 <[email protected]>
AuthorDate: Tue Aug 1 05:56:24 2023 +0200
[FINERACT-1965] Change date resolvement from SMART to STRICT
---
.../infrastructure/core}/api/DateParam.java | 7 +-
.../infrastructure/core/api/JsonCommand.java | 4 -
.../infrastructure/core/data/DateFormat.java | 62 ++++++
.../core/serialization/JsonParserHelper.java | 51 +++--
.../api/JournalEntriesApiResource.java | 6 +-
.../campaigns/email/api/EmailApiResource.java | 9 +-
.../infrastructure/sms/api/SmsApiResource.java | 8 +-
.../fineract/interoperation/util/InteropUtil.java | 2 +-
.../holiday/api/HolidaysApiResource.java | 7 +-
.../api/StandingInstructionHistoryApiResource.java | 8 +-
.../portfolio/group/api/CentersApiResource.java | 5 +-
.../api/LoanTransactionsApiResource.java | 14 +-
.../savings/data/DepositAccountDataValidator.java | 4 -
.../AccountingScenarioIntegrationTest.java | 2 +-
.../integrationtests/DateValidationTest.java | 229 +++++++++++++++++++++
.../integrationtests/FixedDepositTest.java | 9 +-
.../integrationtests/SchedulerJobsTestResults.java | 2 +-
.../fixeddeposit/FixedDepositAccountHelper.java | 16 +-
.../interoperation/InteropHelper.java | 9 +
19 files changed, 403 insertions(+), 51 deletions(-)
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/api/DateParam.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/DateParam.java
similarity index 85%
rename from
fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/api/DateParam.java
rename to
fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/DateParam.java
index c4107ca6c..b801f041c 100755
---
a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/api/DateParam.java
+++
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/DateParam.java
@@ -16,17 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.accounting.journalentry.api;
+package org.apache.fineract.infrastructure.core.api;
import jakarta.ws.rs.WebApplicationException;
import java.time.LocalDate;
import java.util.Locale;
+import org.apache.fineract.infrastructure.core.data.DateFormat;
import org.apache.fineract.infrastructure.core.serialization.JsonParserHelper;
/**
* Class for parsing dates sent as query parameters
- *
- * TODO: Vishwas Should move this class to a more generic package
*/
public class DateParam {
@@ -36,7 +35,7 @@ public class DateParam {
this.dateAsString = dateStr;
}
- public LocalDate getDate(final String parameterName, final String
dateFormat, final String localeAsString) {
+ public LocalDate getDate(final String parameterName, final DateFormat
dateFormat, final String localeAsString) {
final Locale locale =
JsonParserHelper.localeFromString(localeAsString);
return JsonParserHelper.convertFrom(this.dateAsString, parameterName,
dateFormat, locale);
}
diff --git
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java
index 32175c5f7..07c6f0023 100644
---
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java
+++
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java
@@ -418,10 +418,6 @@ public final class JsonCommand {
return this.fromApiJsonHelper.extractLocalTimeNamed(parameterName,
this.parsedCommand);
}
- public LocalDateTime localDateTimeValueOfParameterNamed(final String
parameterName) {
- return this.fromApiJsonHelper.extractLocalDateTimeNamed(parameterName,
this.parsedCommand);
- }
-
public MonthDay extractMonthDayNamed(final String parameterName) {
return this.fromApiJsonHelper.extractMonthDayNamed(parameterName,
this.parsedCommand);
}
diff --git
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/DateFormat.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/DateFormat.java
new file mode 100644
index 000000000..af452c010
--- /dev/null
+++
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/DateFormat.java
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.core.data;
+
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.DateTimeParseException;
+import java.time.format.ResolverStyle;
+import java.time.temporal.ChronoField;
+import java.util.List;
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+import
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+
+public class DateFormat {
+
+ @Getter
+ private final String dateFormat;
+
+ public DateFormat(String rawDateFormat) {
+ if (StringUtils.isBlank(rawDateFormat)) {
+ final ApiParameterError error =
ApiParameterError.parameterError("validation.msg.invalid.dateFormat.format",
+ "Dateformat is null", rawDateFormat);
+ throw new
PlatformApiDataValidationException("validation.msg.invalid.dateFormat.format",
"Validation errors exist.",
+ List.of(error));
+ } else {
+ String compatibleDateFormat = rawDateFormat.replace("yyyy",
"uuuu");
+ validate(compatibleDateFormat);
+ dateFormat = compatibleDateFormat;
+ }
+ }
+
+ private void validate(String dateTimeFormat) {
+ try {
+ DateTimeFormatter formatter = new
DateTimeFormatterBuilder().parseCaseInsensitive().parseLenient().appendPattern(dateTimeFormat)
+ .optionalStart().appendPattern("
HH:mm:ss").optionalEnd().parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
+ .parseDefaulting(ChronoField.MINUTE_OF_HOUR,
0).parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0).toFormatter()
+ .withResolverStyle(ResolverStyle.STRICT);
+ } catch (final IllegalArgumentException | DateTimeParseException e) {
+ final ApiParameterError error =
ApiParameterError.parameterError("validation.msg.invalid.dateFormat.format",
+ "Invalid dateFormat: `" + dateTimeFormat, dateTimeFormat);
+ throw new
PlatformApiDataValidationException("validation.msg.invalid.dateFormat.format",
"Validation errors exist.",
+ List.of(error), e);
+ }
+ }
+}
diff --git
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java
index d93b55519..da47385bc 100644
---
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java
+++
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java
@@ -27,6 +27,7 @@ import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
+import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
@@ -34,14 +35,17 @@ import java.time.MonthDay;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
+import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.data.DateFormat;
import
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.springframework.format.number.NumberStyleFormatter;
@@ -329,7 +333,7 @@ public class JsonParserHelper {
return value;
}
- public MonthDay extractMonthDayNamed(final String parameterName, final
JsonObject element, final String dateFormat,
+ public MonthDay extractMonthDayNamed(final String parameterName, final
JsonObject element, String dateFormat,
final Locale clientApplicationLocale) {
MonthDay value = null;
if (element.isJsonObject()) {
@@ -342,9 +346,9 @@ public class JsonParserHelper {
if (StringUtils.isNotBlank(valueAsString)) {
try {
final DateTimeFormatter formatter = new
DateTimeFormatterBuilder().parseCaseInsensitive().parseLenient()
-
.appendPattern(dateFormat).toFormatter(clientApplicationLocale);
+
.appendPattern(dateFormat).toFormatter(clientApplicationLocale).withResolverStyle(ResolverStyle.STRICT);
value = MonthDay.parse(valueAsString, formatter);
- } catch (final IllegalArgumentException e) {
+ } catch (final IllegalArgumentException |
DateTimeException e) {
final List<ApiParameterError> dataValidationErrors =
new ArrayList<>();
final ApiParameterError error =
ApiParameterError.parameterError("validation.msg.invalid.month.day",
"The parameter `" + parameterName + "` is
invalid based on the monthDayFormat: `" + dateFormat
@@ -396,7 +400,7 @@ public class JsonParserHelper {
if (element.isJsonObject()) {
final JsonObject object = element.getAsJsonObject();
- value = extractLocalDateTimeNamed(parameterName, element,
extractTimeFormatParameter(object), parametersPassedInCommand);
+ value = extractLocalDateTimeNamed(parameterName, element,
extractDateFormatParameter(object), parametersPassedInCommand);
}
return value;
}
@@ -437,11 +441,11 @@ public class JsonParserHelper {
parametersPassedInCommand.add(parameterName);
try {
- DateTimeFormatter timeFormtter =
DateTimeFormatter.ofPattern(timeFormat);
+ DateTimeFormatter timeFormatter =
DateTimeFormatter.ofPattern(timeFormat);
final JsonPrimitive primitive =
object.get(parameterName).getAsJsonPrimitive();
timeValueAsString = primitive.getAsString();
if (StringUtils.isNotBlank(timeValueAsString)) {
- value = LocalTime.parse(timeValueAsString,
timeFormtter);
+ value = LocalTime.parse(timeValueAsString,
timeFormatter);
}
} catch (IllegalArgumentException e) {
final List<ApiParameterError> dataValidationErrors = new
ArrayList<>();
@@ -459,28 +463,31 @@ public class JsonParserHelper {
return value;
}
- public LocalDateTime extractLocalDateTimeNamed(final String parameterName,
final JsonElement element, final String timeFormat,
+ public LocalDateTime extractLocalDateTimeNamed(final String parameterName,
final JsonElement element, String timeFormat,
final Locale clientApplicationLocale, final Set<String>
parametersPassedInCommand) {
LocalDateTime value = null;
- String timeValueAsString = null;
+ String timeValueAsString;
if (element.isJsonObject()) {
final JsonObject object = element.getAsJsonObject();
if (object.has(parameterName) &&
object.get(parameterName).isJsonPrimitive()) {
parametersPassedInCommand.add(parameterName);
try {
- DateTimeFormatter timeFormtter =
DateTimeFormatter.ofPattern(timeFormat);
+ timeFormat = timeFormat.replace("y", "u");
final JsonPrimitive primitive =
object.get(parameterName).getAsJsonPrimitive();
timeValueAsString = primitive.getAsString();
+ DateTimeFormatter timeFormatter = new
DateTimeFormatterBuilder().parseCaseInsensitive().parseLenient()
+
.appendPattern(timeFormat).toFormatter(clientApplicationLocale).withResolverStyle(ResolverStyle.STRICT);
if (StringUtils.isNotBlank(timeValueAsString)) {
- value = LocalDateTime.parse(timeValueAsString,
timeFormtter);
+ value = LocalDateTime.parse(timeValueAsString,
timeFormatter);
}
- } catch (IllegalArgumentException e) {
+ } catch (IllegalArgumentException | DateTimeParseException e) {
final List<ApiParameterError> dataValidationErrors = new
ArrayList<>();
- final String defaultMessage = new StringBuilder("The
parameter `" + timeValueAsString + "` is not in correct format.")
- .toString();
- final ApiParameterError error =
ApiParameterError.parameterError("validation.msg.invalid.TimeFormat",
defaultMessage,
- parameterName);
+ final ApiParameterError error = ApiParameterError
+
.parameterError("validation.msg.invalid.dateFormat.format",
+ "The parameter `" + parameterName + "` is
invalid based on the dateFormat: `" + timeFormat
+ + "` and locale: `" +
clientApplicationLocale + "` provided:",
+ parameterName, value, timeFormat);
dataValidationErrors.add(error);
throw new
PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.",
dataValidationErrors, e);
@@ -518,17 +525,27 @@ public class JsonParserHelper {
return convertDateTimeFrom(dateAsString, parameterName, dateFormat,
clientApplicationLocale).toLocalDate();
}
- public static LocalDateTime convertDateTimeFrom(final String
dateTimeAsString, final String parameterName, final String dateTimeFormat,
+ public static LocalDate convertFrom(final String dateAsString, final
String parameterName, final DateFormat dateFormat,
+ final Locale clientApplicationLocale) {
+
+ String rawDateFormat = Objects.isNull(dateFormat) ? null :
dateFormat.getDateFormat();
+
+ return convertDateTimeFrom(dateAsString, parameterName, rawDateFormat,
clientApplicationLocale).toLocalDate();
+ }
+
+ public static LocalDateTime convertDateTimeFrom(final String
dateTimeAsString, final String parameterName, String dateTimeFormat,
final Locale clientApplicationLocale) {
validateDateFormatAndLocale(parameterName, dateTimeFormat,
clientApplicationLocale);
LocalDateTime eventLocalDateTime = null;
if (StringUtils.isNotBlank(dateTimeAsString)) {
try {
+ dateTimeFormat = dateTimeFormat.replace("y", "u");
DateTimeFormatter formatter = new
DateTimeFormatterBuilder().parseCaseInsensitive().parseLenient()
.appendPattern(dateTimeFormat).optionalStart().appendPattern("
HH:mm:ss").optionalEnd()
.parseDefaulting(ChronoField.HOUR_OF_DAY,
0).parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
- .parseDefaulting(ChronoField.SECOND_OF_MINUTE,
0).toFormatter(clientApplicationLocale);
+ .parseDefaulting(ChronoField.SECOND_OF_MINUTE,
0).toFormatter(clientApplicationLocale)
+ .withResolverStyle(ResolverStyle.STRICT);
eventLocalDateTime = LocalDateTime.parse(dateTimeAsString,
formatter);
} catch (final IllegalArgumentException | DateTimeParseException
e) {
final List<ApiParameterError> dataValidationErrors = new
ArrayList<>();
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/api/JournalEntriesApiResource.java
b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/api/JournalEntriesApiResource.java
index c5fc810ab..c107c8a60 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/api/JournalEntriesApiResource.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/api/JournalEntriesApiResource.java
@@ -57,7 +57,9 @@ import
org.apache.fineract.infrastructure.bulkimport.data.GlobalEntityType;
import
org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookPopulatorService;
import
org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.api.DateParam;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.DateFormat;
import org.apache.fineract.infrastructure.core.data.UploadRequest;
import
org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
@@ -117,7 +119,7 @@ public class JournalEntriesApiResource {
@QueryParam("orderBy") @Parameter(description = "orderBy") final
String orderBy,
@QueryParam("sortOrder") @Parameter(description = "sortOrder")
final String sortOrder,
@QueryParam("locale") @Parameter(description = "locale") final
String locale,
- @QueryParam("dateFormat") @Parameter(description = "dateFormat")
final String dateFormat,
+ @QueryParam("dateFormat") @Parameter(description = "dateFormat")
final String rawDateFormat,
@QueryParam("loanId") @Parameter(description = "loanId") final
Long loanId,
@QueryParam("savingsId") @Parameter(description = "savingsId")
final Long savingsId,
@QueryParam("runningBalance") @Parameter(description =
"runningBalance") final boolean runningBalance,
@@ -125,6 +127,8 @@ public class JournalEntriesApiResource {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
+ final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ?
null : new DateFormat(rawDateFormat);
+
LocalDate fromDate = null;
if (fromDateParam != null) {
fromDate = fromDateParam.getDate("fromDate", dateFormat, locale);
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailApiResource.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailApiResource.java
index 85b33c6d6..50e90b12d 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailApiResource.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailApiResource.java
@@ -33,14 +33,16 @@ import jakarta.ws.rs.core.UriInfo;
import java.time.LocalDate;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
-import org.apache.fineract.accounting.journalentry.api.DateParam;
+import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.campaigns.email.data.EmailData;
import
org.apache.fineract.infrastructure.campaigns.email.service.EmailReadPlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.api.DateParam;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.DateFormat;
import
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.Page;
@@ -104,7 +106,10 @@ public class EmailApiResource {
@QueryParam("limit") final Integer limit, @QueryParam("status")
final Integer status,
@QueryParam("orderBy") final String orderBy,
@QueryParam("sortOrder") final String sortOrder,
@QueryParam("fromDate") final DateParam fromDateParam,
@QueryParam("toDate") final DateParam toDateParam,
- @QueryParam("locale") final String locale,
@QueryParam("dateFormat") final String dateFormat, @Context final UriInfo
uriInfo) {
+ @QueryParam("locale") final String locale,
@QueryParam("dateFormat") final String rawDateFormat,
+ @Context final UriInfo uriInfo) {
+
+ final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ?
null : new DateFormat(rawDateFormat);
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
LocalDate fromDate = null;
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/sms/api/SmsApiResource.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/sms/api/SmsApiResource.java
index 4e191ec1d..e6a7bc455 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/sms/api/SmsApiResource.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/sms/api/SmsApiResource.java
@@ -34,12 +34,14 @@ import jakarta.ws.rs.core.UriInfo;
import java.time.LocalDate;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
-import org.apache.fineract.accounting.journalentry.api.DateParam;
+import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.api.DateParam;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.DateFormat;
import
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.Page;
@@ -93,12 +95,14 @@ public class SmsApiResource {
public String retrieveAllSmsByStatus(@PathParam("campaignId") final Long
campaignId, @Context final UriInfo uriInfo,
@QueryParam("status") final Long status, @QueryParam("fromDate")
final DateParam fromDateParam,
@QueryParam("toDate") final DateParam toDateParam,
@QueryParam("locale") final String locale,
- @QueryParam("dateFormat") final String dateFormat,
@QueryParam("sqlSearch") final String sqlSearch,
+ @QueryParam("dateFormat") final String rawDateFormat,
@QueryParam("sqlSearch") final String sqlSearch,
@QueryParam("offset") final Integer offset, @QueryParam("limit")
final Integer limit,
@QueryParam("orderBy") final String orderBy,
@QueryParam("sortOrder") final String sortOrder) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
final SearchParameters searchParameters =
SearchParameters.forSMSCampaign(sqlSearch, offset, limit, orderBy, sortOrder);
+ final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ?
null : new DateFormat(rawDateFormat);
+
LocalDate fromDate = null;
if (fromDateParam != null) {
fromDate = fromDateParam.getDate("fromDate", dateFormat, locale);
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/interoperation/util/InteropUtil.java
b/fineract-provider/src/main/java/org/apache/fineract/interoperation/util/InteropUtil.java
index d93c24ec2..e120ac7b5 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/interoperation/util/InteropUtil.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/interoperation/util/InteropUtil.java
@@ -26,7 +26,7 @@ public final class InteropUtil {
}
- public static final String ISO8601_DATE_TIME_FORMAT =
"yyyy-MM-ddTHH:mm:ss.SSS[-HH:MM]";
+ public static final String ISO8601_DATE_TIME_FORMAT =
"yyyy-MM-dd'T'HH:mm:ss.SSS[-HH:MM]";
public static final String ISO8601_DATE_FORMAT = "yyyy-MM-dd";
public static final Locale DEFAULT_LOCALE = Locale.US;
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/organisation/holiday/api/HolidaysApiResource.java
b/fineract-provider/src/main/java/org/apache/fineract/organisation/holiday/api/HolidaysApiResource.java
index ddac2519d..916bf63aa 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/organisation/holiday/api/HolidaysApiResource.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/organisation/holiday/api/HolidaysApiResource.java
@@ -46,12 +46,13 @@ import java.time.LocalDate;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
-import org.apache.fineract.accounting.journalentry.api.DateParam;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.api.DateParam;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.DateFormat;
import
org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
@@ -192,10 +193,12 @@ public class HolidaysApiResource {
@QueryParam("fromDate") @Parameter(description = "fromDate") final
DateParam fromDateParam,
@QueryParam("toDate") @Parameter(description = "toDate") final
DateParam toDateParam,
@QueryParam("locale") @Parameter(description = "locale") final
String locale,
- @QueryParam("dateFormat") @Parameter(description = "dateFormat")
final String dateFormat) {
+ @QueryParam("dateFormat") @Parameter(description = "dateFormat")
final String rawDateFormat) {
this.context.authenticatedUser().validateHasReadPermission(HOLIDAY_RESOURCE_NAME);
+ final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ?
null : new DateFormat(rawDateFormat);
+
LocalDate fromDate = null;
if (fromDateParam != null) {
fromDate = fromDateParam.getDate("fromDate", dateFormat, locale);
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionHistoryApiResource.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionHistoryApiResource.java
index 4648830f1..ced9d6390 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionHistoryApiResource.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/StandingInstructionHistoryApiResource.java
@@ -35,8 +35,10 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo;
import java.time.LocalDate;
import lombok.RequiredArgsConstructor;
-import org.apache.fineract.accounting.journalentry.api.DateParam;
+import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.api.DateParam;
+import org.apache.fineract.infrastructure.core.data.DateFormat;
import
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.Page;
@@ -79,12 +81,14 @@ public class StandingInstructionHistoryApiResource {
@QueryParam("fromAccountId") @Parameter(description =
"fromAccountId") final Long fromAccount,
@QueryParam("fromAccountType") @Parameter(description =
"fromAccountType") final Integer fromAccountType,
@QueryParam("locale") @Parameter(description = "locale") final
String locale,
- @QueryParam("dateFormat") @Parameter(description = "dateFormat")
final String dateFormat,
+ @QueryParam("dateFormat") @Parameter(description = "dateFormat")
final String rawDateFormat,
@QueryParam("fromDate") @Parameter(description = "fromDate") final
DateParam fromDateParam,
@QueryParam("toDate") @Parameter(description = "toDate") final
DateParam toDateParam) {
this.context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
+ final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ?
null : new DateFormat(rawDateFormat);
+
final SearchParameters searchParameters =
SearchParameters.forAccountTransfer(sqlSearch, externalId, offset, limit,
orderBy,
sortOrder);
LocalDate startDateRange = null;
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/api/CentersApiResource.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/api/CentersApiResource.java
index 0b491d5df..35c273474 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/api/CentersApiResource.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/api/CentersApiResource.java
@@ -50,7 +50,6 @@ import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
-import org.apache.fineract.accounting.journalentry.api.DateParam;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
@@ -59,8 +58,10 @@ import
org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookP
import
org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookService;
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.api.DateParam;
import org.apache.fineract.infrastructure.core.api.JsonQuery;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.DateFormat;
import org.apache.fineract.infrastructure.core.data.PaginationParameters;
import org.apache.fineract.infrastructure.core.data.UploadRequest;
import
org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
@@ -170,7 +171,7 @@ public class CentersApiResource {
this.context.authenticatedUser().validateHasReadPermission(GroupingTypesApiConstants.CENTER_RESOURCE_NAME);
final ApiRequestJsonSerializationSettings settings =
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if (meetingDateParam != null && officeId != null) {
- LocalDate meetingDate = meetingDateParam.getDate("meetingDate",
dateFormat, locale);
+ LocalDate meetingDate = meetingDateParam.getDate("meetingDate",
new DateFormat(dateFormat), locale);
Collection<StaffCenterData> staffCenterDataArray =
this.centerReadPlatformService.retriveAllCentersByMeetingDate(officeId,
meetingDate, staffId);
return this.toApiJsonSerializer.serialize(settings,
staffCenterDataArray,
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
index 1fd74e59b..c1425e938 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
@@ -45,12 +45,14 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.AllArgsConstructor;
-import org.apache.fineract.accounting.journalentry.api.DateParam;
+import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.api.DateParam;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.DateFormat;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import
org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
@@ -111,10 +113,12 @@ public class LoanTransactionsApiResource {
@ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanTransactionsApiResourceSwagger.GetLoansLoanIdTransactionsTemplateResponse.class)))
})
public String retrieveTransactionTemplate(@PathParam("loanId")
@Parameter(description = "loanId", required = true) final Long loanId,
@QueryParam("command") @Parameter(description = "command") final
String commandParam, @Context final UriInfo uriInfo,
- @QueryParam("dateFormat") @Parameter(description = "dateFormat")
final String dateFormat,
+ @QueryParam("dateFormat") @Parameter(description = "dateFormat")
final String rawDateFormat,
@QueryParam("transactionDate") @Parameter(description =
"transactionDate") final DateParam transactionDateParam,
@QueryParam("locale") @Parameter(description = "locale") final
String locale) {
+ final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ?
null : new DateFormat(rawDateFormat);
+
return retrieveTransactionTemplate(loanId, null, commandParam,
uriInfo, dateFormat, transactionDateParam, locale);
}
@@ -139,10 +143,12 @@ public class LoanTransactionsApiResource {
public String retrieveTransactionTemplate(
@PathParam("loanExternalId") @Parameter(description =
"loanExternalId", required = true) final String loanExternalId,
@QueryParam("command") @Parameter(description = "command") final
String commandParam, @Context final UriInfo uriInfo,
- @QueryParam("dateFormat") @Parameter(description = "dateFormat")
final String dateFormat,
+ @QueryParam("dateFormat") @Parameter(description = "dateFormat")
final String rawDateFormat,
@QueryParam("transactionDate") @Parameter(description =
"transactionDate") final DateParam transactionDateParam,
@QueryParam("locale") @Parameter(description = "locale") final
String locale) {
+ final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ?
null : new DateFormat(rawDateFormat);
+
return retrieveTransactionTemplate(null, loanExternalId, commandParam,
uriInfo, dateFormat, transactionDateParam, locale);
}
@@ -476,7 +482,7 @@ public class LoanTransactionsApiResource {
}
private String retrieveTransactionTemplate(Long loanId, String
loanExternalIdStr, String commandParam, UriInfo uriInfo,
- String dateFormat, DateParam transactionDateParam, String locale) {
+ DateFormat dateFormat, DateParam transactionDateParam, String
locale) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
ExternalId loanExternalId =
ExternalIdFactory.produce(loanExternalIdStr);
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountDataValidator.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountDataValidator.java
index 5603a1526..a28eab439 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountDataValidator.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountDataValidator.java
@@ -720,10 +720,6 @@ public class DepositAccountDataValidator {
final JsonObject savingsChargeElement =
array.get(i).getAsJsonObject();
- // final Long id =
- // this.fromApiJsonHelper.extractLongNamed(idParamName,
- // savingsChargeElement);
-
final Long chargeId =
this.fromApiJsonHelper.extractLongNamed(chargeIdParamName,
savingsChargeElement);
baseDataValidator.reset().parameter(chargeIdParamName).value(chargeId).longGreaterThanZero();
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AccountingScenarioIntegrationTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AccountingScenarioIntegrationTest.java
index f9f72b8cf..db4dfe687 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AccountingScenarioIntegrationTest.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AccountingScenarioIntegrationTest.java
@@ -544,7 +544,7 @@ public class AccountingScenarioIntegrationTest {
LOG.info("--------------------------------APPLYING FOR FIXED DEPOSIT
ACCOUNT --------------------------------");
final String fixedDepositApplicationJSON = new
FixedDepositAccountHelper(requestSpec, responseSpec) //
.withSubmittedOnDate(submittedOnDate).build(clientID,
productID, penalInterestType);
- return
FixedDepositAccountHelper.applyFixedDepositApplication(fixedDepositApplicationJSON,
requestSpec, responseSpec);
+ return
FixedDepositAccountHelper.applyFixedDepositApplicationGetId(fixedDepositApplicationJSON,
requestSpec, responseSpec);
}
private Integer createRecurringDepositProduct(final String validFrom,
final String validTo, Account... accounts) {
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DateValidationTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DateValidationTest.java
new file mode 100644
index 000000000..57a5270fd
--- /dev/null
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DateValidationTest.java
@@ -0,0 +1,229 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.PostClientsRequest;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.accounting.Account;
+import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
+import
org.apache.fineract.integrationtests.common.fixeddeposit.FixedDepositAccountHelper;
+import
org.apache.fineract.integrationtests.common.fixeddeposit.FixedDepositProductHelper;
+import
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
+import
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.apache.fineract.integrationtests.interoperation.InteropHelper;
+import org.apache.fineract.interoperation.domain.InteropInitiatorType;
+import org.apache.fineract.interoperation.domain.InteropTransactionRole;
+import org.apache.fineract.interoperation.domain.InteropTransactionScenario;
+import org.apache.fineract.interoperation.util.InteropUtil;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class DateValidationTest {
+
+ public static final String WHOLE_TERM = "1";
+ public static final String MINIMUM_OPENING_BALANCE = "1000.0";
+ public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
+
+ private ResponseSpecification responseSpec;
+ private ResponseSpecification errorResponseSpec;
+ private RequestSpecification requestSpec;
+ private ClientHelper clientHelper;
+ private LoanTransactionHelper loanTransactionHelper;
+ private InteropHelper interopHelper;
+ private AccountHelper accountHelper;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ this.requestSpec = new
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ this.requestSpec.header("Authorization", "Basic " +
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ this.responseSpec = new
ResponseSpecBuilder().expectStatusCode(200).build();
+ this.errorResponseSpec = new
ResponseSpecBuilder().expectStatusCode(400).build();
+ this.clientHelper = new ClientHelper(this.requestSpec,
this.responseSpec);
+ this.loanTransactionHelper = new LoanTransactionHelper(requestSpec,
responseSpec);
+ this.interopHelper = new InteropHelper(requestSpec, errorResponseSpec);
+ this.accountHelper = new AccountHelper(requestSpec, responseSpec);
+ }
+
+ @Test
+ public void testShouldFailIfDateIsInvalid() {
+
+ String invalidDate = "31 June 2022";
+
+ PostClientsRequest postClientsRequest =
ClientHelper.defaultClientCreationRequest();
+
+ PostClientsResponse client =
clientHelper.createClient(postClientsRequest);
+ Long clientId = client.getClientId();
+
+ final String loanProductJSON = new
LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth()
+
.withRepaymentAfterEvery("1").withNumberOfRepayments("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0")
+
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsDecliningBalance()
+
.withAccountingRuleAsNone().withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withDaysInMonth("30")
+ .withDaysInYear("365").withMoratorium("0",
"0").withInArrearsTolerance("1001").withMultiDisburse()
+ .withDisallowExpectedDisbursements(true).build(null);
+ final Integer loanProductID =
loanTransactionHelper.getLoanProductId(loanProductJSON);
+
+ loanTransactionHelper = new LoanTransactionHelper(requestSpec,
errorResponseSpec);
+
+ final String loanApplicationJSON = new
LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1")
+
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("1").withRepaymentEveryAfter("1")
+
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance()
+
.withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod()
+
.withExpectedDisbursementDate(invalidDate).withSubmittedOnDate("01 March
2022").withLoanType("individual")
+ .build(clientId.toString(), loanProductID.toString(), null);
+ HashMap<String, Object> response = (HashMap)
loanTransactionHelper.createLoanAccount(loanApplicationJSON, "");
+ List<HashMap<String, Object>> errors = (List) response.get("errors");
+ assertNotNull(errors);
+ HashMap<String, Object> error = errors.get(0);
+ assertNotNull(error);
+ assertEquals(
+ "The parameter `expectedDisbursementDate` is invalid based on
the dateFormat: `dd MMMM uuuu` and locale: `en_GB` provided:",
+ error.get("developerMessage"));
+ }
+
+ @Test
+ public void testShouldFailWithInvalidDateTime() {
+ String requestCode = UUID.randomUUID().toString();
+ InteropTransactionRole role = InteropTransactionRole.PAYER;
+ String requestBody = buildRequestBody(requestCode, role);
+ String response = interopHelper.postTransactionRequest(requestCode,
role, requestBody);
+ HashMap<String, Object> map = new Gson().fromJson(response, new
TypeToken<HashMap<String, Object>>() {}.getType());
+ List<Map<String, Object>> errors = (List) map.get("errors");
+ assertNotNull(errors);
+ Map<String, Object> error = errors.get(0);
+ assertNotNull(error);
+ assertEquals("The parameter `expiration` is invalid based on the
dateFormat: `dd MMMM uuuu HH:mm:ss` and locale: `en` provided:",
+ error.get("developerMessage"));
+ }
+
+ @Test
+ public void testShouldFailWithInvalidMonthDay() {
+ final Account assetAccount = this.accountHelper.createAssetAccount();
+ final Account incomeAccount = this.accountHelper.createIncomeAccount();
+ final Account expenseAccount =
this.accountHelper.createExpenseAccount();
+ final Account liabilityAccount =
this.accountHelper.createLiabilityAccount();
+
+ DateTimeFormatter formatter = new
DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter(Locale.US);
+
+ LocalDate todaysDate =
LocalDate.now(ZoneId.systemDefault()).minusMonths(3);
+ final String VALID_FROM = todaysDate.format(formatter);
+ todaysDate = todaysDate.plusYears(10);
+ final String VALID_TO = todaysDate.format(formatter);
+
+ todaysDate = LocalDate.now(ZoneId.systemDefault()).minusMonths(1);
+ final String SUBMITTED_ON_DATE = todaysDate.format(formatter);
+
+ PostClientsRequest postClientsRequest =
ClientHelper.defaultClientCreationRequest();
+
+ Long clientId =
clientHelper.createClient(postClientsRequest).getClientId();
+ Assertions.assertNotNull(clientId);
+
+ Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM,
VALID_TO, assetAccount, liabilityAccount, incomeAccount,
+ expenseAccount);
+ Assertions.assertNotNull(fixedDepositProductId);
+
+ final Integer maturityInstructionId = 400;
+ String response = applyForFixedDepositApplication(clientId.toString(),
fixedDepositProductId.toString(), SUBMITTED_ON_DATE,
+ maturityInstructionId, getCharges());
+ HashMap<String, Object> map = new Gson().fromJson(response, new
TypeToken<HashMap<String, Object>>() {}.getType());
+ List<Map<String, Object>> errors = (List) map.get("errors");
+ assertNotNull(errors);
+ Map<String, Object> error = errors.get(0);
+ assertNotNull(error);
+ assertEquals("The parameter `feeOnMonthDay` is invalid based on the
monthDayFormat: `dd MMM` and locale: `en_GB` provided:",
+ error.get("developerMessage"));
+ }
+
+ private String buildRequestBody(final String requestCode, final
InteropTransactionRole role) {
+ HashMap<String, Object> map = new HashMap<>();
+ map.put(InteropUtil.PARAM_TRANSACTION_CODE,
UUID.randomUUID().toString());
+ map.put(InteropUtil.PARAM_REQUEST_CODE, requestCode);
+ map.put(InteropUtil.PARAM_ACCOUNT_ID, UUID.randomUUID().toString());
+ map.put(InteropUtil.PARAM_TRANSACTION_ROLE, role);
+ map.put(InteropUtil.PARAM_EXPIRATION, "31 November 2022 11:11:11");
+ map.put(InteropUtil.PARAM_LOCALE, "en");
+ map.put(InteropUtil.PARAM_DATE_FORMAT, "dd MMMM yyyy HH:mm:ss");
+
+ HashMap<String, Object> amountMap = new HashMap<>();
+ amountMap.put(InteropUtil.PARAM_AMOUNT, "10");
+ amountMap.put(InteropUtil.PARAM_CURRENCY, "EUR");
+ map.put(InteropUtil.PARAM_AMOUNT, amountMap);
+
+ HashMap<String, Object> typeMap = new HashMap<>();
+ typeMap.put(InteropUtil.PARAM_SCENARIO,
InteropTransactionScenario.PAYMENT);
+ typeMap.put(InteropUtil.PARAM_INITIATOR, InteropTransactionRole.PAYEE);
+ typeMap.put(InteropUtil.PARAM_INITIATOR_TYPE,
InteropInitiatorType.CONSUMER);
+ map.put(InteropUtil.PARAM_TRANSACTION_TYPE, typeMap);
+
+ return new Gson().toJson(map);
+ }
+
+ private Integer createFixedDepositProduct(final String validFrom, final
String validTo, Account... accounts) {
+ log.info("------------------------------CREATING NEW FIXED DEPOSIT
PRODUCT ---------------------------------------");
+ FixedDepositProductHelper fixedDepositProductHelper = new
FixedDepositProductHelper(this.requestSpec, this.responseSpec);
+ fixedDepositProductHelper =
fixedDepositProductHelper.withAccountingRuleAsCashBased(accounts);
+ final String fixedDepositProductJSON =
fixedDepositProductHelper.withPeriodRangeChart() //
+ .build(validFrom, validTo, true);
+ return
FixedDepositProductHelper.createFixedDepositProduct(fixedDepositProductJSON,
requestSpec, responseSpec);
+ }
+
+ private String applyForFixedDepositApplication(final String clientID,
final String productID, final String submittedOnDate,
+ final Integer maturityInstructionId, final List<HashMap<String,
String>> charges) {
+ log.info("--------------------------------APPLYING FOR FIXED DEPOSIT
ACCOUNT --------------------------------");
+ final String fixedDepositApplicationJSON = new
FixedDepositAccountHelper(this.requestSpec, this.errorResponseSpec) //
+
.withSubmittedOnDate(submittedOnDate).withMaturityInstructionId(maturityInstructionId).withCharges(charges)
+ .build(clientID, productID, WHOLE_TERM);
+ return
FixedDepositAccountHelper.applyFixedDepositApplication(fixedDepositApplicationJSON,
this.requestSpec,
+ this.errorResponseSpec);
+ }
+
+ private List<HashMap<String, String>> getCharges() {
+ List<HashMap<String, String>> list = new ArrayList<>();
+ HashMap<String, String> map = new HashMap<>();
+ map.put("feeOnMonthDay", "31 June");
+ list.add(map);
+ return list;
+ }
+}
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/FixedDepositTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/FixedDepositTest.java
index 5da091e8c..69741dcb5 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/FixedDepositTest.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/FixedDepositTest.java
@@ -2584,7 +2584,8 @@ public class FixedDepositTest {
log.info("--------------------------------APPLYING FOR FIXED DEPOSIT
ACCOUNT --------------------------------");
final String fixedDepositApplicationJSON = new
FixedDepositAccountHelper(this.requestSpec, this.responseSpec) //
.withSubmittedOnDate(submittedOnDate).build(clientID,
productID, penalInterestType);
- return
FixedDepositAccountHelper.applyFixedDepositApplication(fixedDepositApplicationJSON,
this.requestSpec, this.responseSpec);
+ return
FixedDepositAccountHelper.applyFixedDepositApplicationGetId(fixedDepositApplicationJSON,
this.requestSpec,
+ this.responseSpec);
}
private Integer applyForFixedDepositApplication(final String clientID,
final String productID, final String submittedOnDate,
@@ -2593,7 +2594,8 @@ public class FixedDepositTest {
final String fixedDepositApplicationJSON = new
FixedDepositAccountHelper(this.requestSpec, this.responseSpec) //
.withSubmittedOnDate(submittedOnDate).withMaturityInstructionId(maturityInstructionId)
.build(clientID, productID, penalInterestType);
- return
FixedDepositAccountHelper.applyFixedDepositApplication(fixedDepositApplicationJSON,
this.requestSpec, this.responseSpec);
+ return
FixedDepositAccountHelper.applyFixedDepositApplicationGetId(fixedDepositApplicationJSON,
this.requestSpec,
+ this.responseSpec);
}
private Integer applyForFixedDepositApplication(final String clientID,
final String productID, final String submittedOnDate,
@@ -2603,7 +2605,8 @@ public class FixedDepositTest {
//
.withSubmittedOnDate(submittedOnDate).withDepositPeriod(depositPeriod).withDepositAmount(depositAmount)
.build(clientID, productID, penalInterestType);
- return
FixedDepositAccountHelper.applyFixedDepositApplication(fixedDepositApplicationJSON,
this.requestSpec, this.responseSpec);
+ return
FixedDepositAccountHelper.applyFixedDepositApplicationGetId(fixedDepositApplicationJSON,
this.requestSpec,
+ this.responseSpec);
}
private Integer createSavingsProduct(final RequestSpecification
requestSpec, final ResponseSpecification responseSpec,
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java
index c6c7e3500..445437797 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java
@@ -1288,7 +1288,7 @@ public class SchedulerJobsTestResults {
final String fixedDepositApplicationJSON = new
FixedDepositAccountHelper(requestSpec, responseSpec)
.withSubmittedOnDate(submittedOnDate).withSavings(savingsId).transferInterest(true)
.withLockinPeriodFrequency("1",
FixedDepositAccountHelper.DAYS).build(clientID, productID, penalInterestType);
- return
FixedDepositAccountHelper.applyFixedDepositApplication(fixedDepositApplicationJSON,
requestSpec, responseSpec);
+ return
FixedDepositAccountHelper.applyFixedDepositApplicationGetId(fixedDepositApplicationJSON,
requestSpec, responseSpec);
}
private void validateNumberForEqualExcludePrecision(String val, String
val2) {
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java
index 9021b5d73..67860327b 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java
@@ -24,6 +24,7 @@ import io.restassured.specification.ResponseSpecification;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
+import java.util.List;
import org.apache.fineract.integrationtests.common.CommonConstants;
import org.apache.fineract.integrationtests.common.Utils;
import org.slf4j.Logger;
@@ -97,6 +98,7 @@ public class FixedDepositAccountHelper {
private String savingsId = null;
private boolean transferInterest = false;
private Integer maturityInstructionId;
+ private List<HashMap<String, String>> charges;
public String build(final String clientId, final String productId, final
String penalInterestType) {
final HashMap<String, Object> map = new HashMap<>();
@@ -129,19 +131,26 @@ public class FixedDepositAccountHelper {
map.put("linkAccountId", savingsId);
map.put("transferInterestToSavings", transferInterest);
map.put("maturityInstructionId", maturityInstructionId);
+ map.put("charges", charges);
String fixedDepositAccountJson = new Gson().toJson(map);
LOG.info("{}", fixedDepositAccountJson);
return fixedDepositAccountJson;
}
- public static Integer applyFixedDepositApplication(final String
fixedDepositAccountAsJson, final RequestSpecification requestSpec,
+ public static Integer applyFixedDepositApplicationGetId(final String
fixedDepositAccountAsJson, final RequestSpecification requestSpec,
final ResponseSpecification responseSpec) {
LOG.info("--------------------- APPLYING FOR FIXED DEPOSIT ACCOUNT
------------------------");
return Utils.performServerPost(requestSpec, responseSpec,
APPLY_FIXED_DEPOSIT_ACCOUNT_URL, fixedDepositAccountAsJson,
CommonConstants.RESPONSE_RESOURCE_ID);
}
+ public static String applyFixedDepositApplication(final String
fixedDepositAccountAsJson, final RequestSpecification requestSpec,
+ final ResponseSpecification responseSpec) {
+ LOG.info("--------------------- APPLYING FOR FIXED DEPOSIT ACCOUNT
------------------------");
+ return Utils.performServerPost(requestSpec, responseSpec,
APPLY_FIXED_DEPOSIT_ACCOUNT_URL, fixedDepositAccountAsJson);
+ }
+
public static HashMap getFixedDepositAccountById(final
RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final Integer accountID) {
final String GET_FIXED_DEPOSIT_BY_ID_URL = FIXED_DEPOSIT_ACCOUNT_URL +
"/" + accountID + "?" + Utils.TENANT_IDENTIFIER;
@@ -475,4 +484,9 @@ public class FixedDepositAccountHelper {
this.maturityInstructionId = maturityInstructionId;
return this;
}
+
+ public FixedDepositAccountHelper withCharges(List<HashMap<String, String>>
charges) {
+ this.charges = charges;
+ return this;
+ }
}
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/interoperation/InteropHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/interoperation/InteropHelper.java
index 0f4103e01..22a36c394 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/interoperation/InteropHelper.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/interoperation/InteropHelper.java
@@ -214,6 +214,15 @@ public class InteropHelper {
return response;
}
+ public String postTransactionRequest(String requestCode,
InteropTransactionRole role, String request) {
+ String url = buildUrl(REQUESTS_URL);
+ LOG.debug("Calling Interoperable POST Request: {}, body: {}", url,
request);
+
+ String response = Utils.performServerPost(requestSpec, responseSpec,
url, request, null);
+ LOG.debug("Response Interoperable POST Request: {}", response);
+ return response;
+ }
+
private String buildTransactionRequestJson(String requestCode,
InteropTransactionRole role) {
HashMap<String, Object> map = new HashMap<>();
map.put(InteropUtil.PARAM_TRANSACTION_CODE, transactionCode);