MIFOSX-1408 Inactivate savings charges
This commit is contained in:
+52
-3
@@ -525,7 +525,7 @@
|
||||
<td><a href="#savings_charges">Savings Account Charges</a></td>
|
||||
<td>savingsaccounts/{accountId}/charges</td>
|
||||
<td><a href="#savings_charges_create">Add a Savings Account Charge</a></td>
|
||||
<td></td>
|
||||
<td><a href="#savings_charges_list">List Savings Account Charges</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
@@ -553,6 +553,14 @@
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>savingsaccounts/{accountId}/charges/{savingsAccountChargeId}?command=inactivate</td>
|
||||
<td><a href="#savings_charges_inactivate">Inactivate a Savings Account Charge</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="#fdaccounts">Fixed Deposit Accounts</a></td>
|
||||
<td>fixeddepositaccounts</td>
|
||||
@@ -18833,6 +18841,11 @@ No Request Body:
|
||||
<p>Example Requests:</p>
|
||||
<div class=apiClick>savingsaccounts/1/charges</div>
|
||||
<br>
|
||||
<div class=apiClick>savingsaccounts/1/charges?chargeStatus=all</div>
|
||||
<br>
|
||||
<div class=apiClick>savingsaccounts/1/charges?chargeStatus=inactive</div>
|
||||
<br>
|
||||
<div class=apiClick>savingsaccounts/1/charges?chargeStatus=active</div>
|
||||
<br>
|
||||
<div class=apiClick>savingsaccounts/1/charges?fields=name,amountOrPercentage</div>
|
||||
</div>
|
||||
@@ -19318,7 +19331,7 @@ No Request Body:
|
||||
<div class="method-description">
|
||||
<h4>Pay a Savings account Charge</h4>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
@@ -19375,6 +19388,34 @@ No Request Body:
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a id="savings_charges_inactivate" name="savings_charges_inactivate" class="old-syle-anchor"> </a>
|
||||
<div class="method-section">
|
||||
<div class="method-description">
|
||||
<h4>Inactivate a Savings account Charge</h4>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
<div class="method-example">
|
||||
<code class="method-declaration">
|
||||
POST https://DomainName/api/v1/savingsaccounts/{accountId}/charges/{savingsAccountChargeId}?command=inactivate
|
||||
</code>
|
||||
<code class="method-request">
|
||||
POST savingsaccounts/1/charges/2?command=inactivate
|
||||
Content-Type: application/json
|
||||
No Request Body:
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"officeId": 1,
|
||||
"clientId": 1,
|
||||
"savingsId": 1,
|
||||
"resourceId": 2
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End of Savings account charges -->
|
||||
|
||||
<!-- start of fixed deposit accounts api -->
|
||||
@@ -32518,7 +32559,7 @@ No Request Body:
|
||||
<td><a href="#savings_charges">Savings Account Charges</a></td>
|
||||
<td>savingsaccounts/{accountId}/charges</td>
|
||||
<td><a href="#savings_charges_create">Add a Savings Account Charge</a></td>
|
||||
<td></td>
|
||||
<td><a href="#savings_charges_list">List Savings Account Charges</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
@@ -32545,6 +32586,14 @@ No Request Body:
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>savingsaccounts/{accountId}/charges/{savingsAccountChargeId}?command=inactivate</td>
|
||||
<td><a href="#savings_charges_inactivate">Inactivate a Savings Account Charge</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="#fdproducts">Fixed Deposit Product</a></td>
|
||||
|
||||
@@ -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`;
|
||||
+30
-2
@@ -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<HashMap> savingsAccountErrorData = (ArrayList<HashMap>) 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<HashMap>) 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<HashMap>) 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));
|
||||
|
||||
+15
-1
@@ -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<String, Object> map = new HashMap<>();
|
||||
map.put("locale", CommonConstants.locale);
|
||||
map.put("dateFormat", CommonConstants.dateFormat);
|
||||
String josn = new Gson().toJson(map);
|
||||
return josn;
|
||||
}
|
||||
}
|
||||
@@ -872,4 +872,8 @@ public class CommandWrapper {
|
||||
return this.actionName.equalsIgnoreCase("CREATE");
|
||||
}
|
||||
|
||||
public boolean isInactivateSavingsAccountCharge() {
|
||||
return this.actionName.equalsIgnoreCase("INACTIVATE") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNTCHARGE");
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -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";
|
||||
|
||||
+2
@@ -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());
|
||||
}
|
||||
|
||||
+3
-2
@@ -206,14 +206,15 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO;
|
||||
final Collection<ChargeData> 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;
|
||||
}
|
||||
|
||||
+1
@@ -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";
|
||||
|
||||
+7
-4
@@ -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<String> 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<String> mandatoryResponseParameters) {
|
||||
final boolean staffInSelectedOfficeOnly, final String chargeStatus, final UriInfo uriInfo, final Set<String> mandatoryResponseParameters) {
|
||||
|
||||
Collection<SavingsAccountTransactionData> transactions = null;
|
||||
Collection<SavingsAccountChargeData> charges = null;
|
||||
@@ -197,7 +200,7 @@ public class FixedDepositAccountsApiResource {
|
||||
if (associationParameters.contains(SavingsApiConstants.charges)) {
|
||||
mandatoryResponseParameters.add(SavingsApiConstants.charges);
|
||||
final Collection<SavingsAccountChargeData> currentCharges = this.savingsAccountChargeReadPlatformService
|
||||
.retrieveSavingsAccountCharges(accountId);
|
||||
.retrieveSavingsAccountCharges(accountId, chargeStatus);
|
||||
if (!CollectionUtils.isEmpty(currentCharges)) {
|
||||
charges = currentCharges;
|
||||
}
|
||||
|
||||
+8
-5
@@ -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<String> 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<String> mandatoryResponseParameters) {
|
||||
final RecurringDepositAccountData savingsAccount, final boolean staffInSelectedOfficeOnly, final String chargeStatus,
|
||||
final UriInfo uriInfo, final Set<String> mandatoryResponseParameters) {
|
||||
|
||||
Collection<SavingsAccountTransactionData> transactions = null;
|
||||
Collection<SavingsAccountChargeData> charges = null;
|
||||
@@ -193,7 +196,7 @@ public class RecurringDepositAccountsApiResource {
|
||||
if (associationParameters.contains(SavingsApiConstants.charges)) {
|
||||
mandatoryResponseParameters.add(SavingsApiConstants.charges);
|
||||
final Collection<SavingsAccountChargeData> currentCharges = this.savingsAccountChargeReadPlatformService
|
||||
.retrieveSavingsAccountCharges(accountId);
|
||||
.retrieveSavingsAccountCharges(accountId, chargeStatus);
|
||||
if (!CollectionUtils.isEmpty(currentCharges)) {
|
||||
charges = currentCharges;
|
||||
}
|
||||
|
||||
+16
-3
@@ -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<SavingsAccountChargeData> 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;
|
||||
|
||||
+8
-4
@@ -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<String> 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<String> mandatoryResponseParameters) {
|
||||
final boolean staffInSelectedOfficeOnly, final String chargeStatus, final UriInfo uriInfo,
|
||||
final Set<String> mandatoryResponseParameters) {
|
||||
|
||||
Collection<SavingsAccountTransactionData> transactions = null;
|
||||
Collection<SavingsAccountChargeData> charges = null;
|
||||
@@ -171,7 +175,7 @@ public class SavingsAccountsApiResource {
|
||||
if (associationParameters.contains(SavingsApiConstants.charges)) {
|
||||
mandatoryResponseParameters.add(SavingsApiConstants.charges);
|
||||
final Collection<SavingsAccountChargeData> currentCharges = this.savingsAccountChargeReadPlatformService
|
||||
.retrieveSavingsAccountCharges(accountId);
|
||||
.retrieveSavingsAccountCharges(accountId, chargeStatus);
|
||||
if (!CollectionUtils.isEmpty(currentCharges)) {
|
||||
charges = currentCharges;
|
||||
}
|
||||
|
||||
+15
-22
@@ -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<ChargeData> chargeOptions;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final boolean penalty;
|
||||
|
||||
private final Boolean isActive;
|
||||
|
||||
private final LocalDate inactivationDate;
|
||||
|
||||
private final Collection<ChargeData> chargeOptions;
|
||||
|
||||
public static SavingsAccountChargeData template(final Collection<ChargeData> 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<ChargeData> 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<ChargeData> 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() {
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+38
-1
@@ -769,7 +769,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
|
||||
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<Long> {
|
||||
|
||||
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<Long> {
|
||||
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<Long> {
|
||||
public BigDecimal minBalanceForInterestCalculation() {
|
||||
return this.minBalanceForInterestCalculation;
|
||||
}
|
||||
|
||||
public void inactivateCharge(SavingsAccountCharge savingsAccountCharge, LocalDate inactivationOnDate) {
|
||||
final List<ApiParameterError> 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;
|
||||
}
|
||||
}
|
||||
+45
-2
@@ -104,6 +104,10 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
|
||||
@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<Long> {
|
||||
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<Long> {
|
||||
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<Long> {
|
||||
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<Long> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
+18
@@ -567,4 +567,22 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
+27
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@ public interface SavingsAccountChargeReadPlatformService {
|
||||
|
||||
ChargeData retrieveSavingsAccountChargeTemplate();
|
||||
|
||||
Collection<SavingsAccountChargeData> retrieveSavingsAccountCharges(Long savingsAccountId);
|
||||
Collection<SavingsAccountChargeData> retrieveSavingsAccountCharges(Long savingsAccountId, String status);
|
||||
|
||||
SavingsAccountChargeData retrieveSavingsAccountChargeDetails(Long savingsAccountChargeId, Long savingsAccountId);
|
||||
|
||||
|
||||
+14
-6
@@ -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<ChargeData> 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<SavingsAccountChargeData> retrieveSavingsAccountCharges(final Long loanId) {
|
||||
public Collection<SavingsAccountChargeData> 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<SavingsAccountAnnualFeeData> {
|
||||
|
||||
+2
@@ -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);
|
||||
|
||||
|
||||
+71
@@ -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<ApiParameterError> 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<SavingsAccountTransaction> 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<SavingsAccountTransaction> chargePayments) {
|
||||
if (!CollectionUtils.isEmpty(chargePayments)) { return chargePayments.get(chargePayments.size() - 1); }
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user