Rate Module (#3000)

New pages were added for Rate definition and rates can be added in loan products and accounts

Co-authored-by: Angel Cajas <angel.cajas@bowpi.com>
This commit is contained in:
Angel Cajas 2020-08-24 14:18:15 -06:00 committed by GitHub
parent 0aa4ad574f
commit ca5bc6325a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 985 additions and 24 deletions

View File

@ -4232,7 +4232,7 @@
"label.createdselfserviceuser": "Self Service User created successfully",
"validation.msg.client.dateOfBirth.is.greater.than.date" : "Client date of birth can not be greater than the client submitted on date.",
"label.input.maturity.instruction" : "Maturity Instruction",
"label.input.apply.maturity.instructions" :"Add Maturity Instructions",
"label.input.transfer.to.account" : "Transfer To Savings Account",
@ -4246,5 +4246,16 @@
"error.msg.user.self.service.user.already.exist" : "Self Service User ID is already created. Go to Admin -> Users to edit or delete the self-service user.",
"label.anchor.rates": "Rates",
"label.heading.rates": "Rates",
"label.rates" : "Define rates for loan products, savings and deposit products.",
"label.button.createrate" : "Create Rate",
"label.anchor.createrate" : "Create Rate",
"label.heading.rateappliesto" : "Rate applies to",
"label.anchor.editrate": "Edit Rate",
"label.error.rate.already.exist" : "Rate already exist.",
"label.selectrate" : "Select Rate",
"----End---": "--End of file--- "
}

View File

@ -4007,5 +4007,15 @@
"label.input.emi.change.upto": "Fecha de finalización de cambio de EMI",
"label.input.changeEMI": "Cambiar EMI",
"label.input.installment.date": "Fecha de abono",
"#Rates":"...",
"label.anchor.rates": "Tasas",
"label.heading.rates": "Tasas",
"label.rates" : "Definir tasas para el Producto-préstamo, ahorros y depósitos de productos.",
"label.button.createrate" : "Crear Tasa",
"label.anchor.createrate" : "Crear Tasa",
"label.heading.rateappliesto" : "Tasa aplica a",
"label.anchor.editrate": "Editar Tasa",
"label.error.rate.already.exist" : "Tasa ya existe.",
"label.selectrate" : "Seleccione Tasa",
"----End---": "---Fin del archivo---"
}

View File

@ -9,6 +9,7 @@
scope.collaterals = [];
scope.restrictDate = new Date();
scope.date = {};
scope.rateFlag = false;
resourceFactory.loanResource.get({loanId: routeParams.id, template: true, associations: 'charges,collateral,meeting,multiDisburseDetails',staffInSelectedOfficeOnly:true}, function (data) {
scope.loanaccountinfo = data;
@ -51,6 +52,7 @@
}
scope.previewClientLoanAccInfo();
scope.ratesEnabled= scope.loanaccountinfo.isRatesEnabled;
});
@ -162,7 +164,66 @@
}
scope.formData.interestRateDifferential = scope.loanaccountinfo.interestRateDifferential ;
scope.formData.isFloatingInterestRate = scope.loanaccountinfo.isFloatingInterestRate ;
}
//Load Rates information
scope.formData.rates = scope.loanaccountinfo.rates;
scope.firstChange = false;
scope.rateOptions = scope.loanaccountinfo.product.rates.filter(function(rate){
var exist = false;
scope.formData.rates.forEach(function(addedRate){
if(rate.id === addedRate.id){
exist = true;
}
});
return !exist;
});
if (scope.formData.rates && scope.formData.rates.length>0){
scope.rateFlag=true;
}else{
scope.rateFlag=false;
}
};
//Rate
scope.rateSelected = function(currentRate){
if(currentRate && !scope.checkIfRateAlreadyExist(currentRate)){
scope.rateFlag=true;
scope.formData.rates.push(currentRate);
scope.rateOptions.splice(scope.rateOptions.indexOf(currentRate),1);
scope.currentRate = '';
currentRate = '';
scope.calculateRates();
}
};
scope.checkIfRateAlreadyExist = function(currentRate){
var exist = false;
scope.formData.rates.forEach(function(rate){
if(rate.id === currentRate.id){
exist = true;
}
});
return exist;
};
scope.calculateRates = function(){
var total = 0;
scope.formData.rates.forEach(function(rate){
total += rate.percentage;
});
if (total===0){
scope.rateFlag=false;
total=undefined;
}
scope.formData.interestRatePerPeriod = total;
};
scope.deleteRate = function (index){
scope.rateOptions.push(scope.formData.rates[index]);
scope.formData.rates.splice(index,1);
scope.calculateRates();
};
scope.addCharge = function () {
if (scope.chargeFormData.chargeId) {

View File

@ -1,6 +1,6 @@
(function (module) {
mifosX.controllers = _.extend(module, {
NewLoanAccAppController: function (scope, routeParams, resourceFactory, location, dateFilter, uiConfigService, WizardHandler) {
NewLoanAccAppController: function (scope, routeParams, resourceFactory, location, dateFilter, uiConfigService, WizardHandler, translate) {
scope.previewRepayment = false;
scope.clientId = routeParams.clientId;
scope.groupId = routeParams.groupId;
@ -22,6 +22,8 @@
scope.customSteps = [];
scope.tempDataTables = [];
scope.disabled = true;
scope.translate= translate;
scope.rateFlag=false;
scope.date.first = new Date();
@ -50,6 +52,7 @@
resourceFactory.loanResource.get(scope.inparams, function (data) {
scope.products = data.productOptions;
scope.ratesEnabled = data.isRatesEnabled;
if (data.clientName) {
scope.clientName = data.clientName;
@ -181,9 +184,58 @@
scope.loandetails = angular.copy(scope.formData);
scope.loandetails.productName = scope.formValue(scope.products,scope.formData.productId,'id','name');
scope.formData.rates = scope.loanaccountinfo.product.rates;
if (scope.formData.rates && scope.formData.rates.length>0){
scope.rateFlag=true;
}
scope.rateOptions = [];
};
scope.$watch('formData',function(newVal){
//Rate
scope.rateSelected = function(currentRate){
if(currentRate && !scope.checkIfRateAlreadyExist(currentRate)){
scope.rateFlag=true;
scope.formData.rates.push(currentRate);
scope.rateOptions.splice(scope.rateOptions.indexOf(currentRate),1);
scope.currentRate = '';
currentRate = '';
scope.calculateRates();
}
};
scope.checkIfRateAlreadyExist = function(currentRate){
var exist = false;
scope.formData.rates.forEach(function(rate){
if(rate.id === currentRate.id){
exist = true;
}
});
return exist
};
scope.calculateRates = function(){
var total = 0;
scope.formData.rates.forEach(function(rate){
total += rate.percentage;
});
if (total===0){
total=undefined;
scope.rateFlag=false;
}
scope.formData.interestRatePerPeriod = total;
};
scope.deleteRate = function (index){
scope.rateOptions.push(scope.formData.rates[index]);
scope.formData.rates.splice(index,1);
scope.calculateRates();
};
scope.$watch('formData',function(newVal){
scope.loandetails = angular.extend(scope.loandetails,newVal);
},true);
@ -416,7 +468,7 @@
}
}
});
mifosX.ng.application.controller('NewLoanAccAppController', ['$scope', '$routeParams', 'ResourceFactory', '$location', 'dateFilter', 'UIConfigService', 'WizardHandler', mifosX.controllers.NewLoanAccAppController]).run(function ($log) {
mifosX.ng.application.controller('NewLoanAccAppController', ['$scope', '$routeParams', 'ResourceFactory', '$location', 'dateFilter', 'UIConfigService', 'WizardHandler', '$translate',mifosX.controllers.NewLoanAccAppController]).run(function ($log) {
$log.info("NewLoanAccAppController initialized");
});
}(mifosX.controllers || {}));

View File

@ -12,6 +12,7 @@
scope.loandetails = [];
scope.routeTo = function (loanId, transactionId, transactionTypeId) {
if (transactionTypeId == 2 || transactionTypeId == 4 || transactionTypeId == 1) {
$rootScope.rates = scope.loandetails.rates;
location.path('/viewloantrxn/' + loanId + '/trxnId/' + transactionId);
};
};

View File

@ -1,12 +1,39 @@
(function (module) {
mifosX.controllers = _.extend(module, {
ViewLoanTransactionController: function (scope, resourceFactory, location, routeParams, dateFilter, $uibModal) {
ViewLoanTransactionController: function (scope, resourceFactory, location, routeParams, dateFilter, $uibModal, $rootScope) {
scope.details = [];
//Get loan rates to be defined in transaction details
scope.rates = $rootScope.rates;
//Obtain total rate percentage
scope.totalRatePercentage = 0;
if (scope.rates){
scope.rates.forEach(function (rate) {
scope.totalRatePercentage += (rate.percentage/100);
});
}
//get Tax from configuration
scope.tax = 0;
resourceFactory.configurationResource.get(function (data) {
for (var i in data.globalConfiguration) {
if('vat-tax' === data.globalConfiguration[i].name){
scope.tax = (data.globalConfiguration[i].value/100);
break;
}
}
for (var i in data.globalConfiguration) {
if('sub-rates' === data.globalConfiguration[i].name){
scope.ratesEnabled = (data.globalConfiguration[i].value);
break;
}
}
});
resourceFactory.loanTrxnsResource.get({loanId: routeParams.accountId, transactionId: routeParams.id}, function (data) {
scope.transaction = data;
scope.transaction.accountId = routeParams.accountId;
scope.generateDetailTable();
});
scope.undo = function (accountId, transactionId) {
$uibModal.open({
templateUrl: 'undotransaction.html',
@ -36,10 +63,118 @@
$uibModalInstance.dismiss('cancel');
};
};
scope.generateDetailTable = function () {
//add principal amount
var principalDetail = {
description: 'label.view.principalpaymentdetail',
containsAmount: true,
boldTitle: true,
align: 'left',
amount: scope.transaction.principalPortion.toFixed(3)
};
scope.details.push(principalDetail);
//Check for interest details
var rateHeader = {
description: 'label.view.interestspayment',
containsAmount: scope.rates? false : true,
boldTitle: true,
amount: scope.rates? undefined : scope.transaction.interestPortion.toFixed(3)
};
scope.details.push(rateHeader);
if (scope.ratesEnabled && scope.rates) {
scope.rates.forEach(function (rate) {
var rateDetail = {
description: rate.name,
containsAmount: true,
boldTitle: false,
amount: (((scope.transaction.interestPortion * (rate.percentage / 100)) / (scope.totalRatePercentage))
/ (1 + (scope.tax ? scope.tax : 0))).toFixed(3)
};
scope.details.push(rateDetail);
if (scope.tax) {
var rateTaxDetail = {
description: 'IVA',
containsAmount: true,
boldTitle: false,
amount: (rateDetail.amount * scope.tax).toFixed(3)
};
scope.details.push(rateTaxDetail);
}
});
//Set total amount for rates
var totalRateDetail = {
description: 'label.view.interestspaymentTotal',
containsAmount: true,
boldTitle: true,
isTotal: true,
align: 'right',
amount: scope.transaction.interestPortion.toFixed(3)
};
scope.details.push(totalRateDetail);
}
//Calculate total amount por charges
scope.availableCharges = {};
if (scope.transaction.loanChargePaidByList) {
scope.transaction.loanChargePaidByList.forEach(function (data) {
var chargePaidBy = {
id: data['id'],
amount: data['amount'],
type: data['name']
};
if (scope.availableCharges.hasOwnProperty(chargePaidBy.type)) {
scope.availableCharges[chargePaidBy.type] = (scope.availableCharges[chargePaidBy.type]
+ chargePaidBy.amount);
} else {
scope.availableCharges[chargePaidBy.type] = chargePaidBy.amount;
}
});
}
//Add charge header
if (Object.keys(scope.availableCharges).length >= 1) {
var chargeHeaderDetail = {
description: 'label.input.charges',
containsAmount: false,
boldTitle: true
};
scope.details.push(chargeHeaderDetail);
}
for (var key in scope.availableCharges) {
var chargeDetail = {
description: key,
containsAmount: true,
boldTitle: false,
amount: (scope.availableCharges[key].toFixed(3) / (1
+ scope.tax ? scope.tax :0)).toFixed(3)
};
scope.details.push(chargeDetail);
if (scope.tax) {
var chargeTaxDetail = {
description: 'IVA',
containsAmount: true,
boldTitle: false,
amount: (chargeDetail.amount * scope.tax).toFixed(3)
};
scope.details.push(chargeTaxDetail);
}
}
if (Object.keys(scope.availableCharges).length >= 1) {
var chargeTotalDetail = {
description: 'Total',
containsAmount: true,
boldTitle: true,
align: 'right',
amount: scope.transaction.penaltyChargesPortion.toFixed(3)
};
scope.details.push(chargeTotalDetail);
}
};
}
});
mifosX.ng.application.controller('ViewLoanTransactionController', ['$scope', 'ResourceFactory', '$location', '$routeParams', 'dateFilter', '$uibModal', mifosX.controllers.ViewLoanTransactionController]).run(function ($log) {
mifosX.ng.application.controller('ViewLoanTransactionController', ['$scope', 'ResourceFactory', '$location', '$routeParams', 'dateFilter', '$uibModal', '$rootScope', mifosX.controllers.ViewLoanTransactionController]).run(function ($log) {
$log.info("ViewLoanTransactionController initialized");
});
}(mifosX.controllers || {}));

View File

@ -1,6 +1,6 @@
(function (module) {
mifosX.controllers = _.extend(module, {
CreateLoanProductController: function (scope, $rootScope, resourceFactory, location, dateFilter,WizardHandler) {
CreateLoanProductController: function (scope, $rootScope, resourceFactory, location, dateFilter,WizardHandler, translate) {
scope.restrictDate = new Date();
scope.formData = {};
scope.loanproduct = {};
@ -32,6 +32,10 @@
scope.transactionProcessingStrategy = true;
scope.allowAttributeConfiguration = true;
scope.interestRecalculationOnDayTypeOptions = [];
scope.translate = translate;
//Rates
scope.rates = [];
scope.rateFlag = false;
for (var i = 1; i <= 28; i++) {
scope.interestRecalculationOnDayTypeOptions.push(i);
}
@ -82,6 +86,10 @@
scope.product.interestRecalculationNthDayTypeOptions.push({"code" : "onDay", "id" : -2, "value" : "on day"});
scope.loanproduct = angular.copy(scope.formData);
scope.isClicked = false;
//Rate Module
scope.rateOptions = scope.product.rateOptions || [];
scope.enableRates = scope.product.isRatesEnabled;
});
scope.$watch('formData',function(newVal){
@ -121,7 +129,50 @@
}
};
scope.deleteCharge = function (index) {
//Rate
scope.rateSelected = function (currentRate) {
if (currentRate) {
scope.rateFlag = true;
scope.rates.push(currentRate);
scope.rateOptions.splice(scope.rateOptions.indexOf(currentRate), 1);
scope.currentRate = '';
scope.calculateRates();
}
};
scope.calculateRates = function () {
var total = 0;
var minRate = 0;
scope.rates.forEach(function (rate) {
if (rate.percentage < minRate || minRate === 0) {
minRate = rate.percentage;
}
total += rate.percentage;
});
if (minRate === 0) {
minRate = undefined;
}
if (total === 0) {
total = undefined;
scope.rateFlag = false;
}
scope.formData.minInterestRatePerPeriod = minRate;
//Assign the same total range to this values.
scope.formData.interestRatePerPeriod = total;
scope.formData.maxInterestRatePerPeriod = total;
scope.calculatedRatePerPeriod = total;
};
scope.deleteRate = function (index) {
scope.rateOptions.push(scope.rates[index]);
scope.rates.splice(index, 1);
scope.calculateRates();
};
scope.deleteCharge = function (index) {
scope.charges.splice(index, 1);
};
@ -325,6 +376,7 @@
this.formData.dateFormat = scope.df;
this.formData.startDate = reqFirstDate;
this.formData.closeDate = reqSecondDate;
this.formData.rates = scope.rates;
//Interest recalculation data
if (this.formData.isInterestRecalculationEnabled) {
@ -392,7 +444,7 @@
};
}
});
mifosX.ng.application.controller('CreateLoanProductController', ['$scope','$rootScope', 'ResourceFactory', '$location', 'dateFilter','WizardHandler', mifosX.controllers.CreateLoanProductController]).run(function ($log) {
mifosX.ng.application.controller('CreateLoanProductController', ['$scope','$rootScope', 'ResourceFactory', '$location', 'dateFilter','WizardHandler', '$translate', mifosX.controllers.CreateLoanProductController]).run(function ($log) {
$log.info("CreateLoanProductController initialized");
});
}(mifosX.controllers || {}));

View File

@ -0,0 +1,37 @@
/**
* Created by Jose on 24/07/2017.
*/
(function (module) {
mifosX.controllers = _.extend(module, {
CreateRateController: function (scope, resourceFactory, location, dateFilter, translate, webStorage) {
scope.template = [];
scope.formData = {};
scope.first = {};
scope.rateError = false;
scope.translate = translate;
//Right now only loan is accepted for a rate.
scope.rateOptions = [{id : "m_loan"}];
scope.setChoice = function () {
if (this.formData.active) {
scope.choice = 1;
}
else if (!this.formData.active) {
scope.choice = 0;
}
};
scope.submit = function () {
this.formData.locale = scope.optlang.code;
resourceFactory.rateResource.save(this.formData, function (data) {
location.path('/rates/');
},function(error){
scope.rateError = true;
});
};
}
});
mifosX.ng.application.controller('CreateRateController', ['$scope', 'ResourceFactory', '$location', 'dateFilter', '$translate','webStorage', mifosX.controllers.CreateRateController]).run(function ($log) {
$log.info("CreateRateController initialized");
});
}(mifosX.controllers || {}));

View File

@ -1,6 +1,6 @@
(function (module) {
mifosX.controllers = _.extend(module, {
EditLoanProductController: function (scope, resourceFactory, location, routeParams, dateFilter) {
EditLoanProductController: function (scope, resourceFactory, location, routeParams, dateFilter, translate) {
scope.formData = {};
scope.restrictDate = new Date();
scope.charges = [];
@ -15,12 +15,17 @@
scope.pvFlag = false;
scope.rvFlag = false;
scope.interestRecalculationOnDayTypeOptions = [];
scope.translate = translate;
//Rates
scope.rates = [];
scope.rateFlag = false;
for (var i = 1; i <= 28; i++) {
scope.interestRecalculationOnDayTypeOptions.push(i);
}
resourceFactory.loanProductResource.get({loanProductId: routeParams.id, template: 'true'}, function (data) {
scope.product = data;
scope.ratesEnabled = data.ratesEnabled;
scope.assetAccountOptions = scope.product.accountingMappingOptions.assetAccountOptions || [];
scope.incomeAccountOptions = scope.product.accountingMappingOptions.incomeAccountOptions || [];
scope.expenseAccountOptions = scope.product.accountingMappingOptions.expenseAccountOptions || [];
@ -241,8 +246,73 @@
scope.formData.minimumGap = scope.product.minimumGap;
scope.formData.maximumGap = scope.product.maximumGap;
scope.formData.canUseForTopup = scope.product.canUseForTopup;
//Rate Module
scope.formData.rates = scope.product.rates;
scope.rateOptions = scope.product.rateOptions || [];
scope.calculatedRatePerPeriod = scope.product.interestRatePerPeriod;
scope.enableRates = scope.product.isRatesEnabled;
if (scope.enableRates){
if (scope.formData.rates && scope.formData.rates.length>0){
scope.rateFlag=true;
}
scope.formData.rates.forEach(function(rate){
scope.rateOptions.forEach(function(rateOption, index, array){
if(rate.name === rateOption.name){
scope.rateOptions.splice(index,1);
}
});
});
}
});
//Rate
scope.rateSelected = function(currentRate){
if(currentRate){
scope.formData.rates.push(currentRate);
scope.rateOptions.splice(scope.rateOptions.indexOf(currentRate),1);
scope.currentRate = '';
scope.calculateRates();
}
};
scope.calculateRates = function(){
var total = 0;
var minRate = 0;
scope.formData.rates.forEach(function(rate){
if(rate.percentage < minRate || minRate === 0){
minRate = rate.percentage;
}
total += rate.percentage;
});
if (minRate===0){
minRate=undefined;
}
if (total===0){
total=undefined;
scope.rateFlag=false;
}
scope.formData.minInterestRatePerPeriod = minRate;
//Assign the same total range to this values.
scope.formData.interestRatePerPeriod = total;
scope.formData.maxInterestRatePerPeriod = total;
scope.calculatedRatePerPeriod = total;
console.log(scope.formData);
};
scope.deleteRate = function (index){
scope.rateOptions.push(scope.formData.rates[index]);
scope.formData.rates.splice(index,1);
scope.calculateRates();
};
scope.chargeSelected = function (chargeId) {
if(chargeId){
resourceFactory.chargeResource.get({chargeId: chargeId, template: 'true'}, this.formData, function (data) {
@ -526,7 +596,7 @@
}
}
});
mifosX.ng.application.controller('EditLoanProductController', ['$scope', 'ResourceFactory', '$location', '$routeParams', 'dateFilter', mifosX.controllers.EditLoanProductController]).run(function ($log) {
mifosX.ng.application.controller('EditLoanProductController', ['$scope', 'ResourceFactory', '$location', '$routeParams', 'dateFilter', '$translate', mifosX.controllers.EditLoanProductController]).run(function ($log) {
$log.info("EditLoanProductController initialized");
});
}(mifosX.controllers || {}));

View File

@ -0,0 +1,42 @@
/**
* Created by Jose on 25/07/2017.
*/
(function (module) {
mifosX.controllers = _.extend(module, {
EditRateController: function (scope, resourceFactory, location, routeParams, dateFilter) {
scope.template = [];
scope.showdatefield = false;
scope.repeatEvery = false;
scope.first = {};
scope.flag = false;
scope.showPenalty = true ;
resourceFactory.rateResource.getRate({rateId: routeParams.rateId}, function (data) {
scope.template = data;
scope.formData = {
id: data.id,
name: data.name,
active: data.active,
percentage: data.percentage,
productApply : data.productApply
};
});
scope.submit = function () {
this.formData.locale = scope.optlang.code;
this.formData.active = this.formData.active || false;
resourceFactory.rateResource.update({rateId: routeParams.rateId}, this.formData, function (data) {
location.path('/viewrate/' + routeParams.rateId);
});
};
}
});
mifosX.ng.application.controller('EditRateController', ['$scope', 'ResourceFactory', '$location', '$routeParams', 'dateFilter', mifosX.controllers.EditRateController]).run(function ($log) {
$log.info("EditRateController initialized");
});
}(mifosX.controllers || {}));

View File

@ -0,0 +1,33 @@
/**
* Created by Jose on 24/07/2017.
*/
(function (module) {
mifosX.controllers = _.extend(module, {
RateController: function (scope, resourceFactory, location) {
scope.rates = [];
scope.routeTo = function (id) {
location.path('/viewrate/' + id);
};
if (!scope.searchCriteria.rates) {
scope.searchCriteria.rates = null;
scope.saveSC();
}
scope.filterText = scope.searchCriteria.rates || '';
scope.onFilter = function () {
scope.searchCriteria.rates = scope.filterText;
scope.saveSC();
};
scope.RatesPerPage = 15;
resourceFactory.rateResource.getAllRates(function (data) {
scope.rates = data;
});
}
});
mifosX.ng.application.controller('RateController', ['$scope', 'ResourceFactory', '$location', mifosX.controllers.RateController]).run(function ($log) {
$log.info("RateController initialized");
});
}(mifosX.controllers || {}));

View File

@ -32,7 +32,7 @@
else{
scope.allowAttributeConfiguration = false;
}
scope.enableRates = scope.loanproduct.isRatesEnabled;
});
scope.scrollto = function (link) {

View File

@ -0,0 +1,19 @@
/**
* Created by Jose on 25/07/2017.
*/
(function (module) {
mifosX.controllers = _.extend(module, {
ViewRateController: function (scope, routeParams, resourceFactory, location, $uibModal) {
scope.rate = [];
scope.choice = 0;
resourceFactory.rateResource.getRate({rateId: routeParams.rateId}, function (data) {
scope.rate = data;
});
}
});
mifosX.ng.application.controller('ViewRateController', ['$scope', '$routeParams', 'ResourceFactory', '$location', '$uibModal', mifosX.controllers.ViewRateController]).run(function ($log) {
$log.info("ViewRateController initialized");
});
}(mifosX.controllers || {}));

View File

@ -327,7 +327,11 @@ define(['Q', 'underscore', 'mifosX'], function (Q) {
'adhocquery/AdHocQueryListController',
'adhocquery/CreateAdHocQueryController',
'adhocquery/ViewAdHocQueryController',
'adhocquery/EditAdHocQueryController'
'adhocquery/EditAdHocQueryController',
'product/RateController',
'product/CreateRateController',
'product/ViewRateController',
'product/EditRateController'
],
filters: [
'StatusLookup',

View File

@ -1019,6 +1019,18 @@
.when('/externalservicesCB/CreditBureau/mapcblp', {
templateUrl: 'views/administration/MapCreditBureauToLP.html'
})
.when('/rates', {
templateUrl: 'views/products/rates.html'
})
.when('/createrate', {
templateUrl: 'views/products/createrate.html'
})
.when('/viewrate/:rateId', {
templateUrl: 'views/products/viewrate.html'
})
.when('/editrate/:rateId', {
templateUrl: 'views/products/editrate.html'
})
.otherwise({
templateUrl: "views/errors/404.html"
});

View File

@ -735,6 +735,12 @@
twoFactorConfigResource: defineResource(apiVer+"/twofactor/configure", {}, {
getAllConfigs: {method: 'GET', params: {}},
put: {method: 'PUT', params: {}}
}),
rateResource: defineResource(apiVer + "/rates/:rateId", {rateId: '@rateId'}, {
getAllRates: {method: 'GET', params: {}, isArray: true},
getRate: {method: 'GET', params: {}},
update: {method: 'PUT', params: {}},
save: {method: 'POST', params: {}}
})
};
}];

View File

@ -198,7 +198,7 @@
class="required">*</span></label></td>
<td class="paddedbottom10">
<input id="interestRatePerPeriod" class="form-control" type="text" name="nominalinterestrate"
ng-model="formData.interestRatePerPeriod"/>&nbsp;&nbsp;{{loanaccountinfo.interestRateFrequencyType.value}}
ng-model="formData.interestRatePerPeriod" ng-disabled="rateFlag"/>&nbsp;&nbsp;{{loanaccountinfo.interestRateFrequencyType.value}}
</td>
<td ng-hide="response.uiDisplayConfigurations.loanAccount.isHiddenField.interestType == true"><label>{{ 'label.input.interestmethod' | translate }}</label></td>
<td class="paddedbottom10" ng-hide="response.uiDisplayConfigurations.loanAccount.isHiddenField.interestType == true">
@ -441,6 +441,42 @@
</table>
</div>
<hr/>
<!--Rate Module-->
<div ng-show="ratesEnabled">
<h3>{{ 'label.heading.rates' | translate }}</h3>
<hr>
<div class="form-group">
<div class="col-md-12">
<select id="currentRate" ng-model="currentRate" class="form-control"
ng-options="rate as rate.name for rate in rateOptions"
value="{{rate}}">
<option class="displaynone" value="">{{'label.selectrate' | translate}}</option>
</select>
<button type="button" class="btn btn-primary"
ng-click="rateSelected(currentRate)">{{ 'label.button.add' | translate }}
</button>
</div>
</div>
<div class="form-group col-md-12">
<table class="table width100">
<tr class="graybg">
<th>{{'label.heading.name' | translate}}</th>
<th>{{'label.heading.percentage' | translate}}</th>
<th></th>
<th>{{'label.heading.actions' | translate}}</th>
</tr>
<tr ng-repeat="rate in formData.rates">
<td>{{rate.name}}</td>
<td>{{rate.percentage| number}}</td>
<td></td>
<td><a ng-click="deleteRate($index)"><i class="fa fa-times icon-white"></i></a></td>
</tr>
</table>
</div>
<!-- End -->
</div>
<div class="col-md-12">
<label><strong>{{ 'label.heading.charges' | translate }}</strong></label>
<select ng-model="chargeFormData.chargeId" class="form-control width170px"

View File

@ -269,7 +269,7 @@
</label>
</td>
<td class="paddedbottom20">
<input id="interestRatePerPeriod" class="form-control" type="text" name="nominalinterestrate" ng-model="formData.interestRatePerPeriod"
<input id="interestRatePerPeriod" class="form-control" type="text" name="nominalinterestrate" ng-model="formData.interestRatePerPeriod" ng-disabled="ratesEnabled&&rateFlag"
/>&nbsp;&nbsp;{{loanaccountinfo.interestRateFrequencyType.value}}
</td>
<td ng-hide="response.uiDisplayConfigurations.loanAccount.isHiddenField.interestType == true">
@ -569,6 +569,44 @@
</tbody>
</table>
</div>
<!--Rate Module-->
<div ng-show="ratesEnabled">
<hr/>
<h3>{{ 'label.heading.rates' | translate }}</h3>
<hr>
<div class="form-group">
<div class="col-md-12">
<select id="currentRate" ng-model="currentRate" class="form-control"
ng-options="rate as rate.name for rate in rateOptions"
value="{{rate}}">
<option class="displaynone" value="">{{'label.selectrate' | translate}}</option>
</select>
<button type="button" class="btn btn-primary"
ng-click="rateSelected(currentRate)">{{ 'label.button.add' | translate }}
</button>
</div>
</div>
<div class="form-group col-md-12">
<table class="table width100">
<tr class="graybg">
<th>{{'label.heading.name' | translate}}</th>
<th>{{'label.heading.percentage' | translate}}</th>
<th></th>
<th>{{'label.heading.actions' | translate}}</th>
</tr>
<tr ng-repeat="rate in formData.rates">
<td>{{rate.name}}</td>
<td>{{rate.percentage | number}}</td>
<td></td>
<td><a ng-click="deleteRate($index)"><i class="fa fa-times icon-white"></i></a></td>
</tr>
</table>
</div>
<!-- End -->
</div>
</div>
<hr>
<button class="btn btn-default pull-left" wz-previous>

View File

@ -49,6 +49,20 @@
</tr>
<tr class="span2"></tr>
</table>
<div ng-show="transaction.type.repayment">
<h3>{{ 'label.heading.transactiondetails' | translate}}</h3>
<div class="row span" >
<div class="col-sm-6 col-md-6">
<table class="table table-striped table-bordered" >
<tr ng-repeat="detail in details">
<th class="table-bold-loan" ng-style="detail.boldTitle && {'font-weight' : 'bold' , 'text-align' : detail.align}">{{detail.description | translate}}</th>
<td ng-show="detail.containsAmount"><span class="padded-td" ng-style="detail.boldTitle && {'font-weight' : 'bold' }">{{transaction.currency.displaySymbol}} {{detail.amount}}</span>
</td>
</tr>
</table>
</div>
</div>
</div>
<table width="100%" ng-show="transaction.paymentDetailData">
<tr>
<td width="20%"><strong>{{ 'label.heading.paymentdetails' | translate}}</strong></td>

View File

@ -212,7 +212,7 @@
<div class="col-sm-2">
<input type="text" placeholder="{{'label.input.default' | translate}}" id="principal"
name="principal"
class="form-control" number-format ng-model="formData.principal" required late-validate/>
class="form-control" number-format ng-model="formData.principal" required late-validate number=true />
</div>
<div class="col-sm-2">
<input type="text" placeholder="{{'label.input.maximum' | translate}}" id="maxPrincipal"
@ -419,6 +419,71 @@
</div>
</div>
<div class="form-group" ng-show="!formData.isLinkedToFloatingInterestRates">
<!--Rate Module-->
<div ng-show="enableRates">
<div class="form-group">
<label class="control-label col-sm-2">{{ 'label.input.nominalinterestrate' |
translate }}
<i class="icon-question-sign"
tooltip="{{'label.tooltip.loanproduct.nominalinterestrate' | translate}}"
tooltip-append-to-body="true"></i>
</label>
<div class="col-sm-2">
<input id="calculatedRatePerPeriod" readonly type="text"
class="form-control" ng-model="calculatedRatePerPeriod">
</div>
<div class="col-sm-2">
<select class="form-control" id="interestRateFrequencyTypeR"
ng-model="formData.interestRateFrequencyType"
ng-options="interestRateFrequencyType.id as interestRateFrequencyType.value for interestRateFrequencyType in product.interestRateFrequencyTypeOptions"
value="{{interestRateFrequencyType.id}}"></select>
</div>
<div class="col-sm-2">
<form-validate valattributeform="createloanproductform"
valattribute="interestRatePerPeriod"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-3">
<select id="currentRate" ng-model="currentRate" class="form-control"
ng-options="rate as rate.name for rate in rateOptions"
value="{{rate}}">
<option class="displaynone" value="">{{'label.selectrate' | translate}}
</option>
</select>
</div>
<div class="col-sm-1">
<button type="button" class="btn btn-primary"
ng-click="rateSelected(currentRate)">{{ 'label.button.add' | translate
}}
</button>
</div>
</div>
<div class="form-group col-md-12">
<table data-ng-show="rateFlag" class="table width100">
<tr class="graybg">
<th>{{'label.heading.name' | translate}}</th>
<th>{{'label.heading.percentage' | translate}}</th>
<th></th>
<th>{{'label.heading.actions' | translate}}</th>
</tr>
<tr ng-repeat="rate in rates">
<td>{{rate.name}}</td>
<td>{{rate.percentage | number}}</td>
<td></td>
<td><a ng-click="deleteRate($index)"><i
class="fa fa-times icon-white"></i></a>
</td>
</tr>
</table>
</div>
</div>
<!-- End -->
<label class="control-label col-sm-2">{{ 'label.input.nominalinterestrate' | translate }}
<span class="required">*</span>
<i class="fa fa-question-circle"
@ -428,15 +493,15 @@
<div class="col-sm-2">
<input id="minInterestRatePerPeriod" placeholder="{{'label.input.minimum' | translate}}" type="text"
class="form-control" ng-model="formData.minInterestRatePerPeriod">
class="form-control" ng-model="formData.minInterestRatePerPeriod" ng-disabled="rateFlag&&enableRates" number-format>
</div>
<div class="col-sm-2">
<input type="text" id="interestRatePerPeriod" placeholder="{{'label.input.default' | translate}}"
name="interestRatePerPeriod" class="form-control" ng-model="formData.interestRatePerPeriod" required late-validate decimals />
name="interestRatePerPeriod" class="form-control" ng-model="formData.interestRatePerPeriod" required late-validate decimals ng-disabled="rateFlag&&enableRates" />
</div>
<div class="col-sm-2">
<input id="maxInterestRatePerPeriod" placeholder="{{'label.input.maximum' | translate}}" type="text"
class="form-control" ng-model="formData.maxInterestRatePerPeriod">
class="form-control" ng-model="formData.maxInterestRatePerPeriod" number-format ng-disabled="rateFlag&&enableRates">
</div>
<div class="col-sm-2">
<select class="form-control" id="interestRateFrequencyType"

View File

@ -0,0 +1,72 @@
<div class="col-md-12" ng-controller="CreateRateController">
<ul class="breadcrumb">
<li><a href="#/products">{{'label.anchor.products' | translate}}</a></li>
<li><a href="#/rates">{{'label.anchor.rates' | translate}}</a></li>
<li class="active">{{'label.anchor.createrate' | translate}}</li>
</ul>
<form name="createrateform" novalidate="" class="form-horizontal well" rc-submit="submit()">
<div ng-show="rateError" role="alert" style="color:red">{{'label.error.rate.already.exist' | translate}}</div>
<fieldset>
<div class="form-group">
<label class="control-label col-sm-2">{{'label.input.chargesappliesto' | translate}}<span
class="required">*</span></label>
<div class="col-sm-3">
<select id="productApply" name="productApply" ng-model="formData.productApply"
ng-options="productApply.id as productApply.id | translate for productApply in rateOptions"
value="{{productApply.id}}" class="form-control"
required>
<option value="">{{'label.selectone' | translate}}</option>
</select>
</div>
<div class="col-sm-2">
<form-validate valattributeform="createrateform" valattribute="productApply"/>
</div>
</div>
<hr/>
<div ng-show="formData.productApply">
<div class="form-group">
<label class="control-label col-sm-2" for="name">{{'label.input.name' | translate}}<span
class="required">*</span></label>
<div class="col-sm-3">
<input name="name" type="text" id="name" ng-model="formData.name" class="form-control" required late-Validate/>
</div>
<div class="col-sm-3">
<form-validate valattributeform="createrateform" valattribute="name"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="percentage">{{'label.input.percentage' | translate}}<span
class="required">*</span></label>
<div class="col-sm-3">
<input id="percentage" name="percentage" type="text" number-format ng-model="formData.percentage" class="form-control" required late-Validate/>
</div>
<div class="col-sm-3">
<form-validate valattributeform="createrateform" valattribute="percentage"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="active">{{'label.input.active' | translate}}</label>
<div class="col-sm-3">
<label class="checkbox">
<input id="active" type="checkbox" ng-model="formData.active" data-ng-change="setChoice()">
</label>
</div>
</div>
<div class="col-md-offset-3">
<a id="cancel" href="#/rates">
<button type="reset" class="btn btn-default">{{'label.button.cancel' | translate}}</button>
</a>
<button id="save" type="submit" class="btn btn-primary" has-permission='CREATE_RATE'>
{{'label.button.save' | translate}}
</button>
</div>
</div>
</fieldset>
</form>
</div>

View File

@ -331,21 +331,76 @@
</div>
<div class="form-group" ng-show="!formData.isLinkedToFloatingInterestRates">
<!--Rate Module-->
<div class="form-group" ng-show="enableRates&&!formData.isLinkedToFloatingInterestRates">
<hr>
<label class="control-label col-sm-2">{{ 'label.input.nominalinterestrate' | translate }}
<i class="icon-question-sign" tooltip="{{'label.tooltip.loanproduct.nominalinterestrate' | translate}}" tooltip-append-to-body="true"></i>
</label>
<div class="col-sm-2">
<input id="calculatedRatePerPeriod" readonly type="text"
class="form-control" ng-model="calculatedRatePerPeriod" number-format>
</div>
<div class="col-sm-2">
<select class="form-control" id="interestRateFrequencyTypeR" ng-model="formData.interestRateFrequencyType"
ng-options="interestRateFrequencyType.id as interestRateFrequencyType.value for interestRateFrequencyType in product.interestRateFrequencyTypeOptions"
value="{{interestRateFrequencyType.id}}"/>
</div>
<div class="col-sm-2">
<form-validate valattributeform="editloanproductform" valattribute="interestRatePerPeriod"/>
</div>
</div>
<div class="form-group" ng-show="enableRates">
<div class="col-sm-3">
<select id="currentRate" ng-model="currentRate" class="form-control"
ng-options="rate as rate.name for rate in rateOptions"
value="{{rate}}">
<option class="displaynone" value="">{{'label.selectrate' | translate}}</option>
</select>
</div>
<div class="col-sm-1">
<button type="button" class="btn btn-primary"
ng-click="rateSelected(currentRate)">{{ 'label.button.add' | translate }}
</button>
</div>
</div>
<div class="form-group col-md-12" ng-show="enableRates">
<table class="table width100">
<tr class="graybg">
<th>{{'label.heading.name' | translate}}</th>
<th>{{'label.heading.percentage' | translate}}</th>
<th></th>
<th>{{'label.heading.actions' | translate}}</th>
</tr>
<tr ng-repeat="rate in formData.rates">
<td>{{rate.name}}</td>
<td>{{rate.percentage|number}}</td>
<td></td>
<td><a ng-click="deleteRate($index)"><i class="fa fa-times"></i></a></td>
</tr>
</table>
</div>
<!-- End -->
<label class="control-label col-sm-2">{{ 'label.input.nominalinterestrate' | translate }}
<i class="fa fa-question-circle" uib-tooltip="{{'label.tooltip.loanproduct.nominalinterestrate' | translate}}" tooltip-append-to-body="true"></i>
</label>
<div class="col-sm-2">
<input id="minInterestRatePerPeriod" placeholder="{{'label.input.minimum' | translate}}" type="text"
class="form-control" number-format ng-model="formData.minInterestRatePerPeriod">
class="form-control" number-format ng-model="formData.minInterestRatePerPeriod" ng-disabled="enableRates&&rateFlag">
</div>
<div class="col-sm-2">
<input type="text" id="interestRatePerPeriod" placeholder="{{'label.input.default' | translate}}"
name="interestRatePerPeriod" class="form-control" ng-model="formData.interestRatePerPeriod"
number-format late-validate/>
number-format late-validate ng-disabled="enableRates&&rateFlag"/>
</div>
<div class="col-sm-2">
<input id="maxInterestRatePerPeriod" placeholder="{{'label.input.maximum' | translate}}" type="text"
number-format class="form-control" ng-model="formData.maxInterestRatePerPeriod">
number-format class="form-control" ng-model="formData.maxInterestRatePerPeriod" ng-disabled="enableRates&&rateFlag">
</div>
<div class="col-sm-2">
<select class="form-control" id="interestRateFrequencyType" ng-model="formData.interestRateFrequencyType"

View File

@ -0,0 +1,55 @@
<div class="col-md-12" ng-controller="EditRateController">
<api-validate></api-validate>
<ul class="breadcrumb">
<li><a href="#/products">{{'label.anchor.products' | translate}}</a></li>
<li><a href="#/rates">{{'label.anchor.rates' | translate}}</a></li>
<li class="active">{{'label.anchor.editcharge' | translate}}</li>
</ul>
<form name="editrateform" novalidate class="form-horizontal well" rc-submit="submit()">
<fieldset>
<legend>{{'label.anchor.editrate' | translate}}</legend>
<div class="form-group">
<label class="control-label col-sm-2">{{'label.heading.rateappliesto' | translate}}</label>
<div class="col-sm-3">
<input type="text" id="productApply" ng-model="formData.productApply" value="{{formData.productApply | translate}}"
ng-disabled="true" class="form-control" />
</div>
</div>
<hr/>
<div class="form-group">
<label class="control-label col-sm-2" for="name">{{'label.input.name' | translate}}</label>
<div class="col-sm-3">
<input name="name" ng-autofocus="true" type="text" id="name" ng-model="formData.name" class="form-control" ng-disabled="true"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2">{{'label.input.percentage' | translate}}<span
class="required">*</span></label>
<div class="col-sm-3">
<input name="percentage" ng-autofocus="true" type="text" id="percentage" number-format ng-model="formData.percentage" class="form-control" required
late-Validate/>
<form-validate valattributeform="editrateform" valattribute="percentage"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="active">{{'label.input.active' | translate}}</label>
<div class="col-sm-3">
<label class="checkbox">
<input id="active" type="checkbox" ng-model="formData.active">
</label>
</div>
</div>
<div class="col-md-offset-3">
<a id="cancel" href="#/viewrate/{{formData.id}}" class="btn btn-default">{{'label.button.cancel' | translate}}</a>
<button id="save" type="submit" class="btn btn-primary" has-permission='UPDATE_RATE'>{{'label.button.save' | translate}}</button>
</div>
</fieldset>
</form>
</div>

View File

@ -33,6 +33,12 @@
<p class="list-group-item-text">{{'label.definecharges' | translate}}</p>
</a>
<!--Rates Module-->
<a class="list-group-item" href="#/rates" has-permission='READ_RATE'>
<h4 class="list-group-item-heading"><i class="fa fa-bar-chart-o fa fa-large"></i>&nbsp;&nbsp;{{
'label.anchor.rates' | translate }}</h4>
<p class="list-group-item-text">{{'label.rates' | translate}}</p>
</a>
</div>
</div>

View File

@ -0,0 +1,40 @@
<!--Rates Module-->
<div class="content-container" ng-controller="RateController">
<ul class="breadcrumb">
<li><a href="#/products">{{'label.anchor.products' | translate}}</a></li>
<li class="active">{{'label.anchor.rates' | translate}}</li>
</ul>
<div class="card well">
<div class="row">
<div class="col-m-9 col-sm-9">
<input ng-autofocus="true" ng-model="filterText" type="text" ng-keyup="onFilter()"
class="form-control" placeholder="{{'label.input.filterbyname' | translate}}">
</div>
<div class="col-m-3 col-sm-3">
<a href="#/createrate" class="btn btn-primary pull-right" has-permission='CREATE_RATE'><i
class="fa fa-plus "></i>{{'label.button.createrate' | translate}}</a>
</div>
</div>
<hr>
<table class="table">
<thead>
<tr class="graybg">
<th>{{'label.heading.name' | translate}}</th>
<th>{{'label.heading.chargeappliesto' | translate}}</th>
<th>{{'label.heading.active' | translate}}</th>
</tr>
</thead>
<tbody>
<tr class="pointer-main" dir-paginate="rate in rates |orderBy:'name':reverse | filter:filterText | itemsPerPage: RatesPerPage">
<td class="pointer" data-ng-click="routeTo(rate.id)">{{rate.name}}</td>
<td class="pointer" data-ng-click="routeTo(rate.id)">{{rate.productApply | translate }}</td>
<td class="pointer" data-ng-click="routeTo(rate.id)">{{rate.active | YesOrNo | translate}}
</td>
</tr>
</tbody>
</table>
<dir-pagination-controls boundary-links="true" template-url="bower_components/angular-utils-pagination/dirPagination.tpl.html"></dir-pagination-controls>
</div>
</div>

View File

@ -0,0 +1,35 @@
<div class="content-container" ng-controller="ViewRateController">
<ul class="breadcrumb">
<li><a href="#/products">{{'label.anchor.products' | translate}}</a></li>
<li><a href="#/rates">{{'label.anchor.rates' | translate}}</a></li>
<li class="active">{{rate.name}}</li>
</ul>
<div class="card well">
<h3 class="bolder">{{rate.name}}
<div class="btn-group pull-right">
<a href="#/editrate/{{rate.id}}" class="btn btn-primary" has-permission='UPDATE_RATE'><i class="icon-edit icon-white"></i>{{'label.button.edit' | translate}}</a>
</div>
</h3>
<hr>
<div class="row">
<div class="col-md-6">
<table class="table table-bordered table-striped">
<tr>
<td>{{'label.heading.percentage' | translate}}</td>
<td>{{rate.percentage | number}}</td>
</tr>
<tr>
<td>{{'label.heading.active' | translate}}</td>
<td>{{rate.active | YesOrNo | translate}}</td>
</tr>
<tr>
<td>{{'label.heading.rateappliesto' | translate}}</td>
<td>{{rate.productApply | translate}}</td>
</tr>
</table>
</div>
</div>
</div>
</div>