diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index aac4d98c8..4581fe480 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -525,7 +525,7 @@
Savings Account Charges |
savingsaccounts/{accountId}/charges |
Add a Savings Account Charge |
- |
+ List Savings Account Charges |
|
|
@@ -553,6 +553,14 @@
|
|
+
+ |
+ savingsaccounts/{accountId}/charges/{savingsAccountChargeId}?command=inactivate |
+ Inactivate a Savings Account Charge |
+ |
+ |
+ |
+
| Fixed Deposit Accounts |
fixeddepositaccounts |
@@ -18833,6 +18841,11 @@ No Request Body:
Example Requests:
savingsaccounts/1/charges
+ savingsaccounts/1/charges?chargeStatus=all
+
+ savingsaccounts/1/charges?chargeStatus=inactive
+
+ savingsaccounts/1/charges?chargeStatus=active
savingsaccounts/1/charges?fields=name,amountOrPercentage
@@ -19318,7 +19331,7 @@ No Request Body:
Pay a Savings account Charge
- A charge will be paid when savings account is active and having sufficient balance.
+ An active charge will be paid when savings account is active and having sufficient balance.
@@ -19375,6 +19388,34 @@ No Request Body:
+
+
+
+
Inactivate a Savings account Charge
+
+ A charge will be allowed to inactivate when savings account is active and not having any dues as of today. If charge is overpaid, corresponding charge payment transactions will be reversed.
+
+
+
+
+POST https://DomainName/api/v1/savingsaccounts/{accountId}/charges/{savingsAccountChargeId}?command=inactivate
+
+
+POST savingsaccounts/1/charges/2?command=inactivate
+Content-Type: application/json
+No Request Body:
+
+
+{
+ "officeId": 1,
+ "clientId": 1,
+ "savingsId": 1,
+ "resourceId": 2
+}
+
+
+
+
@@ -32518,7 +32559,7 @@ No Request Body:
Savings Account Charges |
savingsaccounts/{accountId}/charges |
Add a Savings Account Charge |
- |
+ List Savings Account Charges |
|
|
@@ -32545,6 +32586,14 @@ No Request Body:
|
|
|
+
+
+ |
+ savingsaccounts/{accountId}/charges/{savingsAccountChargeId}?command=inactivate |
+ Inactivate a Savings Account Charge |
+ |
+ |
+ |
| Fixed Deposit Product |
diff --git a/mifosng-db/migrations/core_db/V188__add_savingscharge_inactivate_permissions.sql b/mifosng-db/migrations/core_db/V188__add_savingscharge_inactivate_permissions.sql
new file mode 100644
index 000000000..0800f0f6d
--- /dev/null
+++ b/mifosng-db/migrations/core_db/V188__add_savingscharge_inactivate_permissions.sql
@@ -0,0 +1,5 @@
+INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'INACTIVATE_SAVINGSACCOUNTCHARGE', 'SAVINGSACCOUNTCHARGE', 'INACTIVATE', 0);
+INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'INACTIVATE_SAVINGSACCOUNTCHARGE_CHECKER', 'SAVINGSACCOUNTCHARGE', 'INACTIVATE_CHECKER', 0);
+
+ALTER TABLE `m_savings_account_charge`
+ ADD COLUMN `inactivated_on_date` DATE NULL DEFAULT NULL AFTER `is_active`;
\ No newline at end of file
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientSavingsIntegrationTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientSavingsIntegrationTest.java
index cecc99469..ea8d8c156 100755
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientSavingsIntegrationTest.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ClientSavingsIntegrationTest.java
@@ -404,7 +404,10 @@ public class ClientSavingsIntegrationTest {
@SuppressWarnings("unchecked")
@Test
public void testSavingsAccountCharges() {
+
+ final ResponseSpecification erroResponseSpec = new ResponseSpecBuilder().build();
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
+ final SavingsAccountHelper validationErrorHelper = new SavingsAccountHelper(this.requestSpec, erroResponseSpec);
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
Assert.assertNotNull(clientID);
@@ -416,8 +419,8 @@ public class ClientSavingsIntegrationTest {
minBalanceForInterestCalculation, minRequiredBalance, enforceMinRequiredBalance, allowOverdraft);
Assert.assertNotNull(savingsProductID);
- final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientID, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
Assert.assertNotNull(savingsProductID);
+ final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientID, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec, savingsId);
SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap);
@@ -457,6 +460,13 @@ public class ClientSavingsIntegrationTest {
Assert.assertEquals(1, charges.size());
HashMap savingsChargeForPay = charges.get(0);
+ Integer annualSavingsChargeId = (Integer) savingsChargeForPay.get("id");
+
+ ArrayList savingsAccountErrorData = (ArrayList) validationErrorHelper.inactivateCharge(annualSavingsChargeId,
+ savingsId, CommonConstants.RESPONSE_ERROR);
+ assertEquals("validation.msg.savingsaccountcharge.inactivation.of.charge.not.allowed.when.charge.is.due", savingsAccountErrorData
+ .get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
+
SimpleDateFormat sdf = new SimpleDateFormat(CommonConstants.dateFormat);
Calendar cal = Calendar.getInstance();
List dates = (List) savingsChargeForPay.get("dueDate");
@@ -469,6 +479,10 @@ public class ClientSavingsIntegrationTest {
HashMap paidCharge = this.savingsAccountHelper.getSavingsCharge(savingsId, (Integer) savingsChargeForPay.get("id"));
assertEquals(savingsChargeForPay.get("amount"), paidCharge.get("amountPaid"));
+ Integer inactivatedChargeId = (Integer) this.savingsAccountHelper.inactivateCharge(annualSavingsChargeId, savingsId,
+ CommonConstants.RESPONSE_RESOURCE_ID);
+ assertEquals("Inactivated Savings Charges Id", annualSavingsChargeId, inactivatedChargeId);
+
final Integer monthlyFeechargeId = ChargesHelper.createCharges(this.requestSpec, this.responseSpec,
ChargesHelper.getSavingsMonthlyFeeJSON());
Assert.assertNotNull(monthlyFeechargeId);
@@ -478,6 +492,13 @@ public class ClientSavingsIntegrationTest {
Assert.assertEquals(2, charges.size());
HashMap savingsChargeForWaive = charges.get(1);
+ final Integer monthlySavingsCharge = (Integer) savingsChargeForWaive.get("id");
+
+ savingsAccountErrorData = (ArrayList) validationErrorHelper.inactivateCharge(monthlySavingsCharge, savingsId,
+ CommonConstants.RESPONSE_ERROR);
+ assertEquals("validation.msg.savingsaccountcharge.inactivation.of.charge.not.allowed.when.charge.is.due", savingsAccountErrorData
+ .get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
+
this.savingsAccountHelper.waiveCharge((Integer) savingsChargeForWaive.get("id"), savingsId);
HashMap waiveCharge = this.savingsAccountHelper.getSavingsCharge(savingsId, (Integer) savingsChargeForWaive.get("id"));
assertEquals(savingsChargeForWaive.get("amount"), waiveCharge.get("amountWaived"));
@@ -495,8 +516,15 @@ public class ClientSavingsIntegrationTest {
this.savingsAccountHelper.addChargesForSavings(savingsId, weeklyFeeId);
charges = this.savingsAccountHelper.getSavingsCharges(savingsId);
Assert.assertEquals(3, charges.size());
-
+
savingsChargeForPay = charges.get(2);
+ final Integer weeklySavingsFeeId = (Integer) savingsChargeForPay.get("id");
+
+ savingsAccountErrorData = (ArrayList) validationErrorHelper.inactivateCharge(weeklySavingsFeeId, savingsId,
+ CommonConstants.RESPONSE_ERROR);
+ assertEquals("validation.msg.savingsaccountcharge.inactivation.of.charge.not.allowed.when.charge.is.due", savingsAccountErrorData
+ .get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
+
cal = Calendar.getInstance();
dates = (List) savingsChargeForPay.get("dueDate");
cal.set(Calendar.YEAR, (Integer) dates.get(0));
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/savings/SavingsAccountHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/savings/SavingsAccountHelper.java
index 53be5ebac..fdbf36a60 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/savings/SavingsAccountHelper.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/savings/SavingsAccountHelper.java
@@ -383,6 +383,20 @@ public class SavingsAccountHelper {
}
public HashMap activateSavingsAccount(Integer savingsID, String activationDate) {
- return performSavingApplicationActions(createSavingsOperationURL(ACTIVATE_SAVINGS_COMMAND, savingsID), getAccountActivationJSON(activationDate));
+ return performSavingApplicationActions(createSavingsOperationURL(ACTIVATE_SAVINGS_COMMAND, savingsID),
+ getAccountActivationJSON(activationDate));
+ }
+
+ public Object inactivateCharge(final Integer chargeId, final Integer savingsId, final String jsonAttributeToGetBack) {
+ return performSavingActions(createChargesURL("inactivate", savingsId, chargeId), getSavingsInactivateChargeJSON(),
+ jsonAttributeToGetBack);
+ }
+
+ private String getSavingsInactivateChargeJSON() {
+ final HashMap map = new HashMap<>();
+ map.put("locale", CommonConstants.locale);
+ map.put("dateFormat", CommonConstants.dateFormat);
+ String josn = new Gson().toJson(map);
+ return josn;
}
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
index 8e11783f3..45e483fa1 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
@@ -872,4 +872,8 @@ public class CommandWrapper {
return this.actionName.equalsIgnoreCase("CREATE");
}
+ public boolean isInactivateSavingsAccountCharge() {
+ return this.actionName.equalsIgnoreCase("INACTIVATE") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNTCHARGE");
+ }
+
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java
index d0093fb38..5b1e533aa 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java
@@ -1104,6 +1104,16 @@ public class CommandWrapperBuilder {
}
+ public CommandWrapperBuilder inactivateSavingsAccountCharge(final Long savingsAccountId, final Long savingsAccountChargeId) {
+ this.actionName = "INACTIVATE";
+ this.entityName = "SAVINGSACCOUNTCHARGE";
+ this.entityId = savingsAccountChargeId;
+ this.savingsId = savingsAccountId;
+ this.href = "/savingsaccounts/" + savingsAccountId + "/charges/" + savingsAccountChargeId;
+ return this;
+
+ }
+
public CommandWrapperBuilder deleteSavingsAccountCharge(final Long savingsAccountId, final Long savingsAccountChargeId) {
this.actionName = "DELETE";
this.entityName = "SAVINGSACCOUNTCHARGE";
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
index f6f3885a4..8710e21b9 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
@@ -506,6 +506,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
handler = this.applicationContext.getBean("waiveSavingsAccountChargeCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isPaySavingsAccountCharge()) {
handler = this.applicationContext.getBean("paySavingsAccountChargeCommandHandler", NewCommandSourceHandler.class);
+ } else if (wrapper.isInactivateSavingsAccountCharge()) {
+ handler = this.applicationContext.getBean("inactivateSavingsAccountChargeCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java
index cf66548c3..fb9ff8d27 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java
@@ -206,14 +206,15 @@ public class ChargeData implements Comparable, Serializable {
final BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO;
final Collection chargeOptions = null;
final LocalDate dueAsOfDate = null;
+ final Boolean isActive = null;
+ final LocalDate inactivationDate = 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, this.feeOnMonthDay,
- this.feeInterval);
+ this.feeInterval, isActive, inactivationDate);
}
-
public boolean isPenalty() {
return this.penalty;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java
index 5d77d2bc1..eafa8839c 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java
@@ -42,6 +42,7 @@ public class SavingsApiConstants {
public static String COMMAND_ADJUST_TRANSACTION = "modify";
public static String COMMAND_WAIVE_CHARGE = "waive";
public static String COMMAND_PAY_CHARGE = "paycharge";
+ public static String COMMAND_INACTIVATE_CHARGE = "inactivate";
// general
public static final String localeParamName = "locale";
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountsApiResource.java
index e1fc2e887..c4495d946 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountsApiResource.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/FixedDepositAccountsApiResource.java
@@ -153,16 +153,19 @@ public class FixedDepositAccountsApiResource {
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveOne(@PathParam("accountId") final Long accountId,
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
- @Context final UriInfo uriInfo) {
+ @DefaultValue("all") @QueryParam("chargeStatus") final String chargeStatus, @Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME);
+ if (!(is(chargeStatus, "all") || is(chargeStatus, "active") || is(chargeStatus, "inactive"))) { throw new UnrecognizedQueryParamException(
+ "status", chargeStatus, new Object[] { "all", "active", "inactive" }); }
+
final FixedDepositAccountData account = (FixedDepositAccountData) this.depositAccountReadPlatformService.retrieveOneWithChartSlabs(
DepositAccountType.FIXED_DEPOSIT, accountId);
final Set mandatoryResponseParameters = new HashSet<>();
final FixedDepositAccountData accountTemplate = populateTemplateAndAssociations(accountId, account, staffInSelectedOfficeOnly,
- uriInfo, mandatoryResponseParameters);
+ chargeStatus, uriInfo, mandatoryResponseParameters);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
mandatoryResponseParameters);
@@ -171,7 +174,7 @@ public class FixedDepositAccountsApiResource {
}
private FixedDepositAccountData populateTemplateAndAssociations(final Long accountId, final FixedDepositAccountData savingsAccount,
- final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo, final Set mandatoryResponseParameters) {
+ final boolean staffInSelectedOfficeOnly, final String chargeStatus, final UriInfo uriInfo, final Set mandatoryResponseParameters) {
Collection transactions = null;
Collection charges = null;
@@ -197,7 +200,7 @@ public class FixedDepositAccountsApiResource {
if (associationParameters.contains(SavingsApiConstants.charges)) {
mandatoryResponseParameters.add(SavingsApiConstants.charges);
final Collection currentCharges = this.savingsAccountChargeReadPlatformService
- .retrieveSavingsAccountCharges(accountId);
+ .retrieveSavingsAccountCharges(accountId, chargeStatus);
if (!CollectionUtils.isEmpty(currentCharges)) {
charges = currentCharges;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositAccountsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositAccountsApiResource.java
index 23b792c5f..165701cda 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositAccountsApiResource.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/RecurringDepositAccountsApiResource.java
@@ -150,16 +150,19 @@ public class RecurringDepositAccountsApiResource {
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveOne(@PathParam("accountId") final Long accountId,
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
- @Context final UriInfo uriInfo) {
+ @DefaultValue("all") @QueryParam("chargeStatus") final String chargeStatus, @Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
+ if (!(is(chargeStatus, "all") || is(chargeStatus, "active") || is(chargeStatus, "inactive"))) { throw new UnrecognizedQueryParamException(
+ "status", chargeStatus, new Object[] { "all", "active", "inactive" }); }
+
final RecurringDepositAccountData account = (RecurringDepositAccountData) this.depositAccountReadPlatformService
.retrieveOneWithChartSlabs(DepositAccountType.RECURRING_DEPOSIT, accountId);
final Set mandatoryResponseParameters = new HashSet<>();
final RecurringDepositAccountData accountTemplate = populateTemplateAndAssociations(accountId, account, staffInSelectedOfficeOnly,
- uriInfo, mandatoryResponseParameters);
+ chargeStatus, uriInfo, mandatoryResponseParameters);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
mandatoryResponseParameters);
@@ -168,8 +171,8 @@ public class RecurringDepositAccountsApiResource {
}
private RecurringDepositAccountData populateTemplateAndAssociations(final Long accountId,
- final RecurringDepositAccountData savingsAccount, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo,
- final Set mandatoryResponseParameters) {
+ final RecurringDepositAccountData savingsAccount, final boolean staffInSelectedOfficeOnly, final String chargeStatus,
+ final UriInfo uriInfo, final Set mandatoryResponseParameters) {
Collection transactions = null;
Collection charges = null;
@@ -193,7 +196,7 @@ public class RecurringDepositAccountsApiResource {
if (associationParameters.contains(SavingsApiConstants.charges)) {
mandatoryResponseParameters.add(SavingsApiConstants.charges);
final Collection currentCharges = this.savingsAccountChargeReadPlatformService
- .retrieveSavingsAccountCharges(accountId);
+ .retrieveSavingsAccountCharges(accountId, chargeStatus);
if (!CollectionUtils.isEmpty(currentCharges)) {
charges = currentCharges;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountChargesApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountChargesApiResource.java
index 458cb4ed1..2d42b365d 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountChargesApiResource.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountChargesApiResource.java
@@ -9,11 +9,13 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.COMMAND_PA
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.COMMAND_WAIVE_CHARGE;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGES_RESPONSE_DATA_PARAMETERS;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.COMMAND_INACTIVATE_CHARGE;
import java.util.Collection;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
@@ -79,12 +81,15 @@ public class SavingsAccountChargesApiResource {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllSavingsAccountCharges(@PathParam("savingsAccountId") final Long savingsAccountId,
- @Context final UriInfo uriInfo) {
+ @DefaultValue("all") @QueryParam("chargeStatus") final String chargeStatus, @Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME);
+ if (!(is(chargeStatus, "all") || is(chargeStatus, "active") || is(chargeStatus, "inactive"))) { throw new UnrecognizedQueryParamException(
+ "status", chargeStatus, new Object[] { "all", "active", "inactive" }); }
+
final Collection savingsAccountCharges = this.savingsAccountChargeReadPlatformService
- .retrieveSavingsAccountCharges(savingsAccountId);
+ .retrieveSavingsAccountCharges(savingsAccountId, chargeStatus);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, savingsAccountCharges,
@@ -173,9 +178,17 @@ public class SavingsAccountChargesApiResource {
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ json = this.toApiJsonSerializer.serialize(result);
+ } else if (is(commandParam, COMMAND_INACTIVATE_CHARGE)) {
+ final CommandWrapper commandRequest = new CommandWrapperBuilder()
+ .inactivateSavingsAccountCharge(savingsAccountId, savingsAccountChargeId).withJson(apiRequestBodyAsJson).build();
+
+ final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
json = this.toApiJsonSerializer.serialize(result);
} else {
- throw new UnrecognizedQueryParamException("command", commandParam, COMMAND_PAY_CHARGE, COMMAND_WAIVE_CHARGE);
+ throw new UnrecognizedQueryParamException("command", commandParam, COMMAND_PAY_CHARGE, COMMAND_WAIVE_CHARGE,
+ COMMAND_INACTIVATE_CHARGE);
}
return json;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountsApiResource.java
index bfd81382e..b9c1621e4 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountsApiResource.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountsApiResource.java
@@ -130,15 +130,18 @@ public class SavingsAccountsApiResource {
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveOne(@PathParam("accountId") final Long accountId,
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
- @Context final UriInfo uriInfo) {
+ @DefaultValue("all") @QueryParam("chargeStatus") final String chargeStatus, @Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
+ if (!(is(chargeStatus, "all") || is(chargeStatus, "active") || is(chargeStatus, "inactive"))) { throw new UnrecognizedQueryParamException(
+ "status", chargeStatus, new Object[] { "all", "active", "inactive" }); }
+
final SavingsAccountData savingsAccount = this.savingsAccountReadPlatformService.retrieveOne(accountId);
final Set mandatoryResponseParameters = new HashSet<>();
final SavingsAccountData savingsAccountTemplate = populateTemplateAndAssociations(accountId, savingsAccount,
- staffInSelectedOfficeOnly, uriInfo, mandatoryResponseParameters);
+ staffInSelectedOfficeOnly, chargeStatus, uriInfo, mandatoryResponseParameters);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
mandatoryResponseParameters);
@@ -147,7 +150,8 @@ public class SavingsAccountsApiResource {
}
private SavingsAccountData populateTemplateAndAssociations(final Long accountId, final SavingsAccountData savingsAccount,
- final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo, final Set mandatoryResponseParameters) {
+ final boolean staffInSelectedOfficeOnly, final String chargeStatus, final UriInfo uriInfo,
+ final Set mandatoryResponseParameters) {
Collection transactions = null;
Collection charges = null;
@@ -171,7 +175,7 @@ public class SavingsAccountsApiResource {
if (associationParameters.contains(SavingsApiConstants.charges)) {
mandatoryResponseParameters.add(SavingsApiConstants.charges);
final Collection currentCharges = this.savingsAccountChargeReadPlatformService
- .retrieveSavingsAccountCharges(accountId);
+ .retrieveSavingsAccountCharges(accountId, chargeStatus);
if (!CollectionUtils.isEmpty(currentCharges)) {
charges = currentCharges;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeData.java
index 8f0ff9b69..3ececf6fa 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeData.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeData.java
@@ -18,64 +18,53 @@ import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
/**
* Immutable data object for Savings Account charge data.
*/
+@SuppressWarnings("unused")
public class SavingsAccountChargeData {
- @SuppressWarnings("unused")
private final Long id;
- @SuppressWarnings("unused")
private final Long chargeId;
- @SuppressWarnings("unused")
private final Long accountId;
- @SuppressWarnings("unused")
private final String name;
private final EnumOptionData chargeTimeType;
- @SuppressWarnings("unused")
private final LocalDate dueDate;
- @SuppressWarnings("unused")
private final MonthDay feeOnMonthDay;
- @SuppressWarnings("unused")
private final Integer feeInterval;
private final EnumOptionData chargeCalculationType;
private final BigDecimal percentage;
- @SuppressWarnings("unused")
private final BigDecimal amountPercentageAppliedTo;
- @SuppressWarnings("unused")
private final CurrencyData currency;
private final BigDecimal amount;
- @SuppressWarnings("unused")
private final BigDecimal amountPaid;
- @SuppressWarnings("unused")
private final BigDecimal amountWaived;
- @SuppressWarnings("unused")
private final BigDecimal amountWrittenOff;
- @SuppressWarnings("unused")
private final BigDecimal amountOutstanding;
- @SuppressWarnings("unused")
private final BigDecimal amountOrPercentage;
- @SuppressWarnings("unused")
- private final Collection chargeOptions;
-
- @SuppressWarnings("unused")
private final boolean penalty;
+ private final Boolean isActive;
+
+ private final LocalDate inactivationDate;
+
+ private final Collection chargeOptions;
+
public static SavingsAccountChargeData template(final Collection chargeOptions) {
final Long id = null;
final Long chargeId = null;
@@ -95,10 +84,12 @@ public class SavingsAccountChargeData {
final LocalDate dueAsOfDate = null;
final MonthDay feeOnMonthDay = null;
final Integer feeInterval = null;
+ final Boolean isActive = null;
+ final LocalDate inactivationDate = null;
return new SavingsAccountChargeData(id, chargeId, accountId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage,
amountPercentageAppliedTo, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeOptions,
- penalty, feeOnMonthDay, feeInterval);
+ penalty, feeOnMonthDay, feeInterval, isActive, inactivationDate);
}
public static SavingsAccountChargeData instance(final Long id, final Long chargeId, final Long accountId, final String name,
@@ -106,11 +97,11 @@ public class SavingsAccountChargeData {
final BigDecimal amountWrittenOff, final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType,
final LocalDate dueAsOfDate, final EnumOptionData chargeCalculationType, final BigDecimal percentage,
final BigDecimal amountPercentageAppliedTo, final Collection chargeOptions, final boolean penalty,
- final MonthDay feeOnMonthDay, final Integer feeInterval) {
+ final MonthDay feeOnMonthDay, final Integer feeInterval, final Boolean isActive, final LocalDate inactivationDate) {
return new SavingsAccountChargeData(id, chargeId, accountId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage,
amountPercentageAppliedTo, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeOptions,
- penalty, feeOnMonthDay, feeInterval);
+ penalty, feeOnMonthDay, feeInterval, isActive, inactivationDate);
}
private SavingsAccountChargeData(final Long id, final Long chargeId, final Long accountId, final String name,
@@ -118,7 +109,7 @@ public class SavingsAccountChargeData {
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 chargeOptions, final boolean penalty,
- final MonthDay feeOnMonthDay, final Integer feeInterval) {
+ final MonthDay feeOnMonthDay, final Integer feeInterval, final Boolean isActive, final LocalDate inactivationDate) {
this.id = id;
this.chargeId = chargeId;
this.accountId = accountId;
@@ -139,6 +130,8 @@ public class SavingsAccountChargeData {
this.penalty = penalty;
this.feeOnMonthDay = feeOnMonthDay;
this.feeInterval = feeInterval;
+ this.isActive = isActive;
+ this.inactivationDate = inactivationDate;
}
private BigDecimal getAmountOrPercentage() {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeDataValidator.java
index e411552e5..b9695ee44 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeDataValidator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeDataValidator.java
@@ -11,8 +11,8 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_AC
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 static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeIntervalParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
import java.lang.reflect.Type;
import java.math.BigDecimal;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java
index 454beb82c..40541955a 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java
@@ -769,7 +769,7 @@ public class SavingsAccount extends AbstractPersistable {
private void payWithdrawalFee(final BigDecimal transactionAmoount, final LocalDate transactionDate) {
for (SavingsAccountCharge charge : this.charges()) {
- if (charge.isWithdrawalFee()) {
+ if (charge.isWithdrawalFee() && charge.isActive()) {
charge.updateWithdralFeeAmount(transactionAmoount);
this.payCharge(charge, charge.getAmountOutstanding(this.getCurrency()), transactionDate);
}
@@ -1854,6 +1854,11 @@ public class SavingsAccount extends AbstractPersistable {
final SavingsAccountCharge savingsAccountCharge = getCharge(savingsAccountChargeId);
+ if (savingsAccountCharge.isNotActive()) {
+ baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("charge.is.not.active");
+ if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
+ }
+
if (savingsAccountCharge.isWithdrawalFee()) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.waiver.of.withdrawal.fee.not.supported");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
@@ -1977,6 +1982,11 @@ public class SavingsAccount extends AbstractPersistable {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
+ if (savingsAccountCharge.isNotActive()) {
+ baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("charge.is.not.active");
+ if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
+ }
+
if (getActivationLocalDate() != null && transactionDate.isBefore(getActivationLocalDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getActivationLocalDate().toString(formatter))
.failWithCodeNoParameterAddedToErrorCode("transaction.before.activationDate");
@@ -2182,4 +2192,31 @@ public class SavingsAccount extends AbstractPersistable {
public BigDecimal minBalanceForInterestCalculation() {
return this.minBalanceForInterestCalculation;
}
+
+ public void inactivateCharge(SavingsAccountCharge savingsAccountCharge, LocalDate inactivationOnDate) {
+ final List dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
+ .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); }
+ }
+ savingsAccountCharge.inactiavateCharge(inactivationOnDate);
+ }
+
+ public SavingsAccountCharge getUpdatedChargeDetails(SavingsAccountCharge savingsAccountCharge) {
+ for (final SavingsAccountCharge charge : this.charges) {
+ if (charge.equals(savingsAccountCharge)) {
+ savingsAccountCharge = charge;
+ break;
+ }
+ }
+ return savingsAccountCharge;
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java
index 1a898e6ed..67c6d1a56 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java
@@ -104,6 +104,10 @@ public class SavingsAccountCharge extends AbstractPersistable {
@Column(name = "is_active", nullable = false)
private boolean status = true;
+
+ @Temporal(TemporalType.DATE)
+ @Column(name = "inactivated_on_date")
+ private Date inactivationDate;
public static SavingsAccountCharge createNewFromJson(final SavingsAccount savingsAccount, final Charge chargeDefinition,
final JsonCommand command) {
@@ -302,6 +306,7 @@ public class SavingsAccountCharge extends AbstractPersistable {
resetPropertiesForRecurringFees();
updateToPreviousDueDate();// reset annual and monthly due date.
this.paid = false;
+ this.status = true;
}
public Money waive(final MonetaryCurrency currency) {
@@ -323,6 +328,7 @@ public class SavingsAccountCharge extends AbstractPersistable {
this.amountWaived = amountWaived.getAmount();
this.amountOutstanding = calculateAmountOutstanding(currency);
this.waived = false;
+ this.status = true;
resetPropertiesForRecurringFees();
updateToPreviousDueDate();
@@ -761,14 +767,15 @@ public class SavingsAccountCharge extends AbstractPersistable {
LocalDate nextDueLocalDate = new LocalDate(dueDate);
if (isAnnualFee()) {
nextDueLocalDate = nextDueLocalDate.withMonthOfYear(this.feeOnMonth).minusYears(1);
+ nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
} else if (isMonthlyFee()) {
nextDueLocalDate = nextDueLocalDate.minusMonths(this.feeInterval);
+ nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
} else if (isWeeklyFee()) {
nextDueLocalDate = nextDueLocalDate.minusDays(7 * this.feeInterval);
+ nextDueLocalDate = setDayOfWeek(nextDueLocalDate);
}
- nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
-
this.dueDate = nextDueLocalDate.toDate();
}
}
@@ -780,4 +787,40 @@ public class SavingsAccountCharge extends AbstractPersistable {
public boolean feeSettingsSet() {
return this.feeOnDay != null && this.feeOnMonth != null;
}
+
+ public boolean isRecurringFee() {
+ return isWeeklyFee() || isMonthlyFee() || isAnnualFee();
+ }
+
+ public boolean isChargeIsDue(final LocalDate nextDueDate) {
+ return this.getDueLocalDate().isBefore(nextDueDate);
+ }
+
+ public boolean isChargeIsOverPaid(final LocalDate nextDueDate) {
+ final BigDecimal amountPaid = this.amountPaid == null ? BigDecimal.ZERO : amountPaid();
+ return this.getDueLocalDate().isAfter(nextDueDate) && amountPaid.compareTo(BigDecimal.ZERO) == 1;
+ }
+
+ private BigDecimal amountPaid() {
+ return this.amountPaid;
+ }
+
+ public LocalDate nextDuDate(final LocalDate date) {
+ return calculateNextDueDate(date);
+ }
+
+ public void inactiavateCharge(final LocalDate inactivationOnDate) {
+ this.inactivationDate = inactivationOnDate.toDate();
+ this.status = false;
+ this.amountOutstanding = BigDecimal.ZERO;
+ this.paid = true;
+ }
+
+ public boolean isActive() {
+ return this.status;
+ }
+
+ public boolean isNotActive() {
+ return !isActive();
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java
index f03f326c2..3ad6a8239 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java
@@ -567,4 +567,22 @@ public final class SavingsAccountTransaction extends AbstractPersistable {
return this.createdDate;
}
+ public boolean isPaymentForCurrentCharge(final SavingsAccountCharge savingsAccountCharge) {
+
+ final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
+ final boolean isChargePaidForCurrentCharge;
+ if (chargePaidBy == null) {
+ isChargePaidForCurrentCharge = false;
+ } else if (chargePaidBy.getSavingsAccountCharge().equals(savingsAccountCharge)) {
+ isChargePaidForCurrentCharge = true;
+ } else {
+ isChargePaidForCurrentCharge = false;
+ }
+
+ return isChargePaidForCurrentCharge;
+ }
+
+ public BigDecimal getAmount() {
+ return this.amount;
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/InactivateSavingsAccountChargeCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/InactivateSavingsAccountChargeCommandHandler.java
new file mode 100644
index 000000000..de0f6f2b1
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/InactivateSavingsAccountChargeCommandHandler.java
@@ -0,0 +1,27 @@
+package org.mifosplatform.portfolio.savings.handler;
+
+import org.mifosplatform.commands.handler.NewCommandSourceHandler;
+import org.mifosplatform.infrastructure.core.api.JsonCommand;
+import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
+import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class InactivateSavingsAccountChargeCommandHandler implements NewCommandSourceHandler {
+
+ private final SavingsAccountWritePlatformService writePlatformService;
+
+ @Autowired
+ public InactivateSavingsAccountChargeCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
+ this.writePlatformService = writePlatformService;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult processCommand(JsonCommand command) {
+ return this.writePlatformService.inactivateCharge(command.getSavingsId(), command.entityId());
+ }
+
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformService.java
index 6de5b8bd4..217d28291 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformService.java
@@ -15,7 +15,7 @@ public interface SavingsAccountChargeReadPlatformService {
ChargeData retrieveSavingsAccountChargeTemplate();
- Collection retrieveSavingsAccountCharges(Long savingsAccountId);
+ Collection retrieveSavingsAccountCharges(Long savingsAccountId, String status);
SavingsAccountChargeData retrieveSavingsAccountChargeDetails(Long savingsAccountChargeId, Long savingsAccountId);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java
index ec1ffc4f4..0d6d880bc 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java
@@ -71,6 +71,7 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
+ "sc.fee_on_month as feeOnMonth, "
+ "sc.fee_on_day as feeOnDay, sc.fee_interval as feeInterval, "
+ "sc.charge_calculation_enum as chargeCalculation, "
+ + "sc.is_active as isActive, sc.inactivated_on_date as inactivationDate, "
+ "c.currency_code as currencyCode, 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 "
@@ -119,12 +120,14 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
final int chargeCalculation = rs.getInt("chargeCalculation");
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
final boolean penalty = rs.getBoolean("penalty");
+ final Boolean isActive = rs.getBoolean("isActive");
+ final LocalDate inactivationDate = JdbcSupport.getLocalDate(rs, "inactivationDate");
final Collection chargeOptions = null;
return SavingsAccountChargeData.instance(id, chargeId, accountId, name, currency, amount, amountPaid, amountWaived,
amountWrittenOff, amountOutstanding, chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf,
- amountPercentageAppliedTo, chargeOptions, penalty, feeOnMonthDay, feeInterval);
+ amountPercentageAppliedTo, chargeOptions, penalty, feeOnMonthDay, feeInterval, isActive, inactivationDate);
}
}
@@ -166,15 +169,20 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
}
@Override
- public Collection retrieveSavingsAccountCharges(final Long loanId) {
+ public Collection retrieveSavingsAccountCharges(final Long loanId, final String status) {
this.context.authenticatedUser();
final SavingsAccountChargeMapper rm = new SavingsAccountChargeMapper();
+ final StringBuilder sqlBuilder = new StringBuilder();
+ sqlBuilder.append("select ").append(rm.schema()).append(" where sc.savings_account_id=? ");
+ if (status.equalsIgnoreCase("active")) {
+ sqlBuilder.append(" and sc.is_active = 1 ");
+ } else if (status.equalsIgnoreCase("inactive")) {
+ sqlBuilder.append(" and sc.is_active = 0 ");
+ }
+ sqlBuilder.append(" order by sc.charge_time_enum ASC, sc.charge_due_date ASC, sc.is_penalty ASC");
- final String sql = "select " + rm.schema() + " where sc.savings_account_id=? "
- + " order by sc.charge_time_enum ASC, sc.charge_due_date ASC, sc.is_penalty ASC";
-
- return this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
+ return this.jdbcTemplate.query(sqlBuilder.toString(), rm, new Object[] { loanId });
}
private static final class SavingsAccountChargeDueMapper implements RowMapper {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java
index 9276b4a77..683ad3bbf 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java
@@ -55,6 +55,8 @@ public interface SavingsAccountWritePlatformService {
CommandProcessingResult waiveCharge(Long savingsAccountId, Long savingsAccountChargeId);
CommandProcessingResult payCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command);
+
+ CommandProcessingResult inactivateCharge(Long savingsAccountId, Long savingsAccountChargeId);
void applyChargeDue(final Long savingsAccountChargeId, final Long accountId);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
index 753bcdb1f..f866c5dfa 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
@@ -5,6 +5,7 @@
*/
package org.mifosplatform.portfolio.savings.service;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME;
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;
@@ -81,6 +82,7 @@ import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
@Service
public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements SavingsAccountWritePlatformService {
@@ -938,4 +940,73 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
}
+
+ @Override
+ public CommandProcessingResult inactivateCharge(final Long savingsAccountId, final Long savingsAccountChargeId) {
+
+ this.context.authenticatedUser();
+
+ final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
+ savingsAccountChargeId, savingsAccountId);
+
+ final SavingsAccount account = savingsAccountCharge.savingsAccount();
+ this.savingAccountAssembler.assignSavingAccountHelpers(account);
+
+ final LocalDate inactivationOnDate = DateUtils.getLocalDateOfTenant();
+
+ final List dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
+ .resource(SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME);
+
+ /***
+ * Only recurring fees are allowed to inactivate
+ */
+ if (!savingsAccountCharge.isRecurringFee()) {
+ baseDataValidator.reset().parameter(null).value(savingsAccountCharge.getId())
+ .failWithCodeNoParameterAddedToErrorCode("charge.inactivation.allowed.only.for.recurring.charges");
+ if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
+
+ } else {
+ final LocalDate nextDueDate = savingsAccountCharge.nextDuDate(inactivationOnDate);
+
+ if (savingsAccountCharge.isChargeIsDue(nextDueDate)) {
+ baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("inactivation.of.charge.not.allowed.when.charge.is.due");
+ if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
+ } else if (savingsAccountCharge.isChargeIsOverPaid(nextDueDate)) {
+
+ final List chargePayments = new ArrayList<>();
+ SavingsAccountCharge updatedCharge = savingsAccountCharge;
+ do {
+ chargePayments.clear();
+ for (SavingsAccountTransaction transaction : account.getTransactions()) {
+ if (transaction.isPayCharge() && transaction.isNotReversed()
+ && transaction.isPaymentForCurrentCharge(savingsAccountCharge)) {
+ chargePayments.add(transaction);
+ }
+ }
+ /***
+ * Reverse the excess payments of charge transactions
+ */
+ SavingsAccountTransaction lastChargePayment = getLastChargePayment(chargePayments);
+ this.undoTransaction(savingsAccountCharge.savingsAccount().getId(), lastChargePayment.getId(), false);
+ updatedCharge = account.getUpdatedChargeDetails(savingsAccountCharge);
+ } while (updatedCharge.isChargeIsOverPaid(nextDueDate));
+ }
+ account.inactivateCharge(savingsAccountCharge, inactivationOnDate);
+ }
+
+ return new CommandProcessingResultBuilder() //
+ .withEntityId(savingsAccountCharge.getId()) //
+ .withOfficeId(savingsAccountCharge.savingsAccount().officeId()) //
+ .withClientId(savingsAccountCharge.savingsAccount().clientId()) //
+ .withGroupId(savingsAccountCharge.savingsAccount().groupId()) //
+ .withSavingsId(savingsAccountCharge.savingsAccount().getId()) //
+ .build();
+ }
+
+ private SavingsAccountTransaction getLastChargePayment(final List chargePayments) {
+ if (!CollectionUtils.isEmpty(chargePayments)) { return chargePayments.get(chargePayments.size() - 1); }
+ return null;
+ }
+
}
\ No newline at end of file