squash changes in interest-calculation-extension branch

This commit is contained in:
Keith Woodlock
2012-05-01 17:31:32 +01:00
parent ff120f925c
commit d3d40cabe0
37 changed files with 1639 additions and 406 deletions
@@ -22,6 +22,8 @@ public class LoanProductData implements Serializable {
private int interestRatePeriod;
private BigDecimal annualInterestRate = BigDecimal.ZERO;
private int interestMethod;
private int interestRateCalculatedInPeriod;
private Integer numberOfInterestFreePeriods = Integer.valueOf(0);
private Integer repaidEvery;
private int repaymentPeriodFrequency;
@@ -34,6 +36,7 @@ public class LoanProductData implements Serializable {
private List<CurrencyData> possibleCurrencies = new ArrayList<CurrencyData>();
private List<EnumOptionReadModel> possibleAmortizationOptions = new ArrayList<EnumOptionReadModel>();
private List<EnumOptionReadModel> possibleInterestOptions = new ArrayList<EnumOptionReadModel>();
private List<EnumOptionReadModel> possibleInterestRateCalculatedInPeriodOptions = new ArrayList<EnumOptionReadModel>();
private List<EnumOptionReadModel> repaymentFrequencyOptions = new ArrayList<EnumOptionReadModel>();
private List<EnumOptionReadModel> interestFrequencyOptions = new ArrayList<EnumOptionReadModel>();
@@ -47,7 +50,8 @@ public class LoanProductData implements Serializable {
final MoneyData principalMoney,
final BigDecimal interestRatePerPeriod,
final int interestRatePeriod, final BigDecimal annualInterestRate,
final int interestMethod, final Integer repaidEvery,
final int interestMethod, final int interestCalculationInPeriodMethod,
final Integer repaidEvery,
final int repaymentPeriodFrequency, final Integer numberOfRepayments,
final int amortizationMethod, MoneyData inArrearsTolerance, DateTime createdOn, DateTime lastModifedOn) {
this.id = id;
@@ -60,6 +64,7 @@ public class LoanProductData implements Serializable {
this.interestRatePeriod = interestRatePeriod;
this.annualInterestRate = annualInterestRate;
this.interestMethod = interestMethod;
this.interestRateCalculatedInPeriod = interestCalculationInPeriodMethod;
this.numberOfRepayments = numberOfRepayments;
this.repaidEvery = repaidEvery;
this.repaymentPeriodFrequency = repaymentPeriodFrequency;
@@ -256,4 +261,21 @@ public class LoanProductData implements Serializable {
public void setNumberOfInterestFreePeriods(Integer numberOfInterestFreePeriods) {
this.numberOfInterestFreePeriods = numberOfInterestFreePeriods;
}
public int getInterestRateCalculatedInPeriod() {
return interestRateCalculatedInPeriod;
}
public void setInterestRateCalculatedInPeriod(int interestRateCalculatedInPeriod) {
this.interestRateCalculatedInPeriod = interestRateCalculatedInPeriod;
}
public List<EnumOptionReadModel> getPossibleInterestRateCalculatedInPeriodOptions() {
return possibleInterestRateCalculatedInPeriodOptions;
}
public void setPossibleInterestRateCalculatedInPeriodOptions(
List<EnumOptionReadModel> possibleInterestRateCalculatedInPeriodOptions) {
this.possibleInterestRateCalculatedInPeriodOptions = possibleInterestRateCalculatedInPeriodOptions;
}
}
@@ -21,14 +21,15 @@ public class CalculateLoanScheduleCommand {
public CalculateLoanScheduleCommand(final String currencyCode,
final Integer digitsAfterDecimal, final Number principal,
final Number interestRatePerPeriod, Integer interestRateFrequencyMethod, final Integer interestMethod,
final Number interestRatePerPeriod, Integer interestRateFrequencyMethod, final Integer interestMethod, final Integer interestCalculationPeriodMethod,
final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod,
final boolean flexibleRepaymentSchedule,
final boolean interestRebateAllowed,
final LocalDate expectedDisbursementDate,
final LocalDate repaymentsStartingFromDate, final LocalDate interestCalculatedFromDate) {
commonLoanProperties = new CommonLoanProperties(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod, interestMethod,
commonLoanProperties = new CommonLoanProperties(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod,
interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, Integer.valueOf(0),
flexibleRepaymentSchedule, interestRebateAllowed);
@@ -173,4 +174,12 @@ public class CalculateLoanScheduleCommand {
public void setInterestCalculatedFromDate(LocalDate interestCalculatedFromDate) {
this.interestCalculatedFromDate = interestCalculatedFromDate;
}
public Integer getInterestCalculationPeriodMethod() {
return this.commonLoanProperties.getInterestCalculationPeriodMethod();
}
public void setInterestCalculationPeriodMethod(final Integer interestCalculationPeriodMethod) {
this.commonLoanProperties.setInterestCalculationPeriodMethod(interestCalculationPeriodMethod);
}
}
@@ -21,6 +21,7 @@ public class CommonLoanProperties {
private BigDecimal interestRatePerPeriod;
private Integer interestRateFrequencyMethod;
private Integer interestMethod;
private Integer interestCalculationPeriodMethod;
private boolean flexibleRepaymentSchedule = false;
private boolean interestRebateAllowed = false;
@@ -31,7 +32,8 @@ public class CommonLoanProperties {
public CommonLoanProperties(final String currencyCode,
final Integer digitsAfterDecimal, final Number principal,
final Number interestRatePerPeriod, Integer interestRateFrequencyMethod, final Integer interestMethod,
final Number interestRatePerPeriod, Integer interestRateFrequencyMethod,
final Integer interestMethod, final Integer interestCalculationPeriodMethod,
final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod, final Number toleranceAmount,
final boolean flexibleRepaymentSchedule,
final boolean interestRebateAllowed) {
@@ -46,6 +48,7 @@ public class CommonLoanProperties {
}
this.interestRateFrequencyMethod = interestRateFrequencyMethod;
this.interestMethod = interestMethod;
this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
this.repaymentEvery = repaymentEvery;
this.repaymentFrequency = repaymentFrequency;
@@ -169,4 +172,13 @@ public class CommonLoanProperties {
public void setInArrearsToleranceAmount(BigDecimal inArrearsToleranceAmount) {
this.inArrearsToleranceAmount = inArrearsToleranceAmount;
}
public Integer getInterestCalculationPeriodMethod() {
return interestCalculationPeriodMethod;
}
public void setInterestCalculationPeriodMethod(
Integer interestCalculationPeriodMethod) {
this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
}
}
@@ -19,14 +19,16 @@ public class CreateLoanProductCommand implements LoanProductCommandData {
public CreateLoanProductCommand(final String currencyCode,
final Integer digitsAfterDecimal, final Number principal,
final String name, final String description,
final Number interestRatePerPeriod, Integer interestRateFrequencyMethod, final Integer interestMethod,
final Number interestRatePerPeriod, Integer interestRateFrequencyMethod,
final Integer interestMethod, final Integer interestCalculationPeriodMethod,
final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod, final Number toleranceAmount,
final boolean flexibleRepaymentSchedule,
final boolean interestRebateAllowed) {
this.name = name;
this.description = description;
commonLoanProperties = new CommonLoanProperties(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod, interestMethod,
commonLoanProperties = new CommonLoanProperties(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod,
interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, toleranceAmount, flexibleRepaymentSchedule, interestRebateAllowed);
}
@@ -238,4 +240,13 @@ public class CreateLoanProductCommand implements LoanProductCommandData {
public void setInArrearsToleranceAmount(BigDecimal inArrearsToleranceAmount) {
this.commonLoanProperties.setInArrearsToleranceAmount(inArrearsToleranceAmount);
}
@Override
public Integer getInterestCalculationPeriodMethod() {
return this.commonLoanProperties.getInterestCalculationPeriodMethod();
}
public void setInterestCalculationPeriodMethod(final Integer interestCalculationPeriodMethod) {
this.commonLoanProperties.setInterestCalculationPeriodMethod(interestCalculationPeriodMethod);
}
}
@@ -4,40 +4,41 @@ import java.math.BigDecimal;
public interface LoanProductCommandData {
public abstract String getName();
String getName();
public abstract String getDescription();
String getDescription();
public abstract String getCurrencyCode();
String getCurrencyCode();
public abstract Integer getDigitsAfterDecimal();
Integer getDigitsAfterDecimal();
public abstract Integer getRepaymentEvery();
Integer getRepaymentEvery();
public abstract Integer getRepaymentFrequency();
Integer getRepaymentFrequency();
public abstract Boolean isFlexibleRepaymentSchedule();
Boolean isFlexibleRepaymentSchedule();
public abstract Boolean isInterestRebateAllowed();
Boolean isInterestRebateAllowed();
public abstract BigDecimal getPrincipal();
BigDecimal getPrincipal();
public abstract Boolean getFlexibleRepaymentSchedule();
Boolean getFlexibleRepaymentSchedule();
public abstract Boolean getInterestRebateAllowed();
Boolean getInterestRebateAllowed();
public abstract Integer getInterestRateFrequencyMethod();
Integer getInterestRateFrequencyMethod();
public abstract Integer getInterestMethod();
Integer getInterestMethod();
public abstract Integer getAmortizationMethod();
Integer getAmortizationMethod();
public abstract Integer getNumberOfRepayments();
Integer getNumberOfRepayments();
public abstract BigDecimal getInterestRatePerPeriod();
BigDecimal getInterestRatePerPeriod();
public abstract CommonLoanProperties getCommonLoanProperties();
public abstract BigDecimal getInArrearsToleranceAmount();
CommonLoanProperties getCommonLoanProperties();
BigDecimal getInArrearsToleranceAmount();
Integer getInterestCalculationPeriodMethod();
}
@@ -37,7 +37,7 @@ public class SubmitLoanApplicationCommand {
final LocalDate interestCalculatedFromDate,
final LoanSchedule loanSchedule, final String currencyCode,
final Integer digitsAfterDecimal, final Number principal,
final Number interestRatePerPeriod, Integer interestRateFrequencyMethod, final Integer interestMethod,
final Number interestRatePerPeriod, Integer interestRateFrequencyMethod, final Integer interestMethod, final Integer interestCalculationPeriodMethod,
final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod, final Number toleranceAmount,
final boolean flexibleRepaymentSchedule,
final boolean interestRebateAllowed) {
@@ -46,7 +46,8 @@ public class SubmitLoanApplicationCommand {
this.submittedOnDate = submittedOnDate;
this.submittedOnNote = submittedOnNote;
commonLoanProperties = new CommonLoanProperties(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod, interestMethod,
commonLoanProperties = new CommonLoanProperties(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod,
interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, toleranceAmount, flexibleRepaymentSchedule, interestRebateAllowed);
this.expectedDisbursementDate = expectedDisbursementDate;
@@ -249,4 +250,12 @@ public class SubmitLoanApplicationCommand {
public void setInterestCalculatedFromDate(LocalDate interestCalculatedFromDate) {
this.interestCalculatedFromDate = interestCalculatedFromDate;
}
public Integer getInterestCalculationPeriodMethod() {
return this.commonLoanProperties.getInterestCalculationPeriodMethod();
}
public void setInterestCalculationPeriodMethod(final Integer interestCalculationPeriodMethod) {
this.commonLoanProperties.setInterestCalculationPeriodMethod(interestCalculationPeriodMethod);
}
}
@@ -21,14 +21,15 @@ public class UpdateLoanProductCommand implements LoanProductCommandData {
public UpdateLoanProductCommand(final String currencyCode,
final Integer digitsAfterDecimal, final Number principal,
final String name, final String description,
final Number interestRatePerPeriod, Integer interestRateFrequencyMethod, final Integer interestMethod,
final Number interestRatePerPeriod, Integer interestRateFrequencyMethod, final Integer interestMethod, final Integer interestCalculationPeriodMethod,
final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod, final Number toleranceAmount,
final boolean flexibleRepaymentSchedule,
final boolean interestRebateAllowed) {
this.name = name;
this.description = description;
commonLoanProperties = new CommonLoanProperties(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod, interestMethod,
commonLoanProperties = new CommonLoanProperties(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod,
interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, toleranceAmount, flexibleRepaymentSchedule, interestRebateAllowed);
}
@@ -184,4 +185,13 @@ public class UpdateLoanProductCommand implements LoanProductCommandData {
public void setExternalId(String externalId) {
this.externalId = externalId;
}
@Override
public Integer getInterestCalculationPeriodMethod() {
return this.commonLoanProperties.getInterestCalculationPeriodMethod();
}
public void setInterestCalculationPeriodMethod(final Integer interestCalculationPeriodMethod) {
this.commonLoanProperties.setInterestCalculationPeriodMethod(interestCalculationPeriodMethod);
}
}
+3 -5
View File
@@ -471,6 +471,7 @@ CREATE TABLE `portfolio_product_loan` (
`interest_period_frequency_enum` smallint(5) NOT NULL,
`annual_nominal_interest_rate` decimal(19,6) NOT NULL,
`interest_method_enum` smallint(5) NOT NULL,
`interest_calculated_in_period_enum` smallint(5) NOT NULL DEFAULT 1,
`repay_every` smallint(5) NOT NULL,
`repayment_period_frequency_enum` smallint(5) NOT NULL,
`number_of_repayments` smallint(5) NOT NULL,
@@ -483,11 +484,7 @@ CREATE TABLE `portfolio_product_loan` (
`lastmodifiedby_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK1BD0772854DF2770` (`org_id`),
KEY `FKAUD0000000000003` (`createdby_id`),
KEY `FKAUD0000000000004` (`lastmodifiedby_id`),
CONSTRAINT `FK1BD0772854DF2770` FOREIGN KEY (`org_id`) REFERENCES `org_organisation` (`id`),
CONSTRAINT `FKAUD0000000000003` FOREIGN KEY (`createdby_id`) REFERENCES `admin_appuser` (`id`),
CONSTRAINT `FKAUD0000000000004` FOREIGN KEY (`lastmodifiedby_id`) REFERENCES `admin_appuser` (`id`)
CONSTRAINT `FK1BD0772854DF2770` FOREIGN KEY (`org_id`) REFERENCES `org_organisation` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -512,6 +509,7 @@ CREATE TABLE `portfolio_loan` (
`interest_period_frequency_enum` smallint(5) NOT NULL,
`annual_nominal_interest_rate` decimal(19,6) NOT NULL,
`interest_method_enum` smallint(5) NOT NULL,
`interest_calculated_in_period_enum` smallint(5) NOT NULL DEFAULT 1,
`repay_every` smallint(5) NOT NULL,
`repayment_period_frequency_enum` smallint(5) NOT NULL,
`number_of_repayments` smallint(5) NOT NULL,
@@ -1,3 +1,14 @@
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
INSERT INTO `admin_oauth_consumer_application` VALUES (1,'mifosng-ui-consumer-key','testmifosng','MifosNG: Individual Lending', 'Provides capabilities specific to the individual lending credit methodology. Specifically capture of new customer information, loan appraisal, cashflow analysis, loan disbursement, loan monitoring etc', 'Mifos Community', 'http://localhost:8080/mifosng-individual-lending-app/', 'Individual Lending Data', 'Portfolio information for individual clients and loans.');
INSERT INTO `ref_loan_status`
@@ -13,19 +24,28 @@ VALUES
INSERT INTO `ref_currency`
(`id`,`code`,`decimal_places`,`display_symbol`,`name`, `internationalized_name_code`)
VALUES (1,'AED',2,NULL,'UAE Dirham','currency.AED'),(2,'AFN',2,NULL,'Afghanistan Afghani','currency.AFN'),(3,'ALL',2,NULL,'Albanian Lek','currency.ALL'),(4,'AMD',2,NULL,'Armenian Dram','currency.AMD'),(5,'ANG',2,NULL,'Netherlands Antillian Guilder','currency.ANG'),(6,'AOA',2,NULL,'Angolan Kwanza','currency.AOA'),(7,'ARS',2,NULL,'Argentine Peso','currency.ARS'),(8,'AUD',2,NULL,'Australian Dollar','currency.AUD'),(9,'AWG',2,NULL,'Aruban Guilder','currency.AWG'),(10,'AZM',2,NULL,'Azerbaijanian Manat','currency.AZM'),(11,'BAM',2,NULL,'Bosnia and Herzegovina Convertible Marks','currency.BAM'),
(12,'BBD',2,NULL,'Barbados Dollar','currency.BBD'),(13,'BDT',2,NULL,'Bangladesh Taka','currency.BDT'),(14,'BGN',2,NULL,'Bulgarian Lev','currency.BGN'),(15,'BHD',3,NULL,'Bahraini Dinar','currency.BHD'),(16,'BIF',0,NULL,'Burundi Franc','currency.BIF'),(17,'BMD',2,NULL,'Bermudian Dollar','currency.BMD'),(18,'BND',2,'B$','Brunei Dollar','currency.BND'),
VALUES
(1,'AED',2,NULL,'UAE Dirham','currency.AED'),
(2,'AFN',2,NULL,'Afghanistan Afghani','currency.AFN'),
(3,'ALL',2,NULL,'Albanian Lek','currency.ALL'),(4,'AMD',2,NULL,'Armenian Dram','currency.AMD'),(5,'ANG',2,NULL,'Netherlands Antillian Guilder','currency.ANG'),(6,'AOA',2,NULL,'Angolan Kwanza','currency.AOA'),(7,'ARS',2,NULL,'Argentine Peso','currency.ARS'),(8,'AUD',2,NULL,'Australian Dollar','currency.AUD'),(9,'AWG',2,NULL,'Aruban Guilder','currency.AWG'),(10,'AZM',2,NULL,'Azerbaijanian Manat','currency.AZM'),(11,'BAM',2,NULL,'Bosnia and Herzegovina Convertible Marks','currency.BAM'),
(12,'BBD',2,NULL,'Barbados Dollar','currency.BBD'),(13,'BDT',2,NULL,'Bangladesh Taka','currency.BDT'),(14,'BGN',2,NULL,'Bulgarian Lev','currency.BGN'),(15,'BHD',3,NULL,'Bahraini Dinar','currency.BHD'),(16,'BIF',0,NULL,'Burundi Franc','currency.BIF'),(17,'BMD',2,NULL,'Bermudian Dollar','currency.BMD'),
(18,'BND',2,'B$','Brunei Dollar','currency.BND'),
(19,'BOB',2,NULL,'Bolivian Boliviano','currency.BOB'),(20,'BRL',2,NULL,'Brazilian Real','currency.BRL'),(21,'BSD',2,NULL,'Bahamian Dollar','currency.BSD'),(22,'BTN',2,NULL,'Bhutan Ngultrum','currency.BTN'),(23,'BWP',2,NULL,'Botswana Pula','currency.BWP'),(24,'BYR',0,NULL,'Belarussian Ruble','currency.BYR'),(25,'BZD',2,NULL,'Belize Dollar','currency.BZD'),(26,'CAD',2,NULL,'Canadian Dollar','currency.CAD'),
(27,'CDF',2,NULL,'Franc Congolais','currency.CDF'),(28,'CHF',2,NULL,'Swiss Franc','currency.CHF'),(29,'CLP',0,NULL,'Chilean Peso','currency.CLP'),(30,'CNY',2,NULL,'Chinese Yuan Renminbi','currency.CNY'),(31,'COP',2,NULL,'Colombian Peso','currency.COP'),(32,'CRC',2,NULL,'Costa Rican Colon','currency.CRC'),(33,'CSD',2,NULL,'Serbian Dinar','currency.CSD'),(34,'CUP',2,NULL,'Cuban Peso','currency.CUP'),(35,'CVE',2,NULL,'Cape Verde Escudo','currency.CVE'),
(36,'CYP',2,NULL,'Cyprus Pound','currency.CYP'),(37,'CZK',2,NULL,'Czech Koruna','currency.CZK'),(38,'DJF',0,NULL,'Djibouti Franc','currency.DJF'),(39,'DKK',2,NULL,'Danish Krone','currency.DKK'),(40,'DOP',2,NULL,'Dominican Peso','currency.DOP'),(41,'DZD',2,NULL,'Algerian Dinar','currency.DZD'),(42,'EEK',2,NULL,'Estonian Kroon','currency.EEK'),(43,'EGP',2,NULL,'Egyptian Pound','currency.EGP'),(44,'ERN',2,NULL,'Eritrea Nafka','currency.ERN'),(45,'ETB',2,NULL,'Ethiopian Birr','currency.ETB'),(46,'EUR',2,NULL,'euro','currency.EUR'),(47,'FJD',2,NULL,'Fiji Dollar','currency.FJD'),(48,'FKP',2,NULL,'Falkland Islands Pound','currency.FKP'),(49,'GBP',2,NULL,'Pound Sterling','currency.GBP'),(50,'GEL',2,NULL,'Georgian Lari','currency.GEL'),
(51,'GHC',2,'GHc','Ghana Cedi','currency.GHC'),
(52,'GIP',2,NULL,'Gibraltar Pound','currency.GIP'),(53,'GMD',2,NULL,'Gambian Dalasi','currency.GMD'),(54,'GNF',0,NULL,'Guinea Franc','currency.GNF'),(55,'GTQ',2,NULL,'Guatemala Quetzal','currency.GTQ'),(56,'GYD',2,NULL,'Guyana Dollar','currency.GYD'),(57,'HKD',2,NULL,'Hong Kong Dollar','currency.HKD'),(58,'HNL',2,NULL,'Honduras Lempira','currency.HNL'),(59,'HRK',2,NULL,'Croatian Kuna','currency.HRK'),(60,'HTG',2,NULL,'Haiti Gourde','currency.HTG'),(61,'HUF',2,NULL,'Hungarian Forint','currency.HUF'),(62,'IDR',2,NULL,'Indonesian Rupiah','currency.IDR'),(63,'ILS',2,NULL,'New Israeli Shekel','currency.ILS'),(64,'INR',2,NULL,'Indian Rupee','currency.INR'),(65,'IQD',3,NULL,'Iraqi Dinar','currency.IQD'),(66,'IRR',2,NULL,'Iranian Rial','currency.IRR'),(67,'ISK',0,NULL,'Iceland Krona','currency.ISK'),(68,'JMD',2,NULL,'Jamaican Dollar','currency.JMD'),(69,'JOD',3,NULL,'Jordanian Dinar','currency.JOD'),(70,'JPY',0,NULL,'Japanese Yen','currency.JPY'),
(71,'KES',2,'KSh','Kenyan Shilling','currency.KES'),
(72,'KGS',2,NULL,'Kyrgyzstan Som','currency.KGS'),(73,'KHR',2,NULL,'Cambodia Riel','currency.KHR'),(74,'KMF',0,NULL,'Comoro Franc','currency.KMF'),(75,'KPW',2,NULL,'North Korean Won','currency.KPW'),(76,'KRW',0,NULL,'Korean Won','currency.KRW'),(77,'KWD',3,NULL,'Kuwaiti Dinar','currency.KWD'),(78,'KYD',2,NULL,'Cayman Islands Dollar','currency.KYD'),(79,'KZT',2,NULL,'Kazakhstan Tenge','currency.KZT'),(80,'LAK',2,NULL,'Lao Kip','currency.LAK'),(81,'LBP',2,NULL,'Lebanese Pound','currency.LBP'),(82,'LKR',2,NULL,'Sri Lanka Rupee','currency.LKR'),(83,'LRD',2,NULL,'Liberian Dollar','currency.LRD'),(84,'LSL',2,NULL,'Lesotho Loti','currency.LSL'),
(72,'KGS',2,NULL,'Kyrgyzstan Som','currency.KGS'),(73,'KHR',2,NULL,'Cambodia Riel','currency.KHR'),(74,'KMF',0,NULL,'Comoro Franc','currency.KMF'),(75,'KPW',2,NULL,'North Korean Won','currency.KPW'),(76,'KRW',0,NULL,'Korean Won','currency.KRW'),(77,'KWD',3,NULL,'Kuwaiti Dinar','currency.KWD'),(78,'KYD',2,NULL,'Cayman Islands Dollar','currency.KYD'),(79,'KZT',2,NULL,'Kazakhstan Tenge','currency.KZT'),
(80,'LAK',2,NULL,'Lao Kip','currency.LAK'),
(81,'LBP',2,'L£','Lebanese Pound','currency.LBP'),
(82,'LKR',2,NULL,'Sri Lanka Rupee','currency.LKR'),(83,'LRD',2,NULL,'Liberian Dollar','currency.LRD'),(84,'LSL',2,NULL,'Lesotho Loti','currency.LSL'),
(85,'LTL',2,NULL,'Lithuanian Litas','currency.LTL'),(86,'LVL',2,NULL,'Latvian Lats','currency.LVL'),(87,'LYD',3,NULL,'Libyan Dinar','currency.LYD'),(88,'MAD',2,NULL,'Moroccan Dirham','currency.MAD'),(89,'MDL',2,NULL,'Moldovan Leu','currency.MDL'),(90,'MGA',2,NULL,'Malagasy Ariary','currency.MGA'),(91,'MKD',2,NULL,'Macedonian Denar','currency.MKD'),(92,'MMK',2,NULL,'Myanmar Kyat','currency.MMK'),(93,'MNT',2,NULL,'Mongolian Tugrik','currency.MNT'),(94,'MOP',2,NULL,'Macau Pataca','currency.MOP'),(95,'MRO',2,NULL,'Mauritania Ouguiya','currency.MRO'),(96,'MTL',2,NULL,'Maltese Lira','currency.MTL'),(97,'MUR',2,NULL,'Mauritius Rupee','currency.MUR'),(98,'MVR',2,NULL,'Maldives Rufiyaa','currency.MVR'),(99,'MWK',2,NULL,'Malawi Kwacha','currency.MWK'),(100,'MXN',2,NULL,'Mexican Peso','currency.MXN'),(101,'MYR',2,NULL,'Malaysian Ringgit','currency.MYR'),(102,'MZM',2,NULL,'Mozambique Metical','currency.MZM'),(103,'NAD',2,NULL,'Namibia Dollar','currency.NAD'),(104,'NGN',2,NULL,'Nigerian Naira','currency.NGN'),(105,'NIO',2,NULL,'Nicaragua Cordoba Oro','currency.NIO'),(106,'NOK',2,NULL,'Norwegian Krone','currency.NOK'),(107,'NPR',2,NULL,'Nepalese Rupee','currency.NPR'),(108,'NZD',2,NULL,'New Zealand Dollar','currency.NZD'),(109,'OMR',3,NULL,'Rial Omani','currency.OMR'),(110,'PAB',2,NULL,'Panama Balboa','currency.PAB'),(111,'PEN',2,NULL,'Peruvian Nuevo Sol','currency.PEN'),(112,'PGK',2,NULL,'Papua New Guinea Kina','currency.PGK'),(113,'PHP',2,NULL,'Philippine Peso','currency.PHP'),(114,'PKR',2,NULL,'Pakistan Rupee','currency.PKR'),(115,'PLN',2,NULL,'Polish Zloty','currency.PLN'),(116,'PYG',0,NULL,'Paraguayan Guarani','currency.PYG'),(117,'QAR',2,NULL,'Qatari Rial','currency.QAR'),
(118,'RON',2,NULL,'Romanian Leu','currency.RON'),(119,'RUB',2,NULL,'Russian Ruble','currency.RUB'),(120,'RWF',0,NULL,'Rwanda Franc','currency.RWF'),(121,'SAR',2,NULL,'Saudi Riyal','currency.SAR'),(122,'SBD',2,NULL,'Solomon Islands Dollar','currency.SBD'),(123,'SCR',2,NULL,'Seychelles Rupee','currency.SCR'),(124,'SDD',2,NULL,'Sudanese Dinar','currency.SDD'),(125,'SEK',2,NULL,'Swedish Krona','currency.SEK'),(126,'SGD',2,NULL,'Singapore Dollar','currency.SGD'),(127,'SHP',2,NULL,'St Helena Pound','currency.SHP'),(128,'SIT',2,NULL,'Slovenian Tolar','currency.SIT'),(129,'SKK',2,NULL,'Slovak Koruna','currency.SKK'),(130,'SLL',2,NULL,'Sierra Leone Leone','currency.SLL'),(131,'SOS',2,NULL,'Somali Shilling','currency.SOS'),(132,'SRD',2,NULL,'Surinam Dollar','currency.SRD'),
(133,'STD',2,NULL,'Sao Tome and Principe Dobra','currency.STD'),(134,'SVC',2,NULL,'El Salvador Colon','currency.SVC'),(135,'SYP',2,NULL,'Syrian Pound','currency.SYP'),(136,'SZL',2,NULL,'Swaziland Lilangeni','currency.SZL'),(137,'THB',2,NULL,'Thai Baht','currency.THB'),
(138,'TJS',2,NULL,'Tajik Somoni','currency.TJS'),(139,'TMM',2,NULL,'Turkmenistan Manat','currency.TMM'),(140,'TND',3,NULL,'Tunisian Dinar','currency.TND'),(141,'TOP',2,NULL,'Tonga Pa\'anga','currency.TOP'),(142,'TRY',2,NULL,'Turkish Lira','currency.TRY'),(143,'TTD',2,NULL,'Trinidad and Tobago Dollar','currency.TTD'),(144,'TWD',2,NULL,'New Taiwan Dollar','currency.TWD'),(145,'TZS',2,NULL,'Tanzanian Shilling','currency.TZS'),(146,'UAH',2,NULL,'Ukraine Hryvnia','currency.UAH'),(147,'UGX',2,NULL,'Uganda Shilling','currency.UGX'),
(138,'TJS',2,NULL,'Tajik Somoni','currency.TJS'),(139,'TMM',2,NULL,'Turkmenistan Manat','currency.TMM'),
(140,'TND',3,'DT','Tunisian Dinar','currency.TND'),
(141,'TOP',2,NULL,'Tonga Pa\'anga','currency.TOP'),(142,'TRY',2,NULL,'Turkish Lira','currency.TRY'),(143,'TTD',2,NULL,'Trinidad and Tobago Dollar','currency.TTD'),(144,'TWD',2,NULL,'New Taiwan Dollar','currency.TWD'),(145,'TZS',2,NULL,'Tanzanian Shilling','currency.TZS'),(146,'UAH',2,NULL,'Ukraine Hryvnia','currency.UAH'),(147,'UGX',2,NULL,'Uganda Shilling','currency.UGX'),
(148,'USD',2,'$','US Dollar','currency.USD'),
(149,'UYU',2,NULL,'Peso Uruguayo','currency.UYU'),(150,'UZS',2,NULL,'Uzbekistan Sum','currency.UZS'),(151,'VEB',2,NULL,'Venezuelan Bolivar','currency.VEB'),(152,'VND',2,NULL,'Vietnamese Dong','currency.VND'),(153,'VUV',0,NULL,'Vanuatu Vatu','currency.VUV'),(154,'WST',2,NULL,'Samoa Tala','currency.WST'),(155,'XAF',0,NULL,'CFA Franc BEAC','currency.XAF'),(156,'XCD',2,NULL,'East Caribbean Dollar','currency.XCD'),(157,'XDR',5,NULL,'SDR (Special Drawing Rights)','currency.XDR'),
(158,'XOF',0, 'CFA','CFA Franc BCEAO','currency.XOF'),
@@ -34,3 +54,15 @@ VALUES (1,'AED',2,NULL,'UAE Dirham','currency.AED'),(2,'AFN',2,NULL,'Afghanistan
(161,'ZAR',2,NULL,'South African Rand','currency.ZAR'),
(162,'ZMK',2,NULL,'Zambian Kwacha','currency.ZMK'),
(163,'ZWD',2,NULL,'Zimbabwe Dollar','currency.ZWD');
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+3 -2
View File
@@ -125,6 +125,7 @@ INSERT INTO `mifosngprovider`.`portfolio_product_loan`
`interest_period_frequency_enum`,
`annual_nominal_interest_rate`,
`interest_method_enum`,
`interest_calculated_in_period_enum`,
`repay_every`,
`repayment_period_frequency_enum`,
`number_of_repayments`,
@@ -136,7 +137,7 @@ INSERT INTO `mifosngprovider`.`portfolio_product_loan`
`lastmodified_date`,
`lastmodifiedby_id`)
VALUES
(1,1,'XOF',0,'100000.000000','1000.000000','Agricultural Loan','An agricultural loan given to farmers to help buy crop, stock and machinery. With an arrears tolerance setting of 1,000 CFA, loans are not marked as \'in arrears\' or \'in bad standing\' if the amount outstanding is less than this. Interest rate is described using monthly percentage rate (MPR) even though the loan typically lasts a year and requires one repayment (typically at time when farmer sells crop)','1.750000',2,'21.000000',0,12,2,1,1,'\0','\0',1,'2012-04-12 22:14:34','2012-04-12 22:14:34',1);
(1,1,'XOF',0,'100000.000000','1000.000000','Agricultural Loan','An agricultural loan given to farmers to help buy crop, stock and machinery. With an arrears tolerance setting of 1,000 CFA, loans are not marked as \'in arrears\' or \'in bad standing\' if the amount outstanding is less than this. Interest rate is described using monthly percentage rate (MPR) even though the loan typically lasts a year and requires one repayment (typically at time when farmer sells crop)','1.750000',2,'21.000000',0,1,12,2,1,1,'\0','\0',1,'2012-04-12 22:14:34','2012-04-12 22:14:34',1);
INSERT INTO `mifosngprovider`.`portfolio_client`
@@ -181,7 +182,7 @@ VALUES
first five loans first for five clients
*/
INSERT INTO `portfolio_loan` VALUES
(1,1,NULL,1,1,600,'XOF',0,'100000.000000','1000.000000','1.750000',2,'21.000000',0,10,2,1,1,'\0','\0','0.000000','2009-01-05 12:33:10','2009-01-08 12:33:16','2009-01-15',NULL,NULL,'2009-01-15','2009-11-15',NULL,'2009-11-13 00:00:00',NULL,NULL,NULL,NULL,1,'2012-04-13 12:33:10','2012-04-13 12:34:40',1),(2,1,NULL,2,1,600,'XOF',0,'100000.000000','1000.000000','1.750000',2,'21.000000',0,11,2,1,1,'\0','\0','0.000000','2009-01-08 13:08:16','2009-01-12 13:08:25','2009-01-15',NULL,NULL,'2009-01-15','2009-12-15','2009-12-15','2009-12-15 00:00:00',NULL,NULL,NULL,NULL,1,'2012-04-13 13:08:16','2012-04-13 13:11:53',1),(4,1,NULL,3,1,600,'XOF',0,'100000.000000','1000.000000','1.750000',2,'21.000000',0,12,2,1,1,'\0','\0','0.000000','2009-01-13 13:14:30','2009-01-15 13:14:54','2009-01-22',NULL,NULL,'2009-01-22','2010-01-22','2010-01-07','2010-01-07 00:00:00',NULL,NULL,NULL,NULL,1,'2012-04-13 13:14:30','2012-04-13 13:15:32',1),(5,1,NULL,4,1,600,'XOF',0,'100000.000000','1000.000000','1.750000',2,'21.000000',0,8,2,1,1,'\0','\0','0.000000','2009-01-12 13:23:32','2009-01-15 13:23:44','2009-01-22',NULL,NULL,'2009-01-22','2009-09-22','2009-09-22','2009-09-22 00:00:00',NULL,NULL,NULL,NULL,1,'2012-04-13 13:23:32','2012-04-13 13:24:01',1),(6,1,NULL,5,1,600,'XOF',0,'300000.000000','1000.000000','1.750000',2,'21.000000',0,3,2,4,1,'\0','\0','0.000000','2009-01-26 13:30:54','2009-01-27 13:31:02','2009-01-29','2009-05-01','2009-02-01','2009-01-29','2010-02-01','2010-02-01','2010-02-01 00:00:00',NULL,NULL,NULL,NULL,1,'2012-04-13 13:30:54','2012-04-13 13:33:05',1);
(1,1,NULL,1,1,600,'XOF',0,'100000.000000','1000.000000','1.750000',2,'21.000000',0,1,10,2,1,1,'\0','\0','0.000000','2009-01-05 12:33:10','2009-01-08 12:33:16','2009-01-15',NULL,NULL,'2009-01-15','2009-11-15',NULL,'2009-11-13 00:00:00',NULL,NULL,NULL,NULL,1,'2012-04-13 12:33:10','2012-04-13 12:34:40',1),(2,1,NULL,2,1,600,'XOF',0,'100000.000000','1000.000000','1.750000',2,'21.000000',0,1,11,2,1,1,'\0','\0','0.000000','2009-01-08 13:08:16','2009-01-12 13:08:25','2009-01-15',NULL,NULL,'2009-01-15','2009-12-15','2009-12-15','2009-12-15 00:00:00',NULL,NULL,NULL,NULL,1,'2012-04-13 13:08:16','2012-04-13 13:11:53',1),(4,1,NULL,3,1,600,'XOF',0,'100000.000000','1000.000000','1.750000',2,'21.000000',0,1,12,2,1,1,'\0','\0','0.000000','2009-01-13 13:14:30','2009-01-15 13:14:54','2009-01-22',NULL,NULL,'2009-01-22','2010-01-22','2010-01-07','2010-01-07 00:00:00',NULL,NULL,NULL,NULL,1,'2012-04-13 13:14:30','2012-04-13 13:15:32',1),(5,1,NULL,4,1,600,'XOF',0,'100000.000000','1000.000000','1.750000',2,'21.000000',0,1,8,2,1,1,'\0','\0','0.000000','2009-01-12 13:23:32','2009-01-15 13:23:44','2009-01-22',NULL,NULL,'2009-01-22','2009-09-22','2009-09-22','2009-09-22 00:00:00',NULL,NULL,NULL,NULL,1,'2012-04-13 13:23:32','2012-04-13 13:24:01',1),(6,1,NULL,5,1,600,'XOF',0,'300000.000000','1000.000000','1.750000',2,'21.000000',0,1,3,2,4,1,'\0','\0','0.000000','2009-01-26 13:30:54','2009-01-27 13:31:02','2009-01-29','2009-05-01','2009-02-01','2009-01-29','2010-02-01','2010-02-01','2010-02-01 00:00:00',NULL,NULL,NULL,NULL,1,'2012-04-13 13:30:54','2012-04-13 13:33:05',1);
INSERT INTO `portfolio_loan_repayment_schedule` VALUES
(1,1,1,'2009-11-15',1,'100000.000000','100000.000000','17500.000000','17500.000000','',1,'2012-04-13 12:33:11','2012-04-13 12:34:40',1),
File diff suppressed because one or more lines are too long
@@ -0,0 +1,10 @@
/*
Note: its possible that this script wont apply on windowns through the command line client. The UTF-8 value £ isnt inserted.
In this case you can open up an sql tool like MySql workbench and apply this script in total.
*/
UPDATE `ref_currency` SET `display_symbol` = 'BND' where `id`=18;
UPDATE `ref_currency` SET `display_symbol` = 'GHC' where `id`=51;
UPDATE `ref_currency` SET `display_symbol` = 'KSh' where `id`=71;
UPDATE `ref_currency` SET `display_symbol` = 'DT' where `id`=140;
UPDATE `ref_currency` SET `display_symbol` = '$' where `id`=148;
UPDATE `mifosngprovider`.`ref_currency` SET `display_symbol`='L£' WHERE `id`='81';
@@ -0,0 +1,3 @@
ALTER TABLE `mifosngprovider`.`portfolio_product_loan` add column `interest_calculated_in_period_enum` smallint(5) NOT NULL DEFAULT 1;
ALTER TABLE `mifosngprovider`.`portfolio_loan` add column `interest_calculated_in_period_enum` smallint(5) NOT NULL DEFAULT 1;
@@ -128,6 +128,7 @@ public class LoanController {
@RequestParam(value="nominalInterestRate", required=false) String nominalInterestRate,
@RequestParam(value="selectedInterestFrequencyOption", required=false) Integer interestRateFrequencyMethod,
@RequestParam(value="selectedInterestMethodOption", required=false) Integer interestMethod,
@RequestParam(value="interestRateCalculatedInPeriod", required=false) Integer interestRateCalculatedInPeriod,
@RequestParam(value="expectedDisbursementDate", required=false) String expectedDisbursementDateStr,
@RequestParam(value="repaymentsStartingFromDate", required=false) String repaymentsStartingFromDateStr,
@RequestParam(value="interestCalculatedFromDate", required=false) String interestCalculatedFromDateStr
@@ -146,7 +147,7 @@ public class LoanController {
final LocalDate interestCalculatedFromDate = parseStringToLocalDate(interestCalculatedFromDateStr, "interestCalculatedFromDate");
CalculateLoanScheduleCommand calculateLoanScheduleCommand = new CalculateLoanScheduleCommand(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod,
interestRateFrequencyMethod, interestMethod, repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod,
interestRateFrequencyMethod, interestMethod, interestRateCalculatedInPeriod, repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod,
flexibleRepaymentSchedule, interestRebateAllowed, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
LoanSchedule loanSchedule = this.commonRestOperations.calculateLoanSchedule(calculateLoanScheduleCommand);
@@ -154,7 +155,7 @@ public class LoanController {
SubmitLoanApplicationCommand command = new SubmitLoanApplicationCommand(applicantId, productId,
submittedOnDate, submittedOnNote, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate,
loanSchedule, currencyCode, digitsAfterDecimal, principal,
interestRatePerPeriod, interestRateFrequencyMethod, interestMethod,
interestRatePerPeriod, interestRateFrequencyMethod, interestMethod, interestRateCalculatedInPeriod,
repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod,
toleranceAmount, flexibleRepaymentSchedule, interestRebateAllowed);
@@ -175,6 +176,7 @@ public class LoanController {
@RequestParam(value="nominalInterestRate", required=false) String nominalInterestRate,
@RequestParam(value="selectedInterestFrequencyOption", required=false) Integer interestRateFrequencyMethod,
@RequestParam(value="selectedInterestMethodOption", required=false) Integer interestMethod,
@RequestParam(value="interestRateCalculatedInPeriod", required=false) Integer interestRateCalculatedInPeriod,
@RequestParam(value="expectedDisbursementDate", required=false) String expectedDisbursementDateStr,
@RequestParam(value="repaymentsStartingFromDate", required=false) String repaymentsStartingFromDateStr,
@RequestParam(value="interestCalculatedFromDate", required=false) String interestCalculatedFromDateStr
@@ -189,7 +191,7 @@ public class LoanController {
final LocalDate interestCalculatedFromDate = parseStringToLocalDate(interestCalculatedFromDateStr, "interestCalculatedFromDate");
CalculateLoanScheduleCommand command = new CalculateLoanScheduleCommand(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod,
interestRateFrequencyMethod, interestMethod, repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod,
interestRateFrequencyMethod, interestMethod, interestRateCalculatedInPeriod, repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod,
flexibleRepaymentSchedule, interestRebateAllowed, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
return this.commonRestOperations.calculateLoanSchedule(command);
@@ -102,7 +102,8 @@ public class LoanProductController {
@RequestParam("inArrearsTolerance") String inArrearsToleranceAsNumber,
@RequestParam("nominalInterestRate") String nominalInterestRatePerPeriodAsNumber,
@RequestParam("selectedInterestFrequencyOption") Integer nominalInterestRatePerPeriodFrequency,
@RequestParam("selectedInterestMethodOption") Integer interestMethod
@RequestParam("selectedInterestMethodOption") Integer interestMethod,
@RequestParam(value="interestRateCalculatedInPeriod", required=false) Integer interestCalculationPeriodMethod
) {
CreateLoanProductCommand command = new CreateLoanProductCommand();
@@ -132,6 +133,7 @@ public class LoanProductController {
}
command.setInterestRateFrequencyMethod(nominalInterestRatePerPeriodFrequency);
command.setInterestMethod(interestMethod);
command.setInterestCalculationPeriodMethod(interestCalculationPeriodMethod);
return this.commonRestOperations.createLoanProduct(command);
}
@@ -155,7 +157,8 @@ public class LoanProductController {
@RequestParam("inArrearsTolerance") String inArrearsToleranceAsNumber,
@RequestParam("nominalInterestRate") String nominalInterestRatePerPeriodAsNumber,
@RequestParam("selectedInterestFrequencyOption") Integer nominalInterestRatePerPeriodFrequency,
@RequestParam("selectedInterestMethodOption") Integer interestMethod
@RequestParam("selectedInterestMethodOption") Integer interestMethod,
@RequestParam(value="interestRateCalculatedInPeriod", required=false) Integer interestCalculationPeriodMethod
) {
UpdateLoanProductCommand command = new UpdateLoanProductCommand();
@@ -187,6 +190,7 @@ public class LoanProductController {
}
command.setInterestRateFrequencyMethod(nominalInterestRatePerPeriodFrequency);
command.setInterestMethod(interestMethod);
command.setInterestCalculationPeriodMethod(interestCalculationPeriodMethod);
return this.commonRestOperations.updateLoanProduct(command);
}
@@ -237,6 +237,7 @@ form.label.loan.product.amortization=Amortization:
form.label.loan.product.arrears.tolerance=Arrears tolerance:
form.label.loan.product.nominal.interestrate=Nominal interest rate %:
form.label.loan.product.interest.method=Interest method:
form.label.loan.product.interest.rate.calcutated.in.period=Interest calculation period:
validation.msg.product.name.cannot.be.blank=Product name cannot be blank.
validation.msg.product.description.exceeds.max.length=The content of the description has exceeded max allowed length of 500 characters.
@@ -236,6 +236,7 @@ form.label.loan.product.amortization=Amortization:(fr)
form.label.loan.product.arrears.tolerance=Arrears tolerance:(fr)
form.label.loan.product.nominal.interestrate=Nominal interest rate %:(fr)
form.label.loan.product.interest.method=Interest method:(fr)
form.label.loan.product.interest.rate.calcutated.in.period=Interest calculation period:(fr)
validation.msg.product.name.cannot.be.blank=Product name cannot be blank.(fr)
validation.msg.product.description.exceeds.max.length=The content of the description has exceeded max allowed length of 500 characters.(fr)
@@ -82,26 +82,6 @@
<input id="numberOfRepayments" name="numberOfRepayments" title="" style="width: 50px;" value="{{=$ctx.number(numberOfRepayments)}}"/>
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.amortization"/></td>
<td>
<select id="selectedAmortizationMethodOption" name="selectedAmortizationMethodOption" title="" style="width: 178px;">
{{#each possibleAmortizationOptions}}
{{#if $ctx.number($parent.parent.data.amortizationMethod)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
{{#else}}
<option value="{{=$ctx.number(id)}}">{{=value}}</option>
{{/if}}
{{/each}}
</select>
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.arrears.tolerance"/></td>
<td>
<input id="inArrearsTolerance" name="inArrearsTolerance" title="" style="width: 125px;" value="{{=$ctx.money(inArrearsTolerance)}}" />
</td>
</tr>
</table>
</td>
<td valign="top" style="padding-left: 5px; padding-right: 5px; border-right: 1px solid;">
@@ -109,15 +89,29 @@
<tr>
<td><spring:message code="form.label.loan.product.nominal.interestrate"/></td>
<td>
<input id="nominalInterestRate" name="nominalInterestRate" title="" style="width: 60px;" value="{{=$ctx.decimal(interestRatePerPeriod, 4)}}" />
<input id="nominalInterestRate" name="nominalInterestRate" title="" style="width: 70px;" value="{{=$ctx.decimal(interestRatePerPeriod, 4)}}" />
<select id="selectedInterestFrequencyOption" name="selectedInterestFrequencyOption" title="" style="width: 115px;">
<select id="selectedInterestFrequencyOption" name="selectedInterestFrequencyOption" title="" style="width: 139px;">
{{#each interestFrequencyOptions}}
{{#if $ctx.number($parent.parent.data.interestRatePeriod)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
{{#else}}
<option value="{{=$ctx.number(id)}}">{{=value}}</option>
{{/if}}
{{/each}}
</select>
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.amortization"/></td>
<td>
<select id="selectedAmortizationMethodOption" name="selectedAmortizationMethodOption" title="" style="width: 220px;">
{{#each possibleAmortizationOptions}}
{{#if $ctx.number($parent.parent.data.amortizationMethod)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
{{#else}}
<option value="{{=$ctx.number(id)}}">{{=value}}</option>
{{/if}}
{{/each}}
</select>
</td>
@@ -125,7 +119,7 @@
<tr>
<td><spring:message code="form.label.loan.product.interest.method"/></td>
<td>
<select id="selectedInterestMethodOption" name="selectedInterestMethodOption" title="" style="width: 180px;">
<select id="selectedInterestMethodOption" name="selectedInterestMethodOption" title="" style="width: 220px;">
{{#each possibleInterestOptions}}
{{#if $ctx.number($parent.parent.data.interestMethod)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
@@ -136,6 +130,26 @@
</select>
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.interest.rate.calcutated.in.period"/></td>
<td>
<select id="interestRateCalculatedInPeriod" name="interestRateCalculatedInPeriod" title="" style="width: 220px;">
{{#each possibleInterestRateCalculatedInPeriodOptions}}
{{#if $ctx.number($parent.parent.data.interestRateCalculatedInPeriod)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
{{#else}}
<option value="{{=$ctx.number(id)}}">{{=value}}</option>
{{/if}}
{{/each}}
</select>
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.arrears.tolerance"/></td>
<td>
<input id="inArrearsTolerance" name="inArrearsTolerance" title="" style="width: 215px;" value="{{=$ctx.money(inArrearsTolerance)}}" />
</td>
</tr>
</table>
</td>
</tr>
@@ -201,20 +201,6 @@ $(document).ready(function() {
calculateLoanSchedule();
});
$('#nominalInterestRate').change(function() {
calculateAnnualPercentageRate();
calculateLoanSchedule();
});
$('#selectedInterestFrequencyOption').change(function() {
calculateAnnualPercentageRate();
calculateLoanSchedule();
});
$('#selectedInterestMethodOption').change(function() {
calculateLoanSchedule();
});
$('#repaidEvery').change(function() {
calculateLoanSchedule();
});
@@ -227,10 +213,6 @@ $(document).ready(function() {
calculateLoanSchedule();
});
$('#selectedAmortizationMethodOption').change(function() {
calculateLoanSchedule();
});
$('#expectedDisbursementDate').change(function() {
calculateLoanSchedule();
});
@@ -239,6 +221,28 @@ $(document).ready(function() {
calculateLoanSchedule();
});
$('#nominalInterestRate').change(function() {
calculateAnnualPercentageRate();
calculateLoanSchedule();
});
$('#selectedInterestFrequencyOption').change(function() {
calculateAnnualPercentageRate();
calculateLoanSchedule();
});
$('#selectedAmortizationMethodOption').change(function() {
calculateLoanSchedule();
});
$('#selectedInterestMethodOption').change(function() {
calculateLoanSchedule();
});
$('#interestRateCalculatedInPeriod').change(function() {
calculateLoanSchedule();
});
$('#interestCalculatedFromDate').change(function() {
calculateLoanSchedule();
});
@@ -128,27 +128,6 @@
<input id="numberOfRepayments" name="numberOfRepayments" title="" type="text" value="{{=$ctx.number(selectedProduct.numberOfRepayments)}}" style="width: 50px;" />
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.amortization"/></td>
<td>
<select name="selectedAmortizationMethodOption" id="selectedAmortizationMethodOption" title="" style="width: 178px;">
{{#each selectedProduct.possibleAmortizationOptions}}
{{#if $ctx.number($parent.parent.data.selectedProduct.amortizationMethod)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
{{#else}}
<option value="{{=$ctx.number(id)}}">{{=value}}</option>
{{/if}}
{{/each}}
</select>
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.arrears.tolerance"/></td>
<td>
<input id="inArrearsTolerance" name="inArrearsTolerance" title="" type="text" value="{{=$ctx.money(selectedProduct.inArrearsTolerance)}}" style="width: 125px;" />
<input id="inArrearsTolerance.currencyCode" name="inArrearsTolerance.currencyCode" title="" type="text" value="{{=selectedProduct.inArrearsTolerance.displaySymbol}}" style="width: 40px;" disabled="disabled" />
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.expected.disbursement.date.on"/></td>
<td>
@@ -188,6 +167,20 @@
<input id="interestRatePerYear" name="interestRatePerYear" title="" type="text" style="width: 60px;" disabled="disabled" />
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.amortization"/></td>
<td>
<select name="selectedAmortizationMethodOption" id="selectedAmortizationMethodOption" title="" style="width: 178px;">
{{#each selectedProduct.possibleAmortizationOptions}}
{{#if $ctx.number($parent.parent.data.selectedProduct.amortizationMethod)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
{{#else}}
<option value="{{=$ctx.number(id)}}">{{=value}}</option>
{{/if}}
{{/each}}
</select>
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.interest.method"/></td>
<td>
@@ -202,12 +195,33 @@
</select>
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.interest.rate.calcutated.in.period"/></td>
<td>
<select id="interestRateCalculatedInPeriod" name="interestRateCalculatedInPeriod" title="" style="width: 220px;">
{{#each selectedProduct.possibleInterestRateCalculatedInPeriodOptions}}
{{#if $ctx.number($parent.parent.data.selectedProduct.interestRateCalculatedInPeriod)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
{{#else}}
<option value="{{=$ctx.number(id)}}">{{=value}}</option>
{{/if}}
{{/each}}
</select>
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.interest.charged.from"/></td>
<td>
<input id="interestCalculatedFromDate" name="interestCalculatedFromDate" title="" type="text" class="datepickerfield" value="{{=$ctx.globalDate(interestCalculatedFromDate)}}" style="width:172px;"/>
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.arrears.tolerance"/></td>
<td>
<input id="inArrearsTolerance" name="inArrearsTolerance" title="" type="text" value="{{=$ctx.money(selectedProduct.inArrearsTolerance)}}" style="width: 125px;" />
<input id="inArrearsTolerance.currencyCode" name="inArrearsTolerance.currencyCode" title="" type="text" value="{{=selectedProduct.inArrearsTolerance.displaySymbol}}" style="width: 40px;" disabled="disabled" />
</td>
</tr>
</table>
</td>
<td>
@@ -12,12 +12,14 @@ import org.mifosng.platform.currency.domain.ApplicationCurrency;
import org.mifosng.platform.currency.domain.ApplicationCurrencyRepository;
import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.Loan;
import org.mifosng.platform.loan.domain.LoanPayoffSummary;
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
import org.mifosng.platform.loan.domain.LoanRepository;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.mifosng.platform.loanschedule.domain.AprCalculator;
import org.mifosng.platform.loanschedule.domain.DefaultLoanScheduleGeneratorFactory;
import org.mifosng.platform.loanschedule.domain.LoanScheduleGenerator;
import org.mifosng.platform.loanschedule.domain.LoanScheduleGeneratorFactory;
@@ -31,11 +33,13 @@ public class CalculationPlatformServiceImpl implements
private final LoanScheduleGeneratorFactory loanScheduleFactory;
private final LoanRepository loanRepository;
private final ApplicationCurrencyRepository applicationCurrencyRepository;
private final AprCalculator aprCalculator;
@Autowired
public CalculationPlatformServiceImpl(final LoanRepository loanRepository, final ApplicationCurrencyRepository applicationCurrencyRepository) {
public CalculationPlatformServiceImpl(final LoanRepository loanRepository, final ApplicationCurrencyRepository applicationCurrencyRepository, final AprCalculator aprCalculator) {
this.loanRepository = loanRepository;
this.applicationCurrencyRepository = applicationCurrencyRepository;
this.aprCalculator = aprCalculator;
this.loanScheduleFactory = new DefaultLoanScheduleGeneratorFactory();
}
@@ -45,70 +49,46 @@ public class CalculationPlatformServiceImpl implements
CalculateLoanScheduleCommandValidator validator = new CalculateLoanScheduleCommandValidator(command);
validator.validate();
// assemble 'loan data' into 'domain' object [LoanProductRelatedDetail]
final BigDecimal principalAmount = BigDecimal.valueOf(command.getPrincipal().doubleValue());
final BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(command.getInterestRatePerPeriod().doubleValue());
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodMethod());
final Integer repayEvery = command.getRepaymentEvery();
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequency());
final Integer defaultNumberOfInstallments = command.getNumberOfRepayments();
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
final boolean flexibleRepaymentSchedule = command.isFlexibleRepaymentSchedule();
final boolean interestRebateAllowed = command.isInterestRebateAllowed();
final BigDecimal defaultAnnualNominalInterestRate = this.aprCalculator.calculateFrom(interestPeriodFrequencyType, defaultNominalInterestRatePerPeriod);
final MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimal());
// in arrerars tolerance isnt relevant when auto-calculating a loan schedule
final BigDecimal inArrearsTolerance = BigDecimal.ZERO;
LoanProductRelatedDetail loanScheduleInfo = new LoanProductRelatedDetail(currency, principalAmount,
defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate,
interestMethod, interestCalculationPeriodMethod,
repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod,
inArrearsTolerance, flexibleRepaymentSchedule, interestRebateAllowed);
LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(interestMethod);
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneByCode(command.getCurrencyCode());
CurrencyData currencyData = new CurrencyData(applicationCurrency.getCode(), applicationCurrency.getName(), command.getDigitsAfterDecimal(),
applicationCurrency.getDisplaySymbol(), applicationCurrency.getNameCode());
MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimal());
final BigDecimal principalAmount = BigDecimal.valueOf(command.getPrincipal()
.doubleValue());
final BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(command
.getInterestRatePerPeriod().doubleValue());
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
final Integer repayEvery = command.getRepaymentEvery();
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType
.fromInt(command.getRepaymentFrequency());
final Integer defaultNumberOfInstallments = command.getNumberOfRepayments();
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
final boolean flexibleRepaymentSchedule = command.isFlexibleRepaymentSchedule();
final boolean interestRebateAllowed = command.isInterestRebateAllowed();
// apr calculator
BigDecimal defaultAnnualNominalInterestRate = BigDecimal.ZERO;
switch (interestPeriodFrequencyType) {
case DAYS:
break;
case WEEKS:
defaultAnnualNominalInterestRate = command
.getInterestRatePerPeriod()
.multiply(BigDecimal.valueOf(52));
break;
case MONTHS:
defaultAnnualNominalInterestRate = command
.getInterestRatePerPeriod()
.multiply(BigDecimal.valueOf(12));
break;
case YEARS:
defaultAnnualNominalInterestRate = command
.getInterestRatePerPeriod().multiply(BigDecimal.valueOf(1));
break;
case INVALID:
break;
}
LoanProductRelatedDetail loanScheduleInfo = new LoanProductRelatedDetail(currency, principalAmount,
defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod,
repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, BigDecimal.ZERO, flexibleRepaymentSchedule, interestRebateAllowed);
LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory
.create(interestMethod, amortizationMethod);
return loanScheduleGenerator.generate(loanScheduleInfo, new LocalDate(
command.getExpectedDisbursementDate()), command
.getRepaymentsStartingFromDate(), command.getInterestCalculatedFromDate(), currencyData);
return loanScheduleGenerator.generate(loanScheduleInfo, command.getExpectedDisbursementDate(), command.getRepaymentsStartingFromDate(),
command.getInterestCalculatedFromDate(), currencyData);
}
@Override
public LoanPayoffReadModel calculatePayoffOn(final Long loanId,
final LocalDate payoffDate) {
public LoanPayoffReadModel calculatePayoffOn(final Long loanId, final LocalDate payoffDate) {
// use jdbc approach to calculating payoff if possible.
// TODO - use jdbc approach to calculating payoff if possible.
final Loan loan = this.loanRepository.findOne(loanId);
final LoanPayoffSummary payoffSummary = loan.getPayoffSummaryOn(payoffDate);
@@ -37,6 +37,7 @@ import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.exceptions.ClientNotAuthenticatedException;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.Loan;
import org.mifosng.platform.loan.domain.LoanBuilder;
@@ -167,6 +168,7 @@ public class ImportPlatformServiceImpl implements ImportPlatformService {
}
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodMethod());
final Integer repayEvery = command.getRepaymentEvery();
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType
@@ -177,7 +179,7 @@ public class ImportPlatformServiceImpl implements ImportPlatformService {
final boolean interestRebateAllowed = command.isInterestRebateAllowed();
LoanProductRelatedDetail loanRepaymentScheduleDetail = new LoanProductRelatedDetail(currency, command.getPrincipal(),
defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod,
defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod,
repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, command.getInArrearsToleranceAmount(), flexibleRepaymentSchedule, interestRebateAllowed);
LoanSchedule loanSchedule = command.getLoanSchedule();
@@ -460,11 +462,12 @@ public class ImportPlatformServiceImpl implements ImportPlatformService {
Number interestRatePerPeriod = interestRatePerYear;
Integer interestRatePeriodFrequency = PeriodFrequencyType.YEARS.getValue();
Integer interestMethod = matchingProduct.getInterestMethod().getValue();
Integer interestCalculationPeriodMethod = InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD.getValue();
CalculateLoanScheduleCommand calculateLoanScheduleCommand = new CalculateLoanScheduleCommand(currencyCode, digitsAfterDecimal, principal,
interestRatePerPeriod,
interestRatePeriodFrequency,
interestMethod,
interestMethod, interestCalculationPeriodMethod,
repaymentEvery,
repaymentFrequency, numberOfRepayments, amortizationMethod, flexibleRepaymentSchedule,
matchingProduct.isInterestRebateAllowed(), expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
@@ -474,7 +477,7 @@ public class ImportPlatformServiceImpl implements ImportPlatformService {
SubmitLoanApplicationCommand parsedNewLoanDetails = new SubmitLoanApplicationCommand(matchingClient.getId(), matchingProduct.getId(), submittedOnDate, noNoteComment,
expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate, loanSchedule,
currencyCode, digitsAfterDecimal, principal,
interestRatePerPeriod, interestRatePeriodFrequency, interestMethod,
interestRatePerPeriod, interestRatePeriodFrequency, interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, toleranceAmount.getAmount(),
flexibleRepaymentSchedule, matchingProduct.isInterestRebateAllowed());
@@ -53,6 +53,8 @@ public interface ReadPlatformService {
List<EnumOptionReadModel> retrieveLoanAmortizationMethodOptions();
List<EnumOptionReadModel> retrieveLoanInterestMethodOptions();
List<EnumOptionReadModel> retrieveLoanInterestRateCalculatedInPeriodOptions();
List<EnumOptionReadModel> retrieveRepaymentFrequencyOptions();
@@ -749,7 +749,7 @@ public class ReadPlatformServiceImpl implements ReadPlatformService {
return "lp.id as id, lp.name as name, lp.description as description, lp.flexible_repayment_schedule as isFlexible, lp.interest_rebate as isInterestRebateAllowed, "
+ "lp.principal_amount as principal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, "
+ "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, "
+ "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, "
+ "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod,"
+ "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, "
+ "lp.amortization_method_enum as amortizationMethod, lp.arrearstolerance_amount as tolerance, "
+ "lp.created_date as createdon, lp.lastmodified_date as modifiedon "
@@ -786,6 +786,7 @@ public class ReadPlatformServiceImpl implements ReadPlatformService {
BigDecimal annualInterestRate = rs
.getBigDecimal("annualInterestRate");
int interestMethod = rs.getInt("interestMethod");
int interestCalculationInPeriodMethod = rs.getInt("interestCalculationInPeriodMethod");
int repaidEvery = rs.getInt("repaidEvery");
int repaymentFrequency = rs.getInt("repaymentPeriodFrequency");
@@ -798,7 +799,8 @@ public class ReadPlatformServiceImpl implements ReadPlatformService {
return new LoanProductData(id, name, description, isFlexible,
isInterestRebateAllowed, principalMoney,
interestRatePerPeriod, interestRatePeriod,
annualInterestRate, interestMethod, repaidEvery,
annualInterestRate, interestMethod, interestCalculationInPeriodMethod,
repaidEvery,
repaymentFrequency, numberOfRepayments, amortizationMethod,
toleranceMoney, createdOn, lastModifedOn);
}
@@ -818,7 +820,8 @@ public class ReadPlatformServiceImpl implements ReadPlatformService {
@Override
public LoanProductData retrieveLoanProduct(final Long loanProductId) {
List<CurrencyData> allowedCurrencies = retrieveAllowedCurrencies();
// TODO - switch back to retrieve all allowed organisation currencies
List<CurrencyData> allowedCurrencies = retrieveAllPlatformCurrencies();
LoanProductMapper rm = new LoanProductMapper(allowedCurrencies);
String sql = "select " + rm.loanProductSchema() + " where lp.id = ?";
@@ -838,12 +841,14 @@ public class ReadPlatformServiceImpl implements ReadPlatformService {
List<EnumOptionReadModel> possibleAmortizationOptions = retrieveLoanAmortizationMethodOptions();
List<EnumOptionReadModel> possibleInterestOptions = retrieveLoanInterestMethodOptions();
List<EnumOptionReadModel> possibleInterestRateCalculatedInPeriodOptions = retrieveLoanInterestRateCalculatedInPeriodOptions();
List<EnumOptionReadModel> repaymentFrequencyOptions = retrieveRepaymentFrequencyOptions();
List<EnumOptionReadModel> interestFrequencyOptions = retrieveInterestFrequencyOptions();
productData.setPossibleCurrencies(possibleCurrencies);
productData.setPossibleAmortizationOptions(possibleAmortizationOptions);
productData.setPossibleInterestOptions(possibleInterestOptions);
productData.setPossibleInterestRateCalculatedInPeriodOptions(possibleInterestRateCalculatedInPeriodOptions);
productData.setRepaymentFrequencyOptions(repaymentFrequencyOptions);
productData.setInterestFrequencyOptions(interestFrequencyOptions);
}
@@ -866,6 +871,7 @@ public class ReadPlatformServiceImpl implements ReadPlatformService {
productData.setInterestMethod(Integer.valueOf(0));
productData.setRepaymentPeriodFrequency(2);
productData.setInterestRatePeriod(2);
productData.setInterestRateCalculatedInPeriod(1);
productData.setRepaidEvery(1);
productData.setNumberOfRepayments(0);
@@ -942,7 +948,18 @@ public class ReadPlatformServiceImpl implements ReadPlatformService {
return allowedRepaymentScheduleCalculationMethods;
}
@Override
public List<EnumOptionReadModel> retrieveLoanInterestRateCalculatedInPeriodOptions() {
EnumOptionReadModel option1 = new EnumOptionReadModel("Daily", Long.valueOf(0));
EnumOptionReadModel option2 = new EnumOptionReadModel("Same as repayment period", Long.valueOf(1));
List<EnumOptionReadModel> allowedOptions = Arrays.asList(option1,option2);
return allowedOptions;
}
@Override
public List<EnumOptionReadModel> retrieveRepaymentFrequencyOptions() {
EnumOptionReadModel frequency1 = new EnumOptionReadModel("Days",
@@ -958,14 +975,12 @@ public class ReadPlatformServiceImpl implements ReadPlatformService {
@Override
public List<EnumOptionReadModel> retrieveInterestFrequencyOptions() {
EnumOptionReadModel perWeek = new EnumOptionReadModel("Per week",
PeriodFrequencyType.WEEKS.getValue().longValue());
// support for monthly and annual percentage rate (MPR) and (APR)
EnumOptionReadModel perMonth = new EnumOptionReadModel("Per month",
PeriodFrequencyType.MONTHS.getValue().longValue());
EnumOptionReadModel perYear = new EnumOptionReadModel("Per year",
PeriodFrequencyType.YEARS.getValue().longValue());
List<EnumOptionReadModel> repaymentFrequencyOptions = Arrays.asList(
perWeek, perMonth, perYear);
List<EnumOptionReadModel> repaymentFrequencyOptions = Arrays.asList(perMonth, perYear);
return repaymentFrequencyOptions;
}
@@ -40,7 +40,8 @@ public class SubmitLoanApplicationCommandValidator {
command.getCurrencyCode(), command.getDigitsAfterDecimal(),
command.getPrincipal(), command.getInterestRatePerPeriod(),
command.getInterestRateFrequencyMethod(),
command.getInterestMethod(), command.getRepaymentEvery(),
command.getInterestMethod(), command.getInterestCalculationPeriodMethod(),
command.getRepaymentEvery(),
command.getRepaymentFrequency(),
command.getNumberOfRepayments(),
command.getAmortizationMethod(),
@@ -54,6 +54,7 @@ import org.mifosng.platform.infrastructure.PlatformUser;
import org.mifosng.platform.infrastructure.UsernameAlreadyExistsException;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.Loan;
import org.mifosng.platform.loan.domain.LoanBuilder;
@@ -447,8 +448,8 @@ public class WritePlatformServiceJpaRepositoryImpl implements
validator.validateForCreate(command);
// assemble LoanProduct from data
InterestMethod interestMethod = InterestMethod
.fromInt(command.getInterestMethod());
InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodMethod());
AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
@@ -480,7 +481,7 @@ public class WritePlatformServiceJpaRepositoryImpl implements
LoanProduct loanproduct = new LoanProduct(currentUser.getOrganisation(), command.getName(), command.getDescription(),
currency, command.getPrincipal(),
command.getInterestRatePerPeriod(), interestFrequencyType, annualInterestRate, interestMethod,
command.getInterestRatePerPeriod(), interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod,
command.getRepaymentEvery(), repaymentFrequencyType, command.getNumberOfRepayments(), amortizationMethod, command.getInArrearsToleranceAmount(),
command.isFlexibleRepaymentSchedule(), command.isInterestRebateAllowed());
@@ -652,7 +653,8 @@ public class WritePlatformServiceJpaRepositoryImpl implements
break;
}
final InterestMethod interestMethod = InterestMethod.DECLINING_BALANCE;
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodMethod());
final Integer repayEvery = command.getRepaymentEvery();
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType
@@ -663,7 +665,8 @@ public class WritePlatformServiceJpaRepositoryImpl implements
final boolean interestRebateAllowed = command.isInterestRebateAllowed();
LoanProductRelatedDetail loanRepaymentScheduleDetail = new LoanProductRelatedDetail(currency,
command.getPrincipal(), defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod,
command.getPrincipal(), defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate,
interestMethod, interestCalculationPeriodMethod,
repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, command.getInArrearsToleranceAmount(),
flexibleRepaymentSchedule, interestRebateAllowed);
@@ -890,41 +893,32 @@ public class WritePlatformServiceJpaRepositoryImpl implements
if (loan.isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate)) {
LocalDate repaymentsStartingFromDate = loan
.getExpectedFirstRepaymentOnDate();
LocalDate repaymentsStartingFromDate = loan.getExpectedFirstRepaymentOnDate();
LocalDate interestCalculatedFromDate = loan.getInterestCalculatedFromDate();
Number principalAsDecimal = loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getAmount();
String currencyCode = loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getCurrencyCode();
int currencyDigits = loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getCurrencyDigitsAfterDecimal();
Number principalAsDecimal = loan.getLoanRepaymentScheduleDetail().getPrincipal().getAmount();
String currencyCode = loan.getLoanRepaymentScheduleDetail().getPrincipal().getCurrencyCode();
int currencyDigits = loan.getLoanRepaymentScheduleDetail().getPrincipal().getCurrencyDigitsAfterDecimal();
Number interestRatePerYear = loan.getLoanRepaymentScheduleDetail()
.getAnnualNominalInterestRate();
Integer numberOfInstallments = loan
.getLoanRepaymentScheduleDetail().getNumberOfRepayments();
Number interestRatePerYear = loan.getLoanRepaymentScheduleDetail().getAnnualNominalInterestRate();
Integer numberOfInstallments = loan.getLoanRepaymentScheduleDetail().getNumberOfRepayments();
Integer repaidEvery = loan.getLoanRepaymentScheduleDetail()
.getRepayEvery();
Integer selectedRepaymentFrequency = loan
.getLoanRepaymentScheduleDetail()
.getRepaymentPeriodFrequencyType().getValue();
Integer selectedRepaymentSchedule = loan
.getLoanRepaymentScheduleDetail()
.getAmortizationMethod().getValue();
boolean flexibleRepaymentSchedule = loan
.isFlexibleRepaymentSchedule();
Integer repaidEvery = loan.getLoanRepaymentScheduleDetail().getRepayEvery();
Integer selectedRepaymentFrequency = loan.getLoanRepaymentScheduleDetail().getRepaymentPeriodFrequencyType().getValue();
Integer selectedRepaymentSchedule = loan.getLoanRepaymentScheduleDetail().getAmortizationMethod().getValue();
boolean flexibleRepaymentSchedule = loan.isFlexibleRepaymentSchedule();
// use annual percentage rate to re-calculate loan schedule for late disbursement
Number interestRatePerPeriod = interestRatePerYear;
Integer interestRateFrequencyMethod = PeriodFrequencyType.YEARS.getValue();
Integer interestMethod = InterestMethod.DECLINING_BALANCE.getValue();
boolean interestRebateAllowed = false;
Integer interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod().getValue();
Integer interestCalculationInPeriod = loan.getLoanRepaymentScheduleDetail().getInterestCalculationPeriodMethod().getValue();
boolean interestRebateAllowed = loan.getLoanRepaymentScheduleDetail().isInterestRebateAllowed();
CalculateLoanScheduleCommand calculateCommand = new CalculateLoanScheduleCommand(currencyCode, currencyDigits, principalAsDecimal,
interestRatePerPeriod, interestRateFrequencyMethod, interestMethod, repaidEvery, selectedRepaymentFrequency, numberOfInstallments,
interestRatePerPeriod, interestRateFrequencyMethod, interestMethod, interestCalculationInPeriod,
repaidEvery, selectedRepaymentFrequency, numberOfInstallments,
selectedRepaymentSchedule, flexibleRepaymentSchedule, interestRebateAllowed, actualDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(calculateCommand);
@@ -951,8 +945,7 @@ public class WritePlatformServiceJpaRepositoryImpl implements
interest.getAmount());
modifiedLoanRepaymentSchedule.add(installment);
}
loan.disburseWithModifiedRepaymentSchedule(disbursedOn, comment,
modifiedLoanRepaymentSchedule, defaultLoanLifecycleStateMachine());
loan.disburseWithModifiedRepaymentSchedule(disbursedOn, comment, modifiedLoanRepaymentSchedule, defaultLoanLifecycleStateMachine());
} else {
loan.disburse(disbursedOn, defaultLoanLifecycleStateMachine());
}
@@ -0,0 +1,32 @@
package org.mifosng.platform.loan.domain;
public enum InterestCalculationPeriodMethod {
DAILY(0), SAME_AS_REPAYMENT_PERIOD(1), INVALID(2);
private final Integer value;
private InterestCalculationPeriodMethod(final Integer value) {
this.value = value;
}
public Integer getValue() {
return this.value;
}
public static InterestCalculationPeriodMethod fromInt(final Integer selectedMethod) {
InterestCalculationPeriodMethod repaymentMethod = null;
switch (selectedMethod) {
case 0:
repaymentMethod = InterestCalculationPeriodMethod.DAILY;
break;
case 1:
repaymentMethod = InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD;
break;
default:
repaymentMethod = InterestCalculationPeriodMethod.INVALID;
break;
}
return repaymentMethod;
}
}
@@ -50,7 +50,8 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
}
public LoanProduct(final Organisation organisation, final String name, final String description, final MonetaryCurrency currency, final BigDecimal defaultPrincipal,
final BigDecimal defaultNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType, final BigDecimal defaultAnnualNominalInterestRate, final InterestMethod interestMethod,
final BigDecimal defaultNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType, final BigDecimal defaultAnnualNominalInterestRate,
final InterestMethod interestMethod, final InterestCalculationPeriodMethod interestCalculationPeriodMethod,
final Integer repayEvery, final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfInstallments, final AmortizationMethod amortizationMethod,
final BigDecimal inArrearsTolerance,
final boolean flexibleRepaymentSchedule,
@@ -64,7 +65,8 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
}
this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency,
defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod,
defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate,
interestMethod, interestCalculationPeriodMethod,
repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, inArrearsTolerance, flexibleRepaymentSchedule, interestRebateAllowed);
}
@@ -39,6 +39,10 @@ public class LoanProductRelatedDetail implements
@Enumerated(EnumType.ORDINAL)
@Column(name = "interest_method_enum", nullable = false)
private InterestMethod interestMethod;
@Enumerated(EnumType.ORDINAL)
@Column(name = "interest_calculated_in_period_enum", nullable = false)
private InterestCalculationPeriodMethod interestCalculationPeriodMethod;
@Column(name = "repay_every", nullable = false)
private Integer repayEvery;
@@ -85,7 +89,9 @@ public class LoanProductRelatedDetail implements
final BigDecimal defaultNominalInterestRatePerPeriod,
final PeriodFrequencyType interestPeriodFrequencyType,
final BigDecimal defaultAnnualNominalInterestRate,
final InterestMethod interestMethod, final Integer repayEvery,
final InterestMethod interestMethod,
final InterestCalculationPeriodMethod interestCalculationPeriodMethod,
final Integer repayEvery,
final PeriodFrequencyType repaymentFrequencyType,
final Integer defaultNumberOfRepayments,
final AmortizationMethod amortizationMethod,
@@ -98,6 +104,7 @@ public class LoanProductRelatedDetail implements
this.interestPeriodFrequencyType = interestPeriodFrequencyType;
this.annualNominalInterestRate = defaultAnnualNominalInterestRate;
this.interestMethod = interestMethod;
this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
this.repayEvery = repayEvery;
this.repaymentPeriodFrequencyType = repaymentFrequencyType;
this.numberOfRepayments = defaultNumberOfRepayments;
@@ -135,6 +142,10 @@ public class LoanProductRelatedDetail implements
return interestMethod;
}
public InterestCalculationPeriodMethod getInterestCalculationPeriodMethod() {
return interestCalculationPeriodMethod;
}
@Override
public Integer getRepayEvery() {
return repayEvery;
@@ -175,5 +186,6 @@ public class LoanProductRelatedDetail implements
this.nominalInterestRatePerPeriod = command.getInterestRatePerPeriod();
this.interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
this.interestMethod = InterestMethod.fromInt(command.getInterestMethod());
this.interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodMethod());
}
}
@@ -0,0 +1,32 @@
package org.mifosng.platform.loanschedule.domain;
import java.math.BigDecimal;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.springframework.stereotype.Component;
@Component
public class AprCalculator {
public BigDecimal calculateFrom(PeriodFrequencyType interestPeriodFrequencyType, BigDecimal interestRatePerPeriod) {
BigDecimal defaultAnnualNominalInterestRate = BigDecimal.ZERO;
switch (interestPeriodFrequencyType) {
case DAYS:
break;
case WEEKS:
defaultAnnualNominalInterestRate = interestRatePerPeriod.multiply(BigDecimal.valueOf(52));
break;
case MONTHS:
defaultAnnualNominalInterestRate = interestRatePerPeriod.multiply(BigDecimal.valueOf(12));
break;
case YEARS:
defaultAnnualNominalInterestRate = interestRatePerPeriod.multiply(BigDecimal.valueOf(1));
break;
case INVALID:
break;
}
return defaultAnnualNominalInterestRate;
}
}
@@ -1,107 +0,0 @@
package org.mifosng.platform.loanschedule.domain;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosng.data.CurrencyData;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.MoneyData;
import org.mifosng.data.ScheduledLoanInstallment;
import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.exceptions.NoAuthorizationException;
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
public class DecliningBalanceLoanScheduleGenerator implements
LoanScheduleGenerator {
private final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
@Override
public LoanSchedule generate(
final LoanProductRelatedDetail loanScheduleInfo,
final LocalDate disbursementDate, final LocalDate firstRepaymentDate,
LocalDate interestCalculatedFrom, CurrencyData currencyData) {
if (firstRepaymentDate != null
&& disbursementDate.isAfter(firstRepaymentDate)) {
throw new NoAuthorizationException(
"cannot have disbursement date after first repayment date.");
}
List<ScheduledLoanInstallment> scheduledLoanInstallments = new ArrayList<ScheduledLoanInstallment>();
List<LocalDate> scheduledDates = this.scheduledDateGenerator.generate(
loanScheduleInfo, disbursementDate, firstRepaymentDate);
MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
Money principalPerInstallment = loanScheduleInfo.getPrincipal()
.dividedBy(loanScheduleInfo.getNumberOfRepayments(),
RoundingMode.HALF_EVEN);
BigDecimal paymentPeriodsInYear = BigDecimal
.valueOf(this.paymentPeriodsInOneYearCalculator
.calculate(loanScheduleInfo.getRepaymentPeriodFrequencyType()));
BigDecimal periodicInterestRate = loanScheduleInfo
.getAnnualNominalInterestRate()
.divide(paymentPeriodsInYear, mc)
.divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc)
.multiply(BigDecimal.valueOf(loanScheduleInfo.getRepayEvery()));
Money outstandingBalance = loanScheduleInfo.getPrincipal();
Money totalPrincipal = Money.zero(outstandingBalance.getCurrency());
LocalDate startDate = disbursementDate;
int installmentNumber = 1;
for (LocalDate scheduledDueDate : scheduledDates) {
totalPrincipal = totalPrincipal.plus(principalPerInstallment);
Money interestPerInstallment = outstandingBalance
.multiplyRetainScale(periodicInterestRate,
RoundingMode.HALF_EVEN);
if (installmentNumber == loanScheduleInfo.getNumberOfRepayments()) {
Money difference = totalPrincipal.minus(loanScheduleInfo
.getPrincipal());
if (difference.isLessThanZero()) {
principalPerInstallment = principalPerInstallment
.plus(difference.abs());
} else if (difference.isGreaterThanZero()) {
principalPerInstallment = principalPerInstallment
.minus(difference.abs());
}
}
Money totalInstallmentDue = principalPerInstallment.plus(interestPerInstallment);
outstandingBalance = outstandingBalance
.minus(principalPerInstallment);
MoneyData principalPerInstallmentValue = MoneyData.of(currencyData, principalPerInstallment.getAmount());
MoneyData interestPerInstallmentValue = MoneyData.of(currencyData, interestPerInstallment.getAmount());
MoneyData totalInstallmentDueValue = MoneyData.of(currencyData, totalInstallmentDue.getAmount());
MoneyData outstandingBalanceValue = MoneyData.of(currencyData, outstandingBalance.getAmount());
ScheduledLoanInstallment installment = new ScheduledLoanInstallment(
Integer.valueOf(installmentNumber), startDate,
scheduledDueDate, principalPerInstallmentValue,
interestPerInstallmentValue,
totalInstallmentDueValue,
outstandingBalanceValue);
scheduledLoanInstallments.add(installment);
startDate = scheduledDueDate;
installmentNumber++;
}
return new LoanSchedule(scheduledLoanInstallments);
}
}
@@ -1,11 +1,10 @@
package org.mifosng.platform.loanschedule.domain;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.mifosng.data.CurrencyData;
import org.mifosng.data.LoanSchedule;
@@ -13,14 +12,17 @@ import org.mifosng.data.MoneyData;
import org.mifosng.data.ScheduledLoanInstallment;
import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
public class DecliningBalanceEqualInstallmentsLoanScheduleGenerator implements
public class DecliningBalanceMethodLoanScheduleGenerator implements
LoanScheduleGenerator {
private final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
private final PeriodicInterestRateCalculator periodicInterestRateCalculator = new PeriodicInterestRateCalculator();
private final PmtCalculator pmtCalculator = new PmtCalculator();
@Override
public LoanSchedule generate(
final LoanProductRelatedDetail loanScheduleInfo,
@@ -30,93 +32,76 @@ public class DecliningBalanceEqualInstallmentsLoanScheduleGenerator implements
List<ScheduledLoanInstallment> scheduledLoanInstallments = new ArrayList<ScheduledLoanInstallment>();
List<LocalDate> scheduledDates = this.scheduledDateGenerator.generate(
loanScheduleInfo, disbursementDate, firstRepaymentDate);
// 1. generate valid set of 'due dates' based on some of the 'loan attributes'
List<LocalDate> scheduledDates = this.scheduledDateGenerator.generate(loanScheduleInfo, disbursementDate, firstRepaymentDate);
LocalDate idealDisbursementDateBasedOnFirstRepaymentDate = this.scheduledDateGenerator.idealDisbursementDateBasedOnFirstRepaymentDate(loanScheduleInfo, scheduledDates);
MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
BigDecimal paymentPeriodsInYear = BigDecimal
.valueOf(this.paymentPeriodsInOneYearCalculator.calculate(loanScheduleInfo.getRepaymentPeriodFrequencyType()));
BigDecimal periodicInterestRate = loanScheduleInfo
.getAnnualNominalInterestRate()
.divide(paymentPeriodsInYear, mc)
.divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc)
.multiply(BigDecimal.valueOf(loanScheduleInfo.getRepayEvery()));
double interestRateFraction = periodicInterestRate.doubleValue();
double futureValue = 0;
double numberOfPeriods = loanScheduleInfo.getNumberOfRepayments().doubleValue();
double principal = loanScheduleInfo.getPrincipal().getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
double paymentPerInstallment = pmt(interestRateFraction, numberOfPeriods, principal, futureValue, false);
double totalRepayment = paymentPerInstallment * numberOfPeriods;
// 2. determine the 'periodic' interest rate based on the 'repayment periods' so we can use
final BigDecimal periodInterestRateForRepaymentPeriod = this.periodicInterestRateCalculator.calculateFrom(loanScheduleInfo);
// 3. determine 'total payment' for each repayment based on pmt function (and hence the total due overall)
final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency();
Money totalDuePerInstallment = Money.of(monetaryCurrency, BigDecimal.valueOf(paymentPerInstallment));
Money totalDue = Money.of(monetaryCurrency, BigDecimal.valueOf(totalRepayment));
final Money totalDuePerInstallment = this.pmtCalculator.calculatePaymentForOnePeriodFrom(loanScheduleInfo, periodInterestRateForRepaymentPeriod, monetaryCurrency);
final Money totalDue = this.pmtCalculator.calculateTotalRepaymentFrom(loanScheduleInfo, periodInterestRateForRepaymentPeriod, monetaryCurrency);
Money totalInterestDue = totalDue.minus(loanScheduleInfo.getPrincipal());
Money outstandingBalance = loanScheduleInfo.getPrincipal();
Money totalPrincipal = Money.zero(monetaryCurrency);
Money totalInterest = Money.zero(monetaryCurrency);
LocalDate idealDisbursementDateBasedOnFirstRepaymentDate = this.scheduledDateGenerator.idealDisbursementDateBasedOnFirstRepaymentDate(loanScheduleInfo, scheduledDates);
double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator.calculateRepaymentPeriodAsAFractionOfDays(loanScheduleInfo.getRepaymentPeriodFrequencyType(),
loanScheduleInfo.getRepayEvery(), interestCalculatedFrom, scheduledDates, idealDisbursementDateBasedOnFirstRepaymentDate);
LocalDate startDate = disbursementDate;
int installmentNumber = 1;
for (LocalDate scheduledDueDate : scheduledDates) {
Money interestPerInstallment = outstandingBalance.multiplyRetainScale(periodicInterestRate, RoundingMode.HALF_EVEN);
Money principalPerInstallment = totalDuePerInstallment.minus(interestPerInstallment);
// number of days from startDate to this scheduledDate
int daysInPeriod = Days.daysBetween(startDate.toDateMidnight().toDateTime(), scheduledDueDate.toDateMidnight().toDateTime()).getDays();
Money interestForInstallment = this.periodicInterestRateCalculator.calculateInterestOn(outstandingBalance, periodInterestRateForRepaymentPeriod, daysInPeriod, loanScheduleInfo);
Money principalForInstallment = this.periodicInterestRateCalculator.calculatePrincipalOn(totalDuePerInstallment, interestForInstallment, loanScheduleInfo);
if (interestCalculationGraceOnRepaymentPeriodFraction >= Integer.valueOf(1).doubleValue()) {
Money graceOnInterestForRepaymentPeriod = interestPerInstallment;
interestPerInstallment = interestPerInstallment.minus(graceOnInterestForRepaymentPeriod);
Money graceOnInterestForRepaymentPeriod = interestForInstallment;
interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod);
totalInterestDue = totalInterestDue.minus(graceOnInterestForRepaymentPeriod);
interestCalculationGraceOnRepaymentPeriodFraction = interestCalculationGraceOnRepaymentPeriodFraction - Integer.valueOf(1).doubleValue();
} else if (interestCalculationGraceOnRepaymentPeriodFraction > Double.valueOf("0.25") && interestCalculationGraceOnRepaymentPeriodFraction < Integer.valueOf(1).doubleValue()) {
Money graceOnInterestForRepaymentPeriod = interestPerInstallment.multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction);
interestPerInstallment = interestPerInstallment.minus(graceOnInterestForRepaymentPeriod);
Money graceOnInterestForRepaymentPeriod = interestForInstallment.multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction);
interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod);
totalInterestDue = totalInterestDue.minus(graceOnInterestForRepaymentPeriod);
interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf("0");
}
totalPrincipal = totalPrincipal.plus(principalPerInstallment);
totalInterest = totalInterest.plus(interestPerInstallment);
totalPrincipal = totalPrincipal.plus(principalForInstallment);
totalInterest = totalInterest.plus(interestForInstallment);
if (installmentNumber == loanScheduleInfo.getNumberOfRepayments()) {
Money principalDifference = totalPrincipal.minus(loanScheduleInfo
.getPrincipal());
Money principalDifference = totalPrincipal.minus(loanScheduleInfo.getPrincipal());
if (principalDifference.isLessThanZero()) {
principalPerInstallment = principalPerInstallment
.plus(principalDifference.abs());
principalForInstallment = principalForInstallment.plus(principalDifference.abs());
} else if (principalDifference.isGreaterThanZero()) {
principalPerInstallment = principalPerInstallment
.minus(principalDifference.abs());
principalForInstallment = principalForInstallment.minus(principalDifference.abs());
}
Money interestDifference = totalInterest.minus(totalInterestDue);
if (interestDifference.isLessThanZero()) {
interestPerInstallment = interestPerInstallment
.plus(interestDifference.abs());
} else if (interestDifference.isGreaterThanZero()) {
interestPerInstallment = interestPerInstallment
.minus(interestDifference.abs());
if (loanScheduleInfo.getAmortizationMethod().equals(
AmortizationMethod.EQUAL_INSTALLMENTS)) {
Money interestDifference = totalInterest.minus(totalInterestDue);
if (interestDifference.isLessThanZero()) {
interestForInstallment = interestForInstallment.plus(interestDifference.abs());
} else if (interestDifference.isGreaterThanZero()) {
interestForInstallment = interestForInstallment.minus(interestDifference.abs());
}
}
}
Money totalInstallmentDue = principalPerInstallment.plus(interestPerInstallment);
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment);
outstandingBalance = outstandingBalance.minus(principalPerInstallment);
outstandingBalance = outstandingBalance.minus(principalForInstallment);
MoneyData principalPerInstallmentValue = MoneyData.of(currencyData, principalPerInstallment.getAmount());
MoneyData interestPerInstallmentValue = MoneyData.of(currencyData, interestPerInstallment.getAmount());
MoneyData principalPerInstallmentValue = MoneyData.of(currencyData, principalForInstallment.getAmount());
MoneyData interestPerInstallmentValue = MoneyData.of(currencyData, interestForInstallment.getAmount());
MoneyData totalInstallmentDueValue = MoneyData.of(currencyData, totalInstallmentDue.getAmount());
MoneyData outstandingBalanceValue = MoneyData.of(currencyData, outstandingBalance.getAmount());
@@ -1,46 +1,26 @@
package org.mifosng.platform.loanschedule.domain;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
public class DefaultLoanScheduleGeneratorFactory implements
LoanScheduleGeneratorFactory {
@Override
public LoanScheduleGenerator create(final InterestMethod interestMethod,
AmortizationMethod amortizationMethod) {
public LoanScheduleGenerator create(final InterestMethod interestMethod) {
LoanScheduleGenerator loanScheduleGenerator = null;
switch (amortizationMethod) {
case EQUAL_PRINCIPAL:
switch (interestMethod) {
case FLAT:
loanScheduleGenerator = new FlatLoanScheduleGenerator();
break;
case DECLINING_BALANCE:
loanScheduleGenerator = new DecliningBalanceLoanScheduleGenerator();
break;
case INVALID:
break;
}
switch (interestMethod) {
case FLAT:
loanScheduleGenerator = new FlatLoanScheduleGenerator();
break;
case EQUAL_INSTALLMENTS:
switch (interestMethod) {
case FLAT:
loanScheduleGenerator = new FlatLoanScheduleGenerator();
break;
case DECLINING_BALANCE:
loanScheduleGenerator = new DecliningBalanceEqualInstallmentsLoanScheduleGenerator();
break;
case INVALID:
break;
}
case DECLINING_BALANCE:
loanScheduleGenerator = new DecliningBalanceMethodLoanScheduleGenerator();
break;
case INVALID:
break;
}
return loanScheduleGenerator;
}
}
@@ -1,10 +1,9 @@
package org.mifosng.platform.loanschedule.domain;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
public interface LoanScheduleGeneratorFactory {
LoanScheduleGenerator create(InterestMethod interestMethod, AmortizationMethod amortizationMethod);
LoanScheduleGenerator create(InterestMethod interestMethod);
}
@@ -0,0 +1,67 @@
package org.mifosng.platform.loanschedule.domain;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
public class PeriodicInterestRateCalculator {
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
public BigDecimal calculateFrom(LoanProductRelatedDetail loanScheduleInfo) {
MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
Integer paymentPeriodsInOneYear = this.paymentPeriodsInOneYearCalculator.calculate(loanScheduleInfo.getRepaymentPeriodFrequencyType());
BigDecimal divisor = BigDecimal.valueOf(paymentPeriodsInOneYear * 100);
BigDecimal numberOfPeriods = BigDecimal.valueOf(loanScheduleInfo.getRepayEvery());
return loanScheduleInfo.getAnnualNominalInterestRate().divide(divisor, mc).multiply(numberOfPeriods);
}
public Money calculateInterestOn(Money outstandingBalance, BigDecimal periodInterestRateForRepaymentPeriod,
int daysInPeriod,
LoanProductRelatedDetail loanScheduleInfo) {
MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
Money interestDue = Money.zero(outstandingBalance.getCurrency());
switch (loanScheduleInfo.getInterestCalculationPeriodMethod()) {
case DAILY:
BigDecimal dailyInterestRate = loanScheduleInfo.getAnnualNominalInterestRate()
.divide(BigDecimal.valueOf(Long.valueOf(365)), mc)
.divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc)
.multiply(BigDecimal.valueOf(loanScheduleInfo.getRepayEvery()));
BigDecimal equivalentInterestRateForPeriod = dailyInterestRate.multiply(BigDecimal.valueOf(Long.valueOf(daysInPeriod)));
interestDue = outstandingBalance.multiplyRetainScale(equivalentInterestRateForPeriod, RoundingMode.HALF_EVEN);
break;
default:
interestDue = outstandingBalance.multiplyRetainScale(periodInterestRateForRepaymentPeriod, RoundingMode.HALF_EVEN);
break;
}
return interestDue;
}
public Money calculatePrincipalOn(Money totalDuePerInstallment, Money interestForInstallment, LoanProductRelatedDetail loanScheduleInfo) {
Money principalDue = Money.zero(totalDuePerInstallment.getCurrency());
switch (loanScheduleInfo.getAmortizationMethod()) {
case EQUAL_PRINCIPAL:
principalDue = loanScheduleInfo.getPrincipal().dividedBy(loanScheduleInfo.getNumberOfRepayments(), RoundingMode.HALF_EVEN);
break;
case EQUAL_INSTALLMENTS:
principalDue = totalDuePerInstallment.minus(interestForInstallment);
break;
}
return principalDue;
}
}
@@ -0,0 +1,72 @@
package org.mifosng.platform.loanschedule.domain;
import java.math.BigDecimal;
import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
public class PmtCalculator {
public Money calculatePaymentForOnePeriodFrom(LoanProductRelatedDetail loanScheduleInfo, BigDecimal periodInterestRateForRepaymentPeriod, MonetaryCurrency monetaryCurrency) {
// TODO - check if going to doubles is a problem
double interestRateFraction = periodInterestRateForRepaymentPeriod.doubleValue();
double futureValue = 0;
double numberOfPeriods = loanScheduleInfo.getNumberOfRepayments().doubleValue();
double principal = loanScheduleInfo.getPrincipal().getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
// work out the total payment (principal + interest components) for each installment due.
// use the period type and interest as expressed e.g. 2% per month or 24% per year even is 'daily' interest calculation is selected
double paymentPerInstallment = pmt(interestRateFraction, numberOfPeriods, principal, futureValue, false);
return Money.of(monetaryCurrency, BigDecimal.valueOf(paymentPerInstallment));
}
public Money calculateTotalRepaymentFrom(LoanProductRelatedDetail loanScheduleInfo, BigDecimal periodInterestRateForRepaymentPeriod, MonetaryCurrency monetaryCurrency) {
// TODO - check if going to doubles is a problem
double interestRateFraction = periodInterestRateForRepaymentPeriod.doubleValue();
double futureValue = 0;
double numberOfPeriods = loanScheduleInfo.getNumberOfRepayments().doubleValue();
double principal = loanScheduleInfo.getPrincipal().getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
// work out the total payment (principal + interest components) for each installment due.
// use the period type and interest as expressed e.g. 2% per month or 24% per year even is 'daily' interest calculation is selected
double paymentPerInstallment = pmt(interestRateFraction, numberOfPeriods, principal, futureValue, false);
double totalRepayment = paymentPerInstallment * loanScheduleInfo.getNumberOfRepayments();
return Money.of(monetaryCurrency, BigDecimal.valueOf(totalRepayment));
}
/**
* PMT calculates a fixed monthly payment to be paid by borrower every 'period' to ensure loan is paid off in full (with interest).
*
* This monthly payment c depends upon
* the monthly interest rate r (expressed as a fraction, not a percentage,
* i.e., divide the quoted yearly percentage rate by 100 and by 12 to obtain
* the monthly interest rate), the number of monthly payments N called the
* loan's term, and the amount borrowed P known as the loan's principal; c
* is given by the formula:
*
* c = (r / (1 - (1 + r)^-N))P
*
* @param interestRateFraction
* @param numberOfPayments
* @param principal
* @param futureValue
* @param type
*/
private static double pmt(double interestRateFraction, double numberOfPayments, double principal, double futureValue, boolean type) {
double payment = 0;
if (interestRateFraction == 0) {
payment = -1 * (futureValue + principal) / numberOfPayments;
} else {
double r1 = interestRateFraction + 1;
payment = (futureValue + principal * Math.pow(r1, numberOfPayments)) * interestRateFraction
/ ((type ? r1 : 1) * (1 - Math.pow(r1, numberOfPayments)));
}
return payment;
}
}
@@ -20,7 +20,7 @@ public class PmtTest {
double futureValue = Double.valueOf("0");
boolean type = false;
double payment = DecliningBalanceEqualInstallmentsLoanScheduleGenerator.pmt(interestRateFraction, numberOfPayments, principal, futureValue, type);
double payment = DecliningBalanceMethodLoanScheduleGenerator.pmt(interestRateFraction, numberOfPayments, principal, futureValue, type);
assertThat(payment, is(Double.valueOf("13436.317553939027")));
}