WIP: submit and modify loan application. support for loan purpose, loan collateral, loan fees. Restrict modification of loan application details to be only allowed in 'submitted and pending approval' state which is first improved required to reach loan and cash based accounting integration.

This commit is contained in:
Keith Woodlock
2013-02-13 14:06:55 +00:00
parent c66a7515aa
commit 28e1548475
48 changed files with 1732 additions and 845 deletions
@@ -26,6 +26,7 @@ DROP TABLE IF EXISTS `m_group_client`;
DROP TABLE IF EXISTS `m_guarantor`;
DROP TABLE IF EXISTS `m_loan`;
DROP TABLE IF EXISTS `m_loan_charge`;
DROP TABLE IF EXISTS `m_loan_collateral`;
DROP TABLE IF EXISTS `m_loan_officer_assignment_history`;
DROP TABLE IF EXISTS `m_loan_repayment_schedule`;
DROP TABLE IF EXISTS `m_loan_transaction`;
@@ -501,6 +502,7 @@ CREATE TABLE `m_loan` (
`fund_id` bigint(20) DEFAULT NULL,
`loan_officer_id` bigint(20) DEFAULT NULL,
`guarantor_id` bigint(20) DEFAULT NULL,
`loanpurpose_cv_id` INT(11) DEFAULT NULL,
`loan_status_id` smallint(5) NOT NULL,
`currency_code` varchar(3) NOT NULL,
`currency_digits` smallint(5) NOT NULL,
@@ -544,12 +546,14 @@ CREATE TABLE `m_loan` (
KEY `FK7C885877240145` (`fund_id`),
KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`),
KEY `FK_m_loan_m_staff` (`loan_officer_id`),
KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`),
KEY `group_id` (`group_id`),
CONSTRAINT `FK7C885877240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`),
CONSTRAINT `FKB6F935D87179A0CB` FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`),
CONSTRAINT `FKB6F935D8C8D4B434` FOREIGN KEY (`product_id`) REFERENCES `m_product_loan` (`id`),
CONSTRAINT `FK_loan_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`),
CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`),
CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`),
CONSTRAINT `m_loan_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `m_group` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@@ -602,6 +606,18 @@ CREATE TABLE `m_loan_charge` (
CONSTRAINT `m_loan_charge_ibfk_2` FOREIGN KEY (`loan_id`) REFERENCES `m_loan` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `m_loan_collateral` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`loan_id` bigint(20) NOT NULL,
`type_cv_id` int(11) NOT NULL,
`description` varchar(500) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_collateral_m_loan` (`loan_id`),
KEY `FK_collateral_code_value` (`type_cv_id`),
CONSTRAINT `FK_collateral_m_loan` FOREIGN KEY (`loan_id`) REFERENCES `m_loan` (`id`),
CONSTRAINT `FK_collateral_code_value` FOREIGN KEY (`type_cv_id`) REFERENCES `m_code_value` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `m_loan_officer_assignment_history` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`loan_id` bigint(20) NOT NULL,
@@ -202,14 +202,6 @@ VALUES
INSERT INTO `m_organisation_currency` (`id`, `code`, `decimal_places`, `name`, `display_symbol`, `internationalized_name_code`)
VALUES (21,'USD',2,'US Dollar','$','currency.USD');
-- create single code and code value for client identifiers
INSERT INTO `m_code`
(`id`,`code_name`, `is_system_defined`) VALUES (1,'Customer Identifier',1);
INSERT INTO `m_code_value`
(`id`,`code_id`,`code_value`,`order_position`)
VALUES (1,1,'Passport number',0);
INSERT INTO `m_office` (`id`, `parent_id`, `hierarchy`, `external_id`, `name`, `opening_date`)
VALUES
(1,NULL,'.','1','Head Office','2009-01-01');
@@ -520,4 +512,39 @@ from x_registered_table r;
insert into m_permission(grouping, `code`, entity_name, action_name)
select 'datatable', concat('DELETE_', r.registered_table_name), r.registered_table_name, 'DELETE'
from x_registered_table r;
from x_registered_table r;
-- create single code and code value for client identifiers
INSERT INTO `m_code`
(`code_name`, `is_system_defined`)
VALUES
('Customer Identifier',1),
('LoanCollateral',1),
('LoanPurpose',1),
('Gender',1),
('YesNo',1);
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'Passport', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "Customer Identifier";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'Id', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "Customer Identifier";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'Drivers License', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "Customer Identifier";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'Any Other Id Type', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "Customer Identifier";
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,47 +1,146 @@
DELETE FROM `mifostenant-ceda`.`ref_loan_transaction_processing_strategy` WHERE id in (1, 3, 4);
INSERT INTO `mifostenant-ceda`.`m_code` (`id`,`code_name`, `is_system_defined`)
INSERT INTO `mifostenant-ceda`.`m_code` (`code_name`, `is_system_defined`)
VALUES
(2,'Gender', '0'),
(3,'YesNo', '0'),
(4,'FieldOfEmployment', '0'),
(5,'EducationLevel', '0'),
(6,'MaritalStatus', '0'),
(7,'PovertyStatus', '0'),
(8,'PurposeOfLoan', '0'),
(9,'CollateralType', '0');
('FieldOfEmployment', '0'),
('EducationLevel', '0'),
('MaritalStatus', '0'),
('PovertyStatus', '0');
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Banker', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "FieldOfEmployment";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.SoftwareDeveloper', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "FieldOfEmployment";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.University', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "EducationLevel";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Secondary', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "EducationLevel";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Primary', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "EducationLevel";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Married', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "MaritalStatus";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Single', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "MaritalStatus";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Divorced', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "MaritalStatus";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Widow', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "MaritalStatus";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.PovertyStatus.Band1', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "PovertyStatus";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.PovertyStatus.Band2', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "PovertyStatus";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.PovertyStatus.Band3', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "PovertyStatus";
-- ======= ADD in loan purspose and collateral values to enable this functionality
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.House', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "LoanCollateral";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Television', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "LoanCollateral";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Gold', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "LoanCollateral";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Agriculture', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "LoanPurpose";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Manufacturing', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "LoanPurpose";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.HousingImprovement', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "LoanPurpose";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Male', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "Gender";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Female', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "Gender";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Yes', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "YesNo";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.No', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "YesNo";
DELETE FROM `mifostenant-ceda`.`m_code_value` WHERE id > 0;
INSERT INTO `mifostenant-ceda`.`m_code_value`
(`code_id`,`code_value`,`order_position`)
VALUES
(1, 'Passport', 1),
(1, 'Id', 2),
(1, 'Drivers License', 3),
(1, 'Any Other Id Type', 4),
(2, 'gender.Male', 1),
(2, 'gender.Female', 2),
(3, 'option.Yes', 1),
(3, 'option.No', 2),
(4, 'option.Banker', 1),
(4, 'option.SoftwareDeveloper', 2),
(5, 'option.University', 1),
(5, 'option.Secondary', 2),
(5, 'option.Primary', 3),
(6, 'option.Married', 1),
(6, 'option.Single', 2),
(6, 'option.Divorced', 3),
(6, 'option.Widow', 4),
(7, 'option.PovertyStatus.Band1', 1),
(7, 'option.PovertyStatus.Band2', 2),
(7, 'option.PovertyStatus.Band3', 3),
(8, 'option.Agriculture', 1),
(8, 'option.Manufacturing', 2),
(8, 'option.HousingImprovement', 3),
(9, 'option.House', 1),
(9, 'option.Television', 2),
(9, 'option.Gold', 3);
DROP TABLE IF EXISTS `mifostenant-ceda`.`client additional data`;
CREATE TABLE `mifostenant-ceda`.`client additional data` (
@@ -30,6 +30,9 @@ VALUES
(0, 1, 'keithwoodlock', 'Keith', 'Woodlock',
'4f607e9b6cffbe7d3db92d4bfa3391c7aa751727b4ea29d08fddf9dd72e6e7e3', 'keithwoodlock@gmail.com', 0, 1, 1, 1, 1);
INSERT INTO `mifostenant-ceda`.`m_appuser_role`
(`appuser_id`,`role_id`) VALUES (2,1);
UPDATE `mifostenant-ceda`.`m_office` SET `name` = 'CEDA Microfinance Ltd.' WHERE id=1;
INSERT INTO `mifostenant-ceda`.`m_office`
File diff suppressed because one or more lines are too long
@@ -1,13 +1,53 @@
-- add code and code values for datatables dropdowns
INSERT INTO `m_code` (`id`, `code_name`, `is_system_defined`) VALUES (2, 'Gender', 1);
INSERT INTO `m_code` (`id`, `code_name`, `is_system_defined`) VALUES (3, 'Education', 1);
INSERT INTO `m_code_value` (`code_id`, `code_value`, `order_position`) VALUES (2, 'Male', 1);
INSERT INTO `m_code_value` (`code_id`, `code_value`, `order_position`) VALUES (2, 'Female', 2);
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Male', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "Gender";
INSERT INTO `m_code_value` (`code_id`, `code_value`, `order_position`) VALUES (3, 'Primary', 1);
INSERT INTO `m_code_value` (`code_id`, `code_value`, `order_position`) VALUES (3, 'Secondary', 2);
INSERT INTO `m_code_value` (`code_id`, `code_value`, `order_position`) VALUES (3, 'University', 3);
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Female', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "Gender";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.Yes', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "YesNo";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'option.No', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "YesNo";
INSERT INTO `m_code`
(`code_name`, `is_system_defined`)
VALUES
('Education',1);
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'Primary', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "Education";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'Secondary', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "Education";
INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
select mc.id, 'University', ifnull(max(mv.id), 1)
from m_code mc
join m_code_value mv on mv.code_id = mc.id
where mc.`code_name` = "Education";
-- ========= datatables=======
DROP TABLE IF EXISTS `extra_client_details`;
CREATE TABLE `extra_client_details` (
@@ -49,4 +89,12 @@ CREATE TABLE `extra_loan_details` (
`Comment` text,
PRIMARY KEY (`loan_id`),
CONSTRAINT `FK_extra_loan_details` FOREIGN KEY (`loan_id`) REFERENCES `m_loan` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- datatables mapping
INSERT INTO `x_registered_table`
(`registered_table_name`,`application_table_name`)
VALUES
('extra_client_details', 'm_client'),
('extra_family_details', 'm_loan'),
('extra_loan_details', 'm_loan');
@@ -0,0 +1,18 @@
DROP TABLE IF EXISTS `m_loan_collateral`;
CREATE TABLE `m_loan_collateral` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`loan_id` bigint(20) NOT NULL,
`type_cv_id` int(11) NOT NULL,
`description` varchar(500) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_collateral_m_loan` (`loan_id`),
KEY `FK_collateral_code_value` (`type_cv_id`),
CONSTRAINT `FK_collateral_m_loan` FOREIGN KEY (`loan_id`) REFERENCES `m_loan` (`id`),
CONSTRAINT `FK_collateral_code_value` FOREIGN KEY (`type_cv_id`) REFERENCES `m_code_value` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `m_loan`
ADD COLUMN `loanpurpose_cv_id` INT(11) DEFAULT NULL AFTER `guarantor_id`,
ADD KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`),
ADD CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`);
+1
View File
@@ -100,6 +100,7 @@ dependencies {
[group: 'com.sun.jersey.contribs', name: 'jersey-multipart', version: jerseyVersion],
[group: 'com.google.code.gson', name: 'gson', version: '2.2.2'],
[group: 'com.google.guava', name: 'guava', version: '14.0-rc2'],
[group: 'org.springframework.data', name: 'spring-data-jpa', version: springDataJpaVersion],
@@ -18,6 +18,10 @@ public class CodeValueData {
return new CodeValueData(id, name, position);
}
public static CodeValueData instance(final Long id, final String name) {
return new CodeValueData(id, name, null);
}
private CodeValueData(final Long id, final String name, final Integer position) {
this.id = id;
this.name = name;
@@ -12,6 +12,7 @@ import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.codes.CodeConstants.CODEVALUE_JSON_INPUT_PARAMS;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -81,4 +82,7 @@ public class CodeValue extends AbstractPersistable<Long> {
return actualChanges;
}
public CodeValueData toData() {
return CodeValueData.instance(this.getId(), this.label, this.position);
}
}
@@ -0,0 +1,34 @@
package org.mifosplatform.infrastructure.codes.domain;
import org.mifosplatform.infrastructure.codes.exception.CodeValueNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <p>
* Wrapper for {@link CodeValueRepository} that is responsible for checking if
* {@link CodeValue} is returned when using <code>findOne</code> repository
* method and throwing an appropriate not found exception.
* </p>
*
* <p>
* This is to avoid need for checking and throwing in multiple areas of code
* base where {@link CodeValueRepository} is required.
* </p>
*/
@Service
public class CodeValueRepositoryWrapper {
private final CodeValueRepository repository;
@Autowired
public CodeValueRepositoryWrapper(final CodeValueRepository repository) {
this.repository = repository;
}
public CodeValue findOneWithNotFoundDetection(final Long id) {
final CodeValue codeValue = this.repository.findOne(id);
if (codeValue == null) { throw new CodeValueNotFoundException(id); }
return codeValue;
}
}
@@ -9,20 +9,25 @@ import org.mifosplatform.infrastructure.codes.data.CodeValueData;
*
* There are two types of code information in the platform:
* <ol>
* <li>System defined codes</li>
* <li>User defined codes</li>
* <li>System defined codes</li>
* <li>User defined codes</li>
* </ol>
*
* <p>System defined codes cannot be altered or removed but their code values may be allowed to be added to or removed.</p>
* <p>
* System defined codes cannot be altered or removed but their code values may
* be allowed to be added to or removed.
* </p>
*
* <p>User defined codes can be changed in any way by application users with system permissions.</p>
* <p>
* User defined codes can be changed in any way by application users with system
* permissions.
* </p>
*/
public interface CodeValueReadPlatformService {
// system defined code value data
Collection<CodeValueData> retrieveCustomIdentifierCodeValues();
Collection<CodeValueData> retrieveAllCodeValues(final Long codeId);
CodeValueData retrieveCodeValue(final Long codeValueId);
Collection<CodeValueData> retrieveCodeValuesByCode(final String code);
Collection<CodeValueData> retrieveAllCodeValues(final Long codeId);
CodeValueData retrieveCodeValue(final Long codeValueId);
}
@@ -43,14 +43,14 @@ public class CodeValueReadPlatformServiceImpl implements CodeValueReadPlatformSe
}
@Override
public Collection<CodeValueData> retrieveCustomIdentifierCodeValues() {
public Collection<CodeValueData> retrieveCodeValuesByCode(final String code) {
context.authenticatedUser();
final CodeValueDataMapper rm = new CodeValueDataMapper();
final String sql = "select " + rm.schema() + "where c.code_name like ? order by position";
return this.jdbcTemplate.query(sql, rm, new Object[] { "Customer Identifier" });
return this.jdbcTemplate.query(sql, rm, new Object[] { code });
}
@Override
@@ -63,16 +63,16 @@ public class CodeValueReadPlatformServiceImpl implements CodeValueReadPlatformSe
return this.jdbcTemplate.query(sql, rm, new Object[] { codeId });
}
@Override
public CodeValueData retrieveCodeValue(Long codeValueId){
public CodeValueData retrieveCodeValue(Long codeValueId) {
context.authenticatedUser();
final CodeValueDataMapper rm = new CodeValueDataMapper();
final String sql = "select " + rm.schema() + "where cv.id = ? order by position";
return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { codeValueId });
}
}
@@ -11,6 +11,8 @@ public class DataValidatorBuilder {
private final List<ApiParameterError> dataValidationErrors;
private String resource;
private String parameter;
private String arrayPart;
private Integer arrayIndex;
private Object value;
private boolean ignoreNullValue = false;
@@ -32,6 +34,12 @@ public class DataValidatorBuilder {
return this;
}
public DataValidatorBuilder parameterAtIndexArray(final String arrayPart, final Integer arrayIndex) {
this.arrayPart = arrayPart;
this.arrayIndex = arrayIndex;
return this;
}
public DataValidatorBuilder value(Object value) {
this.value = value;
return this;
@@ -102,11 +110,19 @@ public class DataValidatorBuilder {
public DataValidatorBuilder notNull() {
if (value == null && !ignoreNullValue) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".cannot.be.blank");
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" cannot be blank.");
String realParameterName = this.parameter;
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter);
if (this.arrayIndex != null && StringUtils.isNotBlank(arrayPart)) {
validationErrorCode.append(".").append(this.arrayPart);
realParameterName = new StringBuilder(parameter).append('[').append(this.arrayIndex).append("][").append(arrayPart).append(']')
.toString();
}
validationErrorCode.append(".cannot.be.blank");
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(realParameterName).append(" is mandatory.");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
parameter);
realParameterName, arrayIndex);
dataValidationErrors.add(error);
}
return this;
@@ -116,11 +132,18 @@ public class DataValidatorBuilder {
if (value == null && ignoreNullValue) { return this; }
if (value == null || StringUtils.isBlank(value.toString())) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".cannot.be.blank");
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" cannot be blank.");
String realParameterName = this.parameter;
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter);
if (this.arrayIndex != null && StringUtils.isNotBlank(arrayPart)) {
validationErrorCode.append(".").append(this.arrayPart);
realParameterName = new StringBuilder(parameter).append('[').append(this.arrayIndex).append("][").append(arrayPart).append(']')
.toString();
}
validationErrorCode.append(".cannot.be.blank");
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(realParameterName).append(" is mandatory.");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
parameter);
realParameterName, arrayIndex);
dataValidationErrors.add(error);
}
return this;
@@ -247,6 +270,15 @@ public class DataValidatorBuilder {
return this;
}
public void expectedArrayButIsNot() {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".is.not.an.array");
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" is not an array.");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
parameter);
dataValidationErrors.add(error);
}
public DataValidatorBuilder anyOfNotNull(Object... object) {
boolean hasData = false;
for (Object obj : object) {
@@ -36,7 +36,7 @@ public class FromJsonHelper {
public Map<String, Boolean> extractMap(final Type typeOfMap, final String json) {
return this.gsonConverter.fromJson(json, typeOfMap);
}
public Map<String, String> extractDataMap(final Type typeOfMap, final String json) {
return this.gsonConverter.fromJson(json, typeOfMap);
}
@@ -45,6 +45,10 @@ public class FromJsonHelper {
return this.gsonConverter.fromJson(json, classOfT);
}
public String toJson(final JsonElement jsonElement) {
return this.gsonConverter.toJson(jsonElement);
}
public void checkForUnsupportedParameters(final Type typeOfMap, final String json, final Set<String> supportedParams) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
@@ -63,11 +67,11 @@ public class FromJsonHelper {
public JsonElement parse(final String json) {
return parser.parse(json);
}
public boolean parameterExists(final String parameterName, final JsonElement element) {
return helperDelegator.parameterExists(parameterName, element);
}
public String extractStringNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractStringNamed(parameterName, element, new HashSet<String>());
}
@@ -79,11 +83,11 @@ public class FromJsonHelper {
public Long extractLongNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractLongNamed(parameterName, element, new HashSet<String>());
}
public Long extractLongNamed(final String parameterName, final JsonElement element, final Set<String> parametersPassedInRequest) {
return helperDelegator.extractLongNamed(parameterName, element, parametersPassedInRequest);
}
public String[] extractArrayNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractArrayNamed(parameterName, element, new HashSet<String>());
}
@@ -91,7 +95,7 @@ public class FromJsonHelper {
public String[] extractArrayNamed(final String parameterName, final JsonElement element, final Set<String> parametersPassedInRequest) {
return helperDelegator.extractArrayNamed(parameterName, element, parametersPassedInRequest);
}
public Boolean extractBooleanNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractBooleanNamed(parameterName, element, new HashSet<String>());
}
@@ -99,11 +103,11 @@ public class FromJsonHelper {
public Boolean extractBooleanNamed(final String parameterName, final JsonElement element, final Set<String> parametersPassedInRequest) {
return helperDelegator.extractBooleanNamed(parameterName, element, parametersPassedInRequest);
}
public LocalDate extractLocalDateNamed(final String parameterName, JsonElement element) {
return helperDelegator.extractLocalDateNamed(parameterName, element, new HashSet<String>());
}
public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element, final String dateFormat,
final Locale locale) {
return helperDelegator.extractLocalDateNamed(parameterName, element.getAsJsonObject(), dateFormat, locale, new HashSet<String>());
@@ -122,7 +126,7 @@ public class FromJsonHelper {
public BigDecimal extractBigDecimalWithLocaleNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractBigDecimalWithLocaleNamed(parameterName, element, new HashSet<String>());
}
public BigDecimal extractBigDecimalWithLocaleNamed(final String parameterName, final JsonElement element,
final Set<String> parametersPassedInRequest) {
return helperDelegator.extractBigDecimalWithLocaleNamed(parameterName, element, parametersPassedInRequest);
@@ -131,31 +135,30 @@ public class FromJsonHelper {
public BigDecimal extractBigDecimalNamed(final String parameterName, final JsonElement element, final Locale locale) {
return helperDelegator.extractBigDecimalNamed(parameterName, element.getAsJsonObject(), locale, new HashSet<String>());
}
public BigDecimal extractBigDecimalNamed(final String parameterName, final JsonElement element,
final Set<String> parametersPassedInRequest) {
return helperDelegator.extractBigDecimalNamed(parameterName, element.getAsJsonObject(), Locale.US, parametersPassedInRequest);
}
public Integer extractIntegerWithLocaleNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractIntegerWithLocaleNamed(parameterName, element.getAsJsonObject(), new HashSet<String>());
}
public Integer extractIntegerSansLocaleNamed(final String parameterName, final JsonElement element){
return helperDelegator.extractIntegerSansLocaleNamed(parameterName, element.getAsJsonObject(), new HashSet<String>());
public Integer extractIntegerSansLocaleNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractIntegerSansLocaleNamed(parameterName, element.getAsJsonObject(), new HashSet<String>());
}
public Integer extractIntegerWithLocaleNamed(final String parameterName, final JsonElement element,
final Set<String> parametersPassedInRequest) {
return helperDelegator.extractIntegerWithLocaleNamed(parameterName, element.getAsJsonObject(), parametersPassedInRequest);
}
public Integer extractIntegerNamed(final String parameterName, final JsonElement element, final Locale locale) {
return helperDelegator.extractIntegerNamed(parameterName, element.getAsJsonObject(), locale, new HashSet<String>());
}
public Integer extractIntegerNamed(final String parameterName, final JsonElement element,
final Set<String> parametersPassedInRequest) {
public Integer extractIntegerNamed(final String parameterName, final JsonElement element, final Set<String> parametersPassedInRequest) {
return helperDelegator.extractIntegerNamed(parameterName, element.getAsJsonObject(), Locale.US, parametersPassedInRequest);
}
@@ -4,7 +4,8 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRul
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when loan charge does not exist.
* {@link AbstractPlatformDomainRuleException} thrown when loan charge does not
* exist.
*/
public class LoanChargeNotFoundException extends AbstractPlatformResourceNotFoundException {
@@ -12,7 +13,8 @@ public class LoanChargeNotFoundException extends AbstractPlatformResourceNotFoun
super("error.msg.loanCharge.id.invalid", "Loan charge with identifier " + id + " does not exist", id);
}
public LoanChargeNotFoundException(final Long id, final Long loanId) {
super("error.msg.loanCharge.id.invalid.for.given.loan", "Loan charge with identifier " + id + " does not exist for loan " + loanId, id, loanId);
}
}
public LoanChargeNotFoundException(final Long id, final Long loanId) {
super("error.msg.loanCharge.id.invalid.for.given.loan", "Loan charge with identifier " + id + " does not exist for loan " + loanId,
id, loanId);
}
}
@@ -0,0 +1,20 @@
package org.mifosplatform.portfolio.charge.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when loan collateral does
* not exist.
*/
public class LoanCollateralNotFoundException extends AbstractPlatformResourceNotFoundException {
public LoanCollateralNotFoundException(final Long id) {
super("error.msg.loanCollateral.id.invalid", "Loan collateral with identifier " + id + " does not exist", id);
}
public LoanCollateralNotFoundException(final Long id, final Long loanId) {
super("error.msg.loanCollateral.id.invalid.for.given.loan", "Loan collateral with identifier " + id + " does not exist for loan "
+ loanId, id, loanId);
}
}
@@ -87,7 +87,7 @@ public class ClientIdentifiersApiResource {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
final Collection<CodeValueData> codeValues = codeValueReadPlatformService.retrieveCustomIdentifierCodeValues();
final Collection<CodeValueData> codeValues = codeValueReadPlatformService.retrieveCodeValuesByCode("Customer Identifier");
final ClientIdentifierData clientIdentifierData = ClientIdentifierData.template(codeValues);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
@@ -132,7 +132,8 @@ public class ClientIdentifiersApiResource {
ClientIdentifierData clientIdentifierData = this.clientReadPlatformService.retrieveClientIdentifier(clientId, clientIdentifierId);
if (settings.isTemplate()) {
final Collection<CodeValueData> codeValues = codeValueReadPlatformService.retrieveCustomIdentifierCodeValues();
final Collection<CodeValueData> codeValues = codeValueReadPlatformService
.retrieveCodeValuesByCode("Customer Identifier");
clientIdentifierData = ClientIdentifierData.template(clientIdentifierData, codeValues);
}
@@ -7,7 +7,7 @@ import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepository;
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
import org.mifosplatform.infrastructure.codes.exception.CodeValueNotFoundException;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
@@ -22,8 +22,8 @@ import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.organisation.office.exception.OfficeNotFoundException;
import org.mifosplatform.portfolio.client.command.ClientIdentifierCommand;
import org.mifosplatform.portfolio.client.command.ClientNoteCommand;
import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory;
import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator;
import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientIdentifier;
import org.mifosplatform.portfolio.client.domain.ClientIdentifierRepository;
@@ -54,7 +54,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
private final ClientIdentifierRepository clientIdentifierRepository;
private final OfficeRepository officeRepository;
private final NoteRepository noteRepository;
private final CodeValueRepository codeValueRepository;
private final CodeValueRepositoryWrapper codeValueRepository;
private final ClientCommandFromApiJsonDeserializer fromApiJsonDeserializer;
private final ClientNoteCommandFromApiJsonDeserializer clientNoteFromApiJsonDeserializer;
private final ClientIdentifierCommandFromApiJsonDeserializer clientIdentifierCommandFromApiJsonDeserializer;
@@ -63,7 +63,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
@Autowired
public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final ClientRepository clientRepository,
final ClientIdentifierRepository clientIdentifierRepository, final OfficeRepository officeRepository,
final NoteRepository noteRepository, final CodeValueRepository codeValueRepository,
final NoteRepository noteRepository, final CodeValueRepositoryWrapper codeValueRepository,
final ClientCommandFromApiJsonDeserializer fromApiJsonDeserializer,
final ClientNoteCommandFromApiJsonDeserializer clientNoteFromApiJsonDeserializer,
final ClientIdentifierCommandFromApiJsonDeserializer clientIdentifierCommandFromApiJsonDeserializer,
@@ -254,9 +254,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final Client client = this.clientRepository.findOne(clientId);
if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); }
final CodeValue documentType = this.codeValueRepository.findOne(clientIdentifierCommand.getDocumentTypeId());
if (documentType == null) { throw new CodeValueNotFoundException(clientIdentifierCommand.getDocumentTypeId()); }
final CodeValue documentType = this.codeValueRepository.findOneWithNotFoundDetection(clientIdentifierCommand.getDocumentTypeId());
documentTypeId = documentType.getId();
documentTypeLabel = documentType.label();
@@ -297,7 +295,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final Map<String, Object> changes = clientIdentifierForUpdate.update(command);
if (changes.containsKey("documentTypeId")) {
documentType = this.codeValueRepository.findOne(documentTypeId);
documentType = this.codeValueRepository.findOneWithNotFoundDetection(documentTypeId);
if (documentType == null) { throw new CodeValueNotFoundException(documentTypeId); }
documentTypeId = documentType.getId();
@@ -23,6 +23,8 @@ import org.joda.time.LocalDate;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.api.JsonQuery;
@@ -46,6 +48,7 @@ import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
import org.mifosplatform.portfolio.loanaccount.data.LoanAccountData;
import org.mifosplatform.portfolio.loanaccount.data.LoanBasicDetailsData;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
import org.mifosplatform.portfolio.loanaccount.data.LoanCollateralData;
import org.mifosplatform.portfolio.loanaccount.data.LoanPermissionData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData;
import org.mifosplatform.portfolio.loanaccount.guarantor.data.GuarantorData;
@@ -53,6 +56,7 @@ import org.mifosplatform.portfolio.loanaccount.guarantor.service.GuarantorReadPl
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
import org.mifosplatform.portfolio.loanaccount.service.LoanChargeReadPlatformService;
import org.mifosplatform.portfolio.loanaccount.service.LoanCollateralReadPlatformService;
import org.mifosplatform.portfolio.loanaccount.service.LoanReadPlatformService;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData;
@@ -71,16 +75,17 @@ import com.google.gson.JsonElement;
public class LoansApiResource {
private final Set<String> LOAN_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "accountNo", "externalId", "clientId",
"groupId", "clientName", "groupName", "fundId", "fundName", "loanProductId", "loanProductName", "loanProductDescription",
"currency", "principal", "inArrearsTolerance", "numberOfRepayments", "repaymentEvery", "interestRatePerPeriod",
"annualInterestRate", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType",
"interestCalculationPeriodType", "submittedOnDate", "approvedOnDate", "expectedDisbursementDate", "actualDisbursementDate",
"expectedFirstRepaymentOnDate", "interestChargedFromDate", "closedOnDate", "expectedMaturityDate", "status",
"lifeCycleStatusDate", "repaymentSchedule", "transactions", "permissions", "convenienceData", "charges", "guarantor", "productOptions",
"groupId", "clientName", "groupName", "fundId", "fundName", "loanPurposeId", "loanOfficerId", "loanOfficerName",
"loanPurposeName", "loanProductId", "loanProductName", "loanProductDescription", "currency", "principal", "inArrearsTolerance",
"numberOfRepayments", "repaymentEvery", "interestRatePerPeriod", "annualInterestRate", "repaymentFrequencyType",
"interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "submittedOnDate",
"approvedOnDate", "expectedDisbursementDate", "actualDisbursementDate", "expectedFirstRepaymentOnDate",
"interestChargedFromDate", "closedOnDate", "expectedMaturityDate", "status", "lifeCycleStatusDate", "repaymentSchedule",
"transactions", "permissions", "convenienceData", "charges", "guarantor", "collateral", "productOptions",
"amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "repaymentFrequencyTypeOptions",
"interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions", "loanOfficerId",
"loanOfficerName", "loanOfficerOptions", "chargeTemplate", "transactionProcessingStrategyId",
"termFrequency", "termPeriodFrequencyType"));
"termFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions",
"loanOfficerOptions", "chargeTemplate", "transactionProcessingStrategyId", "termFrequency", "termPeriodFrequencyType",
"loanPurposeOptions", "loanCollateralOptions"));
private final String resourceNameForPermissions = "LOAN";
@@ -91,9 +96,11 @@ public class LoansApiResource {
private final FundReadPlatformService fundReadPlatformService;
private final ChargeReadPlatformService chargeReadPlatformService;
private final LoanChargeReadPlatformService loanChargeReadPlatformService;
private final LoanCollateralReadPlatformService loanCollateralReadPlatformService;
private final LoanScheduleCalculationPlatformService calculationPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
private final GuarantorReadPlatformService guarantorReadPlatformService;
private final CodeValueReadPlatformService codeValueReadPlatformService;
private final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer;
private final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer;
private final DefaultToApiJsonSerializer<BulkTransferLoanOfficerData> loanOfficeTransferToApiJsonSerializer;
@@ -106,8 +113,10 @@ public class LoansApiResource {
final LoanProductReadPlatformService loanProductReadPlatformService,
final LoanDropdownReadPlatformService dropdownReadPlatformService, final FundReadPlatformService fundReadPlatformService,
final ChargeReadPlatformService chargeReadPlatformService, final LoanChargeReadPlatformService loanChargeReadPlatformService,
final LoanCollateralReadPlatformService loanCollateralReadPlatformService,
final LoanScheduleCalculationPlatformService calculationPlatformService,
final StaffReadPlatformService staffReadPlatformService, final GuarantorReadPlatformService guarantorReadPlatformService,
final CodeValueReadPlatformService codeValueReadPlatformService,
final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer,
final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer,
final DefaultToApiJsonSerializer<BulkTransferLoanOfficerData> loanOfficeTransferToApiJsonSerializer,
@@ -120,9 +129,11 @@ public class LoansApiResource {
this.fundReadPlatformService = fundReadPlatformService;
this.chargeReadPlatformService = chargeReadPlatformService;
this.loanChargeReadPlatformService = loanChargeReadPlatformService;
this.loanCollateralReadPlatformService = loanCollateralReadPlatformService;
this.calculationPlatformService = calculationPlatformService;
this.staffReadPlatformService = staffReadPlatformService;
this.guarantorReadPlatformService = guarantorReadPlatformService;
this.codeValueReadPlatformService = codeValueReadPlatformService;
this.toApiJsonSerializer = toApiJsonSerializer;
this.loanScheduleToApiJsonSerializer = loanScheduleToApiJsonSerializer;
this.loanOfficeTransferToApiJsonSerializer = loanOfficeTransferToApiJsonSerializer;
@@ -135,12 +146,11 @@ public class LoansApiResource {
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveDetailsForNewLoanApplicationStepOne(@QueryParam("clientId") final Long clientId,
@QueryParam("groupId") final Long groupId, @QueryParam("productId") final Long productId, @Context final UriInfo uriInfo) {
public String templateForNewLoanApplication(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId,
@QueryParam("productId") final Long productId, @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
// tempate related
Collection<LoanProductData> productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup();
Collection<EnumOptionData> loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
Collection<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
@@ -160,26 +170,34 @@ public class LoansApiResource {
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
ChargeData chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate();
LoanBasicDetailsData loanBasicDetails;
Long officeId;
LoanBasicDetailsData loanBasicDetails = null;
Collection<LoanChargeData> charges = null;
Collection<LoanCollateralData> collateral = null;
Collection<GuarantorData> guarantors = null;
Collection<StaffData> allowedLoanOfficers = null;
if (clientId != null) {
loanBasicDetails = this.loanReadPlatformService.retrieveClientAndProductDetails(clientId, productId);
officeId = loanBasicDetails.getClientOfficeId();
} else {
charges = loanBasicDetails.getCharges();
final Long officeId = loanBasicDetails.getClientOfficeId();
allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(officeId);
} else if (groupId != null) {
loanBasicDetails = this.loanReadPlatformService.retrieveGroupAndProductDetails(groupId, productId);
officeId = loanBasicDetails.getGroupOfficeId();
charges = loanBasicDetails.getCharges();
final Long officeId = loanBasicDetails.getGroupOfficeId();
allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(officeId);
}
final Collection<CodeValueData> loanPurposeOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanPurpose");
final Collection<CodeValueData> loanCollateralOptions = this.codeValueReadPlatformService
.retrieveCodeValuesByCode("LoanCollateral");
final boolean convenienceDataRequired = false;
Collection<LoanChargeData> charges = loanBasicDetails.getCharges();
Collection<StaffData> allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(officeId);
final LoanAccountData newLoanAccount = new LoanAccountData(loanBasicDetails, convenienceDataRequired, null, null, null, charges,
productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, null);
collateral, guarantors, productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions,
loanCollateralOptions);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, newLoanAccount, LOAN_DATA_PARAMETERS);
@@ -201,6 +219,7 @@ public class LoansApiResource {
LoanPermissionData permissions = null;
Collection<LoanChargeData> charges = null;
Collection<GuarantorData> guarantors = null;
Collection<LoanCollateralData> collateral = null;
final Set<String> mandatoryResponseParameters = new HashSet<String>();
boolean convenienceDataRequired = false;
@@ -209,14 +228,14 @@ public class LoansApiResource {
if (associationParameters.contains("all")) {
associationParameters.addAll(Arrays.asList("repaymentSchedule", "transactions", "permissions", "convenienceData",
"charges", "guarantor"));
"charges", "guarantor", "collateral"));
}
if (associationParameters.contains("guarantor")) {
mandatoryResponseParameters.add("guarantor");
guarantors = this.guarantorReadPlatformService.retrieveGuarantorsForLoan(loanId);
if (CollectionUtils.isEmpty(guarantors)) {
guarantors = null; // set back to null so doesn't appear in JSON
guarantors = null;
}
}
@@ -233,7 +252,7 @@ public class LoansApiResource {
if (associationParameters.contains("repaymentSchedule")) {
mandatoryResponseParameters.add("repaymentSchedule");
}
DisbursementData singleDisbursement = loanBasicDetails.toDisburementData();
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, loanBasicDetails.getCurrency(),
singleDisbursement, loanBasicDetails.getTotalDisbursementCharges(), loanBasicDetails.getInArrearsTolerance());
@@ -245,7 +264,7 @@ public class LoansApiResource {
if (repaymentSchedule == null) { throw new IllegalStateException(); }
mandatoryResponseParameters.add("permissions");
// FIXME - KW - Waive feature was changed to waive interest at
// anytime so this permission checking is probably not needed -
// look into.
@@ -271,8 +290,15 @@ public class LoansApiResource {
mandatoryResponseParameters.add("charges");
charges = this.loanChargeReadPlatformService.retrieveLoanCharges(loanId);
if (CollectionUtils.isEmpty(charges)) {
charges = null; // set back to null so doesnt appear in JSON
// is no charges exist.
charges = null;
}
}
if (associationParameters.contains("collateral")) {
mandatoryResponseParameters.add("collateral");
collateral = this.loanCollateralReadPlatformService.retrieveLoanCollateral(loanId);
if (CollectionUtils.isEmpty(collateral)) {
collateral = null;
}
}
}
@@ -286,8 +312,11 @@ public class LoansApiResource {
Collection<EnumOptionData> interestTypeOptions = null;
Collection<EnumOptionData> interestCalculationPeriodTypeOptions = null;
Collection<FundData> fundOptions = null;
Collection<StaffData> allowedLoanOfficers = null;
Collection<ChargeData> chargeOptions = null;
ChargeData chargeTemplate = null;
Collection<CodeValueData> loanPurposeOptions = null;
Collection<CodeValueData> loanCollateralOptions = null;
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
if (template) {
@@ -305,14 +334,19 @@ public class LoansApiResource {
final boolean feeChargesOnly = false;
chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate();
allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails.getOfficeId());
loanPurposeOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanPurpose");
loanCollateralOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanCollateral");
}
final LoanAccountData loanAccount = new LoanAccountData(loanBasicDetails, convenienceDataRequired, repaymentSchedule,
loanRepayments, permissions, charges, productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, null, guarantors);
loanRepayments, permissions, charges, collateral, guarantors, productOptions, loanTermFrequencyTypeOptions,
repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions,
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers,
loanPurposeOptions, loanCollateralOptions);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters(), mandatoryResponseParameters);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
mandatoryResponseParameters);
return this.toApiJsonSerializer.serialize(settings, loanAccount, LOAN_DATA_PARAMETERS);
}
@@ -413,7 +447,7 @@ public class LoansApiResource {
final CommandWrapper commandRequest = builder.undoLoanApplicationDisbursal(loanId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
if (is(commandParam, "assignloanofficer")) {
final CommandWrapper commandRequest = builder.assignLoanOfficer(loanId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
@@ -14,14 +14,18 @@ import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidation
*/
public class LoanChargeCommand implements Comparable<LoanChargeCommand> {
@SuppressWarnings("unused")
private final Long id;
private final Long chargeId;
private final BigDecimal amount;
private final Integer chargeTimeType;
private final LocalDate specifiedDueDate;
private final Integer chargeCalculationType;
@SuppressWarnings("unused")
private final LocalDate specifiedDueDate;
public LoanChargeCommand(final Long chargeId, final BigDecimal amount, final Integer chargeTimeType,
public LoanChargeCommand(final Long id, final Long chargeId, final BigDecimal amount, final Integer chargeTimeType,
final Integer chargeCalculationType, final LocalDate specifiedDueDate) {
this.id = id;
this.chargeId = chargeId;
this.amount = amount;
this.chargeTimeType = chargeTimeType;
@@ -38,28 +42,7 @@ public class LoanChargeCommand implements Comparable<LoanChargeCommand> {
return comparison;
}
public void validateForCreate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
baseDataValidator.reset().parameter("chargeId").value(this.chargeId).notNull().longGreaterThanZero();
baseDataValidator.reset().parameter("amount").value(this.amount).notNull().positiveAmount();
// baseDataValidator.reset().parameter("chargeTimeType").value(command.getChargeTimeType()).ignoreIfNull().inMinMaxRange(1,
// 2);
//
// if (command.getChargeTimeType().equals(Integer.valueOf(2))) {
// // date must be provided
// baseDataValidator.reset().parameter("specifiedDueDate").value(command.getSpecifiedDueDate()).notNull();
// }
//
// baseDataValidator.reset().parameter("chargeCalculationType").value(command.getChargeCalculationType()).ignoreIfNull().inMinMaxRange(1,
// 4);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
@Deprecated
public void validateForUpdate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
@@ -4,6 +4,7 @@ import java.math.BigDecimal;
import java.util.Collection;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.staff.data.StaffData;
@@ -34,6 +35,8 @@ public class LoanAccountData {
private final EnumOptionData status;
private final Long fundId;
private final String fundName;
private final Long loanPurposeId;
private final String loanPurposeName;
private final Long loanOfficerId;
private final String loanOfficerName;
private final CurrencyData currency;
@@ -67,9 +70,10 @@ public class LoanAccountData {
private final LoanScheduleData repaymentSchedule;
private final Collection<LoanTransactionData> transactions;
private final Collection<LoanChargeData> charges;
private final Collection<LoanCollateralData> collateral;
private final Collection<GuarantorData> guarantors;
private final LoanPermissionData permissions;
private final LoanConvenienceData convenienceData;
private final Collection<GuarantorData> guarantors;
// template
private final Collection<StaffData> loanOfficerOptions;
@@ -84,21 +88,27 @@ public class LoanAccountData {
private final Collection<FundData> fundOptions;
private final Collection<ChargeData> chargeOptions;
private final ChargeData chargeTemplate;
private final Collection<CodeValueData> loanPurposeOptions;
private final Collection<CodeValueData> loanCollateralOptions;
public LoanAccountData(final LoanBasicDetailsData basicDetails, final boolean convenienceDataRequired,
final LoanScheduleData repaymentSchedule, final Collection<LoanTransactionData> transactions,
final LoanPermissionData permissions, final Collection<LoanChargeData> charges,
final Collection<LoanCollateralData> collateral, final Collection<GuarantorData> guarantors,
final Collection<LoanProductData> productOptions, final Collection<EnumOptionData> termFrequencyTypeOptions,
final Collection<EnumOptionData> repaymentFrequencyTypeOptions,
final Collection<TransactionProcessingStrategyData> repaymentStrategyOptions,
final Collection<EnumOptionData> interestRateFrequencyTypeOptions, final Collection<EnumOptionData> amortizationTypeOptions,
final Collection<EnumOptionData> interestTypeOptions, final Collection<EnumOptionData> interestCalculationPeriodTypeOptions,
final Collection<FundData> fundOptions, final Collection<ChargeData> chargeOptions, final ChargeData chargeTemplate,
final Collection<StaffData> loanOfficerOptions, final Collection<GuarantorData> guarantors) {
final Collection<StaffData> loanOfficerOptions, final Collection<CodeValueData> loanPurposeOptions,
final Collection<CodeValueData> loanCollateralOptions) {
this.repaymentSchedule = repaymentSchedule;
this.transactions = transactions;
this.permissions = permissions;
this.charges = charges;
this.collateral = collateral;
this.guarantors = guarantors;
this.productOptions = productOptions;
this.termFrequencyTypeOptions = termFrequencyTypeOptions;
this.repaymentFrequencyTypeOptions = repaymentFrequencyTypeOptions;
@@ -111,7 +121,8 @@ public class LoanAccountData {
this.chargeOptions = chargeOptions;
this.chargeTemplate = chargeTemplate;
this.loanOfficerOptions = loanOfficerOptions;
this.guarantors = guarantors;
this.loanPurposeOptions = loanPurposeOptions;
this.loanCollateralOptions = loanCollateralOptions;
if (convenienceDataRequired) {
int maxSubmittedOnOffsetFromToday = basicDetails.getMaxSubmittedOnOffsetFromToday();
@@ -129,48 +140,98 @@ public class LoanAccountData {
this.convenienceData = null;
}
this.id = basicDetails.getId();
this.accountNo = basicDetails.getAccountNo();
this.externalId = basicDetails.getExternalId();
this.clientId = basicDetails.getClientId();
this.clientName = basicDetails.getClientName();
this.groupId = basicDetails.getGroupId();
this.groupName = basicDetails.getGroupName();
this.loanProductId = basicDetails.getLoanProductId();
this.loanProductName = basicDetails.getLoanProductName();
this.loanProductDescription = basicDetails.getLoanProductDescription();
this.fundId = basicDetails.getFundId();
this.fundName = basicDetails.getFundName();
this.loanOfficerId = basicDetails.getLoanOfficerId();
this.loanOfficerName = basicDetails.getLoanOfficerName();
if (basicDetails != null) {
this.id = basicDetails.getId();
this.accountNo = basicDetails.getAccountNo();
this.externalId = basicDetails.getExternalId();
this.clientId = basicDetails.getClientId();
this.clientName = basicDetails.getClientName();
this.groupId = basicDetails.getGroupId();
this.groupName = basicDetails.getGroupName();
this.loanProductId = basicDetails.getLoanProductId();
this.loanProductName = basicDetails.getLoanProductName();
this.loanProductDescription = basicDetails.getLoanProductDescription();
this.fundId = basicDetails.getFundId();
this.fundName = basicDetails.getFundName();
this.loanPurposeId = basicDetails.getLoanPurposeId();
this.loanPurposeName = basicDetails.getLoanPurposeName();
this.loanOfficerId = basicDetails.getLoanOfficerId();
this.loanOfficerName = basicDetails.getLoanOfficerName();
this.submittedOnDate = basicDetails.getSubmittedOnDate();
this.approvedOnDate = basicDetails.getApprovedOnDate();
this.expectedDisbursementDate = basicDetails.getExpectedDisbursementDate();
this.actualDisbursementDate = basicDetails.getActualDisbursementDate();
this.closedOnDate = basicDetails.getClosedOnDate();
this.expectedMaturityDate = basicDetails.getExpectedMaturityDate();
this.repaymentsStartingFromDate = basicDetails.getRepaymentsStartingFromDate();
this.interestChargedFromDate = basicDetails.getInterestChargedFromDate();
this.submittedOnDate = basicDetails.getSubmittedOnDate();
this.approvedOnDate = basicDetails.getApprovedOnDate();
this.expectedDisbursementDate = basicDetails.getExpectedDisbursementDate();
this.actualDisbursementDate = basicDetails.getActualDisbursementDate();
this.closedOnDate = basicDetails.getClosedOnDate();
this.expectedMaturityDate = basicDetails.getExpectedMaturityDate();
this.repaymentsStartingFromDate = basicDetails.getRepaymentsStartingFromDate();
this.interestChargedFromDate = basicDetails.getInterestChargedFromDate();
this.currency = basicDetails.getCurrency();
this.principal = basicDetails.getPrincipal();
this.inArrearsTolerance = basicDetails.getInArrearsTolerance();
this.currency = basicDetails.getCurrency();
this.principal = basicDetails.getPrincipal();
this.inArrearsTolerance = basicDetails.getInArrearsTolerance();
this.termFrequency = basicDetails.getTermFrequency();
this.termPeriodFrequencyType = basicDetails.getTermPeriodFrequencyType();
this.numberOfRepayments = basicDetails.getNumberOfRepayments();
this.repaymentEvery = basicDetails.getRepaymentEvery();
this.transactionProcessingStrategyId = basicDetails.getTransactionStrategyId();
this.interestRatePerPeriod = basicDetails.getInterestRatePerPeriod();
this.annualInterestRate = basicDetails.getAnnualInterestRate();
this.repaymentFrequencyType = basicDetails.getRepaymentFrequencyType();
this.interestRateFrequencyType = basicDetails.getInterestRateFrequencyType();
this.amortizationType = basicDetails.getAmortizationType();
this.interestType = basicDetails.getInterestType();
this.interestCalculationPeriodType = basicDetails.getInterestCalculationPeriodType();
this.termFrequency = basicDetails.getTermFrequency();
this.termPeriodFrequencyType = basicDetails.getTermPeriodFrequencyType();
this.numberOfRepayments = basicDetails.getNumberOfRepayments();
this.repaymentEvery = basicDetails.getRepaymentEvery();
this.transactionProcessingStrategyId = basicDetails.getTransactionStrategyId();
this.interestRatePerPeriod = basicDetails.getInterestRatePerPeriod();
this.annualInterestRate = basicDetails.getAnnualInterestRate();
this.repaymentFrequencyType = basicDetails.getRepaymentFrequencyType();
this.interestRateFrequencyType = basicDetails.getInterestRateFrequencyType();
this.amortizationType = basicDetails.getAmortizationType();
this.interestType = basicDetails.getInterestType();
this.interestCalculationPeriodType = basicDetails.getInterestCalculationPeriodType();
this.status = basicDetails.getStatus();
this.lifeCycleStatusDate = basicDetails.getLifeCycleStatusDate();
this.status = basicDetails.getStatus();
this.lifeCycleStatusDate = basicDetails.getLifeCycleStatusDate();
} else {
this.id = null;
this.accountNo = null;
this.externalId = null;
this.clientId = null;
this.clientName = null;
this.groupId = null;
this.groupName = null;
this.loanProductId = null;
this.loanProductName = null;
this.loanProductDescription = null;
this.fundId = null;
this.fundName = null;
this.loanPurposeId = null;
this.loanPurposeName = null;
this.loanOfficerId = null;
this.loanOfficerName = null;
this.submittedOnDate = null;
this.approvedOnDate = null;
this.expectedDisbursementDate = null;
this.actualDisbursementDate = null;
this.closedOnDate = null;
this.expectedMaturityDate = null;
this.repaymentsStartingFromDate = null;
this.interestChargedFromDate = null;
this.currency = null;
this.principal = null;
this.inArrearsTolerance = null;
this.termFrequency = null;
this.termPeriodFrequencyType = null;
this.numberOfRepayments = null;
this.repaymentEvery = null;
this.transactionProcessingStrategyId = null;
this.interestRatePerPeriod = null;
this.annualInterestRate = null;
this.repaymentFrequencyType = null;
this.interestRateFrequencyType = null;
this.amortizationType = null;
this.interestType = null;
this.interestCalculationPeriodType = null;
this.status = null;
this.lifeCycleStatusDate = null;
}
}
}
@@ -34,6 +34,8 @@ public class LoanBasicDetailsData {
private final EnumOptionData status;
private final Long fundId;
private final String fundName;
private final Long loanPurposeId;
private final String loanPurposeName;
private final Long loanOfficerId;
private final String loanOfficerName;
private final CurrencyData currency;
@@ -120,6 +122,8 @@ public class LoanBasicDetailsData {
this.status = null;
this.fundId = fundId;
this.fundName = fundName;
this.loanPurposeId = null;
this.loanPurposeName = null;
this.loanOfficerId = null;
this.loanOfficerName = null;
this.currency = currency;
@@ -179,6 +183,8 @@ public class LoanBasicDetailsData {
this.status = null;
this.fundId = null;
this.fundName = null;
this.loanPurposeId = null;
this.loanPurposeName = null;
this.loanOfficerId = null;
this.loanOfficerName = null;
this.currency = null;
@@ -216,16 +222,17 @@ public class LoanBasicDetailsData {
public LoanBasicDetailsData(final Long id, final String accountNo, final String externalId, final Long clientId,
final String clientName, final Long clientOfficeId, final Long groupId, final String groupName, final Long groupOfficeId,
final Long loanProductId, final String loanProductName, final String loanProductDescription, final Long fundId,
String fundName, final LocalDate closedOnDate, final LocalDate submittedOnDate, final LocalDate approvedOnDate,
final LocalDate expectedDisbursementDate, final LocalDate actualDisbursementDate, final LocalDate expectedMaturityDate,
final LocalDate repaymentsStartingFromDate, final LocalDate interestChargedFromDate, final CurrencyData currency,
final BigDecimal principal, final BigDecimal inArrearsTolerance, final Integer numberOfRepayments,
final Integer repaymentEvery, final BigDecimal interestRatePerPeriod, final BigDecimal annualInterestRate,
final EnumOptionData repaymentFrequencyType, final EnumOptionData interestRateFrequencyType,
final EnumOptionData amortizationType, final EnumOptionData interestType, final EnumOptionData interestCalculationPeriodType,
final EnumOptionData status, final LocalDate lifeCycleStatusDate, final Integer termFrequency,
final EnumOptionData termPeriodFrequencyType, final Integer transactionStrategyId, final Collection<LoanChargeData> charges,
final Long loanOfficerId, String loanOfficerName, final BigDecimal totalDisbursementCharges) {
final String fundName, final Long loanPurposeId, final String loanPurposeName, final LocalDate closedOnDate,
final LocalDate submittedOnDate, final LocalDate approvedOnDate, final LocalDate expectedDisbursementDate,
final LocalDate actualDisbursementDate, final LocalDate expectedMaturityDate, final LocalDate repaymentsStartingFromDate,
final LocalDate interestChargedFromDate, final CurrencyData currency, final BigDecimal principal,
final BigDecimal inArrearsTolerance, final Integer numberOfRepayments, final Integer repaymentEvery,
final BigDecimal interestRatePerPeriod, final BigDecimal annualInterestRate, final EnumOptionData repaymentFrequencyType,
final EnumOptionData interestRateFrequencyType, final EnumOptionData amortizationType, final EnumOptionData interestType,
final EnumOptionData interestCalculationPeriodType, final EnumOptionData status, final LocalDate lifeCycleStatusDate,
final Integer termFrequency, final EnumOptionData termPeriodFrequencyType, final Integer transactionStrategyId,
final Collection<LoanChargeData> charges, final Long loanOfficerId, String loanOfficerName,
final BigDecimal totalDisbursementCharges) {
this.id = id;
this.accountNo = accountNo;
this.externalId = externalId;
@@ -240,6 +247,8 @@ public class LoanBasicDetailsData {
this.loanProductDescription = loanProductDescription;
this.fundId = fundId;
this.fundName = fundName;
this.loanPurposeId = loanPurposeId;
this.loanPurposeName = loanPurposeName;
this.loanOfficerId = loanOfficerId;
this.loanOfficerName = loanOfficerName;
this.closedOnDate = closedOnDate;
@@ -393,6 +402,14 @@ public class LoanBasicDetailsData {
return fundName;
}
public Long getLoanPurposeId() {
return this.loanPurposeId;
}
public String getLoanPurposeName() {
return this.loanPurposeName;
}
public Long getLoanProductId() {
return loanProductId;
}
@@ -0,0 +1,26 @@
package org.mifosplatform.portfolio.loanaccount.data;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
/**
* Immutable data object for loan collateral data.
*/
public class LoanCollateralData {
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
private final CodeValueData type;
@SuppressWarnings("unused")
private final String description;
public static LoanCollateralData instance(final Long id, final CodeValueData type, final String description) {
return new LoanCollateralData(id, type, description);
}
private LoanCollateralData(final Long id, final CodeValueData type, final String description) {
this.id = id;
this.type = type;
this.description = description;
}
}
@@ -2,7 +2,6 @@ package org.mifosplatform.portfolio.loanaccount.domain;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
@@ -33,6 +32,7 @@ import org.hibernate.annotations.LazyCollectionOption;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
import org.mifosplatform.infrastructure.core.service.DateUtils;
@@ -46,6 +46,7 @@ import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.fund.domain.Fund;
import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
import org.mifosplatform.portfolio.loanaccount.data.LoanCollateralData;
import org.mifosplatform.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
import org.mifosplatform.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
@@ -67,8 +68,7 @@ import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.useradministration.domain.AppUser;
@Entity
@Table(name = "m_loan", uniqueConstraints = {
@UniqueConstraint(columnNames = { "account_no" }, name = "loan_account_no_UNIQUE"),
@Table(name = "m_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "loan_account_no_UNIQUE"),
@UniqueConstraint(columnNames = { "external_id" }, name = "loan_externalid_UNIQUE") })
public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@@ -77,7 +77,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "external_id")
private String externalId;
@ManyToOne(optional = true)
@JoinColumn(name = "client_id")
private Client client;
@@ -96,7 +96,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne
@JoinColumn(name = "loan_officer_id", nullable = true)
private Staff loanofficer;
private Staff loanOfficer;
@ManyToOne
@JoinColumn(name = "loanpurpose_cv_id", nullable = true)
private CodeValue loanPurpose;
@ManyToOne
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
@@ -171,6 +175,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
private Set<LoanCharge> charges = new HashSet<LoanCharge>();
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
private Set<LoanCollateral> collateral = null;
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
private Set<LoanOfficerAssignmentHistory> loanOfficerHistory;
@@ -198,21 +206,24 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessor = new LoanRepaymentScheduleTransactionProcessorFactory();
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final LoanProduct loanProduct,
final Fund fund, final Staff officer, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final LoanSchedule loanSchedule, final Set<LoanCharge> loanCharges) {
final Fund fund, final Staff officer, final CodeValue loanPurpose,
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanSchedule loanSchedule,
final Set<LoanCharge> loanCharges, final Set<LoanCollateral> collateral) {
final LoanStatus status = null;
LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
return new Loan(accountNo, client, null, fund, officer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail,
status, loanCharges);
return new Loan(accountNo, client, null, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
loanRepaymentScheduleDetail, status, loanCharges, collateral);
}
public static Loan newGroupLoanApplication(final String accountNo, final Group group, final LoanProduct loanProduct, final Fund fund,
final Staff officer, final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanSchedule loanSchedule,
final Set<LoanCharge> loanCharges) {
final LoanStatus status = null;
LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
return new Loan(accountNo, null, group, fund, officer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail,
status, loanCharges);
final LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
final CodeValue loanPurpose = null;
final Set<LoanCollateral> collateral = null;
return new Loan(accountNo, null, group, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
loanRepaymentScheduleDetail, status, loanCharges, collateral);
}
public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group,
@@ -220,19 +231,21 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanSchedule loanSchedule,
final Set<LoanCharge> loanCharges) {
final LoanStatus status = null;
LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
return new Loan(accountNo, client, group, fund, officer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail,
status, loanCharges);
final LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
final CodeValue loanPurpose = null;
final Set<LoanCollateral> collateral = null;
return new Loan(accountNo, client, group, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
loanRepaymentScheduleDetail, status, loanCharges, collateral);
}
protected Loan() {
//
}
private Loan(final String accountNo, final Client client, final Group group, Fund fund, Staff loanOfficer,
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanProduct loanProduct,
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final LoanStatus loanStatus, final Set<LoanCharge> loanCharges) {
private Loan(final String accountNo, final Client client, final Group group, final Fund fund, final Staff loanOfficer,
final CodeValue loanPurpose, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final LoanProduct loanProduct, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final LoanStatus loanStatus,
final Set<LoanCharge> loanCharges, final Set<LoanCollateral> collateral) {
if (StringUtils.isBlank(accountNo)) {
this.accountNumber = new RandomPasswordGenerator(19).generate();
this.accountNumberRequiresAutoGeneration = true;
@@ -242,7 +255,9 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.client = client;
this.group = group;
this.fund = fund;
this.loanofficer = loanOfficer;
this.loanOfficer = loanOfficer;
this.loanPurpose = loanPurpose;
this.transactionProcessingStrategy = transactionProcessingStrategy;
this.loanProduct = loanProduct;
this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
@@ -257,6 +272,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} else {
this.charges = null;
}
if (collateral != null && !collateral.isEmpty()) {
this.collateral = associateWithThisLoan(collateral);
} else {
this.collateral = null;
}
this.loanOfficerHistory = null;
}
@@ -280,14 +300,21 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return loanCharges;
}
private Set<LoanCollateral> associateWithThisLoan(final Set<LoanCollateral> collateral) {
for (LoanCollateral item : collateral) {
item.associateWith(this);
}
return collateral;
}
private void updateTotalChargesDueAtDisbursement() {
this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement();
}
public boolean isAccountNumberRequiresAutoGeneration() {
return this.accountNumberRequiresAutoGeneration;
}
public void setAccountNumberRequiresAutoGeneration(boolean accountNumberRequiresAutoGeneration) {
this.accountNumberRequiresAutoGeneration = accountNumberRequiresAutoGeneration;
}
@@ -354,7 +381,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
private void validateChargeHasValidSpecifiedDateIfApplicable(final LoanCharge loanCharge, final LocalDate disbursementDate,
final LocalDate lastRepaymentPeriodDueDate) {
if (loanCharge.isSpecifiedDueDate() && !loanCharge.isDueForCollectionBetween(disbursementDate, lastRepaymentPeriodDueDate)) {
if (loanCharge.isSpecifiedDueDate()
&& !loanCharge.isDueForCollectionFromAndUpToAndIncluding(disbursementDate, lastRepaymentPeriodDueDate)) {
final String defaultUserMessage = "This charge which is due at disbursement cannot be added as the loan is already disbursed.";
throw new LoanChargeCannotBeAddedException("loanCharge", "specified.due.date.outside.range", defaultUserMessage,
getDisbursementDate(), getLastRepaymentPeriodDueDate(), loanCharge.name());
@@ -505,10 +533,30 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.loanProduct = loanProduct;
}
public void updateAccountNo(final String newAccountNo) {
this.accountNumber = newAccountNo;
this.accountNumberRequiresAutoGeneration = false;
}
public void updateFund(final Fund fund) {
this.fund = fund;
}
public void updateLoanPurpose(final CodeValue loanPurpose) {
this.loanPurpose = loanPurpose;
}
public void updateLoanOfficerOnLoanApplication(final Staff newLoanOfficer) {
if (!this.isSubmittedAndPendingApproval()) {
Long loanOfficerId = null;
if (this.loanOfficer != null) {
loanOfficerId = this.loanOfficer.getId();
}
throw new LoanOfficerAssignmentException(this.getId(), loanOfficerId);
}
this.loanOfficer = newLoanOfficer;
}
public void updateTransactionProcessingStrategy(final LoanTransactionProcessingStrategy strategy) {
this.transactionProcessingStrategy = strategy;
}
@@ -519,6 +567,14 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement();
}
public void updateLoanCollateral(final Set<LoanCollateral> loanCollateral) {
if (this.collateral == null) {
this.collateral = new HashSet<LoanCollateral>();
}
this.collateral.clear();
this.collateral.addAll(associateWithThisLoan(loanCollateral));
}
public void updateLoanSchedule(final LoanScheduleData modifiedLoanSchedule) {
this.repaymentScheduleInstallments.clear();
@@ -548,8 +604,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.expectedMaturityDate = determineExpectedMaturityDate().toDate();
}
public Map<String, Object> loanApplicationModification(final JsonCommand command, final LoanChargeCommand[] loanChargeCommands,
final AprCalculator aprCalculator) {
public Map<String, Object> loanApplicationModification(final JsonCommand command, final Set<LoanCharge> possiblyModifedLoanCharges,
final Set<LoanCollateral> possiblyModifedLoanCollateralItems, final AprCalculator aprCalculator) {
final Map<String, Object> actualChanges = this.loanRepaymentScheduleDetail.updateLoanApplicationAttributes(command, aprCalculator);
if (!actualChanges.isEmpty()) {
@@ -566,7 +622,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
actualChanges.put(accountNoParamName, newValue);
this.accountNumber = StringUtils.defaultIfEmpty(newValue, null);
}
final String externalIdParamName = "externalId";
if (command.isChangeInStringParameterNamed(externalIdParamName, this.externalId)) {
final String newValue = command.stringValueOfParameterNamed(externalIdParamName);
@@ -597,21 +653,32 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
actualChanges.put(fundIdParamName, newValue);
}
Long existingLoanOfficerId = null;
if (this.loanOfficer != null) {
existingLoanOfficerId = this.loanOfficer.getId();
}
final String loanOfficerIdParamName = "loanOfficerId";
if (command.isChangeInLongParameterNamed(loanOfficerIdParamName, existingLoanOfficerId)) {
final Long newValue = command.longValueOfParameterNamed(loanOfficerIdParamName);
actualChanges.put(loanOfficerIdParamName, newValue);
}
Long existingLoanPurposeId = null;
if (this.loanPurpose != null) {
existingLoanPurposeId = this.loanPurpose.getId();
}
final String loanPurposeIdParamName = "loanPurposeId";
if (command.isChangeInLongParameterNamed(loanPurposeIdParamName, existingLoanPurposeId)) {
final Long newValue = command.longValueOfParameterNamed(loanPurposeIdParamName);
actualChanges.put(loanPurposeIdParamName, newValue);
}
final String strategyIdParamName = "transactionProcessingStrategyId";
if (command.isChangeInLongParameterNamed(strategyIdParamName, this.transactionProcessingStrategy.getId())) {
final Long newValue = command.longValueOfParameterNamed(strategyIdParamName);
actualChanges.put(strategyIdParamName, newValue);
}
final String chargesParamName = "charges";
if (command.parameterExists(chargesParamName)) {
LoanChargeCommand[] existingLoanChargeData = getLoanCharges();
if (differenceExistsBetween(existingLoanChargeData, loanChargeCommands)) {
actualChanges.put(chargesParamName, loanChargeCommands);
actualChanges.put("recalculateLoanSchedule", true);
}
}
final String submittedOnDateParamName = "submittedOnDate";
if (command.isChangeInLocalDateParameterNamed(submittedOnDateParamName, getSubmittedOnDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(submittedOnDateParamName);
@@ -668,7 +735,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.interestChargedFromDate = null;
}
}
if (getSubmittedOnDate().isAfter(new LocalDate())) {
final String errorMessage = "The date on which a loan is submitted cannot be in the future.";
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.a.future.date", errorMessage, getSubmittedOnDate());
@@ -680,33 +747,59 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.after.expected.disbursement.date", errorMessage,
getSubmittedOnDate(), getExpectedDisbursedOnLocalDate());
}
// FIXME - this constraint doesnt really need to be here. should be
// possible to express loan term as say 12 months whilst also saying
// - that the repayment structure is 6 repayments every bi-monthly.
// validateSelectedPeriodFrequencyTypeIsTheSame(dataValidationErrors, loanTermFrequency, loanTermFrequencyType, numberOfRepayments,
// repaymentEvery, repaymentEveryType);
//
// validateRepaymentsStartingFromDateIsAfterDisbursementDate(dataValidationErrors, expectedDisbursementDate,
// repaymentsStartingFromDate);
//
// validateRepaymentsStartingFromDateAndInterestChargedFromDate(dataValidationErrors, expectedDisbursementDate,
// repaymentsStartingFromDate, interestChargedFromDate);
// charges are optional
for (LoanCharge loanCharge : getNullPointerSafeLoanCharges()) {
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
final String chargesParamName = "charges";
if (command.parameterExists(chargesParamName)) {
final Set<LoanCharge> existingLoanCharges = getNullPointerSafeLoanCharges();
if (!possiblyModifedLoanCharges.equals(existingLoanCharges)) {
actualChanges.put(chargesParamName, getLoanCharges(possiblyModifedLoanCharges));
actualChanges.put(chargesParamName, getLoanCharges(possiblyModifedLoanCharges));
actualChanges.put("recalculateLoanSchedule", true);
for (LoanCharge loanCharge : possiblyModifedLoanCharges) {
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
}
}
// final ImmutableSet<LoanCharge> difference =
// Sets.symmetricDifference(existingLoanCharges,
// possiblyModifedLoanCharges)
// .immutableCopy();
// if (!difference.isEmpty()) {
// actualChanges.put(chargesParamName, getLoanCharges(difference));
// actualChanges.put("recalculateLoanSchedule", true);
//
// for (LoanCharge loanCharge : possiblyModifedLoanCharges) {
// validateChargeHasValidSpecifiedDateIfApplicable(loanCharge,
// getDisbursementDate(), getLastRepaymentPeriodDueDate());
// }
// }
}
final String collateralParamName = "collateral";
if (command.parameterExists(collateralParamName)) {
if (!possiblyModifedLoanCollateralItems.equals(this.collateral)) {
actualChanges.put(collateralParamName, listOfLoanCollateralData(possiblyModifedLoanCollateralItems));
}
//
// final ImmutableSet<LoanCollateral> difference =
// Sets.symmetricDifference(this.collateral,
// possiblyModifedLoanCollateralItems)
// .immutableCopy();
// if (!difference.isEmpty()) {
// actualChanges.put(collateralParamName,
// listOfLoanCollateralData(difference));
// }
}
return actualChanges;
}
private boolean differenceExistsBetween(final LoanChargeCommand[] existingLoanChargeData, final LoanChargeCommand[] newLoanChargeData) {
Arrays.sort(existingLoanChargeData);
Arrays.sort(newLoanChargeData);
return !Arrays.equals(existingLoanChargeData, newLoanChargeData);
}
private Set<LoanCharge> getNullPointerSafeLoanCharges() {
Set<LoanCharge> loanCharges = this.charges;
if (this.charges == null) {
@@ -715,19 +808,34 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return loanCharges;
}
private LoanChargeCommand[] getLoanCharges() {
private LoanCollateralData[] listOfLoanCollateralData(final Set<LoanCollateral> setOfLoanCollateral) {
LoanCollateralData[] existingLoanCollateral = null;
List<LoanCollateralData> loanCollateralList = new ArrayList<LoanCollateralData>();
for (LoanCollateral loanCollateral : setOfLoanCollateral) {
LoanCollateralData data = loanCollateral.toData();
loanCollateralList.add(data);
}
existingLoanCollateral = loanCollateralList.toArray(new LoanCollateralData[loanCollateralList.size()]);
return existingLoanCollateral;
}
private LoanChargeCommand[] getLoanCharges(Set<LoanCharge> setOfLoanCharges) {
LoanChargeCommand[] existingLoanCharges = null;
if (!getNullPointerSafeLoanCharges().isEmpty()) {
List<LoanChargeCommand> loanChargesList = new ArrayList<LoanChargeCommand>();
for (LoanCharge loanCharge : getNullPointerSafeLoanCharges()) {
loanChargesList.add(loanCharge.toCommand());
}
existingLoanCharges = loanChargesList.toArray(new LoanChargeCommand[loanChargesList.size()]);
List<LoanChargeCommand> loanChargesList = new ArrayList<LoanChargeCommand>();
for (LoanCharge loanCharge : setOfLoanCharges) {
loanChargesList.add(loanCharge.toCommand());
}
existingLoanCharges = loanChargesList.toArray(new LoanChargeCommand[loanChargesList.size()]);
return existingLoanCharges;
}
@@ -744,12 +852,13 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
final LocalDate submittedOn, final String externalId) {
final LoanScheduleData loanScheduleData = loanSchedule.generate();
// Have to set expectedDisbursementDate to avoid nullPointer so should be passed down to updateLoanSchedule method
// Have to set expectedDisbursementDate to avoid nullPointer so should
// be passed down to updateLoanSchedule method
if (loanSchedule.getDisbursementDate() != null) {
this.expectedDisbursedOnDate = loanSchedule.getDisbursementDate().toDate();
}
}
updateLoanSchedule(loanScheduleData);
LoanStatus from = null;
@@ -761,7 +870,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.loanStatus = statusEnum.getValue();
this.externalId = externalId;
this.termFrequency = loanSchedule.getLoanTermFrequency();
this.termPeriodFrequencyType = loanSchedule.getLoanTermPeriodFrequencyType().getValue();
@@ -807,7 +916,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
LocalDate rejectedOn = command.localDateValueOfParameterNamed("rejectedOnDate");
if (rejectedOn == null) {
rejectedOn = command.localDateValueOfParameterNamed("eventDate");
@@ -847,7 +956,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
LocalDate withdrawnOn = command.localDateValueOfParameterNamed("withdrawnOnDate");
if (withdrawnOn == null) {
withdrawnOn = command.localDateValueOfParameterNamed("eventDate");
@@ -887,7 +996,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
// only do below if status has changed in the 'approval' case
LocalDate approvedOn = command.localDateValueOfParameterNamed("approvedOnDate");
String approvedOnDateChange = command.stringValueOfParameterNamed("approvedOnDate");
@@ -905,17 +1014,17 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (approvedOn.isBefore(submittalDate)) {
final String errorMessage = "The date on which a loan is approved cannot be before its submittal date: "
+ submittalDate.toString();
throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage, getApprovedOnDate(),
submittalDate);
throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage,
getApprovedOnDate(), submittalDate);
}
if (approvedOn.isAfter(new LocalDate())) {
final String errorMessage = "The date on which a loan is approved cannot be in the future.";
throw new InvalidLoanStateTransitionException("approval", "cannot.be.a.future.date", errorMessage, getApprovedOnDate());
}
if (this.loanofficer != null) {
if (this.loanOfficer != null) {
final LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = LoanOfficerAssignmentHistory.createNew(this,
this.loanofficer, approvedOn);
this.loanOfficer, approvedOn);
this.loanOfficerHistory.add(loanOfficerAssignmentHistory);
}
}
@@ -932,7 +1041,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (!statusEnum.hasStateOf(currentStatus)) {
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
this.approvedOnDate = null;
actualChanges.put("approvedOnDate", "");
@@ -959,7 +1068,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
LocalDate disbursedOn = command.localDateValueOfParameterNamed("disbursedOnDate");
if (disbursedOn == null) {
disbursedOn = command.localDateValueOfParameterNamed("eventDate");
@@ -1074,7 +1183,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (!statusEnum.hasStateOf(currentStatus)) {
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
this.approvedOnDate = null;
actualChanges.put("disbursedOnDate", "");
@@ -1861,8 +1970,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
public boolean hasLoanOfficer(final Staff fromLoanOfficer) {
boolean matchesCurrentLoanOfficer = false;
if (this.loanofficer != null) {
matchesCurrentLoanOfficer = this.loanofficer.identifiedBy(fromLoanOfficer);
if (this.loanOfficer != null) {
matchesCurrentLoanOfficer = this.loanOfficer.identifiedBy(fromLoanOfficer);
} else {
matchesCurrentLoanOfficer = fromLoanOfficer == null;
}
@@ -1920,11 +2029,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
final LoanOfficerAssignmentHistory latestHistoryRecord = findLatestIncompleteHistoryRecord();
if (latestHistoryRecord != null && this.loanofficer.identifiedBy(newLoanOfficer)) {
if (latestHistoryRecord != null && this.loanOfficer.identifiedBy(newLoanOfficer)) {
latestHistoryRecord.updateStartDate(assignmentDate);
} else if (latestHistoryRecord != null && latestHistoryRecord.matchesStartDateOf(assignmentDate)) {
latestHistoryRecord.updateLoanOfficer(newLoanOfficer);
this.loanofficer = newLoanOfficer;
this.loanOfficer = newLoanOfficer;
} else if (latestHistoryRecord != null && latestHistoryRecord.hasStartDateBefore(assignmentDate)) {
throw new LoanOfficerAssignmentException(this.getId(), assignmentDate);
} else {
@@ -1934,16 +2043,15 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
latestHistoryRecord.updateEndDate(assignmentDate);
}
this.loanofficer = newLoanOfficer;
this.loanOfficer = newLoanOfficer;
if (this.isNotSubmittedAndPendingApproval()) {
final LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = LoanOfficerAssignmentHistory.createNew(this,
this.loanofficer, assignmentDate);
this.loanOfficer, assignmentDate);
this.loanOfficerHistory.add(loanOfficerAssignmentHistory);
}
}
}
public void removeLoanOfficer(final LocalDate unassignDate) {
final LoanOfficerAssignmentHistory latestHistoryRecord = findLatestIncompleteHistoryRecord();
@@ -1953,7 +2061,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
latestHistoryRecord.updateEndDate(unassignDate);
}
this.loanofficer = null;
this.loanOfficer = null;
}
private void validateUnassignDate(final LoanOfficerAssignmentHistory latestHistoryRecord, final LocalDate unassignDate) {
@@ -1963,8 +2071,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (latestHistoryRecord.getStartDate().isAfter(unassignDate)) {
throw new LoanOfficerUnassignmentDateException(this.getId(), this.getLoanOfficer().getId(), latestHistoryRecord.getStartDate(),
unassignDate);
} else if (unassignDate.isAfter(today)) { throw new LoanOfficerUnassignmentDateException(
this.getId()); }
} else if (unassignDate.isAfter(today)) { throw new LoanOfficerUnassignmentDateException(this.getId()); }
}
private LoanOfficerAssignmentHistory findLatestIncompleteHistoryRecord() {
@@ -2008,11 +2115,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return officeId;
}
public void updateAccountNo(final String newAccountNo) {
this.accountNumber = newAccountNo;
this.accountNumberRequiresAutoGeneration=false;
}
public List<LoanTransaction> getLoanTransactions() {
return this.loanTransactions;
}
@@ -2020,21 +2122,20 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
public boolean isAccountingEnabledOnLoanProduct() {
return this.loanProduct.isAccountingEnabled();
}
public boolean isCashBasedAccountingEnabledOnLoanProduct(){
public boolean isCashBasedAccountingEnabledOnLoanProduct() {
return this.loanProduct.isCashBasedAccountingEnabled();
}
public boolean isAccrualBasedAccountingEnabledOnLoanProduct(){
public boolean isAccrualBasedAccountingEnabledOnLoanProduct() {
return this.loanProduct.isAccrualBasedAccountingEnabled();
}
public Long productId() {
return this.loanProduct.getId();
}
public Staff getLoanOfficer() {
return this.loanofficer;
}
public Staff getLoanOfficer() {
return this.loanOfficer;
}
}
@@ -15,6 +15,8 @@ import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
@@ -347,9 +349,8 @@ public class LoanCharge extends AbstractPersistable<Long> {
}
public LoanChargeCommand toCommand() {
final LocalDate specifiedDueDate = getDueForCollectionAsOfLocalDate();
return new LoanChargeCommand(this.charge.getId(), this.amount, this.chargeTime, this.chargeCalculation, specifiedDueDate);
return new LoanChargeCommand(this.getId(), this.charge.getId(), this.amount, this.chargeTime, this.chargeCalculation,
getDueForCollectionAsOfLocalDate());
}
public LocalDate getDueForCollectionAsOfLocalDate() {
@@ -411,16 +412,14 @@ public class LoanCharge extends AbstractPersistable<Long> {
return this.loan.hasIdentifyOf(loanId);
}
public boolean isDueForCollectionBetween(final LocalDate fromNotInclusive, final LocalDate toInclusive) {
public boolean isDueForCollectionFromAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive) {
final LocalDate specifiedDueDate = getDueForCollectionAsOfLocalDate();
return specifiedDueDateFallsWithinPeriod(fromNotInclusive, toInclusive, specifiedDueDate);
return occursOnDayFromAndUpToAndIncluding(fromNotInclusive, upToAndInclusive, specifiedDueDate);
}
private boolean specifiedDueDateFallsWithinPeriod(final LocalDate fromNotInclusive, final LocalDate toInclusive,
final LocalDate specifiedDueDate) {
return specifiedDueDate != null && fromNotInclusive.isBefore(specifiedDueDate)
&& (toInclusive.isAfter(specifiedDueDate) || toInclusive.isEqual(specifiedDueDate));
private boolean occursOnDayFromAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive,
final LocalDate target) {
return target != null && target.isAfter(fromNotInclusive) && !target.isAfter(upToAndInclusive);
}
public boolean isFeeCharge() {
@@ -493,4 +492,27 @@ public class LoanCharge extends AbstractPersistable<Long> {
public String name() {
return this.charge.getName();
}
@Override
public boolean equals(final Object obj) {
if (obj == null) { return false; }
if (obj == this) { return true; }
if (obj.getClass() != getClass()) { return false; }
LoanCharge rhs = (LoanCharge) obj;
return new EqualsBuilder().appendSuper(super.equals(obj)) //
.append(this.getId(), rhs.getId()) //
.append(this.charge.getId(), rhs.charge.getId()) //
.append(this.amount, rhs.amount) //
.append(this.getDueForCollectionAsOfLocalDate(), rhs.getDueForCollectionAsOfLocalDate()) //
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(3, 5) //
.append(this.getId()) //
.append(this.charge.getId()) //
.append(this.amount).append(this.getDueForCollectionAsOfLocalDate()) //
.toHashCode();
}
}
@@ -0,0 +1,106 @@
package org.mifosplatform.portfolio.loanaccount.domain;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.portfolio.loanaccount.data.LoanCollateralData;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_loan_collateral")
public class LoanCollateral extends AbstractPersistable<Long> {
@SuppressWarnings("unused")
@ManyToOne(optional = false)
@JoinColumn(name = "loan_id", nullable = false)
private Loan loan;
@ManyToOne
@JoinColumn(name = "type_cv_id", nullable = false)
private CodeValue type;
@Column(name = "description", length = 500)
private String description;
public static LoanCollateral from(final CodeValue collateralType, final String description) {
return new LoanCollateral(null, collateralType, description);
}
protected LoanCollateral() {
//
}
private LoanCollateral(final Loan loan, final CodeValue collateralType, final String description) {
this.loan = loan;
this.type = collateralType;
this.description = StringUtils.defaultIfEmpty(description, null);
}
public void assembleFrom(final CodeValue collateralType, final String description) {
this.type = collateralType;
this.description = description;
}
public void associateWith(final Loan loan) {
this.loan = loan;
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(7);
final String collateralTypeIdParamName = "type";
if (command.isChangeInLongParameterNamed(collateralTypeIdParamName, this.type.getId())) {
final Long newValue = command.longValueOfParameterNamed(collateralTypeIdParamName);
actualChanges.put(collateralTypeIdParamName, newValue);
}
final String descriptionParamName = "description";
if (command.isChangeInStringParameterNamed(descriptionParamName, this.description)) {
final String newValue = command.stringValueOfParameterNamed(descriptionParamName);
actualChanges.put(descriptionParamName, newValue);
this.description = StringUtils.defaultIfEmpty(newValue, null);
}
return actualChanges;
}
public LoanCollateralData toData() {
final CodeValueData typeData = this.type.toData();
return LoanCollateralData.instance(this.getId(), typeData, this.description);
}
@Override
public boolean equals(final Object obj) {
if (obj == null) { return false; }
if (obj == this) { return true; }
if (obj.getClass() != getClass()) { return false; }
LoanCollateral rhs = (LoanCollateral) obj;
return new EqualsBuilder().appendSuper(super.equals(obj)) //
.append(this.getId(), rhs.getId()) //
.append(this.type.getId(), rhs.type.getId()) //
.append(this.description, rhs.description) //
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(3, 5) //
.append(this.getId()) //
.append(this.type.getId()) //
.append(this.description) //
.toHashCode();
}
}
@@ -0,0 +1,8 @@
package org.mifosplatform.portfolio.loanaccount.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface LoanCollateralRepository extends JpaRepository<LoanCollateral, Long>, JpaSpecificationExecutor<LoanCollateral> {
// no added behaviour
}
@@ -65,7 +65,7 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(monetaryCurrency);
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
cumulative = cumulative.plus(loanCharge.amount());
}
}
@@ -79,7 +79,7 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(currency);
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
cumulative = cumulative.plus(loanCharge.getAmountWaived(currency));
}
}
@@ -93,7 +93,7 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(currency);
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
cumulative = cumulative.plus(loanCharge.getAmountWrittenOff(currency));
}
}
@@ -107,7 +107,7 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(currency);
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
cumulative = cumulative.plus(loanCharge.amount());
}
}
@@ -121,7 +121,7 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(currency);
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
cumulative = cumulative.plus(loanCharge.getAmountWaived(currency));
}
}
@@ -135,7 +135,7 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(currency);
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
cumulative = cumulative.plus(loanCharge.getAmountWrittenOff(currency));
}
}
@@ -0,0 +1,16 @@
package org.mifosplatform.portfolio.loanaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when trying to delete a
* loan in an invalid state.
*/
public class LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted extends AbstractPlatformDomainRuleException {
public LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted(final Long id) {
super("error.msg.loan.cannot.delete.loan.in.its.present.state", "Loan with identifier " + id
+ " cannot be deleted in its current state.", id);
}
}
@@ -0,0 +1,16 @@
package org.mifosplatform.portfolio.loanaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when trying to modify a
* loan in an invalid state.
*/
public class LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified extends AbstractPlatformDomainRuleException {
public LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified(final Long id) {
super("error.msg.loan.cannot.modify.loan.in.its.present.state", "Loan application with identifier " + id
+ " cannot be modified in its current state.", id);
}
}
@@ -1,15 +0,0 @@
package org.mifosplatform.portfolio.loanaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when trying to delete a loan in an invalid state.
*/
public class LoanNotInSubmittedAndPendingApprovalStateCannotBeDeleted extends
AbstractPlatformDomainRuleException {
public LoanNotInSubmittedAndPendingApprovalStateCannotBeDeleted(final Long id) {
super("error.msg.loan.cannot.delete.loan.in.its.present.state", "Loan with identifier " + id + " cannot be deleted in its current state.", id);
}
}
@@ -156,7 +156,7 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
Money cumulative = Money.zero(monetaryCurrency);
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
cumulative = cumulative.plus(loanCharge.amount());
}
}
@@ -170,7 +170,7 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
Money cumulative = Money.zero(monetaryCurrency);
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
cumulative = cumulative.plus(loanCharge.amount());
}
}
@@ -146,7 +146,7 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
Money cumulative = Money.zero(monetaryCurrency);
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
cumulative = cumulative.plus(loanCharge.amount());
}
}
@@ -160,7 +160,7 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
Money cumulative = Money.zero(monetaryCurrency);
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
cumulative = cumulative.plus(loanCharge.amount());
}
}
@@ -17,7 +17,6 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -32,12 +31,12 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
/**
* The parameters supported for this command.
*/
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("clientId", "groupId", "productId", "accountNo",
"externalId", "fundId", "transactionProcessingStrategyId", "principal", "inArrearsTolerance", "interestRatePerPeriod",
"repaymentEvery", "numberOfRepayments", "loanTermFrequency", "loanTermFrequencyType", "charges", "repaymentFrequencyType",
"interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "expectedDisbursementDate",
"repaymentsStartingFromDate", "interestChargedFromDate", "submittedOnDate", "submittedOnNote", "locale", "dateFormat",
"loanOfficerId", "id"));
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "clientId", "groupId", "productId", "accountNo",
"externalId", "fundId", "loanOfficerId", "loanPurposeId", "transactionProcessingStrategyId", "principal", "inArrearsTolerance",
"interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "loanTermFrequency", "loanTermFrequencyType",
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
"expectedDisbursementDate", "repaymentsStartingFromDate", "interestChargedFromDate", "submittedOnDate", "submittedOnNote",
"locale", "dateFormat", "charges", "collateral"));
private final FromJsonHelper fromApiJsonHelper;
@@ -124,14 +123,16 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
final String repaymentsStartingFromDateParameterName = "repaymentsStartingFromDate";
if (fromApiJsonHelper.parameterExists(repaymentsStartingFromDateParameterName, element)) {
repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed(repaymentsStartingFromDateParameterName, element);
baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName).value(repaymentsStartingFromDate).ignoreIfNull().notNull();
baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName).value(repaymentsStartingFromDate).ignoreIfNull()
.notNull();
}
LocalDate interestChargedFromDate = null;
final String interestChargedFromDateParameterName = "interestChargedFromDate";
if (fromApiJsonHelper.parameterExists(interestChargedFromDateParameterName, element)) {
interestChargedFromDate = fromApiJsonHelper.extractLocalDateNamed(interestChargedFromDateParameterName, element);
baseDataValidator.reset().parameter(interestChargedFromDateParameterName).value(interestChargedFromDate).ignoreIfNull().notNull();
baseDataValidator.reset().parameter(interestChargedFromDateParameterName).value(interestChargedFromDate).ignoreIfNull()
.notNull();
}
validateRepaymentsStartingFromDateIsAfterDisbursementDate(dataValidationErrors, expectedDisbursementDate,
@@ -140,13 +141,34 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
validateRepaymentsStartingFromDateAndInterestChargedFromDate(dataValidationErrors, expectedDisbursementDate,
repaymentsStartingFromDate, interestChargedFromDate);
final LoanChargeCommand[] charges = extractInToLoanChargeCommands(element);
if (charges != null) {
for (LoanChargeCommand loanChargeCommand : charges) {
try {
loanChargeCommand.validateForCreate();
} catch (PlatformApiDataValidationException e) {
dataValidationErrors.addAll(e.getErrors());
// charges
final String chargesParameterName = "charges";
if (element.isJsonObject() && fromApiJsonHelper.parameterExists(chargesParameterName, element)) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
if (topLevelJsonElement.get(chargesParameterName).isJsonArray()) {
final Type arrayObjectParameterTypeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "chargeId", "amount", "chargeTimeType",
"chargeCalculationType", "specifiedDueDate"));
final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray();
for (int i = 1; i <= array.size(); i++) {
final JsonObject loanChargeElement = array.get(i - 1).getAsJsonObject();
final String arrayObjectJson = this.fromApiJsonHelper.toJson(loanChargeElement);
fromApiJsonHelper.checkForUnsupportedParameters(arrayObjectParameterTypeOfMap, arrayObjectJson, supportedParameters);
final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement);
baseDataValidator.reset().parameter("charges").parameterAtIndexArray("chargeId", i).value(chargeId).notNull()
.integerGreaterThanZero();
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalNamed("amount", loanChargeElement, locale);
baseDataValidator.reset().parameter("charges").parameterAtIndexArray("amount", i).value(amount).notNull()
.positiveAmount();
fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate", loanChargeElement, dateFormat, locale);
}
}
}
@@ -213,32 +235,47 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
}
}
private LoanChargeCommand[] extractInToLoanChargeCommands(final JsonElement element) {
LoanChargeCommand[] charges = null;
if (element.isJsonObject()) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
if (topLevelJsonElement.has("charges") && topLevelJsonElement.get("charges").isJsonArray()) {
final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray();
charges = new LoanChargeCommand[array.size()];
for (int i = 0; i < array.size(); i++) {
final JsonObject loanChargeElement = array.get(i).getAsJsonObject();
final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement);
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalNamed("amount", loanChargeElement, locale);
final Integer chargeTimeType = fromApiJsonHelper.extractIntegerNamed("chargeTimeType", loanChargeElement, locale);
final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", loanChargeElement,
locale);
final LocalDate specifiedDueDate = fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate", loanChargeElement,
dateFormat, locale);
charges[i] = new LoanChargeCommand(chargeId, amount, chargeTimeType, chargeCalculationType, specifiedDueDate);
}
}
}
return charges;
}
// private LoanChargeCommand[] extractInToLoanChargeCommands(final
// JsonElement element) {
// LoanChargeCommand[] charges = null;
// if (element.isJsonObject()) {
// final JsonObject topLevelJsonElement = element.getAsJsonObject();
// final String dateFormat =
// fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
// final Locale locale =
// fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
// if (topLevelJsonElement.has("charges") &&
// topLevelJsonElement.get("charges").isJsonArray()) {
//
// final JsonArray array =
// topLevelJsonElement.get("charges").getAsJsonArray();
// charges = new LoanChargeCommand[array.size()];
// for (int i = 0; i < array.size(); i++) {
//
// final JsonObject loanChargeElement = array.get(i).getAsJsonObject();
//
// final Long id = fromApiJsonHelper.extractLongNamed("id",
// loanChargeElement);
// final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId",
// loanChargeElement);
// final BigDecimal amount =
// fromApiJsonHelper.extractBigDecimalNamed("amount", loanChargeElement,
// locale);
// final Integer chargeTimeType =
// fromApiJsonHelper.extractIntegerNamed("chargeTimeType",
// loanChargeElement, locale);
// final Integer chargeCalculationType =
// fromApiJsonHelper.extractIntegerNamed("chargeCalculationType",
// loanChargeElement,
// locale);
// fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate",
// loanChargeElement, dateFormat, locale);
//
// charges[i] = new LoanChargeCommand(id, chargeId, amount, chargeTimeType,
// chargeCalculationType);
// }
// }
// }
// return charges;
// }
}
@@ -17,7 +17,6 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -32,12 +31,15 @@ public final class LoanApplicationCommandFromApiJsonHelper {
/**
* The parameters supported for this command.
*/
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "clientId", "groupId", "productId", "accountNo",
"externalId", "fundId", "loanOfficerId", "transactionProcessingStrategyId", "principal", "inArrearsTolerance",
"repaymentEvery", "numberOfRepayments", "loanTermFrequency", "loanTermFrequencyType", "charges", "repaymentFrequencyType",
"interestRatePerPeriod", "interestRateFrequencyType", "interestType", "interestCalculationPeriodType", "amortizationType",
"expectedDisbursementDate", "repaymentsStartingFromDate", "interestChargedFromDate", "submittedOnDate", "submittedOnNote",
"locale", "dateFormat"));
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("dateFormat", "locale", "id", "clientId", "groupId",
"productId", "principal", "loanTermFrequency", "loanTermFrequencyType", "numberOfRepayments", "repaymentEvery",
"repaymentFrequencyType", "interestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType",
"interestCalculationPeriodType", "expectedDisbursementDate", "repaymentsStartingFromDate", "interestChargedFromDate",
"submittedOnDate", "submittedOnNote", //
"accountNo", "externalId", "fundId", "loanOfficerId", // optional
"loanPurposeId", "inArrearsTolerance", "charges", "collateral", // optional
"transactionProcessingStrategyId" // settings
));
private final FromJsonHelper fromApiJsonHelper;
@@ -97,6 +99,12 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(loanOfficerIdParameterName).value(loanOfficerId).ignoreIfNull().integerGreaterThanZero();
}
final String loanPurposeIdParameterName = "loanPurposeId";
if (fromApiJsonHelper.parameterExists(loanPurposeIdParameterName, element)) {
final Long loanPurposeId = fromApiJsonHelper.extractLongNamed(loanPurposeIdParameterName, element);
baseDataValidator.reset().parameter(loanPurposeIdParameterName).value(loanPurposeId).ignoreIfNull().integerGreaterThanZero();
}
final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount();
@@ -105,7 +113,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(loanTermFrequencyParameterName).value(loanTermFrequency).notNull().integerGreaterThanZero();
final String loanTermFrequencyTypeParameterName = "loanTermFrequencyType";
final Integer loanTermFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed(loanTermFrequencyTypeParameterName, element);
final Integer loanTermFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(loanTermFrequencyTypeParameterName, element);
baseDataValidator.reset().parameter(loanTermFrequencyTypeParameterName).value(loanTermFrequencyType).notNull().inMinMaxRange(0, 3);
final String numberOfRepaymentsParameterName = "numberOfRepayments";
@@ -117,7 +125,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(repaymentEveryParameterName).value(repaymentEvery).notNull().integerGreaterThanZero();
final String repaymentEveryFrequencyTypeParameterName = "repaymentFrequencyType";
final Integer repaymentEveryType = fromApiJsonHelper.extractIntegerWithLocaleNamed(repaymentEveryFrequencyTypeParameterName,
final Integer repaymentEveryType = fromApiJsonHelper.extractIntegerSansLocaleNamed(repaymentEveryFrequencyTypeParameterName,
element);
baseDataValidator.reset().parameter(repaymentEveryFrequencyTypeParameterName).value(repaymentEveryType).notNull()
.inMinMaxRange(0, 3);
@@ -128,23 +136,23 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod).notNull().positiveAmount();
final String interestRateFrequencyTypeParameterName = "interestRateFrequencyType";
final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed(interestRateFrequencyTypeParameterName,
final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestRateFrequencyTypeParameterName,
element);
baseDataValidator.reset().parameter(interestRateFrequencyTypeParameterName).value(interestRateFrequencyType).notNull()
.inMinMaxRange(0, 3);
final String interestTypeParameterName = "interestType";
final Integer interestType = fromApiJsonHelper.extractIntegerWithLocaleNamed(interestTypeParameterName, element);
final Integer interestType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestTypeParameterName, element);
baseDataValidator.reset().parameter(interestTypeParameterName).value(interestType).notNull().inMinMaxRange(0, 1);
final String interestCalculationPeriodTypeParameterName = "interestCalculationPeriodType";
final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerWithLocaleNamed(
final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
interestCalculationPeriodTypeParameterName, element);
baseDataValidator.reset().parameter(interestCalculationPeriodTypeParameterName).value(interestCalculationPeriodType).notNull()
.inMinMaxRange(0, 1);
final String amortizationTypeParameterName = "amortizationType";
final Integer amortizationType = fromApiJsonHelper.extractIntegerWithLocaleNamed(amortizationTypeParameterName, element);
final Integer amortizationType = fromApiJsonHelper.extractIntegerSansLocaleNamed(amortizationTypeParameterName, element);
baseDataValidator.reset().parameter(amortizationTypeParameterName).value(amortizationType).notNull().inMinMaxRange(0, 1);
final String expectedDisbursementDateParameterName = "expectedDisbursementDate";
@@ -155,21 +163,24 @@ public final class LoanApplicationCommandFromApiJsonHelper {
if (fromApiJsonHelper.parameterExists(repaymentsStartingFromDateParameterName, element)) {
final LocalDate repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed(repaymentsStartingFromDateParameterName,
element);
baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName).value(repaymentsStartingFromDate).ignoreIfNull().notNull();
baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName).value(repaymentsStartingFromDate).ignoreIfNull()
.notNull();
}
final String interestChargedFromDateParameterName = "interestChargedFromDate";
if (fromApiJsonHelper.parameterExists(interestChargedFromDateParameterName, element)) {
final LocalDate interestChargedFromDate = fromApiJsonHelper
.extractLocalDateNamed(interestChargedFromDateParameterName, element);
baseDataValidator.reset().parameter(interestChargedFromDateParameterName).value(interestChargedFromDate).ignoreIfNull().notNull();
baseDataValidator.reset().parameter(interestChargedFromDateParameterName).value(interestChargedFromDate).ignoreIfNull()
.notNull();
}
final String inArrearsToleranceParameterName = "inArrearsTolerance";
if (fromApiJsonHelper.parameterExists(inArrearsToleranceParameterName, element)) {
final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(inArrearsToleranceParameterName,
element);
baseDataValidator.reset().parameter(inArrearsToleranceParameterName).value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount();
baseDataValidator.reset().parameter(inArrearsToleranceParameterName).value(inArrearsTolerance).ignoreIfNull()
.zeroOrPositiveAmount();
}
final String submittedOnDateParameterName = "submittedOnDate";
@@ -181,7 +192,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
final String submittedOnNoteParameterName = "submittedOnNote";
if (fromApiJsonHelper.parameterExists(submittedOnNoteParameterName, element)) {
final String submittedOnNote = fromApiJsonHelper.extractStringNamed(submittedOnNoteParameterName, element);
baseDataValidator.reset().parameter(submittedOnNoteParameterName).value(submittedOnNote).ignoreIfNull().notExceedingLengthOf(500);
baseDataValidator.reset().parameter(submittedOnNoteParameterName).value(submittedOnNote).ignoreIfNull()
.notExceedingLengthOf(500);
}
// not optional at present but might make it so to allow it to pick up
@@ -193,17 +205,67 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(transactionProcessingStrategyIdParameterName).value(transactionProcessingStrategyId).notNull()
.integerGreaterThanZero();
final LoanChargeCommand[] charges = extractInToLoanChargeCommands(element);
if (charges != null) {
for (LoanChargeCommand loanChargeCommand : charges) {
try {
loanChargeCommand.validateForCreate();
} catch (PlatformApiDataValidationException e) {
dataValidationErrors.addAll(e.getErrors());
// charges
final String chargesParameterName = "charges";
if (element.isJsonObject() && fromApiJsonHelper.parameterExists(chargesParameterName, element)) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
if (topLevelJsonElement.get(chargesParameterName).isJsonArray()) {
final Type arrayObjectParameterTypeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "chargeId", "amount", "chargeTimeType",
"chargeCalculationType", "specifiedDueDate"));
final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray();
for (int i = 1; i <= array.size(); i++) {
final JsonObject loanChargeElement = array.get(i - 1).getAsJsonObject();
final String arrayObjectJson = this.fromApiJsonHelper.toJson(loanChargeElement);
fromApiJsonHelper.checkForUnsupportedParameters(arrayObjectParameterTypeOfMap, arrayObjectJson, supportedParameters);
final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement);
baseDataValidator.reset().parameter("charges").parameterAtIndexArray("chargeId", i).value(chargeId).notNull()
.integerGreaterThanZero();
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalNamed("amount", loanChargeElement, locale);
baseDataValidator.reset().parameter("charges").parameterAtIndexArray("amount", i).value(amount).notNull()
.positiveAmount();
fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate", loanChargeElement, dateFormat, locale);
}
}
}
// collateral
final String collateralParameterName = "collateral";
if (element.isJsonObject() && fromApiJsonHelper.parameterExists(collateralParameterName, element)) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
if (topLevelJsonElement.get("collateral").isJsonArray()) {
final Type collateralParameterTypeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "type", "description"));
final JsonArray array = topLevelJsonElement.get("collateral").getAsJsonArray();
for (int i = 1; i <= array.size(); i++) {
final JsonObject collateralItemElement = array.get(i - 1).getAsJsonObject();
final String collateralJson = this.fromApiJsonHelper.toJson(collateralItemElement);
fromApiJsonHelper.checkForUnsupportedParameters(collateralParameterTypeOfMap, collateralJson, supportedParameters);
final Long collateralTypeId = fromApiJsonHelper.extractLongNamed("type", collateralItemElement);
baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("type", i).value(collateralTypeId).notNull()
.integerGreaterThanZero();
final String description = fromApiJsonHelper.extractStringNamed("description", collateralItemElement);
baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("description", i).value(description).notBlank()
.notExceedingLengthOf(500);
}
} else {
baseDataValidator.reset().parameter(collateralParameterName).expectedArrayButIsNot();
}
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
@@ -328,7 +390,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
final Integer repaymentEveryType = fromApiJsonHelper.extractIntegerWithLocaleNamed(repaymentEveryTypeParameterName, element);
baseDataValidator.reset().parameter(repaymentEveryTypeParameterName).value(repaymentEveryType).notNull().inMinMaxRange(0, 3);
}
final String interestRatePerPeriodParameterName = "interestRatePerPeriod";
if (fromApiJsonHelper.parameterExists(interestRatePerPeriodParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
@@ -412,6 +474,69 @@ public final class LoanApplicationCommandFromApiJsonHelper {
.notExceedingLengthOf(500);
}
// charges
final String chargesParameterName = "charges";
if (element.isJsonObject() && fromApiJsonHelper.parameterExists(chargesParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
if (topLevelJsonElement.get(chargesParameterName).isJsonArray()) {
final Type arrayObjectParameterTypeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "chargeId", "amount", "chargeTimeType",
"chargeCalculationType", "specifiedDueDate"));
final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray();
for (int i = 1; i <= array.size(); i++) {
final JsonObject loanChargeElement = array.get(i - 1).getAsJsonObject();
final String arrayObjectJson = this.fromApiJsonHelper.toJson(loanChargeElement);
fromApiJsonHelper.checkForUnsupportedParameters(arrayObjectParameterTypeOfMap, arrayObjectJson, supportedParameters);
final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement);
baseDataValidator.reset().parameter("charges").parameterAtIndexArray("chargeId", i).value(chargeId).notNull()
.integerGreaterThanZero();
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalNamed("amount", loanChargeElement, locale);
baseDataValidator.reset().parameter("charges").parameterAtIndexArray("amount", i).value(amount).notNull()
.positiveAmount();
fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate", loanChargeElement, dateFormat, locale);
}
}
}
// collateral
final String collateralParameterName = "collateral";
if (element.isJsonObject() && fromApiJsonHelper.parameterExists(collateralParameterName, element)) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
if (topLevelJsonElement.get("collateral").isJsonArray()) {
final Type collateralParameterTypeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "type", "description"));
final JsonArray array = topLevelJsonElement.get("collateral").getAsJsonArray();
for (int i = 1; i <= array.size(); i++) {
final JsonObject collateralItemElement = array.get(i - 1).getAsJsonObject();
final String collateralJson = this.fromApiJsonHelper.toJson(collateralItemElement);
fromApiJsonHelper.checkForUnsupportedParameters(collateralParameterTypeOfMap, collateralJson, supportedParameters);
final Long collateralTypeId = fromApiJsonHelper.extractLongNamed("type", collateralItemElement);
baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("type", i).value(collateralTypeId).notNull()
.integerGreaterThanZero();
final String description = fromApiJsonHelper.extractStringNamed("description", collateralItemElement);
baseDataValidator.reset().parameter("collateral").parameterAtIndexArray("description", i).value(description).notBlank()
.notExceedingLengthOf(500);
}
} else {
baseDataValidator.reset().parameter(collateralParameterName).expectedArrayButIsNot();
}
}
if (!atLeastOneParameterPassedForUpdate) {
final Object forceError = null;
baseDataValidator.reset().anyOfNotNull(forceError);
@@ -420,38 +545,4 @@ public final class LoanApplicationCommandFromApiJsonHelper {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public LoanChargeCommand[] extractLoanCharges(final String json) {
final JsonElement element = fromApiJsonHelper.parse(json);
return extractInToLoanChargeCommands(element);
}
private LoanChargeCommand[] extractInToLoanChargeCommands(final JsonElement element) {
LoanChargeCommand[] charges = null;
if (element.isJsonObject()) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
if (topLevelJsonElement.has("charges") && topLevelJsonElement.get("charges").isJsonArray()) {
final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray();
charges = new LoanChargeCommand[array.size()];
for (int i = 0; i < array.size(); i++) {
final JsonObject loanChargeElement = array.get(i).getAsJsonObject();
final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement);
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalNamed("amount", loanChargeElement, locale);
final Integer chargeTimeType = fromApiJsonHelper.extractIntegerNamed("chargeTimeType", loanChargeElement, locale);
final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", loanChargeElement,
locale);
final LocalDate specifiedDueDate = fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate", loanChargeElement,
dateFormat, locale);
charges[i] = new LoanChargeCommand(chargeId, amount, chargeTimeType, chargeCalculationType, specifiedDueDate);
}
}
}
return charges;
}
}
@@ -30,7 +30,7 @@ public final class LoanChargeCommandFromApiJsonDeserializer extends AbstractFrom
/**
* The parameters supported for this command.
*/
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("chargeId", "amount", "chargeTimeType",
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "chargeId", "amount", "chargeTimeType",
"chargeCalculationType", "specifiedDueDate", "locale", "dateFormat"));
private final FromJsonHelper fromApiJsonHelper;
@@ -49,12 +49,13 @@ public final class LoanChargeCommandFromApiJsonDeserializer extends AbstractFrom
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json);
final Long id = fromApiJsonHelper.extractLongNamed("id", element);
final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", element);
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element);
final Integer chargeTimeType = fromApiJsonHelper.extractIntegerWithLocaleNamed("chargeTimeType", element);
final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerWithLocaleNamed("chargeCalculationType", element);
final LocalDate specifiedDueDate = fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate", element);
final LocalDate dueAsOfDate = fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate", element);
return new LoanChargeCommand(chargeId, amount, chargeTimeType, chargeCalculationType, specifiedDueDate);
return new LoanChargeCommand(id, chargeId, amount, chargeTimeType, chargeCalculationType, dueAsOfDate);
}
}
@@ -7,6 +7,7 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.api.JsonQuery;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
@@ -15,6 +16,7 @@ import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityExce
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.security.exception.NoAuthorizationException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator;
import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory;
import org.mifosplatform.portfolio.client.domain.Client;
@@ -23,16 +25,17 @@ import org.mifosplatform.portfolio.client.domain.Note;
import org.mifosplatform.portfolio.client.domain.NoteRepository;
import org.mifosplatform.portfolio.client.exception.ClientNotFoundException;
import org.mifosplatform.portfolio.fund.domain.Fund;
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
import org.mifosplatform.portfolio.loanaccount.command.LoanStateTransitionCommand;
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCollateral;
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
@@ -56,7 +59,7 @@ import com.google.gson.JsonElement;
public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements LoanApplicationWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(LoanApplicationWritePlatformServiceJpaRepositoryImpl.class);
private final PlatformSecurityContext context;
private final FromJsonHelper fromJsonHelper;
private final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer;
@@ -68,6 +71,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
private final ClientRepository clientRepository;
private final LoanProductRepository loanProductRepository;
private final LoanChargeAssembler loanChargeAssembler;
private final LoanCollateralAssembler loanCollateralAssembler;
private final AprCalculator aprCalculator;
private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory;
@@ -76,9 +80,10 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer,
final LoanStateTransitionCommandFromApiJsonDeserializer loanStateTransitionCommandFromApiJsonDeserializer,
final AprCalculator aprCalculator, final LoanAssembler loanAssembler, final LoanChargeAssembler loanChargeAssembler,
final LoanRepository loanRepository, final NoteRepository noteRepository,
final LoanScheduleCalculationPlatformService calculationPlatformService, final ClientRepository clientRepository,
final LoanProductRepository loanProductRepository, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory) {
final LoanCollateralAssembler loanCollateralAssembler, final LoanRepository loanRepository,
final NoteRepository noteRepository, final LoanScheduleCalculationPlatformService calculationPlatformService,
final ClientRepository clientRepository, final LoanProductRepository loanProductRepository,
final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory) {
this.context = context;
this.fromJsonHelper = fromJsonHelper;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
@@ -86,6 +91,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
this.aprCalculator = aprCalculator;
this.loanAssembler = loanAssembler;
this.loanChargeAssembler = loanChargeAssembler;
this.loanCollateralAssembler = loanCollateralAssembler;
this.loanRepository = loanRepository;
this.noteRepository = noteRepository;
this.calculationPlatformService = calculationPlatformService;
@@ -149,9 +155,15 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final Loan existingLoanApplication = retrieveLoanBy(loanId);
final LoanChargeCommand[] charges = this.fromApiJsonDeserializer.extractLoanCharges(command.json());
if (!existingLoanApplication.isSubmittedAndPendingApproval()) { throw new LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified(
loanId); }
final Map<String, Object> changes = existingLoanApplication.loanApplicationModification(command, charges, this.aprCalculator);
final Set<LoanCharge> possiblyModifedLoanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson());
final Set<LoanCollateral> possiblyModifedLoanCollateralItems = this.loanCollateralAssembler
.fromParsedJson(command.parsedJson());
final Map<String, Object> changes = existingLoanApplication.loanApplicationModification(command, possiblyModifedLoanCharges,
possiblyModifedLoanCollateralItems, this.aprCalculator);
final String clientIdParamName = "clientId";
if (changes.containsKey(clientIdParamName)) {
@@ -179,6 +191,20 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
existingLoanApplication.updateFund(fund);
}
final String loanPurposeIdParamName = "loanPurposeId";
if (changes.containsKey(loanPurposeIdParamName)) {
final Long loanPurposeId = command.longValueOfParameterNamed(loanPurposeIdParamName);
final CodeValue loanPurpose = this.loanAssembler.findCodeValueByIdIfProvided(loanPurposeId);
existingLoanApplication.updateLoanPurpose(loanPurpose);
}
final String loanOfficerIdParamName = "loanOfficerId";
if (changes.containsKey(loanOfficerIdParamName)) {
final Long loanOfficerId = command.longValueOfParameterNamed(loanOfficerIdParamName);
final Staff newValue = this.loanAssembler.findLoanOfficerByIdIfProvided(loanOfficerId);
existingLoanApplication.updateLoanOfficerOnLoanApplication(newValue);
}
final String strategyIdParamName = "transactionProcessingStrategyId";
if (changes.containsKey(strategyIdParamName)) {
final Long strategyId = command.longValueOfParameterNamed(strategyIdParamName);
@@ -193,6 +219,12 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
existingLoanApplication.updateLoanCharges(loanCharges);
}
final String collateralParamName = "collateral";
if (changes.containsKey(collateralParamName)) {
final Set<LoanCollateral> loanCollateral = this.loanCollateralAssembler.fromParsedJson(command.parsedJson());
existingLoanApplication.updateLoanCollateral(loanCollateral);
}
if (changes.containsKey("recalculateLoanSchedule")) {
changes.remove("recalculateLoanSchedule");
@@ -224,7 +256,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
return CommandProcessingResult.empty();
}
}
/*
* Guaranteed to throw an exception no matter what the data integrity issue
* is.
@@ -245,10 +277,9 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
}
logAsErrorUnexpectedDataIntegrityException(dve);
throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue", "Unknown data integrity issue with resource.");
}
private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) {
logger.error(dve.getMessage(), dve);
}
@@ -261,7 +292,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final Loan loan = retrieveLoanBy(loanId);
if (loan.isNotSubmittedAndPendingApproval()) { throw new LoanNotInSubmittedAndPendingApprovalStateCannotBeDeleted(loanId); }
if (loan.isNotSubmittedAndPendingApproval()) { throw new LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted(loanId); }
List<Note> relatedNotes = this.noteRepository.findByLoanId(loan.getId());
this.noteRepository.deleteInBatch(relatedNotes);
@@ -296,7 +327,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final Map<String, Object> changes = loan.loanApplicationApproval(command, defaultLoanLifecycleStateMachine());
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
@@ -327,7 +358,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final Map<String, Object> changes = loan.undoApproval(defaultLoanLifecycleStateMachine());
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
@@ -365,7 +396,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final Map<String, Object> changes = loan.loanApplicationRejection(command, defaultLoanLifecycleStateMachine());
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
@@ -403,7 +434,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final Map<String, Object> changes = loan.loanApplicationWithdrawnByApplicant(command, defaultLoanLifecycleStateMachine());
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
@@ -6,6 +6,8 @@ import java.util.List;
import java.util.Set;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.organisation.staff.domain.Staff;
@@ -25,6 +27,7 @@ import org.mifosplatform.portfolio.group.exception.GroupNotFoundException;
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCollateral;
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
@@ -49,16 +52,18 @@ public class LoanAssembler {
private final FundRepository fundRepository;
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
private final StaffRepository staffRepository;
private final LoanChargeAssembler loanChargeAssembler;
private final CodeValueRepositoryWrapper codeValueRepository;
private final LoanScheduleAssembler loanScheduleAssembler;
private final LoanChargeAssembler loanChargeAssembler;
private final LoanCollateralAssembler loanCollateralAssembler;
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository,
final ClientRepository clientRepository, final GroupRepository groupRepository, final FundRepository fundRepository,
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
final StaffRepository staffRepository, final LoanScheduleAssembler loanScheduleAssembler,
final LoanChargeAssembler loanChargeAssembler) {
final StaffRepository staffRepository, final CodeValueRepositoryWrapper codeValueRepository,
final LoanScheduleAssembler loanScheduleAssembler, final LoanChargeAssembler loanChargeAssembler, final LoanCollateralAssembler loanCollateralAssembler) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.loanProductRepository = loanProductRepository;
this.clientRepository = clientRepository;
@@ -66,8 +71,10 @@ public class LoanAssembler {
this.fundRepository = fundRepository;
this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
this.staffRepository = staffRepository;
this.codeValueRepository = codeValueRepository;
this.loanScheduleAssembler = loanScheduleAssembler;
this.loanChargeAssembler = loanChargeAssembler;
this.loanCollateralAssembler = loanCollateralAssembler;
}
public Loan assembleFrom(final JsonCommand command) {
@@ -86,6 +93,7 @@ public class LoanAssembler {
final Long fundId = fromApiJsonHelper.extractLongNamed("fundId", element);
final Long loanOfficerId = fromApiJsonHelper.extractLongNamed("loanOfficerId", element);
final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
final Long loanPurposeId = fromApiJsonHelper.extractLongNamed("loanPurposeId", element);
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
@@ -93,7 +101,11 @@ public class LoanAssembler {
final Fund fund = findFundByIdIfProvided(fundId);
final Staff loanOfficer = findLoanOfficerByIdIfProvided(loanOfficerId);
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId);
CodeValue loanPurpose = null;
if (loanPurposeId != null) {
loanPurpose = codeValueRepository.findOneWithNotFoundDetection(loanPurposeId);
}
final Set<LoanCollateral> collateral = this.loanCollateralAssembler.fromParsedJson(element);
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element);
final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
@@ -106,24 +118,24 @@ public class LoanAssembler {
client = this.clientRepository.findOne(clientId);
if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); }
loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy,
loanSchedule, loanCharges);
loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanProduct, fund, loanOfficer, loanPurpose,
loanTransactionProcessingStrategy, loanSchedule, loanCharges, collateral);
}
if (groupId != null) {
group = this.groupRepository.findOne(groupId);
if (group == null || group.isDeleted()) { throw new GroupNotFoundException(groupId); }
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy,
loanSchedule, loanCharges);
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanProduct, fund, loanOfficer,
loanTransactionProcessingStrategy, loanSchedule, loanCharges);
}
if (client != null && group != null){
if (client != null && group != null) {
if (!group.hasClientAsMember(client)) { throw new ClientNotInGroupException(clientId, groupId); }
loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy,
loanSchedule, loanCharges);
loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanProduct, fund, loanOfficer,
loanTransactionProcessingStrategy, loanSchedule, loanCharges);
}
final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element);
@@ -140,6 +152,14 @@ public class LoanAssembler {
List<LoanStatus> allowedLoanStatuses = Arrays.asList(LoanStatus.values());
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
}
public CodeValue findCodeValueByIdIfProvided(final Long codeValueId) {
CodeValue codeValue = null;
if (codeValueId != null) {
codeValue = this.codeValueRepository.findOneWithNotFoundDetection(codeValueId);
}
return codeValue;
}
public Fund findFundByIdIfProvided(final Long fundId) {
Fund fund = null;
@@ -89,10 +89,7 @@ public class LoanChargeAssembler {
loanCharges.add(loanCharge);
}
}
} else {
// no charges passed, use existing ones against loan
}
}
return loanCharges;
@@ -0,0 +1,71 @@
package org.mifosplatform.portfolio.loanaccount.service;
import java.util.HashSet;
import java.util.Set;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.charge.exception.LoanCollateralNotFoundException;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCollateral;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCollateralRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@Service
public class LoanCollateralAssembler {
private final FromJsonHelper fromApiJsonHelper;
private final CodeValueRepositoryWrapper codeValueRepository;
private final LoanCollateralRepository loanCollateralRepository;
@Autowired
public LoanCollateralAssembler(final FromJsonHelper fromApiJsonHelper, final CodeValueRepositoryWrapper codeValueRepository,
final LoanCollateralRepository loanCollateralRepository) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.codeValueRepository = codeValueRepository;
this.loanCollateralRepository = loanCollateralRepository;
}
public Set<LoanCollateral> fromParsedJson(final JsonElement element) {
final Set<LoanCollateral> collateralItems = new HashSet<LoanCollateral>();
if (element.isJsonObject()) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
if (topLevelJsonElement.has("collateral") && topLevelJsonElement.get("collateral").isJsonArray()) {
final JsonArray array = topLevelJsonElement.get("collateral").getAsJsonArray();
for (int i = 0; i < array.size(); i++) {
final JsonObject collateralItemElement = array.get(i).getAsJsonObject();
final Long id = fromApiJsonHelper.extractLongNamed("id", collateralItemElement);
final Long collateralTypeId = fromApiJsonHelper.extractLongNamed("type", collateralItemElement);
final CodeValue collateralType = this.codeValueRepository.findOneWithNotFoundDetection(collateralTypeId);
final String description = fromApiJsonHelper.extractStringNamed("description", collateralItemElement);
if (id == null) {
collateralItems.add(LoanCollateral.from(collateralType, description));
} else {
LoanCollateral loanCollateralItem = this.loanCollateralRepository.findOne(id);
if (loanCollateralItem == null) { throw new LoanCollateralNotFoundException(id); }
loanCollateralItem.assembleFrom(collateralType, description);
collateralItems.add(loanCollateralItem);
}
}
} else {
// no charges passed, use existing ones against loan
}
}
return collateralItems;
}
}
@@ -0,0 +1,10 @@
package org.mifosplatform.portfolio.loanaccount.service;
import java.util.Collection;
import org.mifosplatform.portfolio.loanaccount.data.LoanCollateralData;
public interface LoanCollateralReadPlatformService {
Collection<LoanCollateralData> retrieveLoanCollateral(Long loanId);
}
@@ -0,0 +1,63 @@
package org.mifosplatform.portfolio.loanaccount.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.loanaccount.data.LoanCollateralData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class LoanCollateralReadPlatformServiceImpl implements LoanCollateralReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context;
@Autowired
public LoanCollateralReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource) {
this.context = context;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
private static final class LoanCollateralMapper implements RowMapper<LoanCollateralData> {
private StringBuilder sqlBuilder = new StringBuilder(
"lc.id as id, lc.description as description, cv.id as typeId, cv.code_value as typeName") //
.append(" FROM m_loan_collateral lc") //
.append(" JOIN m_code_value cv on lc.type_cv_id = cv.id");
public String schema() {
return sqlBuilder.toString();
}
@Override
public LoanCollateralData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String name = rs.getString("description");
final Long typeId = rs.getLong("typeId");
final String typeName = rs.getString("typeName");
final CodeValueData type = CodeValueData.instance(typeId, typeName);
return LoanCollateralData.instance(id, type, name);
}
}
@Override
public Collection<LoanCollateralData> retrieveLoanCollateral(final Long loanId) {
this.context.authenticatedUser();
final LoanCollateralMapper rm = new LoanCollateralMapper();
final String sql = "select " + rm.schema() + " where lc.loan_id=? order by id ASC";
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
}
}
@@ -362,9 +362,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
private static final class LoanMapper implements RowMapper<LoanBasicDetailsData> {
public String loanSchema() {
return "l.id as id, l.account_no as accountNo, l.external_id as externalId, l.fund_id as fundId, f.name as fundName, "
+ " lp.id as loanProductId, lp.name as loanProductName, lp.description as loanProductDescription, c.id as clientId, c.display_name as clientName, "
+ " c.office_id as clientOfficeId, g.id as groupId, g.name as groupName, g.office_id as groupOfficeId,"
return "l.id as id, l.account_no as accountNo, l.external_id as externalId, l.fund_id as fundId, f.name as fundName,"
+ " l.loanpurpose_cv_id as loanPurposeId, cv.code_value as loanPurposeName,"
+ " lp.id as loanProductId, lp.name as loanProductName, lp.description as loanProductDescription,"
+ " c.id as clientId, c.display_name as clientName, c.office_id as clientOfficeId,"
+ " g.id as groupId, g.name as groupName, g.office_id as groupOfficeId,"
+ " l.submittedon_date as submittedOnDate,"
+ " l.total_charges_due_at_disbursement_derived as totalDisbursementCharges,"
+ " l.approvedon_date as approvedOnDate, l.expected_disbursedon_date as expectedDisbursementDate, l.disbursedon_date as actualDisbursementDate, l.expected_firstrepaymenton_date as expectedFirstRepaymentOnDate,"
@@ -379,7 +381,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName" + " from m_loan l"
+ " left join m_client c on c.id = l.client_id" + " left join m_group g on g.id = l.group_id"
+ " join m_product_loan lp on lp.id = l.product_id" + " join m_currency rc on rc.`code` = l.currency_code"
+ " left join m_fund f on f.id = l.fund_id" + " left join m_staff s on s.id = l.loan_officer_id";
+ " left join m_fund f on f.id = l.fund_id" + " left join m_staff s on s.id = l.loan_officer_id"
+ " left join m_code_value cv on cv.id = l.loanpurpose_cv_id";
}
@Override
@@ -407,8 +410,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final Long fundId = JdbcSupport.getLong(rs, "fundId");
final String fundName = rs.getString("fundName");
final Long loanOfficerId = JdbcSupport.getLong(rs, "loanOfficerId");
final String loanOfficerName = rs.getString("loanOfficerName");
final Long loanPurposeId = JdbcSupport.getLong(rs, "loanPurposeId");
final String loanPurposeName = rs.getString("loanPurposeName");
final Long loanProductId = JdbcSupport.getLong(rs, "loanProductId");
final String loanProductName = rs.getString("loanProductName");
final String loanProductDescription = rs.getString("loanProductDescription");
@@ -468,12 +476,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
Collection<LoanChargeData> charges = null;
return new LoanBasicDetailsData(id, accountNo, externalId, clientId, clientName, clientOfficeId, groupId, groupName,
groupOfficeId, loanProductId, loanProductName, loanProductDescription, fundId, fundName, closedOnDate, submittedOnDate,
approvedOnDate, expectedDisbursementDate, actualDisbursementDate, expectedMaturityDate, expectedFirstRepaymentOnDate,
interestChargedFromDate, currencyData, principal, inArrearsTolerance, numberOfRepayments, repaymentEvery,
interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, status, lifeCycleStatusDate, termFrequency, termPeriodFrequencyType,
transactionStrategyId, charges, loanOfficerId, loanOfficerName, totalDisbursementCharges);
groupOfficeId, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId,
loanPurposeName, closedOnDate, submittedOnDate, approvedOnDate, expectedDisbursementDate, actualDisbursementDate,
expectedMaturityDate, expectedFirstRepaymentOnDate, interestChargedFromDate, currencyData, principal,
inArrearsTolerance, numberOfRepayments, repaymentEvery, interestRatePerPeriod, annualInterestRate,
repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType,
status, lifeCycleStatusDate, termFrequency, termPeriodFrequencyType, transactionStrategyId, charges, loanOfficerId,
loanOfficerName, totalDisbursementCharges);
}
}
@@ -44,8 +44,8 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanChargeCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanStateTransitionCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanTransactionCommandFromApiJsonDeserializer;
@@ -78,8 +78,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanStateTransitionCommandFromApiJsonDeserializer loanStateTransitionCommandFromApiJsonDeserializer,
final LoanTransactionCommandFromApiJsonDeserializer loanTransactionCommandFromApiJsonDeserializer,
final LoanChargeCommandFromApiJsonDeserializer loanChargeCommandFromApiJsonDeserializer,
final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer,
final LoanAssembler loanAssembler,
final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, final LoanAssembler loanAssembler,
final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository,
final NoteRepository noteRepository, final ChargeRepository chargeRepository, final LoanChargeRepository loanChargeRepository,
final ApplicationCurrencyRepository applicationCurrencyRepository,
@@ -96,7 +95,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanChargeRepository = loanChargeRepository;
this.applicationCurrencyRepository = applicationCurrencyRepository;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
this.loanUpdateCommandFromApiJsonDeserializer = loanUpdateCommandFromApiJsonDeserializer;
this.loanUpdateCommandFromApiJsonDeserializer = loanUpdateCommandFromApiJsonDeserializer;
}
private boolean isBeforeToday(final LocalDate date) {
@@ -129,26 +128,27 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Map<String, Object> changes = loan.disburse(command, defaultLoanLifecycleStateMachine(), currency);
if (!changes.isEmpty()) {
// variable stores Id's of all existing loan transactions (newly created
// variable stores Id's of all existing loan transactions (newly
// created
// loan transactions would not have an Id before save)
final Set<Long> existingLoanTransactionIds = new HashSet<Long>();
for(LoanTransaction loanTransaction:loan.getLoanTransactions()){
if(!(loanTransaction.getId()==null)){
for (LoanTransaction loanTransaction : loan.getLoanTransactions()) {
if (!(loanTransaction.getId() == null)) {
existingLoanTransactionIds.add(loanTransaction.getId());
}
}
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
}
// make a call to accounting
if (loan.isAccountingEnabledOnLoanProduct()) {
/***
* Variable holds list of all newly created loan transactions during
* this disbursal
* Variable holds list of all newly created loan transactions
* during this disbursal
**/
List<LoanTransaction> newLoanTransactions = new ArrayList<LoanTransaction>();
for (LoanTransaction loanTransaction : loan.getLoanTransactions()) {
@@ -183,7 +183,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Map<String, Object> changes = loan.undoDisbursal(defaultLoanLifecycleStateMachine());
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
@@ -276,15 +276,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount"));
boolean transactionIsRepayment= transactionToAdjust.isRepayment();
boolean transactionIsRepayment = transactionToAdjust.isRepayment();
final LoanTransaction newTransactionDetail = loan.adjustExistingTransaction(transactionDate, transactionAmount,
defaultLoanLifecycleStateMachine(), transactionToAdjust);
if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) {
this.loanTransactionRepository.save(newTransactionDetail);
}
this.loanRepository.save(loan);
final String noteText = command.stringValueOfParameterNamed("note");
@@ -296,8 +296,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
changes.put("locale", command.locale());
changes.put("dateFormat", command.dateFormat());
// make a call to accounting only if transaction being adjusted is a repayment (not waiver)
// make a call to accounting only if transaction being adjusted is a
// repayment (not waiver)
if (loan.isAccountingEnabledOnLoanProduct() && transactionIsRepayment) {
List<LoanTransaction> newLoanTransactions = new ArrayList<LoanTransaction>();
if (newTransactionDetail.getId() != null) {
@@ -307,7 +308,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
LoanDTO loanDTO = populateLoanDTO(loan, newLoanTransactions);
journalEntryWritePlatformService.createJournalEntriesForLoan(loanDTO);
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(transactionId) //
@@ -389,7 +390,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
changes.put("locale", command.locale());
changes.put("dateFormat", command.dateFormat());
// make a call to accounting
if (loan.isAccountingEnabledOnLoanProduct()) {
LoanDTO loanDTO = populateLoanData(loan, writeoff);
@@ -509,8 +510,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
public CommandProcessingResult addLoanCharge(final Long loanId, final JsonCommand command) {
this.context.authenticatedUser();
// FIXME - KW - update loan charge api validation
final LoanChargeCommand loanChargeCommand = this.loanChargeCommandFromApiJsonDeserializer.commandFromApiJson(command.json());
loanChargeCommand.validateForCreate();
// loanChargeCommand.validateForCreate();
final Loan loan = retrieveLoanBy(loanId);
@@ -709,12 +711,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.context.authenticatedUser();
final LoanUpdateCommand loanUpdateCommand = this.loanUpdateCommandFromApiJsonDeserializer.commandFromApiJson(command
.json());
final LoanUpdateCommand loanUpdateCommand = this.loanUpdateCommandFromApiJsonDeserializer.commandFromApiJson(command.json());
loanUpdateCommand.validate();
final LocalDate dateOfLoanOfficerunAssigned = command.localDateValueOfParameterNamed("unassignedDate");
final Loan loan = retrieveLoanBy(loanId);
@@ -738,11 +738,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private LoanDTO populateLoanDTO(Loan loan, List<LoanTransaction> loanTransactions) {
List<LoanTransactionDTO> loanTransactionDTOs = new ArrayList<LoanTransactionDTO>();
for (LoanTransaction loanTransaction : loanTransactions) {
LoanTransactionDTO loanTransactionDTO = new LoanTransactionDTO(loanTransaction.getId().toString(),
loanTransaction.getDateOf(), loanTransaction.getAmount(), loanTransaction.getPrincipalPortion(),
loanTransaction.getInterestPortion(), loanTransaction.getFeePortion(), loanTransaction.getPenaltyChargesPortion(),
loanTransaction.isDisbursement(), loanTransaction.isRepayment(), loanTransaction.isRepaymentAtDisbursement(),
loanTransaction.isContra(), loanTransaction.isWriteOff());
LoanTransactionDTO loanTransactionDTO = new LoanTransactionDTO(loanTransaction.getId().toString(), loanTransaction.getDateOf(),
loanTransaction.getAmount(), loanTransaction.getPrincipalPortion(), loanTransaction.getInterestPortion(),
loanTransaction.getFeePortion(), loanTransaction.getPenaltyChargesPortion(), loanTransaction.isDisbursement(),
loanTransaction.isRepayment(), loanTransaction.isRepaymentAtDisbursement(), loanTransaction.isContra(),
loanTransaction.isWriteOff());
loanTransactionDTOs.add(loanTransactionDTO);
}
return new LoanDTO(loan.getId(), loan.productId(), loan.getOfficeId(), loan.isCashBasedAccountingEnabledOnLoanProduct(),