Merge pull request #818 from pramodn02/MIFOSX-1025
MIFOSX-1025 - Added Overdue Penalty recurrence
This commit is contained in:
+151
-3
@@ -5749,7 +5749,52 @@ GET https://DomainName/api/v1/loans/{loanId}
|
||||
"disbursementDetails":[{"id":71,"expectedDisbursementDate":[2013,11,1],"principal":22000.000000,"approvedPrincipal":22000.000000}],
|
||||
"fixedEmiAmount":1100.000000,
|
||||
"maxOutstandingLoanBalance":35000,
|
||||
"canDisburse":false
|
||||
"canDisburse":false,
|
||||
"emiAmountVariations": [],
|
||||
"inArrears": true,
|
||||
"overdueCharges": [
|
||||
{
|
||||
"id": 20,
|
||||
"name": "overdraft penality",
|
||||
"active": true,
|
||||
"penalty": true,
|
||||
"currency": {
|
||||
"code": "USD",
|
||||
"name": "US Dollar",
|
||||
"decimalPlaces": 2,
|
||||
"displaySymbol": "$",
|
||||
"nameCode": "currency.USD",
|
||||
"displayLabel": "US Dollar ($)"
|
||||
},
|
||||
"amount": 3.000000,
|
||||
"chargeTimeType": {
|
||||
"id": 9,
|
||||
"code": "chargeTimeType.overdueInstallment",
|
||||
"value": "overdue fees"
|
||||
},
|
||||
"chargeAppliesTo": {
|
||||
"id": 1,
|
||||
"code": "chargeAppliesTo.loan",
|
||||
"value": "Loan"
|
||||
},
|
||||
"chargeCalculationType": {
|
||||
"id": 2,
|
||||
"code": "chargeCalculationType.percent.of.amount",
|
||||
"value": "% Amount"
|
||||
},
|
||||
"chargePaymentMode": {
|
||||
"id": 0,
|
||||
"code": "chargepaymentmode.regular",
|
||||
"value": "Regular"
|
||||
},
|
||||
"feeInterval": 2,
|
||||
"feeFrequency": {
|
||||
"id": 1,
|
||||
"code": "feeFrequencyperiodFrequencyType.weeks",
|
||||
"value": "Weeks"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
</code>
|
||||
@@ -12575,9 +12620,15 @@ Request Body:
|
||||
<td>feeInterval</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>Fee to be applied on a specified interval (number of months).<br/>
|
||||
This field is mandatory if <i>chargeTimeType</i> is of type <b>Monthly Fee</b>
|
||||
<td class=fielddesc>Fee to be applied on a specified interval.<br/>
|
||||
This field is mandatory if <i>chargeTimeType</i> is of type <b>Monthly Fee</b> or <i>feeFrequency<i> is selcted
|
||||
</td>
|
||||
</tr>
|
||||
<tr class=alt>
|
||||
<td>feeFrequency</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class=fielddesc>This field is Optional Used to indicate the recurring(days,weeks,months and Years) charge due date starting on system calculated date for overdue penalty.<br/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -12627,6 +12678,16 @@ GET https://DomainName/api/v1/charges/template
|
||||
"id": 2,
|
||||
"code": "chargeCalculationType.percent.of.amount",
|
||||
"value": "% Amount"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"code": "chargeCalculationType.percent.of.amount.and.interest",
|
||||
"value": "% Loan Amount + Interest"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"code": "chargeCalculationType.percent.of.interest",
|
||||
"value": "% Interest"
|
||||
}
|
||||
],
|
||||
"chargeAppliesToOptions": [
|
||||
@@ -12671,6 +12732,21 @@ GET https://DomainName/api/v1/charges/template
|
||||
"id": 7,
|
||||
"code": "chargeTimeType.monthlyFee",
|
||||
"value": "Monthly Fee"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"code": "chargeTimeType.instalmentFee",
|
||||
"value": "Instalment Fee"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"code": "chargeTimeType.overdueInstallment",
|
||||
"value": "overdue fees"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"code": "chargeTimeType.overdraftFee",
|
||||
"value": "Overdraft Fee"
|
||||
}
|
||||
],
|
||||
"chargePaymetModeOptions": [
|
||||
@@ -12695,6 +12771,16 @@ GET https://DomainName/api/v1/charges/template
|
||||
"id": 2,
|
||||
"code": "chargeCalculationType.percent.of.amount",
|
||||
"value": "% Amount"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"code": "chargeCalculationType.percent.of.amount.and.interest",
|
||||
"value": "% Loan Amount + Interest"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"code": "chargeCalculationType.percent.of.interest",
|
||||
"value": "% Interest"
|
||||
}
|
||||
],
|
||||
"loanChargeTimeTypeOptions": [
|
||||
@@ -12707,6 +12793,16 @@ GET https://DomainName/api/v1/charges/template
|
||||
"id": 2,
|
||||
"code": "chargeTimeType.specifiedDueDate",
|
||||
"value": "Specified due date"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"code": "chargeTimeType.instalmentFee",
|
||||
"value": "Instalment Fee"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"code": "chargeTimeType.overdueInstallment",
|
||||
"value": "overdue fees"
|
||||
}
|
||||
],
|
||||
"savingsChargeCalculationTypeOptions": [
|
||||
@@ -12746,6 +12842,33 @@ GET https://DomainName/api/v1/charges/template
|
||||
"id": 7,
|
||||
"code": "chargeTimeType.monthlyFee",
|
||||
"value": "Monthly Fee"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"code": "chargeTimeType.overdraftFee",
|
||||
"value": "Overdraft Fee"
|
||||
}
|
||||
],
|
||||
"feeFrequencyOptions": [
|
||||
{
|
||||
"id": 0,
|
||||
"code": "loanTermFrequency.periodFrequencyType.days",
|
||||
"value": "Days"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"code": "loanTermFrequency.periodFrequencyType.weeks",
|
||||
"value": "Weeks"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"code": "loanTermFrequency.periodFrequencyType.months",
|
||||
"value": "Months"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"code": "loanTermFrequency.periodFrequencyType.years",
|
||||
"value": "Years"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -12853,6 +12976,31 @@ Request Body:
|
||||
<code class="method-response">
|
||||
{
|
||||
"resourceId": 6
|
||||
}
|
||||
</code>
|
||||
<code class="method-request">
|
||||
POST charges
|
||||
Content-Type: application/json
|
||||
Request Body:
|
||||
{
|
||||
"chargeAppliesTo":1,
|
||||
"feeFrequency":1,
|
||||
"feeInterval":"2",
|
||||
"name":"overdue charge",
|
||||
"currencyCode":"USD",
|
||||
"chargeTimeType":9,
|
||||
"chargeCalculationType":1,
|
||||
"chargePaymentMode":0,
|
||||
"amount":"50",
|
||||
"active":true,
|
||||
"penalty":"true",
|
||||
"locale":"en",
|
||||
"monthDayFormat":"dd MMM"
|
||||
}
|
||||
</code>
|
||||
<code class="method-response">
|
||||
{
|
||||
"resourceId": 7
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
ALTER TABLE `m_charge`
|
||||
ADD COLUMN `fee_frequency` SMALLINT(5) NULL DEFAULT NULL AFTER `max_cap`;
|
||||
|
||||
CREATE TABLE `m_loan_overdue_installment_charge` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`loan_charge_id` BIGINT(20) NOT NULL,
|
||||
`loan_schedule_id` BIGINT(20) NOT NULL,
|
||||
`frequency_number` INT(10) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `FK_m_loan_overdue_installment_charge_m_loan_charge` FOREIGN KEY (`loan_charge_id`) REFERENCES `m_loan_charge` (`id`),
|
||||
CONSTRAINT `FK_m_loan_overdue_installment_charge_m_loan_repayment_schedule` FOREIGN KEY (`loan_schedule_id`) REFERENCES `m_loan_repayment_schedule` (`id`)
|
||||
)
|
||||
COLLATE='latin1_swedish_ci'
|
||||
ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO `c_configuration` (`name`, `value`, `enabled`) VALUES ('grace-on-penalty-posting', 0, 1);
|
||||
|
||||
INSERT INTO m_loan_overdue_installment_charge (`loan_charge_id`, `loan_schedule_id`, `frequency_number`) SELECT mlc.id ,ls.id ,1 from m_loan_charge as mlc inner join m_charge mc on mc.id = mlc.charge_id inner join m_loan_repayment_schedule ls on ls.loan_id = mlc.loan_id and ls.duedate = mlc.due_for_collection_as_of_date where mc.charge_time_enum = 9 and mlc.is_active = 1;
|
||||
+2
@@ -32,4 +32,6 @@ public interface ConfigurationDomainService {
|
||||
boolean isPasswordForcedResetEnable();
|
||||
|
||||
Long retrievePasswordLiveTime();
|
||||
|
||||
Long retrieveGraceOnPenaltyPostingPeriod();
|
||||
}
|
||||
+8
@@ -113,6 +113,14 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
|
||||
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
|
||||
return property.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long retrieveGraceOnPenaltyPostingPeriod() {
|
||||
final String propertyName = "grace-on-penalty-posting";
|
||||
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
|
||||
return property.getValue();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isPasswordForcedResetEnable() {
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarFrequencyType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarWeekDaysType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
|
||||
public class CalendarUtils {
|
||||
|
||||
|
||||
+33
-7
@@ -14,6 +14,7 @@ import org.joda.time.LocalDate;
|
||||
import org.joda.time.MonthDay;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData;
|
||||
|
||||
@@ -36,6 +37,7 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
private final Integer feeInterval;
|
||||
private final BigDecimal minCap;
|
||||
private final BigDecimal maxCap;
|
||||
private final EnumOptionData feeFrequency;
|
||||
|
||||
private final Collection<CurrencyData> currencyOptions;
|
||||
private final List<EnumOptionData> chargeCalculationTypeOptions;//
|
||||
@@ -47,16 +49,19 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
private final List<EnumOptionData> loanChargeTimeTypeOptions;
|
||||
private final List<EnumOptionData> savingsChargeCalculationTypeOptions;
|
||||
private final List<EnumOptionData> savingsChargeTimeTypeOptions;
|
||||
private final List<EnumOptionData> feeFrequencyOptions;
|
||||
|
||||
public static ChargeData template(final Collection<CurrencyData> currencyOptions,
|
||||
final List<EnumOptionData> chargeCalculationTypeOptions, final List<EnumOptionData> chargeAppliesToOptions,
|
||||
final List<EnumOptionData> chargeTimeTypeOptions, final List<EnumOptionData> chargePaymentModeOptions,
|
||||
final List<EnumOptionData> loansChargeCalculationTypeOptions, final List<EnumOptionData> loansChargeTimeTypeOptions,
|
||||
final List<EnumOptionData> savingsChargeCalculationTypeOptions, final List<EnumOptionData> savingsChargeTimeTypeOptions) {
|
||||
final List<EnumOptionData> savingsChargeCalculationTypeOptions, final List<EnumOptionData> savingsChargeTimeTypeOptions,
|
||||
final List<EnumOptionData> feeFrequencyOptions) {
|
||||
|
||||
return new ChargeData(null, null, null, null, null, null, null, null, false, false, currencyOptions, chargeCalculationTypeOptions,
|
||||
chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions,
|
||||
loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, null, null, null, null);
|
||||
loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, null, null, null, null,
|
||||
null, feeFrequencyOptions);
|
||||
}
|
||||
|
||||
public static ChargeData withTemplate(final ChargeData charge, final ChargeData template) {
|
||||
@@ -65,13 +70,13 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
template.chargeCalculationTypeOptions, template.chargeAppliesToOptions, template.chargeTimeTypeOptions,
|
||||
template.chargePaymetModeOptions, template.loanChargeCalculationTypeOptions, template.loanChargeTimeTypeOptions,
|
||||
template.savingsChargeCalculationTypeOptions, template.savingsChargeTimeTypeOptions, charge.feeOnMonthDay,
|
||||
charge.feeInterval, charge.minCap, charge.maxCap);
|
||||
charge.feeInterval, charge.minCap, charge.maxCap, charge.feeFrequency, template.feeFrequencyOptions);
|
||||
}
|
||||
|
||||
public static ChargeData instance(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
|
||||
final EnumOptionData chargeTimeType, final EnumOptionData chargeAppliesTo, final EnumOptionData chargeCalculationType,
|
||||
final EnumOptionData chargePaymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval, final boolean penalty,
|
||||
final boolean active, final BigDecimal minCap, final BigDecimal maxCap) {
|
||||
final boolean active, final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency) {
|
||||
|
||||
final Collection<CurrencyData> currencyOptions = null;
|
||||
final List<EnumOptionData> chargeCalculationTypeOptions = null;
|
||||
@@ -82,11 +87,13 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final List<EnumOptionData> loansChargeTimeTypeOptions = null;
|
||||
final List<EnumOptionData> savingsChargeCalculationTypeOptions = null;
|
||||
final List<EnumOptionData> savingsChargeTimeTypeOptions = null;
|
||||
final List<EnumOptionData> feeFrequencyOptions = null;
|
||||
|
||||
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
|
||||
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
|
||||
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap);
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap,
|
||||
feeFrequency, feeFrequencyOptions);
|
||||
}
|
||||
|
||||
public static ChargeData lookup(final Long id, final String name, final boolean isPenalty) {
|
||||
@@ -111,11 +118,14 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final List<EnumOptionData> loansChargeTimeTypeOptions = null;
|
||||
final List<EnumOptionData> savingsChargeCalculationTypeOptions = null;
|
||||
final List<EnumOptionData> savingsChargeTimeTypeOptions = null;
|
||||
final EnumOptionData feeFrequency = null;
|
||||
final List<EnumOptionData> feeFrequencyOptions = null;
|
||||
|
||||
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
|
||||
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
|
||||
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap);
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap,
|
||||
feeFrequency, feeFrequencyOptions);
|
||||
}
|
||||
|
||||
private ChargeData(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
|
||||
@@ -126,7 +136,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final List<EnumOptionData> chargePaymentModeOptions, final List<EnumOptionData> loansChargeCalculationTypeOptions,
|
||||
final List<EnumOptionData> loansChargeTimeTypeOptions, final List<EnumOptionData> savingsChargeCalculationTypeOptions,
|
||||
final List<EnumOptionData> savingsChargeTimeTypeOptions, final MonthDay feeOnMonthDay, final Integer feeInterval,
|
||||
final BigDecimal minCap, final BigDecimal maxCap) {
|
||||
final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency,
|
||||
final List<EnumOptionData> feeFrequencyOptions) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.amount = amount;
|
||||
@@ -150,6 +161,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
this.savingsChargeTimeTypeOptions = savingsChargeTimeTypeOptions;
|
||||
this.loanChargeCalculationTypeOptions = loansChargeCalculationTypeOptions;
|
||||
this.loanChargeTimeTypeOptions = loansChargeTimeTypeOptions;
|
||||
this.feeFrequency = feeFrequency;
|
||||
this.feeFrequencyOptions = feeFrequencyOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -199,4 +212,17 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
this.chargeCalculationType, percentage, amountPercentageAppliedTo, chargeOptions, this.penalty, this.feeOnMonthDay,
|
||||
this.feeInterval);
|
||||
}
|
||||
|
||||
|
||||
public boolean isPenalty() {
|
||||
return this.penalty;
|
||||
}
|
||||
|
||||
public boolean isOverdueInstallmentCharge(){
|
||||
boolean isOverdueInstallmentCharge = false;
|
||||
if(this.chargeTimeType != null){
|
||||
isOverdueInstallmentCharge = ChargeTimeType.fromInt(this.chargeTimeType.getId().intValue()).isOverdueInstallment();
|
||||
}
|
||||
return isOverdueInstallmentCharge;
|
||||
}
|
||||
}
|
||||
+34
-5
@@ -27,6 +27,7 @@ import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidation
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.exception.ChargeDueAtDisbursementCannotBePenaltyException;
|
||||
import org.mifosplatform.portfolio.charge.exception.ChargeMustBePenaltyException;
|
||||
import org.mifosplatform.portfolio.charge.exception.ChargeParameterUpdateNotSupportedException;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeEnumerations;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||
@@ -81,6 +82,9 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
@Column(name = "max_cap", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal maxCap;
|
||||
|
||||
@Column(name = "fee_frequency", nullable = true)
|
||||
private Integer feeFrequency;
|
||||
|
||||
public static Charge fromJson(final JsonCommand command) {
|
||||
|
||||
final String name = command.stringValueOfParameterNamed("name");
|
||||
@@ -101,9 +105,10 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
final Integer feeInterval = command.integerValueOfParameterNamed("feeInterval");
|
||||
final BigDecimal minCap = command.bigDecimalValueOfParameterNamed("minCap");
|
||||
final BigDecimal maxCap = command.bigDecimalValueOfParameterNamed("maxCap");
|
||||
final Integer feeFrequency = command.integerValueOfParameterNamed("feeFrequency");
|
||||
|
||||
return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTimeType, chargeCalculationType, penalty, active, paymentMode,
|
||||
feeOnMonthDay, feeInterval, minCap, maxCap);
|
||||
feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency);
|
||||
}
|
||||
|
||||
protected Charge() {
|
||||
@@ -113,7 +118,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
private Charge(final String name, final BigDecimal amount, final String currencyCode, final ChargeAppliesTo chargeAppliesTo,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty,
|
||||
final boolean active, final ChargePaymentMode paymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval,
|
||||
final BigDecimal minCap, final BigDecimal maxCap) {
|
||||
final BigDecimal minCap, final BigDecimal maxCap, final Integer feeFrequency) {
|
||||
this.name = name;
|
||||
this.amount = amount;
|
||||
this.currencyCode = currencyCode;
|
||||
@@ -130,8 +135,9 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
if (isMonthlyFee() || isAnnualFee()) {
|
||||
this.feeOnMonth = feeOnMonthDay.getMonthOfYear();
|
||||
this.feeOnDay = feeOnMonthDay.getDayOfMonth();
|
||||
this.feeInterval = feeInterval;
|
||||
}
|
||||
this.feeInterval = feeInterval;
|
||||
this.feeFrequency = feeFrequency;
|
||||
|
||||
if (isSavingsCharge()) {
|
||||
|
||||
@@ -154,7 +160,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
} else if (isLoanCharge()) {
|
||||
|
||||
if (penalty && chargeTime.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); }
|
||||
|
||||
if (!penalty && chargeTime.isOverdueInstallment()) { throw new ChargeMustBePenaltyException(name); }
|
||||
if (!isAllowedLoanChargeTime()) {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTime)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.loan");
|
||||
@@ -381,6 +387,18 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.feeInterval = newValue;
|
||||
}
|
||||
|
||||
final String feeFrequency = "feeFrequency";
|
||||
if (command.isChangeInIntegerParameterNamed(feeFrequency, this.feeFrequency)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(feeFrequency);
|
||||
actualChanges.put(feeFrequency, newValue);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.feeFrequency = newValue;
|
||||
}
|
||||
|
||||
if(this.feeFrequency != null){
|
||||
baseDataValidator.reset().parameter("feeInterval").value(this.feeInterval).notNull();
|
||||
}
|
||||
|
||||
final String penaltyParamName = "penalty";
|
||||
if (command.isChangeInBooleanParameterNamed(penaltyParamName, this.penalty)) {
|
||||
@@ -415,6 +433,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
|
||||
if (this.penalty && ChargeTimeType.fromInt(this.chargeTime).isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(
|
||||
this.name); }
|
||||
if (!penalty && ChargeTimeType.fromInt(this.chargeTime).isOverdueInstallment()) { throw new ChargeMustBePenaltyException(name); }
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
|
||||
@@ -438,9 +457,11 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
final EnumOptionData chargeAppliesTo = ChargeEnumerations.chargeAppliesTo(this.chargeAppliesTo);
|
||||
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(this.chargeCalculation);
|
||||
final EnumOptionData chargePaymentmode = ChargeEnumerations.chargePaymentMode(this.chargePaymentMode);
|
||||
final EnumOptionData feeFrequencyType = ChargeEnumerations.chargePaymentMode(this.feeFrequency);
|
||||
final CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, 0, null, null);
|
||||
return ChargeData.instance(getId(), this.name, this.amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType,
|
||||
chargePaymentmode, getFeeOnMonthDay(), this.feeInterval, this.penalty, this.active, this.minCap, this.maxCap);
|
||||
chargePaymentmode, getFeeOnMonthDay(), this.feeInterval, this.penalty, this.active, this.minCap, this.maxCap,
|
||||
feeFrequencyType);
|
||||
}
|
||||
|
||||
public Integer getChargePaymentMode() {
|
||||
@@ -455,6 +476,10 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isAnnualFee();
|
||||
}
|
||||
|
||||
public boolean isOverdueInstallment() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isOverdueInstallment();
|
||||
}
|
||||
|
||||
public MonthDay getFeeOnMonthDay() {
|
||||
MonthDay feeOnMonthDay = null;
|
||||
if (this.feeOnDay != null && this.feeOnMonth != null) {
|
||||
@@ -466,4 +491,8 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
public Integer feeInterval() {
|
||||
return this.feeInterval;
|
||||
}
|
||||
|
||||
public Integer feeFrequency() {
|
||||
return this.feeFrequency;
|
||||
}
|
||||
}
|
||||
+5
@@ -42,6 +42,11 @@ public enum ChargeTimeType {
|
||||
return new Integer[] { ChargeTimeType.DISBURSEMENT.getValue(), ChargeTimeType.SPECIFIED_DUE_DATE.getValue(),
|
||||
ChargeTimeType.INSTALMENT_FEE.getValue(), ChargeTimeType.OVERDUE_INSTALLMENT.getValue() };
|
||||
}
|
||||
|
||||
public static Object[] validLoanChargeValues() {
|
||||
return new Integer[] { ChargeTimeType.DISBURSEMENT.getValue(), ChargeTimeType.SPECIFIED_DUE_DATE.getValue(),
|
||||
ChargeTimeType.INSTALMENT_FEE.getValue()};
|
||||
}
|
||||
|
||||
public static Object[] validSavingsValues() {
|
||||
return new Integer[] { ChargeTimeType.SPECIFIED_DUE_DATE.getValue(), ChargeTimeType.SAVINGS_ACTIVATION.getValue(),
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.charge.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
/**
|
||||
* {@link AbstractPlatformDomainRuleException} thrown when attempting to crate
|
||||
* charge of type penalty due at disbursement .
|
||||
*/
|
||||
public class ChargeMustBePenaltyException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public ChargeMustBePenaltyException(final String name) {
|
||||
super("error.msg.charge.must.be.penalty", "Charge '" + name + "' is invalid.", name, name);
|
||||
}
|
||||
}
|
||||
+16
-3
@@ -41,7 +41,7 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
*/
|
||||
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("name", "amount", "locale", "currencyCode",
|
||||
"currencyOptions", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "penalty",
|
||||
"active", "chargePaymentMode", "feeOnMonthDay", "feeInterval", "monthDayFormat", "minCap", "maxCap"));
|
||||
"active", "chargePaymentMode", "feeOnMonthDay", "feeInterval", "monthDayFormat", "minCap", "maxCap", "feeFrequency"));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@@ -70,6 +70,16 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
final Integer chargeCalculationType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeCalculationType", element);
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType).notNull();
|
||||
|
||||
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed("feeInterval", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("feeInterval").value(feeInterval).integerGreaterThanZero();
|
||||
|
||||
final Integer feeFrequency = this.fromApiJsonHelper.extractIntegerNamed("feeFrequency", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("feeFrequency").value(feeFrequency).inMinMaxRange(0, 3);
|
||||
|
||||
if (feeFrequency != null) {
|
||||
baseDataValidator.reset().parameter("feeInterval").value(feeInterval).notNull();
|
||||
}
|
||||
|
||||
final ChargeAppliesTo appliesTo = ChargeAppliesTo.fromInt(chargeAppliesTo);
|
||||
if (appliesTo.isLoanCharge()) {
|
||||
// loan applicable validation
|
||||
@@ -107,7 +117,6 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed("feeOnMonthDay", element);
|
||||
baseDataValidator.reset().parameter("feeOnMonthDay").value(monthDay).notNull();
|
||||
|
||||
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed("feeInterval", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("feeInterval").value(feeInterval).notNull().inMinMaxRange(1, 12);
|
||||
}
|
||||
|
||||
@@ -216,7 +225,7 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("feeInterval", element)) {
|
||||
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed("feeInterval", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("feeInterval").value(feeInterval).notNull().inMinMaxRange(1, 12);
|
||||
baseDataValidator.reset().parameter("feeInterval").value(feeInterval).integerGreaterThanZero();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("chargeCalculationType", element)) {
|
||||
@@ -247,6 +256,10 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
final BigDecimal maxCap = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("maxCap", element.getAsJsonObject());
|
||||
baseDataValidator.reset().parameter("maxCap").value(maxCap).notNull().positiveAmount();
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists("feeFrequency", element)) {
|
||||
final Integer feeFrequency = this.fromApiJsonHelper.extractIntegerNamed("feeFrequency", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("feeFrequency").value(feeFrequency).inMinMaxRange(0, 3);
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
+3
-1
@@ -17,7 +17,7 @@ public interface ChargeReadPlatformService {
|
||||
|
||||
ChargeData retrieveNewChargeDetails();
|
||||
|
||||
Collection<ChargeData> retrieveLoanApplicableCharges(boolean feeChargesOnly);
|
||||
Collection<ChargeData> retrieveLoanApplicableCharges(boolean feeChargesOnly, Integer[] excludeChargeTimes);
|
||||
|
||||
Collection<ChargeData> retrieveLoanApplicablePenalties();
|
||||
|
||||
@@ -28,4 +28,6 @@ public interface ChargeReadPlatformService {
|
||||
Collection<ChargeData> retrieveSavingsAccountApplicablePenalties();
|
||||
|
||||
Collection<ChargeData> retrieveSavingsProductCharges(Long savingsProductId);
|
||||
|
||||
Collection<ChargeData> retrieveLoanProductCharges(Long loanProductId, Integer chargeTime);
|
||||
}
|
||||
|
||||
+47
-10
@@ -21,6 +21,8 @@ import org.mifosplatform.organisation.monetary.service.CurrencyReadPlatformServi
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeAppliesTo;
|
||||
import org.mifosplatform.portfolio.charge.exception.ChargeNotFoundException;
|
||||
import org.mifosplatform.portfolio.common.service.CommonEnumerations;
|
||||
import org.mifosplatform.portfolio.common.service.DropdownReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
@@ -35,15 +37,18 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
private final PlatformSecurityContext context;
|
||||
private final CurrencyReadPlatformService currencyReadPlatformService;
|
||||
private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService;
|
||||
private final DropdownReadPlatformService dropdownReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public ChargeReadPlatformServiceImpl(final PlatformSecurityContext context,
|
||||
final CurrencyReadPlatformService currencyReadPlatformService,
|
||||
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource) {
|
||||
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource,
|
||||
final DropdownReadPlatformService dropdownReadPlatformService) {
|
||||
this.context = context;
|
||||
this.chargeDropdownReadPlatformService = chargeDropdownReadPlatformService;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.currencyReadPlatformService = currencyReadPlatformService;
|
||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,10 +95,11 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
.retrieveSavingsCalculationTypes();
|
||||
final List<EnumOptionData> savingsChargeTimeTypeOptions = this.chargeDropdownReadPlatformService
|
||||
.retrieveSavingsCollectionTimeTypes();
|
||||
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
|
||||
|
||||
return ChargeData.template(currencyOptions, allowedChargeCalculationTypeOptions, allowedChargeAppliesToOptions,
|
||||
allowedChargeTimeOptions, chargePaymentOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions);
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, feeFrequencyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,19 +115,45 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ChargeData> retrieveLoanApplicableCharges(final boolean feeChargesOnly) {
|
||||
public Collection<ChargeData> retrieveLoanProductCharges(final Long loanProductId, final Integer chargeTime) {
|
||||
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final ChargeMapper rm = new ChargeMapper();
|
||||
|
||||
String sql = "select " + rm.chargeSchema()
|
||||
+ " where c.is_deleted=0 and c.is_active=1 and c.charge_applies_to_enum=? order by c.name ";
|
||||
final String sql = "select " + rm.loanProductChargeSchema()
|
||||
+ " where c.is_deleted=0 and c.is_active=1 and plc.product_loan_id=? and c.charge_time_enum=?";
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { loanProductId, chargeTime });
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ChargeData> retrieveLoanApplicableCharges(final boolean feeChargesOnly, Integer[] excludeChargeTimes) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final ChargeMapper rm = new ChargeMapper();
|
||||
String excludeClause = "";
|
||||
Object[] params = new Object[] { ChargeAppliesTo.LOAN.getValue() };
|
||||
if (excludeChargeTimes != null && excludeChargeTimes.length > 0) {
|
||||
excludeClause = " and c.charge_time_enum not in(?) ";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < excludeChargeTimes.length; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(excludeChargeTimes[i]);
|
||||
}
|
||||
params = new Object[] { ChargeAppliesTo.LOAN.getValue(), sb.toString() };
|
||||
}
|
||||
String sql = "select " + rm.chargeSchema() + " where c.is_deleted=0 and c.is_active=1 and c.charge_applies_to_enum=?"
|
||||
+ excludeClause + " order by c.name ";
|
||||
if (feeChargesOnly) {
|
||||
sql = "select " + rm.chargeSchema()
|
||||
+ " where c.is_deleted=0 and c.is_active=1 and c.is_penalty=0 and c.charge_applies_to_enum=? order by c.name ";
|
||||
+ " where c.is_deleted=0 and c.is_active=1 and c.is_penalty=0 and c.charge_applies_to_enum=? " + excludeClause
|
||||
+ "order by c.name ";
|
||||
}
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { ChargeAppliesTo.LOAN.getValue() });
|
||||
return this.jdbcTemplate.query(sql, rm, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,8 +177,8 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
+ "c.is_active as active, oc.name as currencyName, oc.decimal_places as currencyDecimalPlaces, "
|
||||
+ "oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, "
|
||||
+ "oc.internationalized_name_code as currencyNameCode, c.fee_on_day as feeOnDay, c.fee_on_month as feeOnMonth, "
|
||||
+ "c.fee_interval as feeInterval,c.min_cap as minCap,c.max_cap as maxCap " + "from m_charge c "
|
||||
+ "join m_organisation_currency oc on c.currency_code = oc.code";
|
||||
+ "c.fee_interval as feeInterval, c.fee_frequency as feeFrequency,c.min_cap as minCap,c.max_cap as maxCap "
|
||||
+ "from m_charge c " + "join m_organisation_currency oc on c.currency_code = oc.code";
|
||||
}
|
||||
|
||||
public String loanProductChargeSchema() {
|
||||
@@ -189,6 +221,11 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
final boolean active = rs.getBoolean("active");
|
||||
|
||||
final Integer feeInterval = JdbcSupport.getInteger(rs, "feeInterval");
|
||||
EnumOptionData feeFrequencyType = null;
|
||||
final Integer feeFrequency = JdbcSupport.getInteger(rs, "feeFrequency");
|
||||
if (feeFrequency != null) {
|
||||
feeFrequencyType = CommonEnumerations.termFrequencyType(feeFrequency, "feeFrequency");
|
||||
}
|
||||
MonthDay feeOnMonthDay = null;
|
||||
final Integer feeOnMonth = JdbcSupport.getInteger(rs, "feeOnMonth");
|
||||
final Integer feeOnDay = JdbcSupport.getInteger(rs, "feeOnDay");
|
||||
@@ -199,7 +236,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
final BigDecimal maxCap = rs.getBigDecimal("maxCap");
|
||||
|
||||
return ChargeData.instance(id, name, amount, currency, chargeTimeType, chargeAppliesToType, chargeCalculationType,
|
||||
chargePaymentMode, feeOnMonthDay, feeInterval, penalty, active, minCap, maxCap);
|
||||
chargePaymentMode, feeOnMonthDay, feeInterval, penalty, active, minCap, maxCap, feeFrequencyType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -96,6 +96,10 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
if (isChargeExistWithLoans || isChargeExistWithSavings) { throw new ChargeCannotBeUpdatedException(
|
||||
"error.msg.charge.cannot.be.updated.it.is.used.in.loan", "This charge cannot be updated, it is used in loan"); }
|
||||
}
|
||||
}else if((changes.containsKey("feeFrequency") || changes.containsKey("feeInterval")) && chargeForUpdate.isLoanCharge()){
|
||||
final Boolean isChargeExistWithLoans = isAnyLoanProductsAssociateWithThisCharge(chargeId);
|
||||
if (isChargeExistWithLoans) { throw new ChargeCannotBeUpdatedException(
|
||||
"error.msg.charge.frequency.cannot.be.updated.it.is.used.in.loan", "This charge frequency cannot be updated, it is used in loan"); }
|
||||
}
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanproduct.domain;
|
||||
package org.mifosplatform.portfolio.common.domain;
|
||||
|
||||
public enum PeriodFrequencyType {
|
||||
DAYS(0, "periodFrequencyType.days"), //
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
package org.mifosplatform.portfolio.common.service;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
|
||||
public class CommonEnumerations {
|
||||
|
||||
public static EnumOptionData termFrequencyType(final int id, final String codePrefix) {
|
||||
return termFrequencyType(PeriodFrequencyType.fromInt(id), codePrefix);
|
||||
}
|
||||
|
||||
public static EnumOptionData termFrequencyType(final PeriodFrequencyType type, final String codePrefix) {
|
||||
EnumOptionData optionData = null;
|
||||
switch (type) {
|
||||
case DAYS:
|
||||
optionData = new EnumOptionData(PeriodFrequencyType.DAYS.getValue().longValue(), codePrefix
|
||||
+ PeriodFrequencyType.DAYS.getCode(), "Days");
|
||||
break;
|
||||
case WEEKS:
|
||||
optionData = new EnumOptionData(PeriodFrequencyType.WEEKS.getValue().longValue(), codePrefix
|
||||
+ PeriodFrequencyType.WEEKS.getCode(), "Weeks");
|
||||
break;
|
||||
case MONTHS:
|
||||
optionData = new EnumOptionData(PeriodFrequencyType.MONTHS.getValue().longValue(), codePrefix
|
||||
+ PeriodFrequencyType.MONTHS.getCode(), "Months");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(PeriodFrequencyType.INVALID.getValue().longValue(), PeriodFrequencyType.INVALID.getCode(),
|
||||
"Invalid");
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.mifosplatform.portfolio.common.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
|
||||
|
||||
public interface DropdownReadPlatformService {
|
||||
|
||||
List<EnumOptionData> retrievePeriodFrequencyTypeOptions();
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.mifosplatform.portfolio.common.service;
|
||||
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.loanTermFrequencyType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class DropdownReadPlatformServiceImpl implements DropdownReadPlatformService{
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrievePeriodFrequencyTypeOptions() {
|
||||
final List<EnumOptionData> loanTermFrequencyOptions = Arrays.asList(loanTermFrequencyType(PeriodFrequencyType.DAYS),
|
||||
loanTermFrequencyType(PeriodFrequencyType.WEEKS), loanTermFrequencyType(PeriodFrequencyType.MONTHS),
|
||||
loanTermFrequencyType(PeriodFrequencyType.YEARS));
|
||||
return loanTermFrequencyOptions;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -34,6 +34,7 @@ import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSeriali
|
||||
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||
@@ -101,7 +102,7 @@ public class LoanChargesApiResource {
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||
|
||||
final boolean feeChargesOnly = false;
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly, new Integer[]{ChargeTimeType.OVERDUE_INSTALLMENT.getValue()});
|
||||
final LoanChargeData loanChargeTemplate = LoanChargeData.template(chargeOptions);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
+8
-2
@@ -52,6 +52,7 @@ import org.mifosplatform.portfolio.account.service.PortfolioAccountReadPlatformS
|
||||
import org.mifosplatform.portfolio.calendar.data.CalendarData;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarReadPlatformService;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.client.data.ClientData;
|
||||
import org.mifosplatform.portfolio.collateral.data.CollateralData;
|
||||
@@ -411,7 +412,8 @@ public class LoansApiResource {
|
||||
fundOptions = this.fundReadPlatformService.retrieveAllFunds();
|
||||
repaymentStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies();
|
||||
final boolean feeChargesOnly = false;
|
||||
chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
|
||||
chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly,
|
||||
new Integer[] { ChargeTimeType.OVERDUE_INSTALLMENT.getValue() });
|
||||
chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate();
|
||||
|
||||
allowedLoanOfficers = this.loanReadPlatformService.retrieveAllowedLoanOfficers(loanBasicDetails.officeId(),
|
||||
@@ -438,11 +440,15 @@ public class LoansApiResource {
|
||||
|
||||
}
|
||||
|
||||
Collection<ChargeData> overdueCharges = this.chargeReadPlatformService.retrieveLoanProductCharges(loanBasicDetails.loanProductId(),
|
||||
ChargeTimeType.OVERDUE_INSTALLMENT.getValue());
|
||||
|
||||
final LoanAccountData loanAccount = LoanAccountData.associationsAndTemplate(loanBasicDetails, repaymentSchedule, loanRepayments,
|
||||
charges, collateral, guarantors, meeting, productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
|
||||
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions,
|
||||
loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations);
|
||||
loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
|
||||
overdueCharges);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
|
||||
mandatoryResponseParameters);
|
||||
|
||||
+30
-15
@@ -158,6 +158,8 @@ public class LoanAccountData {
|
||||
|
||||
private final Boolean inArrears;
|
||||
|
||||
private final Collection<ChargeData> overdueCharges;
|
||||
|
||||
/**
|
||||
* Used to produce a {@link LoanAccountData} with only collateral options
|
||||
* for now.
|
||||
@@ -242,6 +244,7 @@ public class LoanAccountData {
|
||||
final Map<Long, LoanBorrowerCycleData> memberVariations = null;
|
||||
final LoanProductData product = null;
|
||||
final Boolean inArrears = null;
|
||||
final Collection<ChargeData> overdueCharges = null;
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
currencyData, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency, termPeriodFrequencyType,
|
||||
@@ -255,7 +258,7 @@ public class LoanAccountData {
|
||||
chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
||||
disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations,
|
||||
product, inArrears, graceOnArrearsAgeing);
|
||||
product, inArrears, graceOnArrearsAgeing, overdueCharges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,6 +345,7 @@ public class LoanAccountData {
|
||||
final Map<Long, LoanBorrowerCycleData> memberVariations = null;
|
||||
final LoanProductData product = null;
|
||||
final Boolean inArrears = null;
|
||||
final Collection<ChargeData> overdueCharges = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
@@ -355,7 +359,7 @@ public class LoanAccountData {
|
||||
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
|
||||
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
|
||||
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount,
|
||||
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing);
|
||||
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges);
|
||||
}
|
||||
|
||||
public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) {
|
||||
@@ -376,7 +380,7 @@ public class LoanAccountData {
|
||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing);
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -464,6 +468,7 @@ public class LoanAccountData {
|
||||
final Map<Long, LoanBorrowerCycleData> memberVariations = null;
|
||||
final LoanProductData product = null;
|
||||
final Boolean inArrears = null;
|
||||
final Collection<ChargeData> overdueCharges = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
@@ -477,7 +482,7 @@ public class LoanAccountData {
|
||||
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
|
||||
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
|
||||
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingBalance,
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing);
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges);
|
||||
}
|
||||
|
||||
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
|
||||
@@ -497,7 +502,7 @@ public class LoanAccountData {
|
||||
acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing);
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges);
|
||||
}
|
||||
|
||||
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
|
||||
@@ -562,7 +567,9 @@ public class LoanAccountData {
|
||||
|
||||
final Collection<LoanChargeData> charges = new ArrayList<LoanChargeData>();
|
||||
for (final ChargeData charge : product.charges()) {
|
||||
charges.add(charge.toLoanChargeData());
|
||||
if (!charge.isOverdueInstallmentCharge()) {
|
||||
charges.add(charge.toLoanChargeData());
|
||||
}
|
||||
}
|
||||
|
||||
final Integer loancounter = null;
|
||||
@@ -617,7 +624,8 @@ public class LoanAccountData {
|
||||
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
|
||||
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
|
||||
notes, accountLinkingOptions, linkedAccount, disbursementData, product.getMultiDisburseLoan(), fixedEmi,
|
||||
product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears, product.getGraceOnArrearsAgeing());
|
||||
product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears,
|
||||
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges());
|
||||
}
|
||||
|
||||
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
|
||||
@@ -669,7 +677,7 @@ public class LoanAccountData {
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(), acc.fixedEmiAmount,
|
||||
product.getOutstandingLoanBalance(), acc.emiAmountVariations, acc.memberVariations, product, acc.inArrears,
|
||||
product.getGraceOnArrearsAgeing());
|
||||
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -721,6 +729,7 @@ public class LoanAccountData {
|
||||
final Collection<LoanTermVariationsData> emiAmountVariations = null;
|
||||
final Map<Long, LoanBorrowerCycleData> memberVariations = null;
|
||||
final LoanProductData product = null;
|
||||
final Collection<ChargeData> overdueCharges = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
@@ -734,7 +743,7 @@ public class LoanAccountData {
|
||||
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
|
||||
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
|
||||
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmont, outstandingLoanBalance,
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing);
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -754,7 +763,7 @@ public class LoanAccountData {
|
||||
final Collection<CodeValueData> loanCollateralOptions, final Collection<CalendarData> calendarOptions,
|
||||
final Collection<NoteData> notes, final Collection<PortfolioAccountData> accountLinkingOptions,
|
||||
final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails,
|
||||
final Collection<LoanTermVariationsData> emiAmountVariations) {
|
||||
final Collection<LoanTermVariationsData> emiAmountVariations, final Collection<ChargeData> overdueCharges) {
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal,
|
||||
@@ -770,7 +779,7 @@ public class LoanAccountData {
|
||||
loanCollateralOptions, calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes,
|
||||
accountLinkingOptions, linkedAccount, disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount,
|
||||
acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing);
|
||||
acc.graceOnArrearsAgeing, overdueCharges);
|
||||
}
|
||||
|
||||
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
||||
@@ -781,7 +790,7 @@ public class LoanAccountData {
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions, acc.notes, accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations);
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations, acc.overdueCharges);
|
||||
}
|
||||
|
||||
public static LoanAccountData associateGroup(final LoanAccountData acc, final GroupGeneralData group) {
|
||||
@@ -800,7 +809,7 @@ public class LoanAccountData {
|
||||
acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing);
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges);
|
||||
}
|
||||
|
||||
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
|
||||
@@ -856,7 +865,7 @@ public class LoanAccountData {
|
||||
acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing);
|
||||
acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges);
|
||||
}
|
||||
|
||||
private LoanAccountData(
|
||||
@@ -909,7 +918,8 @@ public class LoanAccountData {
|
||||
final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails,
|
||||
final Boolean multiDisburseLoan, BigDecimal fixedEmiAmount, final BigDecimal maxOutstandingLoanBalance,
|
||||
final Collection<LoanTermVariationsData> emiAmountVariations, final Map<Long, LoanBorrowerCycleData> memberVariations,
|
||||
final LoanProductData product, final Boolean inArrears, final Integer graceOnArrearsAgeing) {
|
||||
final LoanProductData product, final Boolean inArrears, final Integer graceOnArrearsAgeing,
|
||||
final Collection<ChargeData> overdueCharges) {
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
this.status = status;
|
||||
@@ -1046,6 +1056,7 @@ public class LoanAccountData {
|
||||
this.product = product;
|
||||
this.inArrears = inArrears;
|
||||
this.graceOnArrearsAgeing = graceOnArrearsAgeing;
|
||||
this.overdueCharges = overdueCharges;
|
||||
}
|
||||
|
||||
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
|
||||
@@ -1119,4 +1130,8 @@ public class LoanAccountData {
|
||||
return this.group;
|
||||
}
|
||||
|
||||
public Long loanProductId() {
|
||||
return this.loanProductId;
|
||||
}
|
||||
|
||||
}
|
||||
+24
-11
@@ -66,6 +66,7 @@ import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedExcep
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.collateral.data.CollateralData;
|
||||
import org.mifosplatform.portfolio.collateral.domain.LoanCollateral;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.fund.domain.Fund;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||
@@ -92,7 +93,6 @@ import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
@@ -478,6 +478,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
chargeAmt = loanCharge.getPercentage();
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge);
|
||||
} else if (loanCharge.isOverdueInstallmentCharge()) {
|
||||
totalChargeAmt = loanCharge.amountOutstanding();
|
||||
}
|
||||
} else {
|
||||
chargeAmt = loanCharge.amount();
|
||||
@@ -1940,11 +1942,11 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
loanVariationTermsData.add(data);
|
||||
}
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms
|
||||
.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, getDisbursementDate(),
|
||||
getExpectedFirstRepaymentOnDate(), calculatedRepaymentsStartingFromDate, getInArrearsTolerance(),
|
||||
this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData,
|
||||
this.maxOutstandingLoanBalance,loanVariationTermsData, getInterestChargedFromDate());
|
||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency,
|
||||
loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(),
|
||||
calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
|
||||
this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
|
||||
loanVariationTermsData, getInterestChargedFromDate());
|
||||
|
||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, charges(),
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
@@ -2430,18 +2432,17 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return possibleNextRepaymentDate;
|
||||
}
|
||||
|
||||
public Money possibleNextRepaymentAmount() {
|
||||
final MonetaryCurrency currency = this.loanRepaymentScheduleDetail.getPrincipal().getCurrency();
|
||||
Money possibleNextRepaymentAmount = Money.zero(currency);
|
||||
public LoanRepaymentScheduleInstallment possibleNextRepaymentInstallment() {
|
||||
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = null;
|
||||
|
||||
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||
if (installment.isNotFullyPaidOff()) {
|
||||
possibleNextRepaymentAmount = installment.getTotalOutstanding(currency);
|
||||
loanRepaymentScheduleInstallment = installment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return possibleNextRepaymentAmount;
|
||||
return loanRepaymentScheduleInstallment;
|
||||
}
|
||||
|
||||
public LoanTransaction deriveDefaultInterestWaiverTransaction() {
|
||||
@@ -3735,4 +3736,16 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return emiAmount;
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleInstallment fetchRepaymentScheduleInstallment(final Integer installmentNumber) {
|
||||
LoanRepaymentScheduleInstallment installment = null;
|
||||
if (installmentNumber == null) { return installment; }
|
||||
for (final LoanRepaymentScheduleInstallment scheduleInstallment : this.repaymentScheduleInstallments) {
|
||||
if (scheduleInstallment.getInstallmentNumber().equals(installmentNumber)) {
|
||||
installment = scheduleInstallment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return installment;
|
||||
}
|
||||
|
||||
}
|
||||
+51
-5
@@ -18,9 +18,11 @@ import java.util.Set;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
@@ -111,11 +113,19 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private boolean active = true;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@OneToOne(mappedBy = "loancharge", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
private LoanOverdueInstallmentCharge overdueInstallmentCharge;
|
||||
|
||||
|
||||
public static LoanCharge createNewFromJson(final Loan loan, final Charge chargeDefinition, final JsonCommand command) {
|
||||
|
||||
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("amount");
|
||||
final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate");
|
||||
return createNewFromJson(loan, chargeDefinition, command,dueDate);
|
||||
}
|
||||
|
||||
public static LoanCharge createNewFromJson(final Loan loan, final Charge chargeDefinition, final JsonCommand command,final LocalDate dueDate) {
|
||||
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("amount");
|
||||
|
||||
final ChargeTimeType chargeTime = null;
|
||||
final ChargeCalculationType chargeCalculation = null;
|
||||
@@ -123,13 +133,26 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO;
|
||||
switch (ChargeCalculationType.fromInt(chargeDefinition.getChargeCalculation())) {
|
||||
case PERCENT_OF_AMOUNT:
|
||||
amountPercentageAppliedTo = loan.getPrincpal().getAmount();
|
||||
if (command.hasParameter("principal")) {
|
||||
amountPercentageAppliedTo = command.bigDecimalValueOfParameterNamed("principal");
|
||||
} else {
|
||||
amountPercentageAppliedTo = loan.getPrincpal().getAmount();
|
||||
}
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
amountPercentageAppliedTo = loan.getPrincpal().getAmount().add(loan.getTotalInterest());
|
||||
if (command.hasParameter("principal") && command.hasParameter("interest")) {
|
||||
amountPercentageAppliedTo = command.bigDecimalValueOfParameterNamed("principal").add(
|
||||
command.bigDecimalValueOfParameterNamed("interest"));
|
||||
} else {
|
||||
amountPercentageAppliedTo = loan.getPrincpal().getAmount().add(loan.getTotalInterest());
|
||||
}
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
amountPercentageAppliedTo = loan.getTotalInterest();
|
||||
if (command.hasParameter("interest")) {
|
||||
amountPercentageAppliedTo = command.bigDecimalValueOfParameterNamed("interest");
|
||||
} else {
|
||||
amountPercentageAppliedTo = loan.getTotalInterest();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -474,6 +497,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.INSTALMENT_FEE);
|
||||
}
|
||||
|
||||
public boolean isOverdueInstallmentCharge() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.OVERDUE_INSTALLMENT);
|
||||
}
|
||||
|
||||
private static boolean isGreaterThanZero(final BigDecimal value) {
|
||||
return value.compareTo(BigDecimal.ZERO) == 1;
|
||||
}
|
||||
@@ -792,4 +819,23 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
public BigDecimal amountOrPercentage() {
|
||||
return this.amountOrPercentage;
|
||||
}
|
||||
|
||||
public BigDecimal chargeAmount() {
|
||||
BigDecimal totalChargeAmount = this.amountOutstanding;
|
||||
if (this.amountPaid != null) {
|
||||
totalChargeAmount = totalChargeAmount.add(this.amountPaid);
|
||||
}
|
||||
if (this.amountWaived != null) {
|
||||
totalChargeAmount = totalChargeAmount.add(this.amountWaived);
|
||||
}
|
||||
if (this.amountWrittenOff != null) {
|
||||
totalChargeAmount = totalChargeAmount.add(this.amountWrittenOff);
|
||||
}
|
||||
return totalChargeAmount;
|
||||
}
|
||||
|
||||
|
||||
public void updateOverdueInstallmentCharge(LoanOverdueInstallmentCharge overdueInstallmentCharge) {
|
||||
this.overdueInstallmentCharge = overdueInstallmentCharge;
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.domain;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_loan_overdue_installment_charge")
|
||||
public class LoanOverdueInstallmentCharge extends AbstractPersistable<Long> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "loan_charge_id", referencedColumnName = "id", nullable = false)
|
||||
private LoanCharge loancharge;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "loan_schedule_id", referencedColumnName = "id", nullable = false)
|
||||
private LoanRepaymentScheduleInstallment installment;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name="frequency_number")
|
||||
private Integer frequencyNumber;
|
||||
|
||||
|
||||
public LoanOverdueInstallmentCharge(){
|
||||
|
||||
}
|
||||
|
||||
public LoanOverdueInstallmentCharge(final LoanCharge loanCharge,final LoanRepaymentScheduleInstallment installment, final Integer frequencyNumber){
|
||||
this.loancharge = loanCharge;
|
||||
this.installment = installment;
|
||||
this.frequencyNumber = frequencyNumber;
|
||||
}
|
||||
|
||||
}
|
||||
+9
-1
@@ -75,7 +75,11 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
} else {
|
||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||
}
|
||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
}else if (loanCharge.isOverdueInstallmentCharge()
|
||||
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||
}else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||
@@ -162,6 +166,10 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
} else {
|
||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||
}
|
||||
}else if (loanCharge.isOverdueInstallmentCharge()
|
||||
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
|
||||
+16
-2
@@ -10,15 +10,22 @@ public class OverdueLoanScheduleData {
|
||||
private final BigDecimal amount;
|
||||
private final String dateFormat;
|
||||
private final String dueDate;
|
||||
private final BigDecimal principalOverdue;
|
||||
private final BigDecimal interestOverdue;
|
||||
private final Integer periodNumber;
|
||||
|
||||
public OverdueLoanScheduleData(final Long loanId, final Long chargeId, final String dueDate, final BigDecimal amount,
|
||||
final String dateFormat, final String locale) {
|
||||
final String dateFormat, final String locale, final BigDecimal principalOverdue, final BigDecimal interestOverdue,
|
||||
final Integer periodNumber) {
|
||||
this.loanId = loanId;
|
||||
this.chargeId = chargeId;
|
||||
this.dueDate = dueDate;
|
||||
this.amount = amount;
|
||||
this.dateFormat = dateFormat;
|
||||
this.locale = locale;
|
||||
this.principalOverdue = principalOverdue;
|
||||
this.interestOverdue = interestOverdue;
|
||||
this.periodNumber = periodNumber;
|
||||
}
|
||||
|
||||
public Long getLoanId() {
|
||||
@@ -45,9 +52,16 @@ public class OverdueLoanScheduleData {
|
||||
return this.locale;
|
||||
}
|
||||
|
||||
|
||||
public Integer getPeriodNumber() {
|
||||
return this.periodNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + "chargeId:" + this.chargeId + ", locale:'" + this.locale + '\'' + ", amount:" + this.amount + ", dateFormat:'"
|
||||
+ this.dateFormat + '\'' + ", dueDate:'" + this.dueDate + '\'' + '}';
|
||||
+ this.dateFormat + '\'' + ", dueDate:'" + this.dueDate + '\'' + ", principal:'" + this.principalOverdue + '\''
|
||||
+ ", interest:'" + this.interestOverdue + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-3
@@ -73,7 +73,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
LocalDate periodStartDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||
LocalDate actualRepaymentDate = periodStartDate;
|
||||
boolean isFirstRepayment = true;
|
||||
LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate(periodStartDate, loanApplicationTerms, isFirstRepayment);
|
||||
LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate(periodStartDate, loanApplicationTerms,
|
||||
isFirstRepayment);
|
||||
final LocalDate idealDisbursementDate = this.scheduledDateGenerator.idealDisbursementDateBasedOnFirstRepaymentDate(
|
||||
loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery(), firstRepaymentdate);
|
||||
|
||||
@@ -91,7 +92,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
outstandingBalance = outstandingBalance.zero().plus(disburseAmt);
|
||||
}
|
||||
while (!outstandingBalance.isZero()) {
|
||||
actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms, isFirstRepayment);
|
||||
actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
||||
isFirstRepayment);
|
||||
isFirstRepayment = false;
|
||||
LocalDate scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
@@ -298,6 +300,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
} else {
|
||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||
}
|
||||
} else if (loanCharge.isOverdueInstallmentCharge()
|
||||
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
@@ -344,7 +350,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
} else {
|
||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||
}
|
||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
} else if (loanCharge.isOverdueInstallmentCharge()
|
||||
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||
}else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
|
||||
public class DefaultPaymentPeriodsInOneYearCalculator implements PaymentPeriodsInOneYearCalculator {
|
||||
|
||||
|
||||
+10
-7
@@ -12,7 +12,7 @@ import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||
import org.mifosplatform.organisation.holiday.service.HolidayUtil;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
|
||||
public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
|
||||
@@ -33,13 +33,15 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate generateNextRepaymentDate(final LocalDate lastRepaymentDate, final LoanApplicationTerms loanApplicationTerms, boolean isFirstRepayment) {
|
||||
public LocalDate generateNextRepaymentDate(final LocalDate lastRepaymentDate, final LoanApplicationTerms loanApplicationTerms,
|
||||
boolean isFirstRepayment) {
|
||||
final LocalDate firstRepaymentPeriodDate = loanApplicationTerms.getCalculatedRepaymentsStartingFromLocalDate();
|
||||
LocalDate dueRepaymentPeriodDate = null;
|
||||
if (isFirstRepayment && firstRepaymentPeriodDate != null) {
|
||||
dueRepaymentPeriodDate = firstRepaymentPeriodDate;
|
||||
} else {
|
||||
dueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms, lastRepaymentDate);
|
||||
dueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
|
||||
loanApplicationTerms.getRepaymentEvery(), lastRepaymentDate);
|
||||
}
|
||||
return dueRepaymentPeriodDate;
|
||||
}
|
||||
@@ -48,7 +50,8 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
public LocalDate adjustRepaymentDate(final LocalDate dueRepaymentPeriodDate, final LoanApplicationTerms loanApplicationTerms,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
LocalDate adjustedDate = dueRepaymentPeriodDate;
|
||||
final LocalDate nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms, adjustedDate);
|
||||
final LocalDate nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
|
||||
loanApplicationTerms.getRepaymentEvery(), adjustedDate);
|
||||
adjustedDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(adjustedDate, nextDueRepaymentPeriodDate, workingDays);
|
||||
if (isHolidayEnabled) {
|
||||
adjustedDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(adjustedDate, holidays);
|
||||
@@ -56,10 +59,10 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
return adjustedDate;
|
||||
}
|
||||
|
||||
private LocalDate getRepaymentPeriodDate(final LoanApplicationTerms loanApplicationTerms, final LocalDate startDate) {
|
||||
final int repaidEvery = loanApplicationTerms.getRepaymentEvery();
|
||||
@Override
|
||||
public LocalDate getRepaymentPeriodDate(final PeriodFrequencyType frequency, final int repaidEvery, final LocalDate startDate) {
|
||||
LocalDate dueRepaymentPeriodDate = startDate;
|
||||
switch (loanApplicationTerms.getRepaymentPeriodFrequencyType()) {
|
||||
switch (frequency) {
|
||||
case DAYS:
|
||||
dueRepaymentPeriodDate = startDate.plusDays(repaidEvery);
|
||||
break;
|
||||
|
||||
+1
-1
@@ -16,13 +16,13 @@ import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
|
||||
public final class LoanApplicationTerms {
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
|
||||
public interface PaymentPeriodsInOneYearCalculator {
|
||||
|
||||
|
||||
+4
-1
@@ -10,7 +10,7 @@ import java.util.List;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
|
||||
public interface ScheduledDateGenerator {
|
||||
|
||||
@@ -24,4 +24,7 @@ public interface ScheduledDateGenerator {
|
||||
|
||||
LocalDate adjustRepaymentDate(LocalDate dueRepaymentPeriodDate, LoanApplicationTerms loanApplicationTerms, boolean isHolidayEnabled,
|
||||
List<Holiday> holidays, WorkingDays workingDays);
|
||||
|
||||
LocalDate getRepaymentPeriodDate(PeriodFrequencyType frequency, int repaidEvery, LocalDate startDate);
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,6 +35,7 @@ import org.mifosplatform.portfolio.calendar.exception.MeetingFrequencyMismatchEx
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||
@@ -56,7 +57,6 @@ import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
+9
@@ -17,6 +17,7 @@ import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||
@@ -71,6 +72,14 @@ public class LoanChargeAssembler {
|
||||
locale);
|
||||
if (id == null) {
|
||||
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
|
||||
|
||||
if (chargeDefinition.isOverdueInstallment()) {
|
||||
|
||||
final String defaultUserMessage = "Installment charge cannot be added to the loan.";
|
||||
throw new LoanChargeCannotBeAddedException("loanCharge", "overdue.charge",
|
||||
defaultUserMessage, null, chargeDefinition.getName());
|
||||
}
|
||||
|
||||
ChargeTimeType chargeTime = null;
|
||||
if (chargeTimeType != null) {
|
||||
chargeTime = ChargeTimeType.fromInt(chargeTimeType);
|
||||
|
||||
+2
@@ -22,4 +22,6 @@ public interface LoanChargeReadPlatformService {
|
||||
Collection<LoanChargeData> retrieveLoanChargesForFeePayment(Integer paymentMode, Integer loanStatus);
|
||||
|
||||
Collection<LoanInstallmentChargeData> retrieveInstallmentLoanCharges(Long loanChargeId, boolean onlyPaymentPendingCharges);
|
||||
|
||||
Collection<Integer> retrieveOverdueInstallmentChargeFrequencyNumber(Long installmentId, Long chargeId);
|
||||
}
|
||||
|
||||
+18
-2
@@ -20,6 +20,7 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeDropdownReadPlatformService;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeEnumerations;
|
||||
import org.mifosplatform.portfolio.common.service.DropdownReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -33,13 +34,16 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final PlatformSecurityContext context;
|
||||
private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService;
|
||||
private final DropdownReadPlatformService dropdownReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanChargeReadPlatformServiceImpl(final PlatformSecurityContext context,
|
||||
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource) {
|
||||
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource,
|
||||
final DropdownReadPlatformService dropdownReadPlatformService) {
|
||||
this.context = context;
|
||||
this.chargeDropdownReadPlatformService = chargeDropdownReadPlatformService;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
||||
}
|
||||
|
||||
private static final class LoanChargeMapper implements RowMapper<LoanChargeData> {
|
||||
@@ -128,10 +132,11 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
.retrieveSavingsCalculationTypes();
|
||||
final List<EnumOptionData> savingsChargeTimeTypeOptions = this.chargeDropdownReadPlatformService
|
||||
.retrieveSavingsCollectionTimeTypes();
|
||||
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
|
||||
|
||||
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
|
||||
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
|
||||
savingsChargeTimeTypeOptions);
|
||||
savingsChargeTimeTypeOptions, feeFrequencyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -219,4 +224,15 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
return new LoanInstallmentChargeData(installmentNumber, dueAsOfDate, amount, amountOutstanding, paid, waived);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Integer> retrieveOverdueInstallmentChargeFrequencyNumber(final Long installmentId,final Long chargeId) {
|
||||
String sql = "select oic.frequency_number from m_loan_overdue_installment_charge oic inner join m_loan_charge lc on lc.id=oic.loan_charge_id where oic.loan_schedule_id = ? and lc.is_active = 1";
|
||||
Object[] params = {installmentId};
|
||||
if(chargeId != null){
|
||||
sql += " and lc.charge_id = ? ";
|
||||
params = new Object[]{installmentId,chargeId};
|
||||
}
|
||||
return this.jdbcTemplate.queryForList(sql, Integer.class, params);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ public interface LoanReadPlatformService {
|
||||
* result overdue installments with this charge already applied are not
|
||||
* returned.
|
||||
*/
|
||||
Collection<OverdueLoanScheduleData> retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized(final Long penaltyWaitPeriod);
|
||||
Collection<OverdueLoanScheduleData> retrieveAllLoansWithOverdueInstallments(final Long penaltyWaitPeriod);
|
||||
|
||||
Integer retriveLoanCounter(Long groupId, Integer loanType, Long productId);
|
||||
|
||||
|
||||
+28
-10
@@ -39,6 +39,7 @@ import org.mifosplatform.portfolio.calendar.data.CalendarData;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarReadPlatformService;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.client.data.ClientData;
|
||||
import org.mifosplatform.portfolio.client.domain.ClientEnumerations;
|
||||
@@ -59,6 +60,7 @@ import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
@@ -337,12 +339,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
|
||||
final LocalDate earliestUnpaidInstallmentDate = loan.possibleNextRepaymentDate();
|
||||
|
||||
final Money possibleNextRepaymentAmount = loan.possibleNextRepaymentAmount();
|
||||
final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = loan.possibleNextRepaymentInstallment();
|
||||
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT);
|
||||
final Collection<CodeValueData> paymentOptions = this.codeValueReadPlatformService
|
||||
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
|
||||
return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate,
|
||||
possibleNextRepaymentAmount.getAmount(), null, null, null, null, null, paymentOptions, null, null, null);
|
||||
loanRepaymentScheduleInstallment.getTotalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment
|
||||
.getPrincipalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment.getInterestOutstanding(currency)
|
||||
.getAmount(), loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(),
|
||||
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).getAmount(), null, paymentOptions, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -740,8 +745,23 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final String dueDate = rs.getString("dueDate");
|
||||
final String locale = "en_GB";
|
||||
|
||||
final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDue");
|
||||
final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid");
|
||||
final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff");
|
||||
|
||||
final BigDecimal principalOutstanding = principalDue.subtract(principalPaid).subtract(principalWrittenOff);
|
||||
|
||||
final BigDecimal interestExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestDue");
|
||||
final BigDecimal interestPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestPaid");
|
||||
final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived");
|
||||
final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff");
|
||||
|
||||
final BigDecimal interestActualDue = interestExpectedDue.subtract(interestWaived).subtract(interestWrittenOff);
|
||||
final BigDecimal interestOutstanding = interestActualDue.subtract(interestPaid);
|
||||
|
||||
final Integer installmentNumber = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "period");
|
||||
final OverdueLoanScheduleData overdueLoanScheduleData = new OverdueLoanScheduleData(loanId, chargeId, dueDate, amount,
|
||||
dateFormat, locale);
|
||||
dateFormat, locale, principalOutstanding, interestOutstanding, installmentNumber);
|
||||
|
||||
return overdueLoanScheduleData;
|
||||
}
|
||||
@@ -1115,7 +1135,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final Collection<CodeValueData> loanCollateralOptions = this.codeValueReadPlatformService
|
||||
.retrieveCodeValuesByCode("LoanCollateral");
|
||||
final boolean feeChargesOnly = false;
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly,
|
||||
new Integer[] { ChargeTimeType.OVERDUE_INSTALLMENT.getValue() });
|
||||
Integer loanCycleCounter = null;
|
||||
if (loanProduct.useBorrowerCycle()) {
|
||||
if (clientId == null) {
|
||||
@@ -1201,14 +1222,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<OverdueLoanScheduleData> retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized(final Long penaltyWaitPeriod) {
|
||||
public Collection<OverdueLoanScheduleData> retrieveAllLoansWithOverdueInstallments(final Long penaltyWaitPeriod) {
|
||||
final MusoniOverdueLoanScheduleMapper rm = new MusoniOverdueLoanScheduleMapper();
|
||||
final String sql = "select "
|
||||
+ rm.schema()
|
||||
+ " where DATE_SUB(CURDATE(),INTERVAL ? DAY) > ls.duedate "
|
||||
final String sql = "select " + rm.schema() + " where DATE_SUB(CURDATE(),INTERVAL ? DAY) > ls.duedate "
|
||||
+ " and ls.completed_derived <> 1 and mc.charge_applies_to_enum =1 "
|
||||
+ " and mc.charge_time_enum = 9 and ml.loan_status_id = 300 "
|
||||
+ " and plc.charge_id not in (SELECT charge_id from m_loan_charge as mlc where mlc.loan_id = ml.id and mlc.charge_id = plc.charge_id and mlc.is_active = 1 and mlc.due_for_collection_as_of_date > fromdate AND mlc.due_for_collection_as_of_date <= duedate) ";
|
||||
+ " and mc.charge_time_enum = 9 and ml.loan_status_id = 300 ";
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { penaltyWaitPeriod });
|
||||
}
|
||||
|
||||
|
||||
+86
-33
@@ -61,6 +61,7 @@ import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException.LOAN_CHARGE_CANNOT_BE_DELETED_REASON;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBePayedException;
|
||||
@@ -76,6 +77,7 @@ import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkDi
|
||||
import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
|
||||
import org.mifosplatform.portfolio.collectionsheet.command.SingleDisbursalCommand;
|
||||
import org.mifosplatform.portfolio.collectionsheet.command.SingleRepaymentCommand;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
|
||||
@@ -90,6 +92,8 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanInstallmentCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanOverdueInstallmentCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
@@ -100,7 +104,9 @@ import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentEx
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator;
|
||||
import org.mifosplatform.portfolio.loanaccount.serialization.LoanEventApiJsonValidator;
|
||||
import org.mifosplatform.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
|
||||
@@ -151,7 +157,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
|
||||
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
|
||||
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
|
||||
private final LoanChargeReadPlatformService LoanChargeReadPlatformService;
|
||||
private final LoanChargeReadPlatformService loanChargeReadPlatformService;
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final AccountTransferRepository accountTransferRepository;
|
||||
@@ -196,7 +202,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.accountTransfersWritePlatformService = accountTransfersWritePlatformService;
|
||||
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
|
||||
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
|
||||
this.LoanChargeReadPlatformService = loanChargeReadPlatformService;
|
||||
this.loanChargeReadPlatformService = loanChargeReadPlatformService;
|
||||
this.loanReadPlatformService = loanReadPlatformService;
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.accountTransferRepository = accountTransferRepository;
|
||||
@@ -971,6 +977,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final String errorMessage = "Charge and Loan must have the same currency.";
|
||||
throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage);
|
||||
}
|
||||
if (chargeDefinition.isOverdueInstallment()) {
|
||||
|
||||
final String defaultUserMessage = "Installment charge cannot be added to the loan.";
|
||||
throw new LoanChargeCannotBeAddedException("loanCharge", "overdue.charge", defaultUserMessage, null, chargeDefinition.getName());
|
||||
}
|
||||
|
||||
if (loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
|
||||
final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
|
||||
if (portfolioAccountData == null) {
|
||||
@@ -1226,13 +1238,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
@Override
|
||||
@CronTarget(jobName = JobName.TRANSFER_FEE_CHARGE_FOR_LOANS)
|
||||
public void transferFeeCharges() throws JobExecutionException {
|
||||
final Collection<LoanChargeData> chargeDatas = this.LoanChargeReadPlatformService.retrieveLoanChargesForFeePayment(
|
||||
final Collection<LoanChargeData> chargeDatas = this.loanChargeReadPlatformService.retrieveLoanChargesForFeePayment(
|
||||
ChargePaymentMode.ACCOUNT_TRANSFER.getValue(), LoanStatus.ACTIVE.getValue());
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if (chargeDatas != null) {
|
||||
for (final LoanChargeData chargeData : chargeDatas) {
|
||||
if (chargeData.isInstallmentFee()) {
|
||||
final Collection<LoanInstallmentChargeData> chargePerInstallments = this.LoanChargeReadPlatformService
|
||||
final Collection<LoanInstallmentChargeData> chargePerInstallments = this.loanChargeReadPlatformService
|
||||
.retrieveInstallmentLoanCharges(chargeData.getId(), true);
|
||||
PortfolioAccountData portfolioAccountData = null;
|
||||
for (final LoanInstallmentChargeData installmentChargeData : chargePerInstallments) {
|
||||
@@ -1734,7 +1746,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
final Long penaltyWaitPeriodValue = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||
final Collection<OverdueLoanScheduleData> overdueLoanScheduledInstallments = this.loanReadPlatformService
|
||||
.retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized(penaltyWaitPeriodValue);
|
||||
.retrieveAllLoansWithOverdueInstallments(penaltyWaitPeriodValue);
|
||||
|
||||
if (!overdueLoanScheduledInstallments.isEmpty()) {
|
||||
for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduledInstallments) {
|
||||
@@ -1745,7 +1757,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final JsonCommand command = JsonCommand.from(overdueInstallment.toString(), parsedCommand, this.fromApiJsonHelper,
|
||||
null, null, null, null, null, overdueInstallment.getLoanId(), null, null, null, null, null, null, null);
|
||||
|
||||
applyChargeToOverdueLoanInstallment(overdueInstallment.getLoanId(), overdueInstallment.getChargeId(), command);
|
||||
applyChargeToOverdueLoanInstallment(overdueInstallment.getLoanId(), overdueInstallment.getChargeId(),
|
||||
overdueInstallment.getPeriodNumber(), command);
|
||||
|
||||
} catch (final PlatformApiDataValidationException e) {
|
||||
final List<ApiParameterError> errors = e.getErrors();
|
||||
@@ -1759,50 +1772,90 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void applyChargeToOverdueLoanInstallment(final Long loanId, final Long loanChargeId, final JsonCommand command) {
|
||||
public void applyChargeToOverdueLoanInstallment(final Long loanId, final Long loanChargeId, final Integer periodNumber,
|
||||
final JsonCommand command) {
|
||||
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
checkClientOrGroupActive(loan);
|
||||
|
||||
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(loanChargeId);
|
||||
final LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(periodNumber);
|
||||
|
||||
final LoanCharge loanCharge = LoanCharge.createNewFromJson(loan, chargeDefinition, command);
|
||||
Collection<Integer> frequencyNumbers = loanChargeReadPlatformService.retrieveOverdueInstallmentChargeFrequencyNumber(
|
||||
installment.getId(), chargeDefinition.getId());
|
||||
|
||||
if (!loan.hasCurrencyCodeOf(chargeDefinition.getCurrencyCode())) {
|
||||
final String errorMessage = "Charge and Loan must have the same currency.";
|
||||
throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage);
|
||||
Integer feeFrequency = chargeDefinition.feeFrequency();
|
||||
final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
||||
Map<Integer, LocalDate> scheduleDates = new HashMap<Integer, LocalDate>();
|
||||
final Long penaltyWaitPeriodValue = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||
final Long penaltyPostingWaitPeriodValue = this.configurationDomainService.retrieveGraceOnPenaltyPostingPeriod();
|
||||
final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate");
|
||||
Long diff = penaltyWaitPeriodValue + 1 - penaltyPostingWaitPeriodValue;
|
||||
if (diff < 0) {
|
||||
diff = 0L;
|
||||
}
|
||||
LocalDate startDate = dueDate.plusDays(penaltyWaitPeriodValue.intValue() + 1);
|
||||
Integer frequencyNunber = 1;
|
||||
if (feeFrequency == null) {
|
||||
scheduleDates.put(frequencyNunber++, startDate.minusDays(diff.intValue()));
|
||||
} else {
|
||||
while (new LocalDate().isAfter(startDate)) {
|
||||
scheduleDates.put(frequencyNunber++, startDate.minusDays(diff.intValue()));
|
||||
LocalDate scheduleDate = scheduledDateGenerator.getRepaymentPeriodDate(PeriodFrequencyType.fromInt(feeFrequency),
|
||||
chargeDefinition.feeInterval(), startDate);
|
||||
|
||||
if (loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
|
||||
final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
|
||||
if (portfolioAccountData == null) {
|
||||
final String errorMessage = loanCharge.name() + "Charge requires linked savings account for payment";
|
||||
throw new LinkedAccountRequiredException("loanCharge.add", errorMessage, loanCharge.name());
|
||||
startDate = scheduleDate;
|
||||
}
|
||||
}
|
||||
|
||||
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||
this.loanChargeRepository.save(loanCharge);
|
||||
for (Integer frequency : frequencyNumbers) {
|
||||
scheduleDates.remove(frequency);
|
||||
}
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.addLoanCharge(loanCharge, existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
for (Map.Entry<Integer, LocalDate> entry : scheduleDates.entrySet()) {
|
||||
|
||||
// we want to apply charge transactions only for those loans charges
|
||||
// that are applied when a loan is active
|
||||
if (loan.status().isActive()) {
|
||||
final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null);
|
||||
this.loanTransactionRepository.save(applyLoanChargeTransaction);
|
||||
this.loanRepository.saveAndFlush(loan);
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
this.loanTransactionRepository.save(mapEntry.getValue());
|
||||
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
final LoanCharge loanCharge = LoanCharge.createNewFromJson(loan, chargeDefinition, command, entry.getValue());
|
||||
|
||||
if (!loan.hasCurrencyCodeOf(chargeDefinition.getCurrencyCode())) {
|
||||
final String errorMessage = "Charge and Loan must have the same currency.";
|
||||
throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage);
|
||||
}
|
||||
|
||||
if (loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
|
||||
final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService
|
||||
.retriveLoanAssociation(loanId);
|
||||
if (portfolioAccountData == null) {
|
||||
final String errorMessage = loanCharge.name() + "Charge requires linked savings account for payment";
|
||||
throw new LinkedAccountRequiredException("loanCharge.add", errorMessage, loanCharge.name());
|
||||
}
|
||||
}
|
||||
|
||||
// we post Journal entries only for loans in active status
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
LoanOverdueInstallmentCharge overdueInstallmentCharge = new LoanOverdueInstallmentCharge(loanCharge, installment,
|
||||
entry.getKey());
|
||||
loanCharge.updateOverdueInstallmentCharge(overdueInstallmentCharge);
|
||||
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||
this.loanChargeRepository.save(loanCharge);
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.addLoanCharge(loanCharge, existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
|
||||
// we want to apply charge transactions only for those loans charges
|
||||
// that are applied when a loan is active
|
||||
if (loan.status().isActive()) {
|
||||
final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null);
|
||||
this.loanTransactionRepository.save(applyLoanChargeTransaction);
|
||||
this.loanRepository.saveAndFlush(loan);
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
this.loanTransactionRepository.save(mapEntry.getValue());
|
||||
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// we post Journal entries only for loans in active status
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -231,7 +231,7 @@ public class LoanProductsApiResource {
|
||||
private LoanProductData handleTemplate(final LoanProductData productData) {
|
||||
|
||||
final boolean feeChargesOnly = true;
|
||||
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
|
||||
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly, null);
|
||||
if (chargeOptions.isEmpty()) {
|
||||
chargeOptions = null;
|
||||
}
|
||||
|
||||
+12
-1
@@ -21,11 +21,11 @@ import org.mifosplatform.infrastructure.codes.data.CodeValueData;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.fund.data.FundData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
|
||||
|
||||
/**
|
||||
@@ -659,4 +659,15 @@ public class LoanProductData {
|
||||
public Integer getGraceOnArrearsAgeing() {
|
||||
return this.graceOnArrearsAgeing;
|
||||
}
|
||||
|
||||
public Collection<ChargeData> overdueFeeCharges(){
|
||||
Collection<ChargeData> overdueFeeCharges = new ArrayList<ChargeData>();
|
||||
Collection<ChargeData> charges = charges();
|
||||
for(ChargeData chargeData : charges){
|
||||
if(chargeData.isOverdueInstallmentCharge()){
|
||||
overdueFeeCharges.add(chargeData);
|
||||
}
|
||||
}
|
||||
return overdueFeeCharges;
|
||||
}
|
||||
}
|
||||
+1
@@ -38,6 +38,7 @@ import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.fund.domain.Fund;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
|
||||
|
||||
+2
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanproduct.domain;
|
||||
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
|
||||
/**
|
||||
* Represents the bare minimum repayment details needed for activities related
|
||||
* to generating repayment schedules.
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
|
||||
|
||||
+1
-1
@@ -19,6 +19,7 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
@@ -26,7 +27,6 @@ import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodM
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
+1
-1
@@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.loanproduct.service;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.accountdetails.service.AccountEnumerations;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
@@ -17,7 +18,6 @@ import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodM
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductParamType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
|
||||
public class LoanEnumerations {
|
||||
|
||||
|
||||
+1
-1
@@ -65,8 +65,8 @@ import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeNotFoundException;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
|
||||
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
|
||||
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType;
|
||||
|
||||
+8
-2
@@ -23,6 +23,7 @@ import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeNotFoundException;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeDropdownReadPlatformService;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeEnumerations;
|
||||
import org.mifosplatform.portfolio.common.service.DropdownReadPlatformService;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountAnnualFeeData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType;
|
||||
@@ -38,17 +39,20 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final PlatformSecurityContext context;
|
||||
private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService;
|
||||
private final DropdownReadPlatformService dropdownReadPlatformService;
|
||||
|
||||
// mappers
|
||||
private final SavingsAccountChargeDueMapper chargeDueMapper;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountChargeReadPlatformServiceImpl(final PlatformSecurityContext context,
|
||||
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource) {
|
||||
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource,
|
||||
final DropdownReadPlatformService dropdownReadPlatformService) {
|
||||
this.context = context;
|
||||
this.chargeDropdownReadPlatformService = chargeDropdownReadPlatformService;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.chargeDueMapper = new SavingsAccountChargeDueMapper();
|
||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
||||
}
|
||||
|
||||
private static final class SavingsAccountChargeMapper implements RowMapper<SavingsAccountChargeData> {
|
||||
@@ -138,10 +142,12 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
|
||||
final List<EnumOptionData> savingsChargeTimeTypeOptions = this.chargeDropdownReadPlatformService
|
||||
.retrieveSavingsCollectionTimeTypes();
|
||||
|
||||
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
|
||||
|
||||
// TODO AA : revisit for merge conflict - Not sure method signature
|
||||
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
|
||||
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
|
||||
savingsChargeTimeTypeOptions);
|
||||
savingsChargeTimeTypeOptions, feeFrequencyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -3,12 +3,12 @@ package org.mifosplatform.portfolio.loanaccount;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
|
||||
/**
|
||||
* This class is used to keep in one place configurations for setting up
|
||||
|
||||
Reference in New Issue
Block a user