Savings fee code cleanup and added shcedule for paying charges

This commit is contained in:
ashok-conflux
2013-09-24 20:26:40 +05:30
parent 7e606a2d13
commit 7330ed08ea
27 changed files with 482 additions and 73 deletions
+2
View File
@@ -14344,8 +14344,10 @@ POST savingsaccounts/1/charges/2?command=paycharge
Content-Type: application/json
Request Body:
{
"dateFormat": "dd MMMM yyyy",
"locale": "en",
"amount": "60",
"dueDate": "12 September 2013"
}
</code>
<code class="method-response">
@@ -0,0 +1 @@
INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`, `task_priority`, `group_name`, `previous_run_start_time`, `next_run_time`, `job_key`, `initializing_errorlog`, `is_active`, `currently_running`, `updates_allowed`, `scheduler_group`, `is_misfired`) VALUES ('Pay Due Savings Charges', 'Pay Due Savings Charges', '0 0 12 * * ?', '2013-09-23 00:00:00', 5, NULL, NULL, NULL, 'Pay Due Savings ChargesJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0);
@@ -5,7 +5,7 @@ public enum JobName {
UPDATE_LOAN_SUMMARY("Update loan Summary"), UPDATE_LOAN_ARREARS_AGEING("Update Loan Arrears Ageing"), UPDATE_LOAN_PAID_IN_ADVANCE(
"Update Loan Paid In Advance"), APPLY_ANNUAL_FEE_FOR_SAVINGS("Apply Annual Fee For Savings"), APPLY_HOLIDAYS_TO_LOANS(
"Apply Holidays To Loans"), POST_INTEREST_FOR_SAVINGS("Post Interest For Savings"), TRANSFER_FEE_CHARGE_FOR_LOANS(
"Transfer Fee For Loans From Savings");
"Transfer Fee For Loans From Savings"), PAY_DUE_SAVINGS_CHARGES("Pay Due Savings Charges");
private final String name;
@@ -1,9 +1,14 @@
package org.mifosplatform.organisation.holiday.service;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface HolidayWritePlatformService {
CommandProcessingResult createHoliday(JsonCommand command);
boolean isHoliday(Long officeId, LocalDate transactionDate);
boolean isTransactionAllowedOnHoliday();
}
@@ -1,9 +1,11 @@
package org.mifosplatform.organisation.holiday.service;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
@@ -39,7 +41,7 @@ public class HolidayWritePlatformServiceJpaRepositoryImpl implements HolidayWrit
private final HolidayDataValidator fromApiJsonDeserializer;
private final HolidayRepository holidayRepository;
private final WorkingDaysRepositoryWrapper daysRepositoryWrapper;
private final ConfigurationDomainService configurationDomainService;
private final PlatformSecurityContext context;
private final OfficeRepository officeRepository;
private final FromJsonHelper fromApiJsonHelper;
@@ -47,13 +49,14 @@ public class HolidayWritePlatformServiceJpaRepositoryImpl implements HolidayWrit
@Autowired
public HolidayWritePlatformServiceJpaRepositoryImpl(final HolidayDataValidator fromApiJsonDeserializer,
final HolidayRepository holidayRepository, final PlatformSecurityContext context, final OfficeRepository officeRepository,
final FromJsonHelper fromApiJsonHelper, final WorkingDaysRepositoryWrapper daysRepositoryWrapper) {
final FromJsonHelper fromApiJsonHelper, final WorkingDaysRepositoryWrapper daysRepositoryWrapper, final ConfigurationDomainService configurationDomainService) {
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.holidayRepository = holidayRepository;
this.context = context;
this.officeRepository = officeRepository;
this.fromApiJsonHelper = fromApiJsonHelper;
this.daysRepositoryWrapper = daysRepositoryWrapper;
this.configurationDomainService = configurationDomainService;
}
@Transactional
@@ -152,4 +155,16 @@ public class HolidayWritePlatformServiceJpaRepositoryImpl implements HolidayWrit
toDate.toString(), repaymentsRescheduledTo.toString());
}
}
@Override
public boolean isHoliday(Long officeId, LocalDate transactionDate) {
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, transactionDate.toDate());
return HolidayUtil.isHoliday(transactionDate, holidays);
}
@Override
public boolean isTransactionAllowedOnHoliday() {
return this.configurationDomainService.allowTransactionsOnHolidayEnabled();
}
}
@@ -0,0 +1,10 @@
package org.mifosplatform.organisation.workingdays.service;
import org.joda.time.LocalDate;
public interface WorkingDaysWritePlatformService {
boolean isWorkingDay(LocalDate transactionDate);
boolean isTransactionAllowedOnNonWorkingDay();
}
@@ -0,0 +1,34 @@
package org.mifosplatform.organisation.workingdays.service;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class WorkingDaysWritePlatformServiceJpaRepositoryImpl implements WorkingDaysWritePlatformService {
private final WorkingDaysRepositoryWrapper daysRepositoryWrapper;
private final ConfigurationDomainService configurationDomainService;
@Autowired
public WorkingDaysWritePlatformServiceJpaRepositoryImpl(final WorkingDaysRepositoryWrapper daysRepositoryWrapper,
final ConfigurationDomainService configurationDomainService) {
this.daysRepositoryWrapper = daysRepositoryWrapper;
this.configurationDomainService = configurationDomainService;
}
@Override
public boolean isWorkingDay(LocalDate transactionDate) {
final WorkingDays workingDays = this.daysRepositoryWrapper.findOne();
return WorkingDaysUtil.isWorkingDay(workingDays, transactionDate);
}
@Override
public boolean isTransactionAllowedOnNonWorkingDay() {
return this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
}
}
@@ -43,7 +43,8 @@ public class ChargesApiResource {
private final Set<String> CHARGES_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "name", "amount", "currency", "penalty",
"active", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions",
"chargeAppliesToOptions", "chargeTimeTypeOptions", "currencyOptions"));
"chargeAppliesToOptions", "chargeTimeTypeOptions", "currencyOptions", "loanChargeCalculationTypeOptions",
"loanChargeTimeTypeOptions", "savingsChargeCalculationTypeOptions", "savingsChargeTimeTypeOptions"));
private final String resourceNameForPermissions = "CHARGE";
@@ -33,30 +33,53 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
private final EnumOptionData chargePaymentMode;
private final Collection<CurrencyData> currencyOptions;
private final List<EnumOptionData> chargeCalculationTypeOptions;
private final List<EnumOptionData> chargeAppliesToOptions;
private final List<EnumOptionData> chargeTimeTypeOptions;
private final List<EnumOptionData> chargePaymetModeOptions;
private final List<EnumOptionData> chargeCalculationTypeOptions;//
private final List<EnumOptionData> chargeAppliesToOptions;//
private final List<EnumOptionData> chargeTimeTypeOptions;//
private final List<EnumOptionData> chargePaymetModeOptions;//
private final List<EnumOptionData> loanChargeCalculationTypeOptions;
private final List<EnumOptionData> loanChargeTimeTypeOptions;
private final List<EnumOptionData> savingsChargeCalculationTypeOptions;
private final List<EnumOptionData> savingsChargeTimeTypeOptions;
public static ChargeData template(final Collection<CurrencyData> currencyOptions,
final List<EnumOptionData> chargeCalculationTypeOptions, final List<EnumOptionData> chargeAppliesToOptions,
final List<EnumOptionData> chargeTimeTypeOptions, final List<EnumOptionData> chargePaymentModeOptions) {
final List<EnumOptionData> chargeTimeTypeOptions, final List<EnumOptionData> chargePaymentModeOptions,
final List<EnumOptionData> loansChargeCalculationTypeOptions, final List<EnumOptionData> loansChargeTimeTypeOptions,
final List<EnumOptionData> savingsChargeCalculationTypeOptions, final List<EnumOptionData> savingsChargeTimeTypeOptions) {
return new ChargeData(null, null, null, null, null, null, null, null, false, false, currencyOptions, chargeCalculationTypeOptions,
chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions);
chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions,
loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions);
}
public static ChargeData withTemplate(final ChargeData charge, final ChargeData template) {
return new ChargeData(charge.id, charge.name, charge.amount, charge.currency, charge.chargeTimeType, charge.chargeAppliesTo,
charge.chargeCalculationType, charge.chargePaymentMode, charge.penalty, charge.active, template.currencyOptions,
template.chargeCalculationTypeOptions, template.chargeAppliesToOptions, template.chargeTimeTypeOptions,
template.chargePaymetModeOptions);
template.chargePaymetModeOptions, template.loanChargeCalculationTypeOptions, template.loanChargeTimeTypeOptions,
template.savingsChargeCalculationTypeOptions, template.savingsChargeTimeTypeOptions);
}
public static ChargeData instance(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
final EnumOptionData chargeTimeType, final EnumOptionData chargeAppliesTo, final EnumOptionData chargeCalculationType,
final EnumOptionData chargePaymentMode, final boolean penalty, final boolean active) {
final Collection<CurrencyData> currencyOptions = null;
final List<EnumOptionData> chargeCalculationTypeOptions = null;
final List<EnumOptionData> chargeAppliesToOptions = null;
final List<EnumOptionData> chargeTimeTypeOptions = null;
final List<EnumOptionData> chargePaymentModeOptions = null;
final List<EnumOptionData> loansChargeCalculationTypeOptions = null;
final List<EnumOptionData> loansChargeTimeTypeOptions = null;
final List<EnumOptionData> savingsChargeCalculationTypeOptions = null;
final List<EnumOptionData> savingsChargeTimeTypeOptions = null;
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
penalty, active, null, null, null, null, null);
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions);
}
public static ChargeData lookup(final Long id, final String name, final boolean isPenalty) {
@@ -73,9 +96,15 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
final List<EnumOptionData> chargeAppliesToOptions = null;
final List<EnumOptionData> chargeTimeTypeOptions = null;
final List<EnumOptionData> chargePaymentModeOptions = null;
final List<EnumOptionData> loansChargeCalculationTypeOptions = null;
final List<EnumOptionData> loansChargeTimeTypeOptions = null;
final List<EnumOptionData> savingsChargeCalculationTypeOptions = null;
final List<EnumOptionData> savingsChargeTimeTypeOptions = null;
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
chargePaymentModeOptions);
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions);
}
private ChargeData(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
@@ -83,7 +112,9 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
final EnumOptionData chargePaymentMode, final boolean penalty, final boolean active,
final Collection<CurrencyData> currencyOptions, final List<EnumOptionData> chargeCalculationTypeOptions,
final List<EnumOptionData> chargeAppliesToOptions, final List<EnumOptionData> chargeTimeTypeOptions,
final List<EnumOptionData> chargePaymentModeOptions) {
final List<EnumOptionData> chargePaymentModeOptions, final List<EnumOptionData> loansChargeCalculationTypeOptions,
final List<EnumOptionData> loansChargeTimeTypeOptions, final List<EnumOptionData> savingsChargeCalculationTypeOptions,
final List<EnumOptionData> savingsChargeTimeTypeOptions) {
this.id = id;
this.name = name;
this.amount = amount;
@@ -99,6 +130,10 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
this.chargeAppliesToOptions = chargeAppliesToOptions;
this.chargeTimeTypeOptions = chargeTimeTypeOptions;
this.chargePaymetModeOptions = chargePaymentModeOptions;
this.savingsChargeCalculationTypeOptions = savingsChargeCalculationTypeOptions;
this.savingsChargeTimeTypeOptions = savingsChargeTimeTypeOptions;
this.loanChargeCalculationTypeOptions = loansChargeCalculationTypeOptions;
this.loanChargeTimeTypeOptions = loansChargeTimeTypeOptions;
}
@Override
@@ -18,4 +18,13 @@ public interface ChargeDropdownReadPlatformService {
List<EnumOptionData> retrieveCollectionTimeTypes();
List<EnumOptionData> retrivePaymentModes();
List<EnumOptionData> retrieveLoanCalculationTypes();
List<EnumOptionData> retrieveLoanCollectionTimeTypes();
List<EnumOptionData> retrieveSavingsCalculationTypes();
List<EnumOptionData> retrieveSavingsCollectionTimeTypes();
}
@@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.charge.service;
import static org.mifosplatform.portfolio.charge.service.ChargeEnumerations.chargeCalculationType;
import static org.mifosplatform.portfolio.charge.service.ChargeEnumerations.chargePaymentMode;
import static org.mifosplatform.portfolio.charge.service.ChargeEnumerations.chargeTimeType;
import java.util.ArrayList;
import java.util.Arrays;
@@ -68,4 +69,30 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead
public List<EnumOptionData> retrivePaymentModes() {
return Arrays.asList(chargePaymentMode(ChargePaymentMode.REGULAR), chargePaymentMode(ChargePaymentMode.ACCOUNT_TRANSFER));
}
@Override
public List<EnumOptionData> retrieveLoanCalculationTypes() {
return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT), chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT));
}
@Override
public List<EnumOptionData> retrieveLoanCollectionTimeTypes() {
return Arrays.asList(chargeTimeType(ChargeTimeType.DISBURSEMENT), chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE));
}
@Override
public List<EnumOptionData> retrieveSavingsCalculationTypes() {
return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT)
//Enable when percentage of amount is supported for savings charges
//, chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT)
);
}
@Override
public List<EnumOptionData> retrieveSavingsCollectionTimeTypes() {
return Arrays.asList(chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE)
//enable when support is added for recurring Monthly and yearly charges
//, chargeTimeType(ChargeTimeType.MONTHLY), chargeTimeType(ChargeTimeType.YEARLY)
);
}
}
@@ -82,9 +82,17 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
final List<EnumOptionData> allowedChargeAppliesToOptions = this.chargeDropdownReadPlatformService.retrieveApplicableToTypes();
final List<EnumOptionData> allowedChargeTimeOptions = this.chargeDropdownReadPlatformService.retrieveCollectionTimeTypes();
final List<EnumOptionData> chargePaymentOptions = this.chargeDropdownReadPlatformService.retrivePaymentModes();
final List<EnumOptionData> loansChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService
.retrieveLoanCalculationTypes();
final List<EnumOptionData> loansChargeTimeTypeOptions = this.chargeDropdownReadPlatformService.retrieveLoanCollectionTimeTypes();
final List<EnumOptionData> savingsChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService
.retrieveSavingsCalculationTypes();
final List<EnumOptionData> savingsChargeTimeTypeOptions = this.chargeDropdownReadPlatformService
.retrieveSavingsCollectionTimeTypes();
return ChargeData.template(currencyOptions, allowedChargeCalculationTypeOptions, allowedChargeAppliesToOptions,
allowedChargeTimeOptions, chargePaymentOptions);
allowedChargeTimeOptions, chargePaymentOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions);
}
@Override
@@ -115,8 +115,17 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
final List<EnumOptionData> allowedChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService.retrieveCalculationTypes();
final List<EnumOptionData> allowedChargeTimeOptions = this.chargeDropdownReadPlatformService.retrieveCollectionTimeTypes();
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null);
final List<EnumOptionData> loansChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService
.retrieveLoanCalculationTypes();
final List<EnumOptionData> loansChargeTimeTypeOptions = this.chargeDropdownReadPlatformService.retrieveLoanCollectionTimeTypes();
final List<EnumOptionData> savingsChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService
.retrieveSavingsCalculationTypes();
final List<EnumOptionData> savingsChargeTimeTypeOptions = this.chargeDropdownReadPlatformService
.retrieveSavingsCollectionTimeTypes();
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
savingsChargeTimeTypeOptions);
}
@Override
@@ -192,4 +192,7 @@ public class SavingsApiConstants {
public static final Set<String> SAVINGS_ACCOUNT_CHARGES_ADD_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
chargeIdParamName, amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName));
public static final Set<String> SAVINGS_ACCOUNT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName));
}
@@ -155,7 +155,7 @@ public class SavingsAccountChargesApiResource {
@Path("{savingsAccountChargeId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String waiveSavingsAccountCharge(@PathParam("savingsAccountId") final Long savingsAccountId,
public String payOrWaiveSavingsAccountCharge(@PathParam("savingsAccountId") final Long savingsAccountId,
@PathParam("savingsAccountChargeId") final Long savingsAccountChargeId, @QueryParam("command") final String commandParam,
final String apiRequestBodyAsJson) {
@@ -6,6 +6,7 @@
package org.mifosplatform.portfolio.savings.data;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGES_ADD_REQUEST_DATA_PARAMETERS;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
@@ -18,6 +19,7 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
@@ -57,7 +59,8 @@ public class SavingsAccountChargeDataValidator {
baseDataValidator.reset().parameter(amountParamName).value(amount).notNull().positiveAmount();
if (this.fromApiJsonHelper.parameterExists(dueAsOfDateParamName, element)) {
this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, element);
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, element);
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate).notNull();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
@@ -79,12 +82,34 @@ public class SavingsAccountChargeDataValidator {
baseDataValidator.reset().parameter(amountParamName).value(amount).notNull().positiveAmount();
if (this.fromApiJsonHelper.parameterExists(dueAsOfDateParamName, element)) {
this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, element);
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, element);
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate).notNull();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validatePayCharge(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SAVINGS_ACCOUNT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(amountParamName, element);
baseDataValidator.reset().parameter(amountParamName).value(amount).notNull().positiveAmount();
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, element);
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate).notNull();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
@@ -14,6 +14,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeri
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
import java.math.BigDecimal;
import java.math.MathContext;
@@ -735,7 +736,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return transactionBeforeLastInterestPosting;
}
public void validateAccountBalanceDoesNotBecomeNegative(final BigDecimal transactionAmount) {
final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions();
Money runningBalance = Money.zero(this.currency);
@@ -1893,7 +1894,6 @@ public class SavingsAccount extends AbstractPersistable<Long> {
}
public boolean hasCurrencyCodeOf(final String matchingCurrencyCode) {
// TODO AA: revisit
if (this.currency == null) { return false; }
return this.currency.getCode().equalsIgnoreCase(matchingCurrencyCode);
}
@@ -1907,7 +1907,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME + "dueDate");
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME);
if (isClosed()) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.is.closed");
@@ -1935,13 +1935,6 @@ public class SavingsAccount extends AbstractPersistable<Long> {
// waive charge
final Money amountWaived = savingsAccountCharge.waive(getCurrency());
// TODO : AA revisit the code
/*
* Money feeChargesWaived = amountWaived; Money penaltyChargesWaived =
* Money.zero(this.getCurrency()); if
* (savingsAccountCharge.isPenaltyCharge()) { penaltyChargesWaived =
* amountWaived; feeChargesWaived = Money.zero(this.getCurrency()); }
*/
// persist an entry in transaction details
final SavingsAccountTransaction chargeTransaction = SavingsAccountTransaction.charge(this, office(),
@@ -1958,18 +1951,13 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME + "dueDate");
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME);
if (isClosed()) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.is.closed");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
if (isNotActive()) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.is.not.active");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
if (!hasCurrencyCodeOf(chargeDefinition.getCurrencyCode())) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
"transaction.invalid.account.currency.and.charge.currency.not.same");
@@ -1979,7 +1967,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final LocalDate chargeDueDate = savingsAccountCharge.getDueLocalDate();
if (chargeDueDate != null && chargeDueDate.isBefore(getActivationLocalDate())) {
baseDataValidator.reset().parameter("dueDate").value(getActivationLocalDate().toString(formatter))
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getActivationLocalDate().toString(formatter))
.failWithCodeNoParameterAddedToErrorCode("before.activationDate");
throw new PlatformApiDataValidationException(dataValidationErrors);
}
@@ -1991,12 +1979,12 @@ public class SavingsAccount extends AbstractPersistable<Long> {
}
public void payCharge(final Long savingsAccountChargeId, final BigDecimal amountPaid, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
public void payCharge(final Long savingsAccountChargeId, final BigDecimal amountPaid, final LocalDate transactionDate,
final DateTimeFormatter formatter, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME + "dueDate");
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME);
if (isClosed()) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.is.closed");
@@ -2008,6 +1996,12 @@ public class SavingsAccount extends AbstractPersistable<Long> {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
if (transactionDate != null && transactionDate.isBefore(getActivationLocalDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getActivationLocalDate().toString(formatter))
.failWithCodeNoParameterAddedToErrorCode("before.activationDate");
throw new PlatformApiDataValidationException(dataValidationErrors);
}
final SavingsAccountCharge savingsAccountCharge = getCharge(savingsAccountChargeId);
// validate charge is not already paid or waived
@@ -2018,17 +2012,19 @@ public class SavingsAccount extends AbstractPersistable<Long> {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.charge.is.paid");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
final Money amountPaidMoney = Money.of(currency, amountPaid);
if (!savingsAccountCharge.getAmountOutstanding(getCurrency()).isGreaterThanOrEqualTo(amountPaidMoney)){
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.charge.amount.paid.in.access");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
final Money chargePaid = savingsAccountCharge.pay(getCurrency(), amountPaid);
// If null reset to current date
final LocalDate transactionDate = (savingsAccountCharge.getDueLocalDate() == null) ? DateUtils.getLocalDateOfTenant()
: savingsAccountCharge.getDueLocalDate();
// persist an entry in transaction details
final Money chargePaid = savingsAccountCharge.pay(getCurrency(), amountPaidMoney);
// persist transaction details
final SavingsAccountTransaction chargeTransaction = SavingsAccountTransaction.charge(this, office(), transactionDate, chargePaid);
final SavingsAccountChargePaidBy chargePaidBy = SavingsAccountChargePaidBy.instance(chargeTransaction, savingsAccountCharge,
@@ -2051,6 +2047,36 @@ public class SavingsAccount extends AbstractPersistable<Long> {
}
public void payDueCharge(final SavingsAccountCharge savingsAccountCharge, final LocalDate transactionDate, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
final Money amountPaid = savingsAccountCharge.payDueCharge(getCurrency());
// persist transaction details
final SavingsAccountTransaction chargeTransaction = SavingsAccountTransaction.charge(this, office(), transactionDate, amountPaid);
final SavingsAccountChargePaidBy chargePaidBy = SavingsAccountChargePaidBy.instance(chargeTransaction, savingsAccountCharge,
amountPaid.getAmount());
chargeTransaction.getSavingsAccountChargesPaid().add(chargePaidBy);
this.transactions.add(chargeTransaction);
final MathContext mc = MathContext.DECIMAL64;
if (isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
calculateInterestUsing(mc, today);
}
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
validateAccountBalanceDoesNotBecomeNegative(amountPaid.getAmount());
}
private SavingsAccountCharge getCharge(final Long savingsAccountChargeId) {
SavingsAccountCharge charge = null;
for (final SavingsAccountCharge existingCharge : this.charges) {
@@ -228,13 +228,25 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
return getAmountWaived(currency);
}
public Money pay(final MonetaryCurrency currency, final BigDecimal chargePaid) {
public Money pay(final MonetaryCurrency currency, final Money amountPaid) {
Money amountPaidToDate = Money.of(currency, this.amountPaid);
final Money amountPaid = Money.of(currency, chargePaid);
Money amountOutstanding = Money.of(currency, this.amountOutstanding);
amountPaidToDate = amountPaidToDate.plus(amountPaid);
amountOutstanding = amountOutstanding.minus(chargePaid);
amountOutstanding = amountOutstanding.minus(amountPaid);
this.amountPaid = amountPaidToDate.getAmount();
this.amountOutstanding = amountOutstanding.getAmount();
this.paid = determineIfFullyPaid();
return amountPaid;
}
public Money payDueCharge(final MonetaryCurrency currency) {
Money amountPaidToDate = Money.of(currency, this.amountPaid);
Money amountOutstanding = Money.of(currency, this.amountOutstanding);
final Money amountPaid = Money.of(currency, this.amountOutstanding);;
amountPaidToDate = amountPaidToDate.plus(amountPaid);
amountOutstanding = amountOutstanding.minus(amountPaid);
this.amountPaid = amountPaidToDate.getAmount();
this.amountOutstanding = amountOutstanding.getAmount();
this.paid = determineIfFullyPaid();
@@ -440,6 +452,10 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
return Money.of(currency, this.amountWrittenOff);
}
public Money getAmountOutstanding(final MonetaryCurrency currency){
return Money.of(currency, this.amountOutstanding);
}
/**
* @param incrementBy
* Amount used to pay off this charge
@@ -486,6 +502,10 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
return ChargeTimeType.fromInt(this.chargeTime).isOnSpecifiedDueDate();
}
public SavingsAccount savingsAccount(){
return this.savingsAccount;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) { return false; }
@@ -5,11 +5,19 @@
*/
package org.mifosplatform.portfolio.savings.domain;
import java.util.Date;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface SavingsAccountChargeRepository extends JpaRepository<SavingsAccountCharge, Long>,
JpaSpecificationExecutor<SavingsAccountCharge> {
SavingsAccountCharge findByIdAndSavingsAccountId(Long id, Long savingsAccountId);
@Query("from SavingsAccountCharge sac where sac.dueDate <=:transactionDate and sac.waived = 0 and sac.paid=0")
List<SavingsAccountCharge> findPendingCharges(@Param("transactionDate") Date transactionDate);
}
@@ -5,6 +5,10 @@
*/
package org.mifosplatform.portfolio.savings.domain;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -36,11 +40,19 @@ public class SavingsAccountChargeRepositoryWrapper {
if (savingsAccountCharge == null) { throw new SavingsAccountChargeNotFoundException(id); }
return savingsAccountCharge;
}
public List<SavingsAccountCharge> findPendingCharges(final Date transactionDate){
return this.repository.findPendingCharges(transactionDate);
}
public void save(final SavingsAccountCharge savingsAccountCharge) {
this.repository.save(savingsAccountCharge);
}
public void save(final Iterable<SavingsAccountCharge> savingsAccountCharges){
this.repository.save(savingsAccountCharges);
}
public void saveAndFlush(final SavingsAccountCharge savingsAccountCharge) {
this.repository.saveAndFlush(savingsAccountCharge);
}
@@ -10,6 +10,7 @@ import java.util.List;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Transient;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
@@ -49,6 +50,13 @@ public final class SavingsAccountSummary {
@Column(name = "account_balance_derived", scale = 6, precision = 19)
private BigDecimal accountBalance = BigDecimal.ZERO;
//TODO: AA do we need this data to be persisted.
@Transient
private BigDecimal totalFeeChargesWaived = BigDecimal.ZERO;
@Transient
private BigDecimal totalPenaltyChargesWaived = BigDecimal.ZERO;
protected SavingsAccountSummary() {
//
}
@@ -63,6 +71,8 @@ public final class SavingsAccountSummary {
this.totalAnnualFees = wrapper.calculateTotalAnnualFees(currency, transactions);
this.totalFeeCharge = wrapper.calculateTotalFeesCharge(currency, transactions);
this.totalPenaltyCharge = wrapper.calculateTotalPenaltyCharge(currency, transactions);
this.totalFeeChargesWaived = wrapper.calculateTotalFeesChargeWaived(currency, transactions);
this.totalPenaltyChargesWaived = wrapper.calculateTotalPenaltyChargeWaived(currency, transactions);
this.accountBalance = Money.of(currency, this.totalDeposits).plus(this.totalInterestPosted).minus(this.totalWithdrawals)
.minus(this.totalWithdrawalFees).minus(this.totalAnnualFees).minus(this.totalFeeCharge).minus(this.totalPenaltyCharge)
@@ -437,6 +437,10 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
public boolean isCharge() {
return SavingsAccountTransactionType.fromInt(this.typeOf).isCharge();
}
public boolean isWaiveCharge(){
return SavingsAccountTransactionType.fromInt(this.typeOf).isWaiveCharge();
}
public boolean isFeeCharge() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
@@ -456,6 +460,24 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
return isPenaltyCharge() && isNotReversed();
}
public boolean isWaiveFeeCharge() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return (isWaiveCharge() && chargePaidBy != null) ? chargePaidBy.isFeeCharge() : false;
}
public boolean isWaivePenaltyCharge() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return (isWaiveCharge() && chargePaidBy != null) ? chargePaidBy.isPenaltyCharge() : false;
}
public boolean isWaiveFeeChargeAndNotReversed() {
return isWaiveFeeCharge() && isNotReversed();
}
public boolean isWaivePenaltyChargeAndNotReversed() {
return isWaivePenaltyCharge() && isNotReversed();
}
private SavingsAccountChargePaidBy getSavingsAccountChargePaidBy() {
if (!CollectionUtils.isEmpty(this.savingsAccountChargesPaid)) { return this.savingsAccountChargesPaid.iterator().next(); }
return null;
@@ -72,17 +72,37 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalFeesCharge(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
if (transaction.isFeeChargeAndNotReversed() && transaction.isNotReversed()) {
if (transaction.isFeeChargeAndNotReversed()) {
total = total.plus(transaction.getAmount(currency));
}
}
return total.getAmountDefaultedToNullIfZero();
}
public BigDecimal calculateTotalFeesChargeWaived(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
if (transaction.isWaiveFeeChargeAndNotReversed()) {
total = total.plus(transaction.getAmount(currency));
}
}
return total.getAmountDefaultedToNullIfZero();
}
public BigDecimal calculateTotalPenaltyCharge(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
if (transaction.isPenaltyChargeAndNotReversed() && transaction.isNotReversed()) {
if (transaction.isPenaltyChargeAndNotReversed()) {
total = total.plus(transaction.getAmount(currency));
}
}
return total.getAmountDefaultedToNullIfZero();
}
public BigDecimal calculateTotalPenaltyChargeWaived(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
if (transaction.isWaivePenaltyChargeAndNotReversed()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -27,6 +27,6 @@ public class WaiveSavingsAccountChargeCommandHandler implements NewCommandSource
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.waiveCharge(command.getSavingsId(), command.entityId(), command);
return this.writePlatformService.waiveCharge(command.getSavingsId(), command.entityId());
}
}
@@ -112,9 +112,18 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
final List<EnumOptionData> allowedChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService.retrieveCalculationTypes();
final List<EnumOptionData> allowedChargeTimeOptions = this.chargeDropdownReadPlatformService.retrieveCollectionTimeTypes();
final List<EnumOptionData> loansChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService
.retrieveLoanCalculationTypes();
final List<EnumOptionData> loansChargeTimeTypeOptions = this.chargeDropdownReadPlatformService.retrieveLoanCollectionTimeTypes();
final List<EnumOptionData> savingsChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService
.retrieveSavingsCalculationTypes();
final List<EnumOptionData> savingsChargeTimeTypeOptions = this.chargeDropdownReadPlatformService
.retrieveSavingsCollectionTimeTypes();
// TODO AA : revisit for merge conflict - Not sure method signature
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null);
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
savingsChargeTimeTypeOptions);
}
@Override
@@ -48,7 +48,9 @@ public interface SavingsAccountWritePlatformService {
CommandProcessingResult deleteSavingsAccountCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command);
CommandProcessingResult waiveCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command);
CommandProcessingResult waiveCharge(Long savingsAccountId, Long savingsAccountChargeId);
CommandProcessingResult payCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command);
void payDueCharges();
}
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.savings.service;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
import java.math.BigDecimal;
import java.math.MathContext;
@@ -34,11 +35,13 @@ import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
import org.mifosplatform.infrastructure.jobs.service.JobName;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.holiday.service.HolidayWritePlatformService;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.organisation.workingdays.service.WorkingDaysWritePlatformService;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
import org.mifosplatform.portfolio.account.service.AccountAssociationsReadPlatformService;
import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService;
@@ -65,6 +68,7 @@ import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionRepository;
import org.mifosplatform.portfolio.savings.exception.InsufficientAccountBalanceException;
import org.mifosplatform.portfolio.savings.exception.SavingsAccountClosingNotAllowedException;
import org.mifosplatform.portfolio.savings.exception.SavingsAccountTransactionNotFoundException;
import org.mifosplatform.portfolio.savings.exception.TransactionUpdateNotAllowedException;
@@ -91,6 +95,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
private final ChargeRepositoryWrapper chargeRepository;
private final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository;
private final HolidayWritePlatformService holidayWritePlatformService;
private final WorkingDaysWritePlatformService workingDaysWritePlatformService;
@Autowired
public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -105,7 +111,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final SavingsAccountDomainService savingsAccountDomainService, final NoteRepository noteRepository,
final AccountTransfersReadPlatformService accountTransfersReadPlatformService,
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
final ChargeRepositoryWrapper chargeRepository, final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository) {
final ChargeRepositoryWrapper chargeRepository, final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository,
final HolidayWritePlatformService holidayWritePlatformService,
final WorkingDaysWritePlatformService workingDaysWritePlatformService) {
this.context = context;
this.savingAccountRepository = savingAccountRepository;
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
@@ -121,6 +129,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
this.chargeRepository = chargeRepository;
this.savingsAccountChargeRepository = savingsAccountChargeRepository;
this.holidayWritePlatformService = holidayWritePlatformService;
this.workingDaysWritePlatformService = workingDaysWritePlatformService;
}
@Transactional
@@ -586,6 +596,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
public CommandProcessingResult addSavingsAccountCharge(final JsonCommand command) {
this.context.authenticatedUser();
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME);
final Long savingsAccountId = command.getSavingsId();
this.savingsAccountChargeDataValidator.validateAdd(command.json());
@@ -599,6 +613,23 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewFromJson(savingsAccount, chargeDefinition, command);
if (savingsAccountCharge.getDueLocalDate() != null) {
// transaction date should not be on a holiday or non working day
if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday()
&& this.holidayWritePlatformService.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
if (!this.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay()
&& !this.workingDaysWritePlatformService.isWorkingDay(savingsAccountCharge.getDueLocalDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
savingsAccount.addCharge(fmt, savingsAccountCharge, chargeDefinition);
this.savingAccountRepository.saveAndFlush(savingsAccount);
@@ -616,9 +647,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional
@Override
public CommandProcessingResult updateSavingsAccountCharge(final JsonCommand command) {
this.context.authenticatedUser();
this.savingsAccountChargeDataValidator.validateUpdate(command.json());
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(SAVINGS_ACCOUNT_RESOURCE_NAME);
final Long savingsAccountId = command.getSavingsId();
// SavingsAccount Charge entity
final Long savingsChargeId = command.entityId();
@@ -630,20 +664,30 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
savingsAccountId);
final Map<String, Object> changes = savingsAccountCharge.update(command);
if (savingsAccountCharge.getDueLocalDate() != null) {
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
// transaction date should not be on a holiday or non working day
if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday()
&& this.holidayWritePlatformService.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
if (!this.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay()
&& !this.workingDaysWritePlatformService.isWorkingDay(savingsAccountCharge.getDueLocalDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
this.savingsAccountChargeRepository.saveAndFlush(savingsAccountCharge);
// TODO AA: revisit the code
/*
* // Charges may be edited only when the loan associated with them are
* // yet to be approved (are in submitted and pending status) if
* (!loan.status().isSubmittedAndPendingApproval()) { throw new
* LoanChargeCannotBeUpdatedException(
* LOAN_CHARGE_CANNOT_BE_UPDATED_REASON
* .LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE,
* loanCharge.getId()); }
*/
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsChargeId) //
// .withOfficeId(savings) //
// .withClientId(loan.getClientId()) //
@@ -655,7 +699,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional
@Override
public CommandProcessingResult waiveCharge(final Long savingsAccountId, final Long savingsAccountChargeId, final JsonCommand command) {
public CommandProcessingResult waiveCharge(final Long savingsAccountId, final Long savingsAccountChargeId) {
this.context.authenticatedUser();
@@ -670,7 +714,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.savingAccountRepository.saveAndFlush(savingsAccount);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsAccountChargeId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
@@ -707,21 +750,45 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional
@Override
public CommandProcessingResult payCharge(final Long savingsAccountId, final Long savingsAccountChargeId, final JsonCommand command) {
this.context.authenticatedUser();
this.context.authenticatedUser();
this.savingsAccountChargeDataValidator.validatePayCharge(command.json());
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsAccountId);
checkClientOrGroupActive(savingsAccount);
final BigDecimal chargeAmount = command.bigDecimalValueOfParameterNamed(amountParamName);
savingsAccount.payCharge(savingsAccountChargeId, chargeAmount, existingTransactionIds, existingReversedTransactionIds);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(SAVINGS_ACCOUNT_RESOURCE_NAME);
//transaction date should not be on a holiday or non working day
if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday()
&& this.holidayWritePlatformService.isHoliday(savingsAccount.officeId(), transactionDate)) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.on.holiday");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
if (!this.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay()
&& !this.workingDaysWritePlatformService.isWorkingDay(transactionDate)) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.a.nonworking.day");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
savingsAccount.payCharge(savingsAccountChargeId, chargeAmount, transactionDate, fmt, existingTransactionIds, existingReversedTransactionIds);
this.savingAccountRepository.saveAndFlush(savingsAccount);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsAccountChargeId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
@@ -731,4 +798,33 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
}
@Transactional
@Override
@CronTarget(jobName = JobName.PAY_DUE_SAVINGS_CHARGES)
public void payDueCharges() {
//always use current date as transaction date for batch job
final LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
final List<SavingsAccountCharge> pendingCharges = this.savingsAccountChargeRepository.findPendingCharges(transactionDate.toDate());
final List<SavingsAccountCharge> paidCharges = new ArrayList<SavingsAccountCharge>(pendingCharges.size());
for (SavingsAccountCharge savingsAccountCharge : pendingCharges) {
// Get Savings account from savings charge
final SavingsAccount account = savingsAccountCharge.savingsAccount();
this.savingAccountAssembler.assignSavingAccountHelpers(account);
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
try {
account.payDueCharge(savingsAccountCharge, transactionDate, existingTransactionIds, existingReversedTransactionIds);
paidCharges.add(savingsAccountCharge);
} catch (InsufficientAccountBalanceException isab) {
// TODO: AA - provide a meaningful error with failed
// transactions list
}
}
this.savingsAccountChargeRepository.save(paidCharges);
}
}