Merge pull request #628 from ashok-conflux/MIFOSX-728
Add recurring fee support for savings
This commit is contained in:
+92
-2
@@ -11035,7 +11035,8 @@ Request Body:
|
||||
2 = Specified Due Date : Adhoc charge can be applied for loans and savings on a specified due date.<br/>
|
||||
3 = Savings Activation : This charge is applicable for savings account and charged at the time of account activation. This charge will be autodeducted once the account is activated with sufficient opening balance.<br/>
|
||||
5 = Withdrawal fee : Charge applied to every withdrawal of savings account.<br/>
|
||||
6 = Annual Fee : Recurring charge applied annually for savings on a specified Month and day.
|
||||
6 = Annual Fee : Recurring charge applied annually for savings on a specified Month and day.<br/>
|
||||
7 = Monthly Fee : Recurring charge applied on regular interval of months for savings.
|
||||
</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
@@ -11052,6 +11053,22 @@ Request Body:
|
||||
<td class=fielddesc>Rules for calculating the amount for a particular Charge.
|
||||
Refer <a href="#charges_template">Charges Template</a> for additional details</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>feeOnMonthDay</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>Used along with <i>monthDayFormat</i> to indicate the recurring charge due date starting on a given day of the month and follow specified <i>feeInterval</i>. e.g. fee due date starting on <b>10 May</b> with <i>feeInterval</i> of two (2) months then a recurring fee on 10th of every two months is applied to savings account. <i>monthDayFormat</i> indicates day and month formatting used e.g. "dd MMM" for 10 May <br/>
|
||||
This field is mandatory if <i>chargeTimeType</i> is of type <b>Monthly Fee</b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>feeInterval</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>Fee to be applied on a specified interval (number of months).<br/>
|
||||
This field is mandatory if <i>chargeTimeType</i> is of type <b>Monthly Fee</b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -11144,6 +11161,11 @@ GET https://DomainName/api/v1/charges/template
|
||||
"id": 6,
|
||||
"code": "chargeTimeType.annualFee",
|
||||
"value": "Annual Fee"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"code": "chargeTimeType.monthlyFee",
|
||||
"value": "Monthly Fee"
|
||||
}
|
||||
],
|
||||
"chargePaymetModeOptions":
|
||||
@@ -11201,6 +11223,10 @@ GET https://DomainName/api/v1/charges/template
|
||||
{ "code" : "chargeTimeType.annualFee",
|
||||
"id" : 6,
|
||||
"value" : "Annual Fee"
|
||||
},
|
||||
{ "code" : "chargeTimeType.monthlyFee",
|
||||
"id" : 7,
|
||||
"value" : "Monthly Fee"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -11283,7 +11309,31 @@ Request Body:
|
||||
{
|
||||
"resourceId": 5
|
||||
}
|
||||
</code>
|
||||
</code>
|
||||
<code class="method-request">
|
||||
POST charges
|
||||
Content-Type: application/json
|
||||
Request Body:
|
||||
{
|
||||
"locale": "en",
|
||||
"name": "Quarterly Fee",
|
||||
"amount": "5",
|
||||
"currencyCode": "USD",
|
||||
"chargeAppliesTo": "2",
|
||||
"chargeTimeType": "7",
|
||||
"feeOnMonthDay": "10 May",
|
||||
"monthDayFormat": "dd MMM",
|
||||
"feeInterval": "4",
|
||||
"chargeCalculationType": "1",
|
||||
"active": "true",
|
||||
"penalty": "false"
|
||||
}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"resourceId": 6
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13242,6 +13292,46 @@ Content-Type: application/json
|
||||
"code": "chargepaymentmode.regular",
|
||||
"value": "Regular"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "Quarterly fee",
|
||||
"active": true,
|
||||
"penalty": false,
|
||||
"currency": {
|
||||
"code": "USD",
|
||||
"name": "US Dollar",
|
||||
"decimalPlaces": 2,
|
||||
"displaySymbol": "$",
|
||||
"nameCode": "currency.USD",
|
||||
"displayLabel": "US Dollar ($)"
|
||||
},
|
||||
"amount": 5,
|
||||
"chargeTimeType": {
|
||||
"id": 7,
|
||||
"code": "chargeTimeType.monthlyFee",
|
||||
"value": "Monthly Fee"
|
||||
},
|
||||
"chargeAppliesTo": {
|
||||
"id": 2,
|
||||
"code": "chargeAppliesTo.savings",
|
||||
"value": "Savings"
|
||||
},
|
||||
"chargeCalculationType": {
|
||||
"id": 1,
|
||||
"code": "chargeCalculationType.flat",
|
||||
"value": "Flat"
|
||||
},
|
||||
"chargePaymentMode": {
|
||||
"id": 0,
|
||||
"code": "chargepaymentmode.regular",
|
||||
"value": "Regular"
|
||||
},
|
||||
"feeOnMonthDay": [
|
||||
10,
|
||||
5
|
||||
],
|
||||
"feeInterval": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
ALTER TABLE `m_charge`
|
||||
ADD COLUMN `fee_on_day` SMALLINT(5) NULL AFTER `amount`,
|
||||
ADD COLUMN `fee_interval` SMALLINT(5) NULL AFTER `fee_on_day`,
|
||||
ADD COLUMN `fee_on_month` SMALLINT(5) NULL AFTER `fee_interval`;
|
||||
|
||||
|
||||
ALTER TABLE `m_savings_account_charge`
|
||||
ADD COLUMN `fee_interval` SMALLINT(5) NULL DEFAULT NULL AFTER `fee_on_day`;
|
||||
+1
-1
@@ -299,7 +299,7 @@ public class JsonParserHelper {
|
||||
} catch (final IllegalArgumentException e) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.month.day",
|
||||
"The parameter " + parameterName + " is invalid based on the dateFormat: '" + dateFormat
|
||||
"The parameter " + parameterName + " is invalid based on the monthDayFormat: '" + dateFormat
|
||||
+ "' and locale: '" + clientApplicationLocale + "' provided:", parameterName, valueAsString,
|
||||
dateFormat);
|
||||
dataValidationErrors.add(error);
|
||||
|
||||
+13
-8
@@ -32,6 +32,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
private final EnumOptionData chargeAppliesTo;
|
||||
private final EnumOptionData chargeCalculationType;
|
||||
private final EnumOptionData chargePaymentMode;
|
||||
private final MonthDay feeOnMonthDay;
|
||||
private final Integer feeInterval;
|
||||
|
||||
private final Collection<CurrencyData> currencyOptions;
|
||||
private final List<EnumOptionData> chargeCalculationTypeOptions;//
|
||||
@@ -52,7 +54,7 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
|
||||
return new ChargeData(null, null, null, null, null, null, null, null, false, false, currencyOptions, chargeCalculationTypeOptions,
|
||||
chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions,
|
||||
loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions);
|
||||
loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, null, null);
|
||||
}
|
||||
|
||||
public static ChargeData withTemplate(final ChargeData charge, final ChargeData template) {
|
||||
@@ -60,12 +62,12 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
charge.chargeCalculationType, charge.chargePaymentMode, charge.penalty, charge.active, template.currencyOptions,
|
||||
template.chargeCalculationTypeOptions, template.chargeAppliesToOptions, template.chargeTimeTypeOptions,
|
||||
template.chargePaymetModeOptions, template.loanChargeCalculationTypeOptions, template.loanChargeTimeTypeOptions,
|
||||
template.savingsChargeCalculationTypeOptions, template.savingsChargeTimeTypeOptions);
|
||||
template.savingsChargeCalculationTypeOptions, template.savingsChargeTimeTypeOptions, charge.feeOnMonthDay, charge.feeInterval);
|
||||
}
|
||||
|
||||
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 EnumOptionData chargePaymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval, final boolean penalty, final boolean active) {
|
||||
|
||||
final Collection<CurrencyData> currencyOptions = null;
|
||||
final List<EnumOptionData> chargeCalculationTypeOptions = null;
|
||||
@@ -80,7 +82,7 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
|
||||
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
|
||||
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions);
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, feeOnMonthDay, feeInterval);
|
||||
}
|
||||
|
||||
public static ChargeData lookup(final Long id, final String name, final boolean isPenalty) {
|
||||
@@ -90,6 +92,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final EnumOptionData chargeAppliesTo = null;
|
||||
final EnumOptionData chargeCalculationType = null;
|
||||
final EnumOptionData chargePaymentMode = null;
|
||||
final MonthDay feeOnMonthDay = null;
|
||||
final Integer feeInterval = null;
|
||||
final Boolean penalty = isPenalty;
|
||||
final Boolean active = false;
|
||||
final Collection<CurrencyData> currencyOptions = null;
|
||||
@@ -105,7 +109,7 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
|
||||
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
|
||||
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions);
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, feeOnMonthDay, feeInterval);
|
||||
}
|
||||
|
||||
private ChargeData(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
|
||||
@@ -115,7 +119,7 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final List<EnumOptionData> chargeAppliesToOptions, final List<EnumOptionData> chargeTimeTypeOptions,
|
||||
final List<EnumOptionData> chargePaymentModeOptions, final List<EnumOptionData> loansChargeCalculationTypeOptions,
|
||||
final List<EnumOptionData> loansChargeTimeTypeOptions, final List<EnumOptionData> savingsChargeCalculationTypeOptions,
|
||||
final List<EnumOptionData> savingsChargeTimeTypeOptions) {
|
||||
final List<EnumOptionData> savingsChargeTimeTypeOptions, final MonthDay feeOnMonthDay, final Integer feeInterval) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.amount = amount;
|
||||
@@ -124,6 +128,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
this.chargeAppliesTo = chargeAppliesTo;
|
||||
this.chargeCalculationType = chargeCalculationType;
|
||||
this.chargePaymentMode = chargePaymentMode;
|
||||
this.feeInterval = feeInterval;
|
||||
this.feeOnMonthDay = feeOnMonthDay;
|
||||
this.penalty = penalty;
|
||||
this.active = active;
|
||||
this.currencyOptions = currencyOptions;
|
||||
@@ -178,10 +184,9 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
final LocalDate dueAsOfDate = null;
|
||||
final MonthDay feeOnMonthDay = null;
|
||||
|
||||
return SavingsAccountChargeData.instance(savingsChargeId, this.id, savingsAccountId, this.name, this.currency, this.amount, amountPaid, amountWaived,
|
||||
amountWrittenOff, amountOutstanding, this.chargeTimeType, dueAsOfDate, this.chargeCalculationType, percentage,
|
||||
amountPercentageAppliedTo, chargeOptions, this.penalty, feeOnMonthDay);
|
||||
amountPercentageAppliedTo, chargeOptions, this.penalty, this.feeOnMonthDay, this.feeInterval);
|
||||
}
|
||||
}
|
||||
+73
-5
@@ -18,6 +18,7 @@ import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
import org.joda.time.MonthDay;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
@@ -56,6 +57,15 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
@Column(name = "charge_payment_mode_enum")
|
||||
private Integer chargePaymentMode;
|
||||
|
||||
@Column(name = "fee_on_day", nullable = true)
|
||||
private Integer feeOnDay;
|
||||
|
||||
@Column(name = "fee_interval", nullable = true)
|
||||
private Integer feeInterval;
|
||||
|
||||
@Column(name = "fee_on_month", nullable = true)
|
||||
private Integer feeOnMonth;
|
||||
|
||||
@Column(name = "is_penalty", nullable = false)
|
||||
private boolean penalty;
|
||||
|
||||
@@ -79,8 +89,11 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
|
||||
final boolean penalty = command.booleanPrimitiveValueOfParameterNamed("penalty");
|
||||
final boolean active = command.booleanPrimitiveValueOfParameterNamed("active");
|
||||
final MonthDay feeOnMonthDay = command.extractMonthDayNamed("feeOnMonthDay");
|
||||
final Integer feeInterval = command.integerValueOfParameterNamed("feeInterval");
|
||||
|
||||
return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTimeType, chargeCalculationType, penalty, active, paymentMode);
|
||||
return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTimeType, chargeCalculationType, penalty, active, paymentMode,
|
||||
feeOnMonthDay, feeInterval);
|
||||
}
|
||||
|
||||
protected Charge() {
|
||||
@@ -89,7 +102,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
|
||||
private Charge(final String name, final BigDecimal amount, final String currencyCode, final ChargeAppliesTo chargeAppliesTo,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty,
|
||||
final boolean active, final ChargePaymentMode paymentMode) {
|
||||
final boolean active, final ChargePaymentMode paymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval) {
|
||||
this.name = name;
|
||||
this.amount = amount;
|
||||
this.currencyCode = currencyCode;
|
||||
@@ -103,6 +116,12 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charges");
|
||||
|
||||
if (isMonthlyFee()) {
|
||||
this.feeOnMonth = feeOnMonthDay.getMonthOfYear();
|
||||
this.feeOnDay = feeOnMonthDay.getDayOfMonth();
|
||||
this.feeInterval = feeInterval;
|
||||
}
|
||||
|
||||
if (isSavingsCharge()) {
|
||||
|
||||
if (!isAllowedSavingsChargeTime()) {
|
||||
@@ -129,7 +148,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.loan");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
@@ -246,13 +265,20 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTime)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.savings");
|
||||
}
|
||||
//if charge time is changed to monthly then validate for feeOnMonthDay and feeInterval
|
||||
if(isMonthlyFee()){
|
||||
final MonthDay monthDay = command.extractMonthDayNamed("feeOnMonthDay");
|
||||
baseDataValidator.reset().parameter("feeOnMonthDay").value(monthDay).notNull();
|
||||
|
||||
final Integer feeInterval = command.integerValueOfParameterNamed("feeInterval");
|
||||
baseDataValidator.reset().parameter("feeInterval").value(feeInterval).notNull().inMinMaxRange(1, 12);
|
||||
}
|
||||
} else if (isLoanCharge()) {
|
||||
if (!isAllowedLoanChargeTime()) {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTime)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.loan");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final String chargeAppliesToParamName = "chargeAppliesTo";
|
||||
@@ -297,7 +323,33 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.chargePaymentMode = ChargePaymentMode.fromInt(newValue).getValue();
|
||||
}
|
||||
|
||||
if (command.hasParameter("feeOnMonthDay")) {
|
||||
final MonthDay monthDay = command.extractMonthDayNamed("feeOnMonthDay");
|
||||
final String actualValueEntered = command.stringValueOfParameterNamed("feeOnMonthDay");
|
||||
final Integer dayOfMonthValue = monthDay.getDayOfMonth();
|
||||
if (this.feeOnDay != dayOfMonthValue) {
|
||||
actualChanges.put("feeOnMonthDay", actualValueEntered);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.feeOnDay = dayOfMonthValue;
|
||||
}
|
||||
|
||||
final Integer monthOfYear = monthDay.getMonthOfYear();
|
||||
if (this.feeOnMonth != monthOfYear) {
|
||||
actualChanges.put("feeOnMonthDay", actualValueEntered);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.feeOnMonth = monthOfYear;
|
||||
}
|
||||
}
|
||||
|
||||
final String feeInterval = "feeInterval";
|
||||
if (command.isChangeInIntegerParameterNamed(feeInterval, this.feeInterval)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(feeInterval);
|
||||
actualChanges.put(feeInterval, newValue);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.feeInterval = newValue;
|
||||
}
|
||||
|
||||
final String penaltyParamName = "penalty";
|
||||
if (command.isChangeInBooleanParameterNamed(penaltyParamName, this.penalty)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(penaltyParamName);
|
||||
@@ -339,10 +391,26 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
final EnumOptionData chargePaymentmode = ChargeEnumerations.chargePaymentMode(this.chargePaymentMode);
|
||||
final CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, 0, null, null);
|
||||
return ChargeData.instance(getId(), this.name, this.amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType,
|
||||
chargePaymentmode, this.penalty, this.active);
|
||||
chargePaymentmode, this.getFeeOnMonthDay(), this.feeInterval, this.penalty, this.active);
|
||||
}
|
||||
|
||||
public Integer getChargePaymentMode() {
|
||||
return this.chargePaymentMode;
|
||||
}
|
||||
|
||||
public boolean isMonthlyFee() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isMonthlyFee();
|
||||
}
|
||||
|
||||
public MonthDay getFeeOnMonthDay(){
|
||||
MonthDay feeOnMonthDay = null;
|
||||
if (feeOnDay != null && feeOnMonth != null) {
|
||||
feeOnMonthDay = new MonthDay(feeOnMonth, feeOnDay);
|
||||
}
|
||||
return feeOnMonthDay;
|
||||
}
|
||||
|
||||
public Integer feeInterval(){
|
||||
return this.feeInterval;
|
||||
}
|
||||
}
|
||||
+11
-2
@@ -13,7 +13,8 @@ public enum ChargeTimeType {
|
||||
SAVINGS_ACTIVATION(3, "chargeTimeType.savingsActivation"), // only for savings
|
||||
SAVINGS_CLOSURE(4, "chargeTimeType.savingsClosure"), // only for savings
|
||||
WITHDRAWAL_FEE(5, "chargeTimeType.withdrawalFee"), // only for savings
|
||||
ANNUAL_FEE(6, "chargeTimeType.annualFee"); // only for savings
|
||||
ANNUAL_FEE(6, "chargeTimeType.annualFee"), // only for savings
|
||||
MONTHLY_FEE(7, "chargeTimeType.monthlyFee"); // only for savings
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -64,6 +65,9 @@ public enum ChargeTimeType {
|
||||
case 6:
|
||||
chargeTimeType = ANNUAL_FEE;
|
||||
break;
|
||||
case 7:
|
||||
chargeTimeType = MONTHLY_FEE;
|
||||
break;
|
||||
default:
|
||||
chargeTimeType = INVALID;
|
||||
break;
|
||||
@@ -95,11 +99,16 @@ public enum ChargeTimeType {
|
||||
return this.value.equals(ChargeTimeType.ANNUAL_FEE.getValue());
|
||||
}
|
||||
|
||||
public boolean isMonthlyFee() {
|
||||
return this.value.equals(ChargeTimeType.MONTHLY_FEE.getValue());
|
||||
}
|
||||
|
||||
public boolean isAllowedLoanChargeTime() {
|
||||
return isTimeOfDisbursement() || isOnSpecifiedDueDate();
|
||||
}
|
||||
|
||||
public boolean isAllowedSavingsChargeTime() {
|
||||
return isOnSpecifiedDueDate() || isSavingsActivation() || isSavingsClosure() || isWithdrawalFee() || isAnnualFee();
|
||||
return isOnSpecifiedDueDate() || isSavingsActivation() || isSavingsClosure() || isWithdrawalFee() || isAnnualFee()
|
||||
|| isMonthlyFee();
|
||||
}
|
||||
}
|
||||
+21
-1
@@ -16,6 +16,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.MonthDay;
|
||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
||||
@@ -37,7 +38,7 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
*/
|
||||
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("name", "amount", "locale", "currencyCode",
|
||||
"currencyOptions", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "penalty",
|
||||
"active", "chargePaymentMode"));
|
||||
"active", "chargePaymentMode", "feeOnMonthDay", "feeInterval", "monthDayFormat"));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@@ -90,6 +91,15 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
final Boolean active = this.fromApiJsonHelper.extractBooleanNamed("active", element);
|
||||
baseDataValidator.reset().parameter("active").value(active).notNull();
|
||||
}
|
||||
|
||||
boolean isMonthlyFee = ChargeTimeType.fromInt(chargeTimeType).isMonthlyFee();
|
||||
if(isMonthlyFee){
|
||||
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed("feeOnMonthDay", element);
|
||||
baseDataValidator.reset().parameter("feeOnMonthDay").value(monthDay).notNull();
|
||||
|
||||
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed("feeInterval", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("feeInterval").value(feeInterval).notNull().inMinMaxRange(1, 12);
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
@@ -130,6 +140,16 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull()
|
||||
.inMinMaxRange(ChargeTimeType.minValue(), ChargeTimeType.maxValue());
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("feeOnMonthDay", element)) {
|
||||
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed("feeOnMonthDay", element);
|
||||
baseDataValidator.reset().parameter("feeOnMonthDay").value(monthDay).notNull();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("feeInterval", element)) {
|
||||
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed("feeInterval", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("feeInterval").value(feeInterval).notNull().inMinMaxRange(1, 12);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("chargeCalculationType", element)) {
|
||||
final Integer chargeCalculationType = this.fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", element,
|
||||
|
||||
+1
-1
@@ -83,6 +83,6 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead
|
||||
return Arrays.asList(chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE), chargeTimeType(ChargeTimeType.SAVINGS_ACTIVATION),
|
||||
//chargeTimeType(ChargeTimeType.SAVINGS_CLOSURE),
|
||||
chargeTimeType(ChargeTimeType.WITHDRAWAL_FEE),
|
||||
chargeTimeType(ChargeTimeType.ANNUAL_FEE));
|
||||
chargeTimeType(ChargeTimeType.ANNUAL_FEE), chargeTimeType(ChargeTimeType.MONTHLY_FEE));
|
||||
}
|
||||
}
|
||||
+3
@@ -40,6 +40,9 @@ public class ChargeEnumerations {
|
||||
case ANNUAL_FEE:
|
||||
optionData = new EnumOptionData(ChargeTimeType.ANNUAL_FEE.getValue().longValue(), ChargeTimeType.ANNUAL_FEE.getCode(), "Annual Fee");
|
||||
break;
|
||||
case MONTHLY_FEE:
|
||||
optionData = new EnumOptionData(ChargeTimeType.MONTHLY_FEE.getValue().longValue(), ChargeTimeType.MONTHLY_FEE.getCode(), "Monthly Fee");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(ChargeTimeType.INVALID.getValue().longValue(), ChargeTimeType.INVALID.getCode(), "Invalid");
|
||||
break;
|
||||
|
||||
+15
-4
@@ -11,6 +11,7 @@ import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.MonthDay;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
||||
@@ -140,9 +141,11 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
return "c.id as id, c.name as name, c.amount as amount, c.currency_code as currencyCode, "
|
||||
+ "c.charge_applies_to_enum as chargeAppliesTo, c.charge_time_enum as chargeTime, "
|
||||
+ "c.charge_payment_mode_enum as chargePaymentMode, "
|
||||
+ "c.charge_calculation_enum as chargeCalculation, c.is_penalty as penalty, c.is_active as active, oc.name as currencyName, "
|
||||
+ "oc.decimal_places as currencyDecimalPlaces,oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, "
|
||||
+ "oc.internationalized_name_code as currencyNameCode from m_charge c "
|
||||
+ "c.charge_calculation_enum as chargeCalculation, c.is_penalty as penalty, "
|
||||
+ "c.is_active as active, oc.name as currencyName, oc.decimal_places as currencyDecimalPlaces, "
|
||||
+ "oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, "
|
||||
+ "oc.internationalized_name_code as currencyNameCode, c.fee_on_day as feeOnDay, c.fee_on_month as feeOnMonth, "
|
||||
+ "c.fee_interval as feeInterval from m_charge c "
|
||||
+ "join m_organisation_currency oc on c.currency_code = oc.code";
|
||||
}
|
||||
|
||||
@@ -185,8 +188,16 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
final boolean penalty = rs.getBoolean("penalty");
|
||||
final boolean active = rs.getBoolean("active");
|
||||
|
||||
final Integer feeInterval = JdbcSupport.getInteger(rs, "feeInterval");
|
||||
MonthDay feeOnMonthDay = null;
|
||||
final Integer feeOnMonth = JdbcSupport.getInteger(rs, "feeOnMonth");
|
||||
final Integer feeOnDay = JdbcSupport.getInteger(rs, "feeOnDay");
|
||||
if (feeOnDay != null) {
|
||||
feeOnMonthDay = new MonthDay(feeOnMonth, feeOnDay);
|
||||
}
|
||||
|
||||
return ChargeData.instance(id, name, amount, currency, chargeTimeType, chargeAppliesToType, chargeCalculationType,
|
||||
chargePaymentMode, penalty, active);
|
||||
chargePaymentMode, feeOnMonthDay, feeInterval, penalty, active);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -83,6 +83,7 @@ public class SavingsApiConstants {
|
||||
public static final String withdrawalFeeForTransfersParamName = "withdrawalFeeForTransfers";
|
||||
public static final String feeAmountParamName = "feeAmount";//to be deleted
|
||||
public static final String feeOnMonthDayParamName = "feeOnMonthDay";
|
||||
public static final String feeIntervalParamName = "feeInterval";
|
||||
public static final String accountingRuleParamName = "accountingRule";
|
||||
public static final String paymentTypeIdParamName = "paymentTypeId";
|
||||
public static final String transactionAccountNumberParamName = "accountNumber";
|
||||
@@ -203,7 +204,7 @@ public class SavingsApiConstants {
|
||||
amountPaidParamName, chargeOptionsParamName));
|
||||
|
||||
public static final Set<String> SAVINGS_ACCOUNT_CHARGES_ADD_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
|
||||
chargeIdParamName, amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName, feeOnMonthDayParamName, monthDayFormatParamName));
|
||||
chargeIdParamName, amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName, feeOnMonthDayParamName, monthDayFormatParamName, feeIntervalParamName));
|
||||
|
||||
public static final Set<String> SAVINGS_ACCOUNT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
|
||||
amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName));
|
||||
|
||||
+9
-5
@@ -40,6 +40,9 @@ public class SavingsAccountChargeData {
|
||||
@SuppressWarnings("unused")
|
||||
private final MonthDay feeOnMonthDay;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Integer feeInterval;
|
||||
|
||||
private final EnumOptionData chargeCalculationType;
|
||||
|
||||
private final BigDecimal percentage;
|
||||
@@ -91,29 +94,29 @@ public class SavingsAccountChargeData {
|
||||
final boolean penalty = false;
|
||||
final LocalDate dueAsOfDate = null;
|
||||
final MonthDay feeOnMonthDay = null;
|
||||
|
||||
final Integer feeInterval = null;
|
||||
|
||||
return new SavingsAccountChargeData(id, chargeId, accountId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage,
|
||||
amountPercentageAppliedTo, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeOptions,
|
||||
penalty, feeOnMonthDay);
|
||||
penalty, feeOnMonthDay, feeInterval);
|
||||
}
|
||||
|
||||
public static SavingsAccountChargeData instance(final Long id, final Long chargeId, final Long accountId, final String name, final CurrencyData currency,
|
||||
final BigDecimal amount, final BigDecimal amountPaid, final BigDecimal amountWaived, final BigDecimal amountWrittenOff,
|
||||
final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType, final LocalDate dueAsOfDate,
|
||||
final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo,
|
||||
final Collection<ChargeData> chargeOptions, final boolean penalty, final MonthDay feeOnMonthDay) {
|
||||
final Collection<ChargeData> chargeOptions, final boolean penalty, final MonthDay feeOnMonthDay, final Integer feeInterval) {
|
||||
|
||||
return new SavingsAccountChargeData(id, chargeId, accountId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage,
|
||||
amountPercentageAppliedTo, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeOptions,
|
||||
penalty, feeOnMonthDay);
|
||||
penalty, feeOnMonthDay, feeInterval);
|
||||
}
|
||||
|
||||
private SavingsAccountChargeData(final Long id, final Long chargeId, final Long accountId, final String name, final EnumOptionData chargeTimeType,
|
||||
final LocalDate dueAsOfDate, final EnumOptionData chargeCalculationType, final BigDecimal percentage,
|
||||
final BigDecimal amountPercentageAppliedTo, final CurrencyData currency, final BigDecimal amount, final BigDecimal amountPaid,
|
||||
final BigDecimal amountWaived, final BigDecimal amountWrittenOff, final BigDecimal amountOutstanding,
|
||||
final Collection<ChargeData> chargeOptions, final boolean penalty, final MonthDay feeOnMonthDay) {
|
||||
final Collection<ChargeData> chargeOptions, final boolean penalty, final MonthDay feeOnMonthDay, final Integer feeInterval) {
|
||||
this.id = id;
|
||||
this.chargeId = chargeId;
|
||||
this.accountId = accountId;
|
||||
@@ -133,6 +136,7 @@ public class SavingsAccountChargeData {
|
||||
this.chargeOptions = chargeOptions;
|
||||
this.penalty = penalty;
|
||||
this.feeOnMonthDay = feeOnMonthDay;
|
||||
this.feeInterval = feeInterval;
|
||||
}
|
||||
|
||||
private BigDecimal getAmountOrPercentage() {
|
||||
|
||||
+14
@@ -12,11 +12,13 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountPara
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeIntervalParamName;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
@@ -92,6 +94,18 @@ public class SavingsAccountChargeDataValidator {
|
||||
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, element);
|
||||
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate).notNull();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, element)) {
|
||||
final String monthDayFormat = this.fromApiJsonHelper.extractMonthDayFormatParameter(element.getAsJsonObject());
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
|
||||
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element.getAsJsonObject(), monthDayFormat, locale);
|
||||
baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDay).notNull();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(feeIntervalParamName, element)) {
|
||||
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed(feeIntervalParamName, element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter(feeIntervalParamName).value(feeInterval).notNull().inMinMaxRange(1, 12);
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
+12
-12
@@ -11,7 +11,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdPa
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.clientIdParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.externalIdParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeAmountParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeIntervalParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.fieldOfficerIdParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.groupIdParamName;
|
||||
@@ -196,6 +196,7 @@ public class SavingsAccountDataValidator {
|
||||
if (element.isJsonObject()) {
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
|
||||
final String monthDayFormat = this.fromApiJsonHelper.extractMonthDayFormatParameter(topLevelJsonElement);
|
||||
if (topLevelJsonElement.has(chargesParamName) && topLevelJsonElement.get(chargesParamName).isJsonArray()) {
|
||||
final JsonArray array = topLevelJsonElement.get(chargesParamName).getAsJsonArray();
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
@@ -209,6 +210,16 @@ public class SavingsAccountDataValidator {
|
||||
|
||||
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed(amountParamName, savingsChargeElement, locale);
|
||||
baseDataValidator.reset().parameter(amountParamName).value(amount).notNull().positiveAmount();
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, savingsChargeElement)) {
|
||||
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, savingsChargeElement, monthDayFormat, locale);
|
||||
baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDay).notNull();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(feeIntervalParamName, savingsChargeElement)) {
|
||||
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed(feeIntervalParamName, savingsChargeElement, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter(feeIntervalParamName).value(feeInterval).notNull().inMinMaxRange(1, 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,18 +359,7 @@ public class SavingsAccountDataValidator {
|
||||
.ignoreIfNull().validateForBooleanValue();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(feeAmountParamName, element)) {
|
||||
final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(feeAmountParamName, element);
|
||||
baseDataValidator.reset().parameter(feeAmountParamName).value(annualFeeAmount).ignoreIfNull().zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, element)) {
|
||||
final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element);
|
||||
baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDayOfAnnualFee).ignoreIfNull();
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
|
||||
+9
-17
@@ -1207,12 +1207,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
final SavingsAccountCharge chargeToUndo = savingsAccountChargePaidBy.getSavingsAccountCharge();
|
||||
if (transactionToUndo.isChargeTransaction()) {
|
||||
chargeToUndo.undoPayment(this.getCurrency(), transactionToUndo.getAmount(this.getCurrency()));
|
||||
if(chargeToUndo.isAnnualFee()){
|
||||
chargeToUndo.updateToPreviousAnnualFeeDueDate();
|
||||
}
|
||||
} else if (transactionToUndo.isWaiveCharge()) {
|
||||
chargeToUndo.undoWaiver(this.getCurrency(), transactionToUndo.getAmount(this.getCurrency()));
|
||||
chargeToUndo.updateToPreviousAnnualFeeDueDate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1459,7 +1455,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
|
||||
//update annual fee due date
|
||||
for (SavingsAccountCharge charge : this.charges()) {
|
||||
charge.updateToNextAnnualFeeDueDateFrom(getActivationLocalDate());
|
||||
charge.updateToNextDueDateFrom(getActivationLocalDate());
|
||||
}
|
||||
|
||||
//auto pay the activation time charges
|
||||
@@ -1677,11 +1673,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
|
||||
// waive charge
|
||||
final Money amountWaived = savingsAccountCharge.waive(getCurrency());
|
||||
|
||||
handleWaiverChargeTransactions(savingsAccountCharge, amountWaived);
|
||||
|
||||
//update next annual fee due date
|
||||
savingsAccountCharge.updateToNextAnnualFeeDueDate();
|
||||
|
||||
}
|
||||
|
||||
public void addCharge(final DateTimeFormatter formatter, final SavingsAccountCharge savingsAccountCharge, final Charge chargeDefinition) {
|
||||
@@ -1738,13 +1731,15 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(savingsAccountCharge.isAnnualFee() || savingsAccountCharge.isMonthlyFee()){
|
||||
//update due date
|
||||
if(isActive()){
|
||||
savingsAccountCharge.updateToNextAnnualFeeDueDateFrom(getActivationLocalDate());
|
||||
savingsAccountCharge.updateToNextDueDateFrom(getActivationLocalDate());
|
||||
}else if(isApproved()){
|
||||
savingsAccountCharge.updateToNextAnnualFeeDueDateFrom(getApprovedOnLocalDate());
|
||||
}
|
||||
//if account is in submitted and pending approval state,
|
||||
//upon account activation annual fee charges due date get updated.
|
||||
savingsAccountCharge.updateToNextDueDateFrom(getApprovedOnLocalDate());
|
||||
}
|
||||
}
|
||||
|
||||
//activation charge and withdrawal charges not required this validation
|
||||
@@ -1843,9 +1838,6 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
this.payCharge(savingsAccountCharge, chargePaid, transactionDate);
|
||||
|
||||
//update next annual fee due date
|
||||
savingsAccountCharge.updateToNextAnnualFeeDueDate();
|
||||
}
|
||||
|
||||
public void payCharge(final SavingsAccountCharge savingsAccountCharge, final Money amountPaid, final LocalDate transactionDate) {
|
||||
|
||||
+107
-28
@@ -8,8 +8,9 @@ package org.mifosplatform.portfolio.savings.domain;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dateFormatParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeIntervalParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
@@ -64,6 +65,9 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
@Column(name = "fee_on_day", nullable = true)
|
||||
private Integer feeOnDay;
|
||||
|
||||
@Column(name = "fee_interval", nullable = true)
|
||||
private Integer feeInterval;
|
||||
|
||||
@Column(name = "charge_calculation_enum")
|
||||
private Integer chargeCalculation;
|
||||
|
||||
@@ -107,20 +111,20 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
final BigDecimal amount = command.bigDecimalValueOfParameterNamed(amountParamName);
|
||||
final LocalDate dueDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName);
|
||||
final MonthDay feeOnMonthDay = command.extractMonthDayNamed(feeOnMonthDayParamName);
|
||||
final Integer feeInterval = command.integerValueOfParameterNamed(feeIntervalParamName);
|
||||
final ChargeTimeType chargeTime = null;
|
||||
final ChargeCalculationType chargeCalculation = null;
|
||||
final boolean status = true;
|
||||
|
||||
return new SavingsAccountCharge(savingsAccount, chargeDefinition, amount, chargeTime, chargeCalculation, dueDate, status,
|
||||
feeOnMonthDay);
|
||||
feeOnMonthDay, feeInterval);
|
||||
}
|
||||
|
||||
// TODO AA: refactor method signature
|
||||
public static SavingsAccountCharge createNewWithoutSavingsAccount(final Charge chargeDefinition, final BigDecimal amountPayable,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate, final boolean status,
|
||||
final MonthDay feeOnMonthDay) {
|
||||
final MonthDay feeOnMonthDay, final Integer feeInterval) {
|
||||
return new SavingsAccountCharge(null, chargeDefinition, amountPayable, chargeTime, chargeCalculation, dueDate, status,
|
||||
feeOnMonthDay);
|
||||
feeOnMonthDay, feeInterval);
|
||||
}
|
||||
|
||||
protected SavingsAccountCharge() {
|
||||
@@ -129,7 +133,7 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
|
||||
private SavingsAccountCharge(final SavingsAccount savingsAccount, final Charge chargeDefinition, final BigDecimal amount,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate, final boolean status,
|
||||
final MonthDay feeOnMonthDay) {
|
||||
MonthDay feeOnMonthDay, final Integer feeInterval) {
|
||||
|
||||
this.savingsAccount = savingsAccount;
|
||||
this.charge = chargeDefinition;
|
||||
@@ -145,14 +149,25 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
|
||||
}
|
||||
|
||||
if (isAnnualFee()) {
|
||||
if (isAnnualFee() || isMonthlyFee()) {
|
||||
feeOnMonthDay = (feeOnMonthDay == null) ? chargeDefinition.getFeeOnMonthDay() : feeOnMonthDay;
|
||||
if (feeOnMonthDay == null) {
|
||||
final String defaultUserMessage = "Savings Account charge is missing due date.";
|
||||
throw new SavingsAccountChargeWithoutMandatoryFieldException("savingsaccount.charge", dueAsOfDateParamName,
|
||||
defaultUserMessage, chargeDefinition.getId(), chargeDefinition.getName());
|
||||
}
|
||||
|
||||
this.feeOnMonth = feeOnMonthDay.getMonthOfYear();
|
||||
this.feeOnDay = feeOnMonthDay.getDayOfMonth();
|
||||
|
||||
if (isMonthlyFee()) {
|
||||
this.feeInterval = (feeInterval == null) ? chargeDefinition.feeInterval() : feeInterval;
|
||||
}
|
||||
|
||||
} else {
|
||||
this.feeOnDay = null;
|
||||
this.feeOnMonth = null;
|
||||
this.feeInterval = null;
|
||||
}
|
||||
|
||||
this.dueDate = (dueDate == null) ? null : dueDate.toDate();
|
||||
@@ -170,14 +185,23 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
final BigDecimal transactionAmount = new BigDecimal(0);
|
||||
|
||||
populateDerivedFields(transactionAmount, chargeAmount);
|
||||
resetOutstandingAmount();// reset only if it's withdrawal or annual fee
|
||||
|
||||
if (this.isWithdrawalFee()) {
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
this.paid = determineIfFullyPaid();
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
private void resetOutstandingAmount() {
|
||||
if (this.isWithdrawalFee()) {
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
public void recalculateAmountOutstanding() {
|
||||
if (isMonthlyFee() || isAnnualFee()) {
|
||||
// FIXME: AA: If charge is percentage of x amount then need to
|
||||
// update amount outstanding accordingly.
|
||||
// Right now annual and monthly charges supports charge calculation type flat.
|
||||
this.amountOutstanding = this.amount;
|
||||
this.paid = false;//reset to false for recurring fee.
|
||||
this.waived = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,9 +276,13 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
amountPaid = amountPaid.minus(transactionAmount);
|
||||
this.amountPaid = amountPaid.getAmount();
|
||||
this.amountOutstanding = calculateAmountOutstanding(currency);
|
||||
|
||||
if (this.isWithdrawalFee()) {
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
}
|
||||
//to reset amount outstanding for annual and monthly fee
|
||||
recalculateAmountOutstanding();
|
||||
updateToPreviousDueDate();//reset annual and monthly due date.
|
||||
this.paid = false;
|
||||
}
|
||||
|
||||
@@ -262,6 +290,10 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
this.amountWaived = this.amountOutstanding;
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
this.waived = true;
|
||||
|
||||
recalculateAmountOutstanding();
|
||||
updateToNextDueDate();
|
||||
|
||||
return getAmountWaived(currency);
|
||||
}
|
||||
|
||||
@@ -271,10 +303,12 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
this.amountWaived = amountWaived.getAmount();
|
||||
this.amountOutstanding = calculateAmountOutstanding(currency);
|
||||
this.waived = false;
|
||||
|
||||
recalculateAmountOutstanding();
|
||||
updateToPreviousDueDate();
|
||||
}
|
||||
|
||||
public Money pay(final MonetaryCurrency currency, final Money amountPaid) {
|
||||
|
||||
Money amountPaidToDate = Money.of(currency, this.amountPaid);
|
||||
Money amountOutstanding = Money.of(currency, this.amountOutstanding);
|
||||
amountPaidToDate = amountPaidToDate.plus(amountPaid);
|
||||
@@ -282,7 +316,14 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
this.amountPaid = amountPaidToDate.getAmount();
|
||||
this.amountOutstanding = amountOutstanding.getAmount();
|
||||
this.paid = determineIfFullyPaid();
|
||||
return amountOutstanding;
|
||||
|
||||
if (BigDecimal.ZERO.compareTo(this.amountOutstanding) == 0) {
|
||||
//full outstanding is paid, update to next due date
|
||||
updateToNextDueDate();
|
||||
recalculateAmountOutstanding();
|
||||
}
|
||||
|
||||
return Money.of(currency, this.amountOutstanding);
|
||||
}
|
||||
|
||||
private BigDecimal calculateAmountOutstanding(final MonetaryCurrency currency) {
|
||||
@@ -293,7 +334,7 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
this.savingsAccount = savingsAccount;
|
||||
}
|
||||
|
||||
public void update(final BigDecimal amount, final LocalDate dueDate, final MonthDay feeOnMonthDay) {
|
||||
public void update(final BigDecimal amount, final LocalDate dueDate, final MonthDay feeOnMonthDay, final Integer feeInterval) {
|
||||
final BigDecimal transactionAmount = BigDecimal.ZERO;
|
||||
if (dueDate != null) {
|
||||
this.dueDate = dueDate.toDate();
|
||||
@@ -304,6 +345,10 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
this.feeOnDay = feeOnMonthDay.getDayOfMonth();
|
||||
}
|
||||
|
||||
if (feeInterval != null) {
|
||||
this.feeInterval = feeInterval;
|
||||
}
|
||||
|
||||
if (amount != null) {
|
||||
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
|
||||
case INVALID:
|
||||
@@ -574,6 +619,10 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isAnnualFee();
|
||||
}
|
||||
|
||||
public boolean isMonthlyFee() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isMonthlyFee();
|
||||
}
|
||||
|
||||
public boolean hasCurrencyCodeOf(final String matchingCurrencyCode) {
|
||||
if (this.currencyCode() == null || matchingCurrencyCode == null) { return false; }
|
||||
return this.currencyCode().equalsIgnoreCase(matchingCurrencyCode);
|
||||
@@ -613,30 +662,60 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
return amountPaybale;
|
||||
}
|
||||
|
||||
public void updateToNextAnnualFeeDueDateFrom(final LocalDate startingDate) {
|
||||
if (isAnnualFee()) {
|
||||
LocalDate nextDueLocalDate = startingDate.withMonthOfYear(this.feeOnMonth).withDayOfMonth(this.feeOnDay);
|
||||
if (startingDate.isAfter(nextDueLocalDate)) {
|
||||
nextDueLocalDate = nextDueLocalDate.plusYears(1);
|
||||
public void updateToNextDueDateFrom(final LocalDate startingDate) {
|
||||
if (isAnnualFee() || isMonthlyFee()) {
|
||||
LocalDate nextDueLocalDate = startingDate.withMonthOfYear(this.feeOnMonth);
|
||||
while (startingDate.isAfter(nextDueLocalDate)) {
|
||||
if (isAnnualFee()) {
|
||||
nextDueLocalDate = nextDueLocalDate.plusYears(1);
|
||||
} else if (isMonthlyFee()) {
|
||||
nextDueLocalDate = nextDueLocalDate.plusMonths(this.feeInterval);
|
||||
}
|
||||
}
|
||||
//set day of month
|
||||
int maxDayOfMonth = nextDueLocalDate.dayOfMonth().withMaximumValue().getDayOfMonth();
|
||||
int newDayOfMonth = (this.feeOnDay.intValue() <= maxDayOfMonth) ? this.feeOnDay.intValue() : maxDayOfMonth;
|
||||
nextDueLocalDate = nextDueLocalDate.withDayOfMonth(newDayOfMonth);
|
||||
this.dueDate = nextDueLocalDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateToNextAnnualFeeDueDate() {
|
||||
if (isAnnualFee()) {
|
||||
|
||||
public void updateToNextDueDate() {
|
||||
if(isAnnualFee() || isMonthlyFee()){
|
||||
LocalDate nextDueLocalDate = new LocalDate(dueDate);
|
||||
this.dueDate = nextDueLocalDate.withMonthOfYear(this.feeOnMonth).withDayOfMonth(this.feeOnDay).plusYears(1).toDate();
|
||||
if(isAnnualFee()){
|
||||
nextDueLocalDate = nextDueLocalDate.withMonthOfYear(this.feeOnMonth).plusYears(1);
|
||||
}else if(isMonthlyFee()){
|
||||
nextDueLocalDate = nextDueLocalDate.plusMonths(this.feeInterval);
|
||||
}
|
||||
|
||||
//set day of month
|
||||
int maxDayOfMonth = nextDueLocalDate.dayOfMonth().withMaximumValue().getDayOfMonth();
|
||||
int newDayOfMonth = (this.feeOnDay.intValue() < maxDayOfMonth) ? this.feeOnDay.intValue() : maxDayOfMonth;
|
||||
nextDueLocalDate = nextDueLocalDate.withDayOfMonth(newDayOfMonth);
|
||||
|
||||
this.dueDate = nextDueLocalDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateToPreviousAnnualFeeDueDate() {
|
||||
if (isAnnualFee()) {
|
||||
LocalDate previousDueLocalDate = new LocalDate(dueDate);
|
||||
this.dueDate = previousDueLocalDate.withMonthOfYear(this.feeOnMonth).withDayOfMonth(this.feeOnDay).minusYears(1).toDate();
|
||||
public void updateToPreviousDueDate() {
|
||||
if(isAnnualFee() || isMonthlyFee()){
|
||||
LocalDate nextDueLocalDate = new LocalDate(dueDate);
|
||||
if(isAnnualFee()){
|
||||
nextDueLocalDate = nextDueLocalDate.withMonthOfYear(this.feeOnMonth).minusYears(1);
|
||||
}else if(isMonthlyFee()){
|
||||
nextDueLocalDate = nextDueLocalDate.minusMonths(this.feeInterval);
|
||||
}
|
||||
|
||||
//set day of month
|
||||
int maxDayOfMonth = nextDueLocalDate.dayOfMonth().withMaximumValue().getDayOfMonth();
|
||||
int newDayOfMonth = (this.feeOnDay.intValue() < maxDayOfMonth) ? this.feeOnDay.intValue() : maxDayOfMonth;
|
||||
nextDueLocalDate = nextDueLocalDate.withDayOfMonth(newDayOfMonth);
|
||||
|
||||
this.dueDate = nextDueLocalDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean feeSettingsNotSet() {
|
||||
return !feeSettingsSet();
|
||||
}
|
||||
|
||||
+9
-6
@@ -13,6 +13,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeTime
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeIntervalParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.idParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -82,6 +83,7 @@ public class SavingsAccountChargeAssembler {
|
||||
dateFormat, locale);
|
||||
|
||||
final MonthDay feeOnMonthDay = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, savingsChargeElement, monthDayFormat, locale);
|
||||
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed(feeIntervalParamName, savingsChargeElement, locale);
|
||||
|
||||
if (id == null) {
|
||||
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
|
||||
@@ -92,18 +94,19 @@ public class SavingsAccountChargeAssembler {
|
||||
throw new ChargeCannotBeAppliedToException("savings.product", errorMessage, chargeDefinition.getId());
|
||||
}
|
||||
|
||||
final ChargeTimeType chargeTime = null;
|
||||
ChargeTimeType chargeTime = null;
|
||||
if (chargeTimeType != null) {
|
||||
ChargeTimeType.fromInt(chargeTimeType);
|
||||
chargeTime = ChargeTimeType.fromInt(chargeTimeType);
|
||||
}
|
||||
final ChargeCalculationType chargeCalculation = null;
|
||||
|
||||
ChargeCalculationType chargeCalculation = null;
|
||||
if (chargeCalculationType != null) {
|
||||
ChargeCalculationType.fromInt(chargeCalculationType);
|
||||
chargeCalculation = ChargeCalculationType.fromInt(chargeCalculationType);
|
||||
}
|
||||
|
||||
final boolean status = true;
|
||||
final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewWithoutSavingsAccount(
|
||||
chargeDefinition, amount, chargeTime, chargeCalculation, dueDate, status, feeOnMonthDay);
|
||||
chargeDefinition, amount, chargeTime, chargeCalculation, dueDate, status, feeOnMonthDay, feeInterval);
|
||||
savingsAccountCharges.add(savingsAccountCharge);
|
||||
} else {
|
||||
final Long savingsAccountChargeId = id;
|
||||
@@ -111,7 +114,7 @@ public class SavingsAccountChargeAssembler {
|
||||
.findOne(savingsAccountChargeId);
|
||||
if (savingsAccountCharge == null) { throw new SavingsAccountChargeNotFoundException(savingsAccountChargeId); }
|
||||
|
||||
savingsAccountCharge.update(amount, dueDate, feeOnMonthDay);
|
||||
savingsAccountCharge.update(amount, dueDate, feeOnMonthDay, feeInterval);
|
||||
|
||||
savingsAccountCharges.add(savingsAccountCharge);
|
||||
}
|
||||
|
||||
+4
-2
@@ -12,6 +12,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeTime
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeIntervalParamName;
|
||||
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.idParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -73,6 +74,7 @@ public class SavingsProductChargeAssembler {
|
||||
final LocalDate dueDate = this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, savingsChargeElement,
|
||||
dateFormat, locale);
|
||||
final MonthDay feeOnMonthDay = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, savingsChargeElement);
|
||||
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed(feeIntervalParamName, savingsChargeElement, locale);
|
||||
|
||||
if (id == null) {
|
||||
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
|
||||
@@ -85,7 +87,7 @@ public class SavingsProductChargeAssembler {
|
||||
ChargeCalculationType.fromInt(chargeCalculationType);
|
||||
}
|
||||
final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewWithoutSavingsAccount(
|
||||
chargeDefinition, amount, chargeTime, chargeCalculation, dueDate, true, feeOnMonthDay);
|
||||
chargeDefinition, amount, chargeTime, chargeCalculation, dueDate, true, feeOnMonthDay, feeInterval);
|
||||
savingsAccountCharges.add(savingsAccountCharge);
|
||||
} else {
|
||||
final Long savingsAccountChargeId = id;
|
||||
@@ -93,7 +95,7 @@ public class SavingsProductChargeAssembler {
|
||||
.findOne(savingsAccountChargeId);
|
||||
if (savingsAccountCharge == null) { throw new SavingsAccountChargeNotFoundException(savingsAccountChargeId); }
|
||||
|
||||
savingsAccountCharge.update(amount, dueDate, null);
|
||||
savingsAccountCharge.update(amount, dueDate, null, null);
|
||||
|
||||
savingsAccountCharges.add(savingsAccountCharge);
|
||||
}
|
||||
|
||||
+3
-2
@@ -65,7 +65,7 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
|
||||
+ "sc.is_penalty as penalty, "
|
||||
+ "sc.charge_due_date as dueAsOfDate, "
|
||||
+ "sc.fee_on_month as feeOnMonth, "
|
||||
+ "sc.fee_on_day as feeOnDay, "
|
||||
+ "sc.fee_on_day as feeOnDay, sc.fee_interval as feeInterval, "
|
||||
+ "sc.charge_calculation_enum as chargeCalculation, "
|
||||
+ "c.currency_code as currencyCode, oc.name as currencyName, "
|
||||
+ "oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, "
|
||||
@@ -104,6 +104,7 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
|
||||
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
|
||||
|
||||
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
|
||||
final Integer feeInterval = JdbcSupport.getInteger(rs, "feeInterval");
|
||||
MonthDay feeOnMonthDay = null;
|
||||
final Integer feeOnMonth = JdbcSupport.getInteger(rs, "feeOnMonth");
|
||||
final Integer feeOnDay = JdbcSupport.getInteger(rs, "feeOnDay");
|
||||
@@ -119,7 +120,7 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
|
||||
|
||||
return SavingsAccountChargeData.instance(id, chargeId, accountId, name, currency, amount, amountPaid, amountWaived,
|
||||
amountWrittenOff, amountOutstanding, chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf,
|
||||
amountPercentageAppliedTo, chargeOptions, penalty, feeOnMonthDay);
|
||||
amountPercentageAppliedTo, chargeOptions, penalty, feeOnMonthDay, feeInterval);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -849,7 +849,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
account.calculateInterestUsing(mc, today);
|
||||
}
|
||||
|
||||
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name());
|
||||
account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode());
|
||||
|
||||
this.savingAccountRepository.save(account);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user