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_guarantor`;
DROP TABLE IF EXISTS `m_loan`; DROP TABLE IF EXISTS `m_loan`;
DROP TABLE IF EXISTS `m_loan_charge`; 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_officer_assignment_history`;
DROP TABLE IF EXISTS `m_loan_repayment_schedule`; DROP TABLE IF EXISTS `m_loan_repayment_schedule`;
DROP TABLE IF EXISTS `m_loan_transaction`; DROP TABLE IF EXISTS `m_loan_transaction`;
@@ -501,6 +502,7 @@ CREATE TABLE `m_loan` (
`fund_id` bigint(20) DEFAULT NULL, `fund_id` bigint(20) DEFAULT NULL,
`loan_officer_id` bigint(20) DEFAULT NULL, `loan_officer_id` bigint(20) DEFAULT NULL,
`guarantor_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, `loan_status_id` smallint(5) NOT NULL,
`currency_code` varchar(3) NOT NULL, `currency_code` varchar(3) NOT NULL,
`currency_digits` smallint(5) NOT NULL, `currency_digits` smallint(5) NOT NULL,
@@ -544,12 +546,14 @@ CREATE TABLE `m_loan` (
KEY `FK7C885877240145` (`fund_id`), KEY `FK7C885877240145` (`fund_id`),
KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`), KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`),
KEY `FK_m_loan_m_staff` (`loan_officer_id`), KEY `FK_m_loan_m_staff` (`loan_officer_id`),
KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`),
KEY `group_id` (`group_id`), KEY `group_id` (`group_id`),
CONSTRAINT `FK7C885877240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`), CONSTRAINT `FK7C885877240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`),
CONSTRAINT `FKB6F935D87179A0CB` FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`), CONSTRAINT `FKB6F935D87179A0CB` FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`),
CONSTRAINT `FKB6F935D8C8D4B434` FOREIGN KEY (`product_id`) REFERENCES `m_product_loan` (`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_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_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`) CONSTRAINT `m_loan_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `m_group` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) 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`) CONSTRAINT `m_loan_charge_ibfk_2` FOREIGN KEY (`loan_id`) REFERENCES `m_loan` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) 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` ( CREATE TABLE `m_loan_officer_assignment_history` (
`id` bigint(20) NOT NULL AUTO_INCREMENT, `id` bigint(20) NOT NULL AUTO_INCREMENT,
`loan_id` bigint(20) NOT NULL, `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`) INSERT INTO `m_organisation_currency` (`id`, `code`, `decimal_places`, `name`, `display_symbol`, `internationalized_name_code`)
VALUES (21,'USD',2,'US Dollar','$','currency.USD'); 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`) INSERT INTO `m_office` (`id`, `parent_id`, `hierarchy`, `external_id`, `name`, `opening_date`)
VALUES VALUES
(1,NULL,'.','1','Head Office','2009-01-01'); (1,NULL,'.','1','Head Office','2009-01-01');
@@ -521,3 +513,38 @@ from x_registered_table r;
insert into m_permission(grouping, `code`, entity_name, action_name) insert into m_permission(grouping, `code`, entity_name, action_name)
select 'datatable', concat('DELETE_', r.registered_table_name), r.registered_table_name, 'DELETE' 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); 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 VALUES
(2,'Gender', '0'), ('FieldOfEmployment', '0'),
(3,'YesNo', '0'), ('EducationLevel', '0'),
(4,'FieldOfEmployment', '0'), ('MaritalStatus', '0'),
(5,'EducationLevel', '0'), ('PovertyStatus', '0');
(6,'MaritalStatus', '0'),
(7,'PovertyStatus', '0'), INSERT INTO `m_code_value`(`code_id`,`code_value`,`order_position`)
(8,'PurposeOfLoan', '0'), select mc.id, 'option.Banker', ifnull(max(mv.id), 1)
(9,'CollateralType', '0'); 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`; DROP TABLE IF EXISTS `mifostenant-ceda`.`client additional data`;
CREATE TABLE `mifostenant-ceda`.`client additional data` ( CREATE TABLE `mifostenant-ceda`.`client additional data` (
@@ -30,6 +30,9 @@ VALUES
(0, 1, 'keithwoodlock', 'Keith', 'Woodlock', (0, 1, 'keithwoodlock', 'Keith', 'Woodlock',
'4f607e9b6cffbe7d3db92d4bfa3391c7aa751727b4ea29d08fddf9dd72e6e7e3', 'keithwoodlock@gmail.com', 0, 1, 1, 1, 1); '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; UPDATE `mifostenant-ceda`.`m_office` SET `name` = 'CEDA Microfinance Ltd.' WHERE id=1;
INSERT INTO `mifostenant-ceda`.`m_office` 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 -- 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`)
INSERT INTO `m_code_value` (`code_id`, `code_value`, `order_position`) VALUES (2, 'Female', 2); 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`)
INSERT INTO `m_code_value` (`code_id`, `code_value`, `order_position`) VALUES (3, 'Secondary', 2); select mc.id, 'option.Female', ifnull(max(mv.id), 1)
INSERT INTO `m_code_value` (`code_id`, `code_value`, `order_position`) VALUES (3, 'University', 3); 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`; DROP TABLE IF EXISTS `extra_client_details`;
CREATE TABLE `extra_client_details` ( CREATE TABLE `extra_client_details` (
@@ -50,3 +90,11 @@ CREATE TABLE `extra_loan_details` (
PRIMARY KEY (`loan_id`), PRIMARY KEY (`loan_id`),
CONSTRAINT `FK_extra_loan_details` FOREIGN KEY (`loan_id`) REFERENCES `m_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.sun.jersey.contribs', name: 'jersey-multipart', version: jerseyVersion],
[group: 'com.google.code.gson', name: 'gson', version: '2.2.2'], [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], [group: 'org.springframework.data', name: 'spring-data-jpa', version: springDataJpaVersion],
@@ -18,6 +18,10 @@ public class CodeValueData {
return new CodeValueData(id, name, position); 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) { private CodeValueData(final Long id, final String name, final Integer position) {
this.id = id; this.id = id;
this.name = name; this.name = name;
@@ -12,6 +12,7 @@ import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.codes.CodeConstants.CODEVALUE_JSON_INPUT_PARAMS; 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.mifosplatform.infrastructure.core.api.JsonCommand;
import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -81,4 +82,7 @@ public class CodeValue extends AbstractPersistable<Long> {
return actualChanges; 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: * There are two types of code information in the platform:
* <ol> * <ol>
* <li>System defined codes</li> * <li>System defined codes</li>
* <li>User defined codes</li> * <li>User defined codes</li>
* </ol> * </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 { public interface CodeValueReadPlatformService {
// system defined code value data Collection<CodeValueData> retrieveCodeValuesByCode(final String code);
Collection<CodeValueData> retrieveCustomIdentifierCodeValues();
Collection<CodeValueData> retrieveAllCodeValues(final Long codeId); Collection<CodeValueData> retrieveAllCodeValues(final Long codeId);
CodeValueData retrieveCodeValue(final Long codeValueId); CodeValueData retrieveCodeValue(final Long codeValueId);
} }
@@ -43,14 +43,14 @@ public class CodeValueReadPlatformServiceImpl implements CodeValueReadPlatformSe
} }
@Override @Override
public Collection<CodeValueData> retrieveCustomIdentifierCodeValues() { public Collection<CodeValueData> retrieveCodeValuesByCode(final String code) {
context.authenticatedUser(); context.authenticatedUser();
final CodeValueDataMapper rm = new CodeValueDataMapper(); final CodeValueDataMapper rm = new CodeValueDataMapper();
final String sql = "select " + rm.schema() + "where c.code_name like ? order by position"; 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 @Override
@@ -65,7 +65,7 @@ public class CodeValueReadPlatformServiceImpl implements CodeValueReadPlatformSe
} }
@Override @Override
public CodeValueData retrieveCodeValue(Long codeValueId){ public CodeValueData retrieveCodeValue(Long codeValueId) {
context.authenticatedUser(); context.authenticatedUser();
@@ -11,6 +11,8 @@ public class DataValidatorBuilder {
private final List<ApiParameterError> dataValidationErrors; private final List<ApiParameterError> dataValidationErrors;
private String resource; private String resource;
private String parameter; private String parameter;
private String arrayPart;
private Integer arrayIndex;
private Object value; private Object value;
private boolean ignoreNullValue = false; private boolean ignoreNullValue = false;
@@ -32,6 +34,12 @@ public class DataValidatorBuilder {
return this; return this;
} }
public DataValidatorBuilder parameterAtIndexArray(final String arrayPart, final Integer arrayIndex) {
this.arrayPart = arrayPart;
this.arrayIndex = arrayIndex;
return this;
}
public DataValidatorBuilder value(Object value) { public DataValidatorBuilder value(Object value) {
this.value = value; this.value = value;
return this; return this;
@@ -102,11 +110,19 @@ public class DataValidatorBuilder {
public DataValidatorBuilder notNull() { public DataValidatorBuilder notNull() {
if (value == null && !ignoreNullValue) { if (value == null && !ignoreNullValue) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".cannot.be.blank"); String realParameterName = this.parameter;
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" cannot be blank."); 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(), ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
parameter); realParameterName, arrayIndex);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
return this; return this;
@@ -116,11 +132,18 @@ public class DataValidatorBuilder {
if (value == null && ignoreNullValue) { return this; } if (value == null && ignoreNullValue) { return this; }
if (value == null || StringUtils.isBlank(value.toString())) { if (value == null || StringUtils.isBlank(value.toString())) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) String realParameterName = this.parameter;
.append(".cannot.be.blank"); StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter);
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" cannot be blank."); 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(), ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
parameter); realParameterName, arrayIndex);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
return this; return this;
@@ -247,6 +270,15 @@ public class DataValidatorBuilder {
return this; 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) { public DataValidatorBuilder anyOfNotNull(Object... object) {
boolean hasData = false; boolean hasData = false;
for (Object obj : object) { for (Object obj : object) {
@@ -45,6 +45,10 @@ public class FromJsonHelper {
return this.gsonConverter.fromJson(json, classOfT); 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) { public void checkForUnsupportedParameters(final Type typeOfMap, final String json, final Set<String> supportedParams) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
@@ -141,7 +145,7 @@ public class FromJsonHelper {
return helperDelegator.extractIntegerWithLocaleNamed(parameterName, element.getAsJsonObject(), new HashSet<String>()); return helperDelegator.extractIntegerWithLocaleNamed(parameterName, element.getAsJsonObject(), new HashSet<String>());
} }
public Integer extractIntegerSansLocaleNamed(final String parameterName, final JsonElement element){ public Integer extractIntegerSansLocaleNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractIntegerSansLocaleNamed(parameterName, element.getAsJsonObject(), new HashSet<String>()); return helperDelegator.extractIntegerSansLocaleNamed(parameterName, element.getAsJsonObject(), new HashSet<String>());
} }
@@ -154,8 +158,7 @@ public class FromJsonHelper {
return helperDelegator.extractIntegerNamed(parameterName, element.getAsJsonObject(), locale, new HashSet<String>()); return helperDelegator.extractIntegerNamed(parameterName, element.getAsJsonObject(), locale, new HashSet<String>());
} }
public Integer extractIntegerNamed(final String parameterName, final JsonElement element, public Integer extractIntegerNamed(final String parameterName, final JsonElement element, final Set<String> parametersPassedInRequest) {
final Set<String> parametersPassedInRequest) {
return helperDelegator.extractIntegerNamed(parameterName, element.getAsJsonObject(), Locale.US, 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; 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 { 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); super("error.msg.loanCharge.id.invalid", "Loan charge with identifier " + id + " does not exist", id);
} }
public LoanChargeNotFoundException(final Long id, final Long 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); 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); 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 ClientIdentifierData clientIdentifierData = ClientIdentifierData.template(codeValues);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
@@ -132,7 +132,8 @@ public class ClientIdentifiersApiResource {
ClientIdentifierData clientIdentifierData = this.clientReadPlatformService.retrieveClientIdentifier(clientId, clientIdentifierId); ClientIdentifierData clientIdentifierData = this.clientReadPlatformService.retrieveClientIdentifier(clientId, clientIdentifierId);
if (settings.isTemplate()) { if (settings.isTemplate()) {
final Collection<CodeValueData> codeValues = codeValueReadPlatformService.retrieveCustomIdentifierCodeValues(); final Collection<CodeValueData> codeValues = codeValueReadPlatformService
.retrieveCodeValuesByCode("Customer Identifier");
clientIdentifierData = ClientIdentifierData.template(clientIdentifierData, codeValues); clientIdentifierData = ClientIdentifierData.template(clientIdentifierData, codeValues);
} }
@@ -7,7 +7,7 @@ import java.util.Map;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.codes.domain.CodeValue; 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.codes.exception.CodeValueNotFoundException;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; 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.organisation.office.exception.OfficeNotFoundException;
import org.mifosplatform.portfolio.client.command.ClientIdentifierCommand; import org.mifosplatform.portfolio.client.command.ClientIdentifierCommand;
import org.mifosplatform.portfolio.client.command.ClientNoteCommand; 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.AccountNumberGenerator;
import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory;
import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientIdentifier; import org.mifosplatform.portfolio.client.domain.ClientIdentifier;
import org.mifosplatform.portfolio.client.domain.ClientIdentifierRepository; import org.mifosplatform.portfolio.client.domain.ClientIdentifierRepository;
@@ -54,7 +54,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
private final ClientIdentifierRepository clientIdentifierRepository; private final ClientIdentifierRepository clientIdentifierRepository;
private final OfficeRepository officeRepository; private final OfficeRepository officeRepository;
private final NoteRepository noteRepository; private final NoteRepository noteRepository;
private final CodeValueRepository codeValueRepository; private final CodeValueRepositoryWrapper codeValueRepository;
private final ClientCommandFromApiJsonDeserializer fromApiJsonDeserializer; private final ClientCommandFromApiJsonDeserializer fromApiJsonDeserializer;
private final ClientNoteCommandFromApiJsonDeserializer clientNoteFromApiJsonDeserializer; private final ClientNoteCommandFromApiJsonDeserializer clientNoteFromApiJsonDeserializer;
private final ClientIdentifierCommandFromApiJsonDeserializer clientIdentifierCommandFromApiJsonDeserializer; private final ClientIdentifierCommandFromApiJsonDeserializer clientIdentifierCommandFromApiJsonDeserializer;
@@ -63,7 +63,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
@Autowired @Autowired
public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final ClientRepository clientRepository, public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final ClientRepository clientRepository,
final ClientIdentifierRepository clientIdentifierRepository, final OfficeRepository officeRepository, final ClientIdentifierRepository clientIdentifierRepository, final OfficeRepository officeRepository,
final NoteRepository noteRepository, final CodeValueRepository codeValueRepository, final NoteRepository noteRepository, final CodeValueRepositoryWrapper codeValueRepository,
final ClientCommandFromApiJsonDeserializer fromApiJsonDeserializer, final ClientCommandFromApiJsonDeserializer fromApiJsonDeserializer,
final ClientNoteCommandFromApiJsonDeserializer clientNoteFromApiJsonDeserializer, final ClientNoteCommandFromApiJsonDeserializer clientNoteFromApiJsonDeserializer,
final ClientIdentifierCommandFromApiJsonDeserializer clientIdentifierCommandFromApiJsonDeserializer, final ClientIdentifierCommandFromApiJsonDeserializer clientIdentifierCommandFromApiJsonDeserializer,
@@ -254,9 +254,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final Client client = this.clientRepository.findOne(clientId); final Client client = this.clientRepository.findOne(clientId);
if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); } if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); }
final CodeValue documentType = this.codeValueRepository.findOne(clientIdentifierCommand.getDocumentTypeId()); final CodeValue documentType = this.codeValueRepository.findOneWithNotFoundDetection(clientIdentifierCommand.getDocumentTypeId());
if (documentType == null) { throw new CodeValueNotFoundException(clientIdentifierCommand.getDocumentTypeId()); }
documentTypeId = documentType.getId(); documentTypeId = documentType.getId();
documentTypeLabel = documentType.label(); documentTypeLabel = documentType.label();
@@ -297,7 +295,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final Map<String, Object> changes = clientIdentifierForUpdate.update(command); final Map<String, Object> changes = clientIdentifierForUpdate.update(command);
if (changes.containsKey("documentTypeId")) { if (changes.containsKey("documentTypeId")) {
documentType = this.codeValueRepository.findOne(documentTypeId); documentType = this.codeValueRepository.findOneWithNotFoundDetection(documentTypeId);
if (documentType == null) { throw new CodeValueNotFoundException(documentTypeId); } if (documentType == null) { throw new CodeValueNotFoundException(documentTypeId); }
documentTypeId = documentType.getId(); documentTypeId = documentType.getId();
@@ -23,6 +23,8 @@ import org.joda.time.LocalDate;
import org.mifosplatform.commands.domain.CommandWrapper; import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; 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.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.api.JsonQuery; 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.LoanAccountData;
import org.mifosplatform.portfolio.loanaccount.data.LoanBasicDetailsData; import org.mifosplatform.portfolio.loanaccount.data.LoanBasicDetailsData;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; 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.LoanPermissionData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData; import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData;
import org.mifosplatform.portfolio.loanaccount.guarantor.data.GuarantorData; 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.data.LoanScheduleData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService; import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
import org.mifosplatform.portfolio.loanaccount.service.LoanChargeReadPlatformService; 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.loanaccount.service.LoanReadPlatformService;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData; import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData;
@@ -71,16 +75,17 @@ import com.google.gson.JsonElement;
public class LoansApiResource { public class LoansApiResource {
private final Set<String> LOAN_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "accountNo", "externalId", "clientId", private final Set<String> LOAN_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "accountNo", "externalId", "clientId",
"groupId", "clientName", "groupName", "fundId", "fundName", "loanProductId", "loanProductName", "loanProductDescription", "groupId", "clientName", "groupName", "fundId", "fundName", "loanPurposeId", "loanOfficerId", "loanOfficerName",
"currency", "principal", "inArrearsTolerance", "numberOfRepayments", "repaymentEvery", "interestRatePerPeriod", "loanPurposeName", "loanProductId", "loanProductName", "loanProductDescription", "currency", "principal", "inArrearsTolerance",
"annualInterestRate", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "numberOfRepayments", "repaymentEvery", "interestRatePerPeriod", "annualInterestRate", "repaymentFrequencyType",
"interestCalculationPeriodType", "submittedOnDate", "approvedOnDate", "expectedDisbursementDate", "actualDisbursementDate", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "submittedOnDate",
"expectedFirstRepaymentOnDate", "interestChargedFromDate", "closedOnDate", "expectedMaturityDate", "status", "approvedOnDate", "expectedDisbursementDate", "actualDisbursementDate", "expectedFirstRepaymentOnDate",
"lifeCycleStatusDate", "repaymentSchedule", "transactions", "permissions", "convenienceData", "charges", "guarantor", "productOptions", "interestChargedFromDate", "closedOnDate", "expectedMaturityDate", "status", "lifeCycleStatusDate", "repaymentSchedule",
"transactions", "permissions", "convenienceData", "charges", "guarantor", "collateral", "productOptions",
"amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "repaymentFrequencyTypeOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "repaymentFrequencyTypeOptions",
"interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions", "loanOfficerId", "termFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions",
"loanOfficerName", "loanOfficerOptions", "chargeTemplate", "transactionProcessingStrategyId", "loanOfficerOptions", "chargeTemplate", "transactionProcessingStrategyId", "termFrequency", "termPeriodFrequencyType",
"termFrequency", "termPeriodFrequencyType")); "loanPurposeOptions", "loanCollateralOptions"));
private final String resourceNameForPermissions = "LOAN"; private final String resourceNameForPermissions = "LOAN";
@@ -91,9 +96,11 @@ public class LoansApiResource {
private final FundReadPlatformService fundReadPlatformService; private final FundReadPlatformService fundReadPlatformService;
private final ChargeReadPlatformService chargeReadPlatformService; private final ChargeReadPlatformService chargeReadPlatformService;
private final LoanChargeReadPlatformService loanChargeReadPlatformService; private final LoanChargeReadPlatformService loanChargeReadPlatformService;
private final LoanCollateralReadPlatformService loanCollateralReadPlatformService;
private final LoanScheduleCalculationPlatformService calculationPlatformService; private final LoanScheduleCalculationPlatformService calculationPlatformService;
private final StaffReadPlatformService staffReadPlatformService; private final StaffReadPlatformService staffReadPlatformService;
private final GuarantorReadPlatformService guarantorReadPlatformService; private final GuarantorReadPlatformService guarantorReadPlatformService;
private final CodeValueReadPlatformService codeValueReadPlatformService;
private final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer; private final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer;
private final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer; private final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer;
private final DefaultToApiJsonSerializer<BulkTransferLoanOfficerData> loanOfficeTransferToApiJsonSerializer; private final DefaultToApiJsonSerializer<BulkTransferLoanOfficerData> loanOfficeTransferToApiJsonSerializer;
@@ -106,8 +113,10 @@ public class LoansApiResource {
final LoanProductReadPlatformService loanProductReadPlatformService, final LoanProductReadPlatformService loanProductReadPlatformService,
final LoanDropdownReadPlatformService dropdownReadPlatformService, final FundReadPlatformService fundReadPlatformService, final LoanDropdownReadPlatformService dropdownReadPlatformService, final FundReadPlatformService fundReadPlatformService,
final ChargeReadPlatformService chargeReadPlatformService, final LoanChargeReadPlatformService loanChargeReadPlatformService, final ChargeReadPlatformService chargeReadPlatformService, final LoanChargeReadPlatformService loanChargeReadPlatformService,
final LoanCollateralReadPlatformService loanCollateralReadPlatformService,
final LoanScheduleCalculationPlatformService calculationPlatformService, final LoanScheduleCalculationPlatformService calculationPlatformService,
final StaffReadPlatformService staffReadPlatformService, final GuarantorReadPlatformService guarantorReadPlatformService, final StaffReadPlatformService staffReadPlatformService, final GuarantorReadPlatformService guarantorReadPlatformService,
final CodeValueReadPlatformService codeValueReadPlatformService,
final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer, final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer,
final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer, final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer,
final DefaultToApiJsonSerializer<BulkTransferLoanOfficerData> loanOfficeTransferToApiJsonSerializer, final DefaultToApiJsonSerializer<BulkTransferLoanOfficerData> loanOfficeTransferToApiJsonSerializer,
@@ -120,9 +129,11 @@ public class LoansApiResource {
this.fundReadPlatformService = fundReadPlatformService; this.fundReadPlatformService = fundReadPlatformService;
this.chargeReadPlatformService = chargeReadPlatformService; this.chargeReadPlatformService = chargeReadPlatformService;
this.loanChargeReadPlatformService = loanChargeReadPlatformService; this.loanChargeReadPlatformService = loanChargeReadPlatformService;
this.loanCollateralReadPlatformService = loanCollateralReadPlatformService;
this.calculationPlatformService = calculationPlatformService; this.calculationPlatformService = calculationPlatformService;
this.staffReadPlatformService = staffReadPlatformService; this.staffReadPlatformService = staffReadPlatformService;
this.guarantorReadPlatformService = guarantorReadPlatformService; this.guarantorReadPlatformService = guarantorReadPlatformService;
this.codeValueReadPlatformService = codeValueReadPlatformService;
this.toApiJsonSerializer = toApiJsonSerializer; this.toApiJsonSerializer = toApiJsonSerializer;
this.loanScheduleToApiJsonSerializer = loanScheduleToApiJsonSerializer; this.loanScheduleToApiJsonSerializer = loanScheduleToApiJsonSerializer;
this.loanOfficeTransferToApiJsonSerializer = loanOfficeTransferToApiJsonSerializer; this.loanOfficeTransferToApiJsonSerializer = loanOfficeTransferToApiJsonSerializer;
@@ -135,12 +146,11 @@ public class LoansApiResource {
@Path("template") @Path("template")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveDetailsForNewLoanApplicationStepOne(@QueryParam("clientId") final Long clientId, public String templateForNewLoanApplication(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId,
@QueryParam("groupId") final Long groupId, @QueryParam("productId") final Long productId, @Context final UriInfo uriInfo) { @QueryParam("productId") final Long productId, @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
// tempate related
Collection<LoanProductData> productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup(); Collection<LoanProductData> productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup();
Collection<EnumOptionData> loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions(); Collection<EnumOptionData> loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
Collection<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions(); Collection<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
@@ -160,26 +170,34 @@ public class LoansApiResource {
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly); Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
ChargeData chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate(); ChargeData chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate();
LoanBasicDetailsData loanBasicDetails; LoanBasicDetailsData loanBasicDetails = null;
Long officeId; Collection<LoanChargeData> charges = null;
Collection<LoanCollateralData> collateral = null;
Collection<GuarantorData> guarantors = null;
Collection<StaffData> allowedLoanOfficers = null;
if (clientId != null) { if (clientId != null) {
loanBasicDetails = this.loanReadPlatformService.retrieveClientAndProductDetails(clientId, productId); loanBasicDetails = this.loanReadPlatformService.retrieveClientAndProductDetails(clientId, productId);
officeId = loanBasicDetails.getClientOfficeId(); charges = loanBasicDetails.getCharges();
} else { final Long officeId = loanBasicDetails.getClientOfficeId();
allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(officeId);
} else if (groupId != null) {
loanBasicDetails = this.loanReadPlatformService.retrieveGroupAndProductDetails(groupId, productId); 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; 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, final LoanAccountData newLoanAccount = new LoanAccountData(loanBasicDetails, convenienceDataRequired, null, null, null, charges,
productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, collateral, guarantors, productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, null); interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions,
loanCollateralOptions);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, newLoanAccount, LOAN_DATA_PARAMETERS); return this.toApiJsonSerializer.serialize(settings, newLoanAccount, LOAN_DATA_PARAMETERS);
@@ -201,6 +219,7 @@ public class LoansApiResource {
LoanPermissionData permissions = null; LoanPermissionData permissions = null;
Collection<LoanChargeData> charges = null; Collection<LoanChargeData> charges = null;
Collection<GuarantorData> guarantors = null; Collection<GuarantorData> guarantors = null;
Collection<LoanCollateralData> collateral = null;
final Set<String> mandatoryResponseParameters = new HashSet<String>(); final Set<String> mandatoryResponseParameters = new HashSet<String>();
boolean convenienceDataRequired = false; boolean convenienceDataRequired = false;
@@ -209,14 +228,14 @@ public class LoansApiResource {
if (associationParameters.contains("all")) { if (associationParameters.contains("all")) {
associationParameters.addAll(Arrays.asList("repaymentSchedule", "transactions", "permissions", "convenienceData", associationParameters.addAll(Arrays.asList("repaymentSchedule", "transactions", "permissions", "convenienceData",
"charges", "guarantor")); "charges", "guarantor", "collateral"));
} }
if (associationParameters.contains("guarantor")) { if (associationParameters.contains("guarantor")) {
mandatoryResponseParameters.add("guarantor"); mandatoryResponseParameters.add("guarantor");
guarantors = this.guarantorReadPlatformService.retrieveGuarantorsForLoan(loanId); guarantors = this.guarantorReadPlatformService.retrieveGuarantorsForLoan(loanId);
if (CollectionUtils.isEmpty(guarantors)) { if (CollectionUtils.isEmpty(guarantors)) {
guarantors = null; // set back to null so doesn't appear in JSON guarantors = null;
} }
} }
@@ -271,8 +290,15 @@ public class LoansApiResource {
mandatoryResponseParameters.add("charges"); mandatoryResponseParameters.add("charges");
charges = this.loanChargeReadPlatformService.retrieveLoanCharges(loanId); charges = this.loanChargeReadPlatformService.retrieveLoanCharges(loanId);
if (CollectionUtils.isEmpty(charges)) { if (CollectionUtils.isEmpty(charges)) {
charges = null; // set back to null so doesnt appear in JSON charges = null;
// is no charges exist. }
}
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> interestTypeOptions = null;
Collection<EnumOptionData> interestCalculationPeriodTypeOptions = null; Collection<EnumOptionData> interestCalculationPeriodTypeOptions = null;
Collection<FundData> fundOptions = null; Collection<FundData> fundOptions = null;
Collection<StaffData> allowedLoanOfficers = null;
Collection<ChargeData> chargeOptions = null; Collection<ChargeData> chargeOptions = null;
ChargeData chargeTemplate = null; ChargeData chargeTemplate = null;
Collection<CodeValueData> loanPurposeOptions = null;
Collection<CodeValueData> loanCollateralOptions = null;
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
if (template) { if (template) {
@@ -305,14 +334,19 @@ public class LoansApiResource {
final boolean feeChargesOnly = false; final boolean feeChargesOnly = false;
chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly); chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate(); 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, final LoanAccountData loanAccount = new LoanAccountData(loanBasicDetails, convenienceDataRequired, repaymentSchedule,
loanRepayments, permissions, charges, productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions, loanRepayments, permissions, charges, collateral, guarantors, productOptions, loanTermFrequencyTypeOptions,
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions,
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, null, guarantors); 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); return this.toApiJsonSerializer.serialize(settings, loanAccount, LOAN_DATA_PARAMETERS);
} }
@@ -14,14 +14,18 @@ import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidation
*/ */
public class LoanChargeCommand implements Comparable<LoanChargeCommand> { public class LoanChargeCommand implements Comparable<LoanChargeCommand> {
@SuppressWarnings("unused")
private final Long id;
private final Long chargeId; private final Long chargeId;
private final BigDecimal amount; private final BigDecimal amount;
private final Integer chargeTimeType; private final Integer chargeTimeType;
private final LocalDate specifiedDueDate;
private final Integer chargeCalculationType; 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) { final Integer chargeCalculationType, final LocalDate specifiedDueDate) {
this.id = id;
this.chargeId = chargeId; this.chargeId = chargeId;
this.amount = amount; this.amount = amount;
this.chargeTimeType = chargeTimeType; this.chargeTimeType = chargeTimeType;
@@ -38,28 +42,7 @@ public class LoanChargeCommand implements Comparable<LoanChargeCommand> {
return comparison; return comparison;
} }
public void validateForCreate() { @Deprecated
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); }
}
public void validateForUpdate() { public void validateForUpdate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
@@ -4,6 +4,7 @@ import java.math.BigDecimal;
import java.util.Collection; import java.util.Collection;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.staff.data.StaffData; import org.mifosplatform.organisation.staff.data.StaffData;
@@ -34,6 +35,8 @@ public class LoanAccountData {
private final EnumOptionData status; private final EnumOptionData status;
private final Long fundId; private final Long fundId;
private final String fundName; private final String fundName;
private final Long loanPurposeId;
private final String loanPurposeName;
private final Long loanOfficerId; private final Long loanOfficerId;
private final String loanOfficerName; private final String loanOfficerName;
private final CurrencyData currency; private final CurrencyData currency;
@@ -67,9 +70,10 @@ public class LoanAccountData {
private final LoanScheduleData repaymentSchedule; private final LoanScheduleData repaymentSchedule;
private final Collection<LoanTransactionData> transactions; private final Collection<LoanTransactionData> transactions;
private final Collection<LoanChargeData> charges; private final Collection<LoanChargeData> charges;
private final Collection<LoanCollateralData> collateral;
private final Collection<GuarantorData> guarantors;
private final LoanPermissionData permissions; private final LoanPermissionData permissions;
private final LoanConvenienceData convenienceData; private final LoanConvenienceData convenienceData;
private final Collection<GuarantorData> guarantors;
// template // template
private final Collection<StaffData> loanOfficerOptions; private final Collection<StaffData> loanOfficerOptions;
@@ -84,21 +88,27 @@ public class LoanAccountData {
private final Collection<FundData> fundOptions; private final Collection<FundData> fundOptions;
private final Collection<ChargeData> chargeOptions; private final Collection<ChargeData> chargeOptions;
private final ChargeData chargeTemplate; private final ChargeData chargeTemplate;
private final Collection<CodeValueData> loanPurposeOptions;
private final Collection<CodeValueData> loanCollateralOptions;
public LoanAccountData(final LoanBasicDetailsData basicDetails, final boolean convenienceDataRequired, public LoanAccountData(final LoanBasicDetailsData basicDetails, final boolean convenienceDataRequired,
final LoanScheduleData repaymentSchedule, final Collection<LoanTransactionData> transactions, final LoanScheduleData repaymentSchedule, final Collection<LoanTransactionData> transactions,
final LoanPermissionData permissions, final Collection<LoanChargeData> charges, 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<LoanProductData> productOptions, final Collection<EnumOptionData> termFrequencyTypeOptions,
final Collection<EnumOptionData> repaymentFrequencyTypeOptions, final Collection<EnumOptionData> repaymentFrequencyTypeOptions,
final Collection<TransactionProcessingStrategyData> repaymentStrategyOptions, final Collection<TransactionProcessingStrategyData> repaymentStrategyOptions,
final Collection<EnumOptionData> interestRateFrequencyTypeOptions, final Collection<EnumOptionData> amortizationTypeOptions, final Collection<EnumOptionData> interestRateFrequencyTypeOptions, final Collection<EnumOptionData> amortizationTypeOptions,
final Collection<EnumOptionData> interestTypeOptions, final Collection<EnumOptionData> interestCalculationPeriodTypeOptions, final Collection<EnumOptionData> interestTypeOptions, final Collection<EnumOptionData> interestCalculationPeriodTypeOptions,
final Collection<FundData> fundOptions, final Collection<ChargeData> chargeOptions, final ChargeData chargeTemplate, 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.repaymentSchedule = repaymentSchedule;
this.transactions = transactions; this.transactions = transactions;
this.permissions = permissions; this.permissions = permissions;
this.charges = charges; this.charges = charges;
this.collateral = collateral;
this.guarantors = guarantors;
this.productOptions = productOptions; this.productOptions = productOptions;
this.termFrequencyTypeOptions = termFrequencyTypeOptions; this.termFrequencyTypeOptions = termFrequencyTypeOptions;
this.repaymentFrequencyTypeOptions = repaymentFrequencyTypeOptions; this.repaymentFrequencyTypeOptions = repaymentFrequencyTypeOptions;
@@ -111,7 +121,8 @@ public class LoanAccountData {
this.chargeOptions = chargeOptions; this.chargeOptions = chargeOptions;
this.chargeTemplate = chargeTemplate; this.chargeTemplate = chargeTemplate;
this.loanOfficerOptions = loanOfficerOptions; this.loanOfficerOptions = loanOfficerOptions;
this.guarantors = guarantors; this.loanPurposeOptions = loanPurposeOptions;
this.loanCollateralOptions = loanCollateralOptions;
if (convenienceDataRequired) { if (convenienceDataRequired) {
int maxSubmittedOnOffsetFromToday = basicDetails.getMaxSubmittedOnOffsetFromToday(); int maxSubmittedOnOffsetFromToday = basicDetails.getMaxSubmittedOnOffsetFromToday();
@@ -129,48 +140,98 @@ public class LoanAccountData {
this.convenienceData = null; this.convenienceData = null;
} }
this.id = basicDetails.getId(); if (basicDetails != null) {
this.accountNo = basicDetails.getAccountNo(); this.id = basicDetails.getId();
this.externalId = basicDetails.getExternalId(); this.accountNo = basicDetails.getAccountNo();
this.clientId = basicDetails.getClientId(); this.externalId = basicDetails.getExternalId();
this.clientName = basicDetails.getClientName(); this.clientId = basicDetails.getClientId();
this.groupId = basicDetails.getGroupId(); this.clientName = basicDetails.getClientName();
this.groupName = basicDetails.getGroupName(); this.groupId = basicDetails.getGroupId();
this.loanProductId = basicDetails.getLoanProductId(); this.groupName = basicDetails.getGroupName();
this.loanProductName = basicDetails.getLoanProductName(); this.loanProductId = basicDetails.getLoanProductId();
this.loanProductDescription = basicDetails.getLoanProductDescription(); this.loanProductName = basicDetails.getLoanProductName();
this.fundId = basicDetails.getFundId(); this.loanProductDescription = basicDetails.getLoanProductDescription();
this.fundName = basicDetails.getFundName(); this.fundId = basicDetails.getFundId();
this.loanOfficerId = basicDetails.getLoanOfficerId(); this.fundName = basicDetails.getFundName();
this.loanOfficerName = basicDetails.getLoanOfficerName(); this.loanPurposeId = basicDetails.getLoanPurposeId();
this.loanPurposeName = basicDetails.getLoanPurposeName();
this.loanOfficerId = basicDetails.getLoanOfficerId();
this.loanOfficerName = basicDetails.getLoanOfficerName();
this.submittedOnDate = basicDetails.getSubmittedOnDate(); this.submittedOnDate = basicDetails.getSubmittedOnDate();
this.approvedOnDate = basicDetails.getApprovedOnDate(); this.approvedOnDate = basicDetails.getApprovedOnDate();
this.expectedDisbursementDate = basicDetails.getExpectedDisbursementDate(); this.expectedDisbursementDate = basicDetails.getExpectedDisbursementDate();
this.actualDisbursementDate = basicDetails.getActualDisbursementDate(); this.actualDisbursementDate = basicDetails.getActualDisbursementDate();
this.closedOnDate = basicDetails.getClosedOnDate(); this.closedOnDate = basicDetails.getClosedOnDate();
this.expectedMaturityDate = basicDetails.getExpectedMaturityDate(); this.expectedMaturityDate = basicDetails.getExpectedMaturityDate();
this.repaymentsStartingFromDate = basicDetails.getRepaymentsStartingFromDate(); this.repaymentsStartingFromDate = basicDetails.getRepaymentsStartingFromDate();
this.interestChargedFromDate = basicDetails.getInterestChargedFromDate(); this.interestChargedFromDate = basicDetails.getInterestChargedFromDate();
this.currency = basicDetails.getCurrency(); this.currency = basicDetails.getCurrency();
this.principal = basicDetails.getPrincipal(); this.principal = basicDetails.getPrincipal();
this.inArrearsTolerance = basicDetails.getInArrearsTolerance(); this.inArrearsTolerance = basicDetails.getInArrearsTolerance();
this.termFrequency = basicDetails.getTermFrequency(); this.termFrequency = basicDetails.getTermFrequency();
this.termPeriodFrequencyType = basicDetails.getTermPeriodFrequencyType(); this.termPeriodFrequencyType = basicDetails.getTermPeriodFrequencyType();
this.numberOfRepayments = basicDetails.getNumberOfRepayments(); this.numberOfRepayments = basicDetails.getNumberOfRepayments();
this.repaymentEvery = basicDetails.getRepaymentEvery(); this.repaymentEvery = basicDetails.getRepaymentEvery();
this.transactionProcessingStrategyId = basicDetails.getTransactionStrategyId(); this.transactionProcessingStrategyId = basicDetails.getTransactionStrategyId();
this.interestRatePerPeriod = basicDetails.getInterestRatePerPeriod(); this.interestRatePerPeriod = basicDetails.getInterestRatePerPeriod();
this.annualInterestRate = basicDetails.getAnnualInterestRate(); this.annualInterestRate = basicDetails.getAnnualInterestRate();
this.repaymentFrequencyType = basicDetails.getRepaymentFrequencyType(); this.repaymentFrequencyType = basicDetails.getRepaymentFrequencyType();
this.interestRateFrequencyType = basicDetails.getInterestRateFrequencyType(); this.interestRateFrequencyType = basicDetails.getInterestRateFrequencyType();
this.amortizationType = basicDetails.getAmortizationType(); this.amortizationType = basicDetails.getAmortizationType();
this.interestType = basicDetails.getInterestType(); this.interestType = basicDetails.getInterestType();
this.interestCalculationPeriodType = basicDetails.getInterestCalculationPeriodType(); this.interestCalculationPeriodType = basicDetails.getInterestCalculationPeriodType();
this.status = basicDetails.getStatus(); this.status = basicDetails.getStatus();
this.lifeCycleStatusDate = basicDetails.getLifeCycleStatusDate(); 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 EnumOptionData status;
private final Long fundId; private final Long fundId;
private final String fundName; private final String fundName;
private final Long loanPurposeId;
private final String loanPurposeName;
private final Long loanOfficerId; private final Long loanOfficerId;
private final String loanOfficerName; private final String loanOfficerName;
private final CurrencyData currency; private final CurrencyData currency;
@@ -120,6 +122,8 @@ public class LoanBasicDetailsData {
this.status = null; this.status = null;
this.fundId = fundId; this.fundId = fundId;
this.fundName = fundName; this.fundName = fundName;
this.loanPurposeId = null;
this.loanPurposeName = null;
this.loanOfficerId = null; this.loanOfficerId = null;
this.loanOfficerName = null; this.loanOfficerName = null;
this.currency = currency; this.currency = currency;
@@ -179,6 +183,8 @@ public class LoanBasicDetailsData {
this.status = null; this.status = null;
this.fundId = null; this.fundId = null;
this.fundName = null; this.fundName = null;
this.loanPurposeId = null;
this.loanPurposeName = null;
this.loanOfficerId = null; this.loanOfficerId = null;
this.loanOfficerName = null; this.loanOfficerName = null;
this.currency = 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, 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 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, final Long loanProductId, final String loanProductName, final String loanProductDescription, final Long fundId,
String fundName, final LocalDate closedOnDate, final LocalDate submittedOnDate, final LocalDate approvedOnDate, final String fundName, final Long loanPurposeId, final String loanPurposeName, final LocalDate closedOnDate,
final LocalDate expectedDisbursementDate, final LocalDate actualDisbursementDate, final LocalDate expectedMaturityDate, final LocalDate submittedOnDate, final LocalDate approvedOnDate, final LocalDate expectedDisbursementDate,
final LocalDate repaymentsStartingFromDate, final LocalDate interestChargedFromDate, final CurrencyData currency, final LocalDate actualDisbursementDate, final LocalDate expectedMaturityDate, final LocalDate repaymentsStartingFromDate,
final BigDecimal principal, final BigDecimal inArrearsTolerance, final Integer numberOfRepayments, final LocalDate interestChargedFromDate, final CurrencyData currency, final BigDecimal principal,
final Integer repaymentEvery, final BigDecimal interestRatePerPeriod, final BigDecimal annualInterestRate, final BigDecimal inArrearsTolerance, final Integer numberOfRepayments, final Integer repaymentEvery,
final EnumOptionData repaymentFrequencyType, final EnumOptionData interestRateFrequencyType, final BigDecimal interestRatePerPeriod, final BigDecimal annualInterestRate, final EnumOptionData repaymentFrequencyType,
final EnumOptionData amortizationType, final EnumOptionData interestType, final EnumOptionData interestCalculationPeriodType, final EnumOptionData interestRateFrequencyType, final EnumOptionData amortizationType, final EnumOptionData interestType,
final EnumOptionData status, final LocalDate lifeCycleStatusDate, final Integer termFrequency, final EnumOptionData interestCalculationPeriodType, final EnumOptionData status, final LocalDate lifeCycleStatusDate,
final EnumOptionData termPeriodFrequencyType, final Integer transactionStrategyId, final Collection<LoanChargeData> charges, final Integer termFrequency, final EnumOptionData termPeriodFrequencyType, final Integer transactionStrategyId,
final Long loanOfficerId, String loanOfficerName, final BigDecimal totalDisbursementCharges) { final Collection<LoanChargeData> charges, final Long loanOfficerId, String loanOfficerName,
final BigDecimal totalDisbursementCharges) {
this.id = id; this.id = id;
this.accountNo = accountNo; this.accountNo = accountNo;
this.externalId = externalId; this.externalId = externalId;
@@ -240,6 +247,8 @@ public class LoanBasicDetailsData {
this.loanProductDescription = loanProductDescription; this.loanProductDescription = loanProductDescription;
this.fundId = fundId; this.fundId = fundId;
this.fundName = fundName; this.fundName = fundName;
this.loanPurposeId = loanPurposeId;
this.loanPurposeName = loanPurposeName;
this.loanOfficerId = loanOfficerId; this.loanOfficerId = loanOfficerId;
this.loanOfficerName = loanOfficerName; this.loanOfficerName = loanOfficerName;
this.closedOnDate = closedOnDate; this.closedOnDate = closedOnDate;
@@ -393,6 +402,14 @@ public class LoanBasicDetailsData {
return fundName; return fundName;
} }
public Long getLoanPurposeId() {
return this.loanPurposeId;
}
public String getLoanPurposeName() {
return this.loanPurposeName;
}
public Long getLoanProductId() { public Long getLoanProductId() {
return loanProductId; 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.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
@@ -33,6 +32,7 @@ import org.hibernate.annotations.LazyCollectionOption;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter; 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.api.JsonCommand;
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom; import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
import org.mifosplatform.infrastructure.core.service.DateUtils; 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.fund.domain.Fund;
import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand; 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.InvalidLoanStateTransitionException;
import org.mifosplatform.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException; import org.mifosplatform.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
@@ -67,8 +68,7 @@ import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
@Entity @Entity
@Table(name = "m_loan", uniqueConstraints = { @Table(name = "m_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "loan_account_no_UNIQUE"),
@UniqueConstraint(columnNames = { "account_no" }, name = "loan_account_no_UNIQUE"),
@UniqueConstraint(columnNames = { "external_id" }, name = "loan_externalid_UNIQUE") }) @UniqueConstraint(columnNames = { "external_id" }, name = "loan_externalid_UNIQUE") })
public class Loan extends AbstractAuditableCustom<AppUser, Long> { public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@@ -96,7 +96,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne @ManyToOne
@JoinColumn(name = "loan_officer_id", nullable = true) @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 @ManyToOne
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true) @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) @OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
private Set<LoanCharge> charges = new HashSet<LoanCharge>(); 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) @LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true) @OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
private Set<LoanOfficerAssignmentHistory> loanOfficerHistory; private Set<LoanOfficerAssignmentHistory> loanOfficerHistory;
@@ -198,21 +206,24 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessor = new LoanRepaymentScheduleTransactionProcessorFactory(); private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessor = new LoanRepaymentScheduleTransactionProcessorFactory();
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final LoanProduct loanProduct, public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final LoanProduct loanProduct,
final Fund fund, final Staff officer, final LoanTransactionProcessingStrategy transactionProcessingStrategy, final Fund fund, final Staff officer, final CodeValue loanPurpose,
final LoanSchedule loanSchedule, final Set<LoanCharge> loanCharges) { final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanSchedule loanSchedule,
final Set<LoanCharge> loanCharges, final Set<LoanCollateral> collateral) {
final LoanStatus status = null; final LoanStatus status = null;
LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail(); LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
return new Loan(accountNo, client, null, fund, officer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, return new Loan(accountNo, client, null, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
status, loanCharges); loanRepaymentScheduleDetail, status, loanCharges, collateral);
} }
public static Loan newGroupLoanApplication(final String accountNo, final Group group, final LoanProduct loanProduct, final Fund fund, 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 Staff officer, final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanSchedule loanSchedule,
final Set<LoanCharge> loanCharges) { final Set<LoanCharge> loanCharges) {
final LoanStatus status = null; final LoanStatus status = null;
LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail(); final LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
return new Loan(accountNo, null, group, fund, officer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, final CodeValue loanPurpose = null;
status, loanCharges); 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, 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 LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanSchedule loanSchedule,
final Set<LoanCharge> loanCharges) { final Set<LoanCharge> loanCharges) {
final LoanStatus status = null; final LoanStatus status = null;
LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail(); final LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
return new Loan(accountNo, client, group, fund, officer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, final CodeValue loanPurpose = null;
status, loanCharges); final Set<LoanCollateral> collateral = null;
return new Loan(accountNo, client, group, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
loanRepaymentScheduleDetail, status, loanCharges, collateral);
} }
protected Loan() { protected Loan() {
// //
} }
private Loan(final String accountNo, final Client client, final Group group, Fund fund, Staff loanOfficer, private Loan(final String accountNo, final Client client, final Group group, final Fund fund, final Staff loanOfficer,
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanProduct loanProduct, final CodeValue loanPurpose, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final LoanStatus loanStatus, final Set<LoanCharge> loanCharges) { final LoanProduct loanProduct, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final LoanStatus loanStatus,
final Set<LoanCharge> loanCharges, final Set<LoanCollateral> collateral) {
if (StringUtils.isBlank(accountNo)) { if (StringUtils.isBlank(accountNo)) {
this.accountNumber = new RandomPasswordGenerator(19).generate(); this.accountNumber = new RandomPasswordGenerator(19).generate();
this.accountNumberRequiresAutoGeneration = true; this.accountNumberRequiresAutoGeneration = true;
@@ -242,7 +255,9 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.client = client; this.client = client;
this.group = group; this.group = group;
this.fund = fund; this.fund = fund;
this.loanofficer = loanOfficer; this.loanOfficer = loanOfficer;
this.loanPurpose = loanPurpose;
this.transactionProcessingStrategy = transactionProcessingStrategy; this.transactionProcessingStrategy = transactionProcessingStrategy;
this.loanProduct = loanProduct; this.loanProduct = loanProduct;
this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail; this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
@@ -257,6 +272,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} else { } else {
this.charges = null; this.charges = null;
} }
if (collateral != null && !collateral.isEmpty()) {
this.collateral = associateWithThisLoan(collateral);
} else {
this.collateral = null;
}
this.loanOfficerHistory = null; this.loanOfficerHistory = null;
} }
@@ -280,6 +300,13 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return loanCharges; return loanCharges;
} }
private Set<LoanCollateral> associateWithThisLoan(final Set<LoanCollateral> collateral) {
for (LoanCollateral item : collateral) {
item.associateWith(this);
}
return collateral;
}
private void updateTotalChargesDueAtDisbursement() { private void updateTotalChargesDueAtDisbursement() {
this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement(); this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement();
} }
@@ -354,7 +381,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
private void validateChargeHasValidSpecifiedDateIfApplicable(final LoanCharge loanCharge, final LocalDate disbursementDate, private void validateChargeHasValidSpecifiedDateIfApplicable(final LoanCharge loanCharge, final LocalDate disbursementDate,
final LocalDate lastRepaymentPeriodDueDate) { 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."; 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, throw new LoanChargeCannotBeAddedException("loanCharge", "specified.due.date.outside.range", defaultUserMessage,
getDisbursementDate(), getLastRepaymentPeriodDueDate(), loanCharge.name()); getDisbursementDate(), getLastRepaymentPeriodDueDate(), loanCharge.name());
@@ -505,10 +533,30 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.loanProduct = loanProduct; this.loanProduct = loanProduct;
} }
public void updateAccountNo(final String newAccountNo) {
this.accountNumber = newAccountNo;
this.accountNumberRequiresAutoGeneration = false;
}
public void updateFund(final Fund fund) { public void updateFund(final Fund fund) {
this.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) { public void updateTransactionProcessingStrategy(final LoanTransactionProcessingStrategy strategy) {
this.transactionProcessingStrategy = strategy; this.transactionProcessingStrategy = strategy;
} }
@@ -519,6 +567,14 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement(); 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) { public void updateLoanSchedule(final LoanScheduleData modifiedLoanSchedule) {
this.repaymentScheduleInstallments.clear(); this.repaymentScheduleInstallments.clear();
@@ -548,8 +604,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.expectedMaturityDate = determineExpectedMaturityDate().toDate(); this.expectedMaturityDate = determineExpectedMaturityDate().toDate();
} }
public Map<String, Object> loanApplicationModification(final JsonCommand command, final LoanChargeCommand[] loanChargeCommands, public Map<String, Object> loanApplicationModification(final JsonCommand command, final Set<LoanCharge> possiblyModifedLoanCharges,
final AprCalculator aprCalculator) { final Set<LoanCollateral> possiblyModifedLoanCollateralItems, final AprCalculator aprCalculator) {
final Map<String, Object> actualChanges = this.loanRepaymentScheduleDetail.updateLoanApplicationAttributes(command, aprCalculator); final Map<String, Object> actualChanges = this.loanRepaymentScheduleDetail.updateLoanApplicationAttributes(command, aprCalculator);
if (!actualChanges.isEmpty()) { if (!actualChanges.isEmpty()) {
@@ -597,21 +653,32 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
actualChanges.put(fundIdParamName, newValue); 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"; final String strategyIdParamName = "transactionProcessingStrategyId";
if (command.isChangeInLongParameterNamed(strategyIdParamName, this.transactionProcessingStrategy.getId())) { if (command.isChangeInLongParameterNamed(strategyIdParamName, this.transactionProcessingStrategy.getId())) {
final Long newValue = command.longValueOfParameterNamed(strategyIdParamName); final Long newValue = command.longValueOfParameterNamed(strategyIdParamName);
actualChanges.put(strategyIdParamName, newValue); 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"; final String submittedOnDateParamName = "submittedOnDate";
if (command.isChangeInLocalDateParameterNamed(submittedOnDateParamName, getSubmittedOnDate())) { if (command.isChangeInLocalDateParameterNamed(submittedOnDateParamName, getSubmittedOnDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(submittedOnDateParamName); final String valueAsInput = command.stringValueOfParameterNamed(submittedOnDateParamName);
@@ -681,32 +748,58 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
getSubmittedOnDate(), getExpectedDisbursedOnLocalDate()); getSubmittedOnDate(), getExpectedDisbursedOnLocalDate());
} }
// FIXME - this constraint doesnt really need to be here. should be final String chargesParamName = "charges";
// possible to express loan term as say 12 months whilst also saying if (command.parameterExists(chargesParamName)) {
// - 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 final Set<LoanCharge> existingLoanCharges = getNullPointerSafeLoanCharges();
for (LoanCharge loanCharge : getNullPointerSafeLoanCharges()) {
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); 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; 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() { private Set<LoanCharge> getNullPointerSafeLoanCharges() {
Set<LoanCharge> loanCharges = this.charges; Set<LoanCharge> loanCharges = this.charges;
if (this.charges == null) { if (this.charges == null) {
@@ -715,19 +808,34 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return loanCharges; 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; LoanChargeCommand[] existingLoanCharges = null;
if (!getNullPointerSafeLoanCharges().isEmpty()) { List<LoanChargeCommand> loanChargesList = new ArrayList<LoanChargeCommand>();
List<LoanChargeCommand> loanChargesList = new ArrayList<LoanChargeCommand>(); for (LoanCharge loanCharge : setOfLoanCharges) {
for (LoanCharge loanCharge : getNullPointerSafeLoanCharges()) { loanChargesList.add(loanCharge.toCommand());
loanChargesList.add(loanCharge.toCommand());
}
existingLoanCharges = loanChargesList.toArray(new LoanChargeCommand[loanChargesList.size()]);
} }
existingLoanCharges = loanChargesList.toArray(new LoanChargeCommand[loanChargesList.size()]);
return existingLoanCharges; return existingLoanCharges;
} }
@@ -745,7 +853,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
final LoanScheduleData loanScheduleData = loanSchedule.generate(); 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) { if (loanSchedule.getDisbursementDate() != null) {
this.expectedDisbursedOnDate = loanSchedule.getDisbursementDate().toDate(); this.expectedDisbursedOnDate = loanSchedule.getDisbursementDate().toDate();
} }
@@ -905,17 +1014,17 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (approvedOn.isBefore(submittalDate)) { if (approvedOn.isBefore(submittalDate)) {
final String errorMessage = "The date on which a loan is approved cannot be before its submittal date: " final String errorMessage = "The date on which a loan is approved cannot be before its submittal date: "
+ submittalDate.toString(); + submittalDate.toString();
throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage, getApprovedOnDate(), throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage,
submittalDate); getApprovedOnDate(), submittalDate);
} }
if (approvedOn.isAfter(new LocalDate())) { if (approvedOn.isAfter(new LocalDate())) {
final String errorMessage = "The date on which a loan is approved cannot be in the future."; 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()); 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, final LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = LoanOfficerAssignmentHistory.createNew(this,
this.loanofficer, approvedOn); this.loanOfficer, approvedOn);
this.loanOfficerHistory.add(loanOfficerAssignmentHistory); this.loanOfficerHistory.add(loanOfficerAssignmentHistory);
} }
} }
@@ -1861,8 +1970,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
public boolean hasLoanOfficer(final Staff fromLoanOfficer) { public boolean hasLoanOfficer(final Staff fromLoanOfficer) {
boolean matchesCurrentLoanOfficer = false; boolean matchesCurrentLoanOfficer = false;
if (this.loanofficer != null) { if (this.loanOfficer != null) {
matchesCurrentLoanOfficer = this.loanofficer.identifiedBy(fromLoanOfficer); matchesCurrentLoanOfficer = this.loanOfficer.identifiedBy(fromLoanOfficer);
} else { } else {
matchesCurrentLoanOfficer = fromLoanOfficer == null; matchesCurrentLoanOfficer = fromLoanOfficer == null;
} }
@@ -1920,11 +2029,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
final LoanOfficerAssignmentHistory latestHistoryRecord = findLatestIncompleteHistoryRecord(); final LoanOfficerAssignmentHistory latestHistoryRecord = findLatestIncompleteHistoryRecord();
if (latestHistoryRecord != null && this.loanofficer.identifiedBy(newLoanOfficer)) { if (latestHistoryRecord != null && this.loanOfficer.identifiedBy(newLoanOfficer)) {
latestHistoryRecord.updateStartDate(assignmentDate); latestHistoryRecord.updateStartDate(assignmentDate);
} else if (latestHistoryRecord != null && latestHistoryRecord.matchesStartDateOf(assignmentDate)) { } else if (latestHistoryRecord != null && latestHistoryRecord.matchesStartDateOf(assignmentDate)) {
latestHistoryRecord.updateLoanOfficer(newLoanOfficer); latestHistoryRecord.updateLoanOfficer(newLoanOfficer);
this.loanofficer = newLoanOfficer; this.loanOfficer = newLoanOfficer;
} else if (latestHistoryRecord != null && latestHistoryRecord.hasStartDateBefore(assignmentDate)) { } else if (latestHistoryRecord != null && latestHistoryRecord.hasStartDateBefore(assignmentDate)) {
throw new LoanOfficerAssignmentException(this.getId(), assignmentDate); throw new LoanOfficerAssignmentException(this.getId(), assignmentDate);
} else { } else {
@@ -1934,16 +2043,15 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
latestHistoryRecord.updateEndDate(assignmentDate); latestHistoryRecord.updateEndDate(assignmentDate);
} }
this.loanofficer = newLoanOfficer; this.loanOfficer = newLoanOfficer;
if (this.isNotSubmittedAndPendingApproval()) { if (this.isNotSubmittedAndPendingApproval()) {
final LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = LoanOfficerAssignmentHistory.createNew(this, final LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = LoanOfficerAssignmentHistory.createNew(this,
this.loanofficer, assignmentDate); this.loanOfficer, assignmentDate);
this.loanOfficerHistory.add(loanOfficerAssignmentHistory); this.loanOfficerHistory.add(loanOfficerAssignmentHistory);
} }
} }
} }
public void removeLoanOfficer(final LocalDate unassignDate) { public void removeLoanOfficer(final LocalDate unassignDate) {
final LoanOfficerAssignmentHistory latestHistoryRecord = findLatestIncompleteHistoryRecord(); final LoanOfficerAssignmentHistory latestHistoryRecord = findLatestIncompleteHistoryRecord();
@@ -1953,7 +2061,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
latestHistoryRecord.updateEndDate(unassignDate); latestHistoryRecord.updateEndDate(unassignDate);
} }
this.loanofficer = null; this.loanOfficer = null;
} }
private void validateUnassignDate(final LoanOfficerAssignmentHistory latestHistoryRecord, final LocalDate unassignDate) { 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)) { if (latestHistoryRecord.getStartDate().isAfter(unassignDate)) {
throw new LoanOfficerUnassignmentDateException(this.getId(), this.getLoanOfficer().getId(), latestHistoryRecord.getStartDate(), throw new LoanOfficerUnassignmentDateException(this.getId(), this.getLoanOfficer().getId(), latestHistoryRecord.getStartDate(),
unassignDate); unassignDate);
} else if (unassignDate.isAfter(today)) { throw new LoanOfficerUnassignmentDateException( } else if (unassignDate.isAfter(today)) { throw new LoanOfficerUnassignmentDateException(this.getId()); }
this.getId()); }
} }
private LoanOfficerAssignmentHistory findLatestIncompleteHistoryRecord() { private LoanOfficerAssignmentHistory findLatestIncompleteHistoryRecord() {
@@ -2008,11 +2115,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return officeId; return officeId;
} }
public void updateAccountNo(final String newAccountNo) {
this.accountNumber = newAccountNo;
this.accountNumberRequiresAutoGeneration=false;
}
public List<LoanTransaction> getLoanTransactions() { public List<LoanTransaction> getLoanTransactions() {
return this.loanTransactions; return this.loanTransactions;
} }
@@ -2021,11 +2123,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return this.loanProduct.isAccountingEnabled(); return this.loanProduct.isAccountingEnabled();
} }
public boolean isCashBasedAccountingEnabledOnLoanProduct(){ public boolean isCashBasedAccountingEnabledOnLoanProduct() {
return this.loanProduct.isCashBasedAccountingEnabled(); return this.loanProduct.isCashBasedAccountingEnabled();
} }
public boolean isAccrualBasedAccountingEnabledOnLoanProduct(){ public boolean isAccrualBasedAccountingEnabledOnLoanProduct() {
return this.loanProduct.isAccrualBasedAccountingEnabled(); return this.loanProduct.isAccrualBasedAccountingEnabled();
} }
@@ -2034,7 +2136,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
public Staff getLoanOfficer() { public Staff getLoanOfficer() {
return this.loanofficer; return this.loanOfficer;
} }
} }
@@ -15,6 +15,8 @@ import javax.persistence.Table;
import javax.persistence.Temporal; import javax.persistence.Temporal;
import javax.persistence.TemporalType; 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.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
@@ -347,9 +349,8 @@ public class LoanCharge extends AbstractPersistable<Long> {
} }
public LoanChargeCommand toCommand() { public LoanChargeCommand toCommand() {
return new LoanChargeCommand(this.getId(), this.charge.getId(), this.amount, this.chargeTime, this.chargeCalculation,
final LocalDate specifiedDueDate = getDueForCollectionAsOfLocalDate(); getDueForCollectionAsOfLocalDate());
return new LoanChargeCommand(this.charge.getId(), this.amount, this.chargeTime, this.chargeCalculation, specifiedDueDate);
} }
public LocalDate getDueForCollectionAsOfLocalDate() { public LocalDate getDueForCollectionAsOfLocalDate() {
@@ -411,16 +412,14 @@ public class LoanCharge extends AbstractPersistable<Long> {
return this.loan.hasIdentifyOf(loanId); 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(); final LocalDate specifiedDueDate = getDueForCollectionAsOfLocalDate();
return occursOnDayFromAndUpToAndIncluding(fromNotInclusive, upToAndInclusive, specifiedDueDate);
return specifiedDueDateFallsWithinPeriod(fromNotInclusive, toInclusive, specifiedDueDate);
} }
private boolean specifiedDueDateFallsWithinPeriod(final LocalDate fromNotInclusive, final LocalDate toInclusive, private boolean occursOnDayFromAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive,
final LocalDate specifiedDueDate) { final LocalDate target) {
return specifiedDueDate != null && fromNotInclusive.isBefore(specifiedDueDate) return target != null && target.isAfter(fromNotInclusive) && !target.isAfter(upToAndInclusive);
&& (toInclusive.isAfter(specifiedDueDate) || toInclusive.isEqual(specifiedDueDate));
} }
public boolean isFeeCharge() { public boolean isFeeCharge() {
@@ -493,4 +492,27 @@ public class LoanCharge extends AbstractPersistable<Long> {
public String name() { public String name() {
return this.charge.getName(); 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); Money cumulative = Money.zero(monetaryCurrency);
for (LoanCharge loanCharge : loanCharges) { for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) { if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
cumulative = cumulative.plus(loanCharge.amount()); cumulative = cumulative.plus(loanCharge.amount());
} }
} }
@@ -79,7 +79,7 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(currency); Money cumulative = Money.zero(currency);
for (LoanCharge loanCharge : loanCharges) { for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) { if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
cumulative = cumulative.plus(loanCharge.getAmountWaived(currency)); cumulative = cumulative.plus(loanCharge.getAmountWaived(currency));
} }
} }
@@ -93,7 +93,7 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(currency); Money cumulative = Money.zero(currency);
for (LoanCharge loanCharge : loanCharges) { for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) { if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
cumulative = cumulative.plus(loanCharge.getAmountWrittenOff(currency)); cumulative = cumulative.plus(loanCharge.getAmountWrittenOff(currency));
} }
} }
@@ -107,7 +107,7 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(currency); Money cumulative = Money.zero(currency);
for (LoanCharge loanCharge : loanCharges) { for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) { if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
cumulative = cumulative.plus(loanCharge.amount()); cumulative = cumulative.plus(loanCharge.amount());
} }
} }
@@ -121,7 +121,7 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(currency); Money cumulative = Money.zero(currency);
for (LoanCharge loanCharge : loanCharges) { for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) { if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
cumulative = cumulative.plus(loanCharge.getAmountWaived(currency)); cumulative = cumulative.plus(loanCharge.getAmountWaived(currency));
} }
} }
@@ -135,7 +135,7 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(currency); Money cumulative = Money.zero(currency);
for (LoanCharge loanCharge : loanCharges) { for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) { if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
cumulative = cumulative.plus(loanCharge.getAmountWrittenOff(currency)); 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); Money cumulative = Money.zero(monetaryCurrency);
for (LoanCharge loanCharge : loanCharges) { for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) { if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
cumulative = cumulative.plus(loanCharge.amount()); cumulative = cumulative.plus(loanCharge.amount());
} }
} }
@@ -170,7 +170,7 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
Money cumulative = Money.zero(monetaryCurrency); Money cumulative = Money.zero(monetaryCurrency);
for (LoanCharge loanCharge : loanCharges) { for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) { if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
cumulative = cumulative.plus(loanCharge.amount()); cumulative = cumulative.plus(loanCharge.amount());
} }
} }
@@ -146,7 +146,7 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
Money cumulative = Money.zero(monetaryCurrency); Money cumulative = Money.zero(monetaryCurrency);
for (LoanCharge loanCharge : loanCharges) { for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) { if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
cumulative = cumulative.plus(loanCharge.amount()); cumulative = cumulative.plus(loanCharge.amount());
} }
} }
@@ -160,7 +160,7 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
Money cumulative = Money.zero(monetaryCurrency); Money cumulative = Money.zero(monetaryCurrency);
for (LoanCharge loanCharge : loanCharges) { for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) { if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
cumulative = cumulative.plus(loanCharge.amount()); 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.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -32,12 +31,12 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
/** /**
* The parameters supported for this command. * The parameters supported for this command.
*/ */
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("clientId", "groupId", "productId", "accountNo", final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "clientId", "groupId", "productId", "accountNo",
"externalId", "fundId", "transactionProcessingStrategyId", "principal", "inArrearsTolerance", "interestRatePerPeriod", "externalId", "fundId", "loanOfficerId", "loanPurposeId", "transactionProcessingStrategyId", "principal", "inArrearsTolerance",
"repaymentEvery", "numberOfRepayments", "loanTermFrequency", "loanTermFrequencyType", "charges", "repaymentFrequencyType", "interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "loanTermFrequency", "loanTermFrequencyType",
"interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "expectedDisbursementDate", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
"repaymentsStartingFromDate", "interestChargedFromDate", "submittedOnDate", "submittedOnNote", "locale", "dateFormat", "expectedDisbursementDate", "repaymentsStartingFromDate", "interestChargedFromDate", "submittedOnDate", "submittedOnNote",
"loanOfficerId", "id")); "locale", "dateFormat", "charges", "collateral"));
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@@ -124,14 +123,16 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
final String repaymentsStartingFromDateParameterName = "repaymentsStartingFromDate"; final String repaymentsStartingFromDateParameterName = "repaymentsStartingFromDate";
if (fromApiJsonHelper.parameterExists(repaymentsStartingFromDateParameterName, element)) { if (fromApiJsonHelper.parameterExists(repaymentsStartingFromDateParameterName, element)) {
repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed(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; LocalDate interestChargedFromDate = null;
final String interestChargedFromDateParameterName = "interestChargedFromDate"; final String interestChargedFromDateParameterName = "interestChargedFromDate";
if (fromApiJsonHelper.parameterExists(interestChargedFromDateParameterName, element)) { if (fromApiJsonHelper.parameterExists(interestChargedFromDateParameterName, element)) {
interestChargedFromDate = fromApiJsonHelper.extractLocalDateNamed(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, validateRepaymentsStartingFromDateIsAfterDisbursementDate(dataValidationErrors, expectedDisbursementDate,
@@ -140,13 +141,34 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
validateRepaymentsStartingFromDateAndInterestChargedFromDate(dataValidationErrors, expectedDisbursementDate, validateRepaymentsStartingFromDateAndInterestChargedFromDate(dataValidationErrors, expectedDisbursementDate,
repaymentsStartingFromDate, interestChargedFromDate); repaymentsStartingFromDate, interestChargedFromDate);
final LoanChargeCommand[] charges = extractInToLoanChargeCommands(element); // charges
if (charges != null) { final String chargesParameterName = "charges";
for (LoanChargeCommand loanChargeCommand : charges) { if (element.isJsonObject() && fromApiJsonHelper.parameterExists(chargesParameterName, element)) {
try { final JsonObject topLevelJsonElement = element.getAsJsonObject();
loanChargeCommand.validateForCreate(); final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
} catch (PlatformApiDataValidationException e) { final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
dataValidationErrors.addAll(e.getErrors());
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) { // private LoanChargeCommand[] extractInToLoanChargeCommands(final
LoanChargeCommand[] charges = null; // JsonElement element) {
if (element.isJsonObject()) { // LoanChargeCommand[] charges = null;
final JsonObject topLevelJsonElement = element.getAsJsonObject(); // if (element.isJsonObject()) {
final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement); // final JsonObject topLevelJsonElement = element.getAsJsonObject();
final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement); // final String dateFormat =
if (topLevelJsonElement.has("charges") && topLevelJsonElement.get("charges").isJsonArray()) { // fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
// final Locale locale =
final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray(); // fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
charges = new LoanChargeCommand[array.size()]; // if (topLevelJsonElement.has("charges") &&
for (int i = 0; i < array.size(); i++) { // topLevelJsonElement.get("charges").isJsonArray()) {
//
final JsonObject loanChargeElement = array.get(i).getAsJsonObject(); // final JsonArray array =
// topLevelJsonElement.get("charges").getAsJsonArray();
final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement); // charges = new LoanChargeCommand[array.size()];
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalNamed("amount", loanChargeElement, locale); // for (int i = 0; i < array.size(); i++) {
final Integer chargeTimeType = fromApiJsonHelper.extractIntegerNamed("chargeTimeType", loanChargeElement, locale); //
final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", loanChargeElement, // final JsonObject loanChargeElement = array.get(i).getAsJsonObject();
locale); //
final LocalDate specifiedDueDate = fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate", loanChargeElement, // final Long id = fromApiJsonHelper.extractLongNamed("id",
dateFormat, locale); // loanChargeElement);
// final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId",
charges[i] = new LoanChargeCommand(chargeId, amount, chargeTimeType, chargeCalculationType, specifiedDueDate); // loanChargeElement);
} // final BigDecimal amount =
} // fromApiJsonHelper.extractBigDecimalNamed("amount", loanChargeElement,
} // locale);
return charges; // 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.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -32,12 +31,15 @@ public final class LoanApplicationCommandFromApiJsonHelper {
/** /**
* The parameters supported for this command. * The parameters supported for this command.
*/ */
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "clientId", "groupId", "productId", "accountNo", final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("dateFormat", "locale", "id", "clientId", "groupId",
"externalId", "fundId", "loanOfficerId", "transactionProcessingStrategyId", "principal", "inArrearsTolerance", "productId", "principal", "loanTermFrequency", "loanTermFrequencyType", "numberOfRepayments", "repaymentEvery",
"repaymentEvery", "numberOfRepayments", "loanTermFrequency", "loanTermFrequencyType", "charges", "repaymentFrequencyType", "repaymentFrequencyType", "interestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType",
"interestRatePerPeriod", "interestRateFrequencyType", "interestType", "interestCalculationPeriodType", "amortizationType", "interestCalculationPeriodType", "expectedDisbursementDate", "repaymentsStartingFromDate", "interestChargedFromDate",
"expectedDisbursementDate", "repaymentsStartingFromDate", "interestChargedFromDate", "submittedOnDate", "submittedOnNote", "submittedOnDate", "submittedOnNote", //
"locale", "dateFormat")); "accountNo", "externalId", "fundId", "loanOfficerId", // optional
"loanPurposeId", "inArrearsTolerance", "charges", "collateral", // optional
"transactionProcessingStrategyId" // settings
));
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@@ -97,6 +99,12 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(loanOfficerIdParameterName).value(loanOfficerId).ignoreIfNull().integerGreaterThanZero(); 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); final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount(); baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount();
@@ -105,7 +113,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(loanTermFrequencyParameterName).value(loanTermFrequency).notNull().integerGreaterThanZero(); baseDataValidator.reset().parameter(loanTermFrequencyParameterName).value(loanTermFrequency).notNull().integerGreaterThanZero();
final String loanTermFrequencyTypeParameterName = "loanTermFrequencyType"; 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); baseDataValidator.reset().parameter(loanTermFrequencyTypeParameterName).value(loanTermFrequencyType).notNull().inMinMaxRange(0, 3);
final String numberOfRepaymentsParameterName = "numberOfRepayments"; final String numberOfRepaymentsParameterName = "numberOfRepayments";
@@ -117,7 +125,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(repaymentEveryParameterName).value(repaymentEvery).notNull().integerGreaterThanZero(); baseDataValidator.reset().parameter(repaymentEveryParameterName).value(repaymentEvery).notNull().integerGreaterThanZero();
final String repaymentEveryFrequencyTypeParameterName = "repaymentFrequencyType"; final String repaymentEveryFrequencyTypeParameterName = "repaymentFrequencyType";
final Integer repaymentEveryType = fromApiJsonHelper.extractIntegerWithLocaleNamed(repaymentEveryFrequencyTypeParameterName, final Integer repaymentEveryType = fromApiJsonHelper.extractIntegerSansLocaleNamed(repaymentEveryFrequencyTypeParameterName,
element); element);
baseDataValidator.reset().parameter(repaymentEveryFrequencyTypeParameterName).value(repaymentEveryType).notNull() baseDataValidator.reset().parameter(repaymentEveryFrequencyTypeParameterName).value(repaymentEveryType).notNull()
.inMinMaxRange(0, 3); .inMinMaxRange(0, 3);
@@ -128,23 +136,23 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod).notNull().positiveAmount(); baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod).notNull().positiveAmount();
final String interestRateFrequencyTypeParameterName = "interestRateFrequencyType"; final String interestRateFrequencyTypeParameterName = "interestRateFrequencyType";
final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed(interestRateFrequencyTypeParameterName, final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestRateFrequencyTypeParameterName,
element); element);
baseDataValidator.reset().parameter(interestRateFrequencyTypeParameterName).value(interestRateFrequencyType).notNull() baseDataValidator.reset().parameter(interestRateFrequencyTypeParameterName).value(interestRateFrequencyType).notNull()
.inMinMaxRange(0, 3); .inMinMaxRange(0, 3);
final String interestTypeParameterName = "interestType"; 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); baseDataValidator.reset().parameter(interestTypeParameterName).value(interestType).notNull().inMinMaxRange(0, 1);
final String interestCalculationPeriodTypeParameterName = "interestCalculationPeriodType"; final String interestCalculationPeriodTypeParameterName = "interestCalculationPeriodType";
final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerWithLocaleNamed( final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
interestCalculationPeriodTypeParameterName, element); interestCalculationPeriodTypeParameterName, element);
baseDataValidator.reset().parameter(interestCalculationPeriodTypeParameterName).value(interestCalculationPeriodType).notNull() baseDataValidator.reset().parameter(interestCalculationPeriodTypeParameterName).value(interestCalculationPeriodType).notNull()
.inMinMaxRange(0, 1); .inMinMaxRange(0, 1);
final String amortizationTypeParameterName = "amortizationType"; 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); baseDataValidator.reset().parameter(amortizationTypeParameterName).value(amortizationType).notNull().inMinMaxRange(0, 1);
final String expectedDisbursementDateParameterName = "expectedDisbursementDate"; final String expectedDisbursementDateParameterName = "expectedDisbursementDate";
@@ -155,21 +163,24 @@ public final class LoanApplicationCommandFromApiJsonHelper {
if (fromApiJsonHelper.parameterExists(repaymentsStartingFromDateParameterName, element)) { if (fromApiJsonHelper.parameterExists(repaymentsStartingFromDateParameterName, element)) {
final LocalDate repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed(repaymentsStartingFromDateParameterName, final LocalDate repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed(repaymentsStartingFromDateParameterName,
element); element);
baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName).value(repaymentsStartingFromDate).ignoreIfNull().notNull(); baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName).value(repaymentsStartingFromDate).ignoreIfNull()
.notNull();
} }
final String interestChargedFromDateParameterName = "interestChargedFromDate"; final String interestChargedFromDateParameterName = "interestChargedFromDate";
if (fromApiJsonHelper.parameterExists(interestChargedFromDateParameterName, element)) { if (fromApiJsonHelper.parameterExists(interestChargedFromDateParameterName, element)) {
final LocalDate interestChargedFromDate = fromApiJsonHelper final LocalDate interestChargedFromDate = fromApiJsonHelper
.extractLocalDateNamed(interestChargedFromDateParameterName, element); .extractLocalDateNamed(interestChargedFromDateParameterName, element);
baseDataValidator.reset().parameter(interestChargedFromDateParameterName).value(interestChargedFromDate).ignoreIfNull().notNull(); baseDataValidator.reset().parameter(interestChargedFromDateParameterName).value(interestChargedFromDate).ignoreIfNull()
.notNull();
} }
final String inArrearsToleranceParameterName = "inArrearsTolerance"; final String inArrearsToleranceParameterName = "inArrearsTolerance";
if (fromApiJsonHelper.parameterExists(inArrearsToleranceParameterName, element)) { if (fromApiJsonHelper.parameterExists(inArrearsToleranceParameterName, element)) {
final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(inArrearsToleranceParameterName, final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(inArrearsToleranceParameterName,
element); element);
baseDataValidator.reset().parameter(inArrearsToleranceParameterName).value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount(); baseDataValidator.reset().parameter(inArrearsToleranceParameterName).value(inArrearsTolerance).ignoreIfNull()
.zeroOrPositiveAmount();
} }
final String submittedOnDateParameterName = "submittedOnDate"; final String submittedOnDateParameterName = "submittedOnDate";
@@ -181,7 +192,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
final String submittedOnNoteParameterName = "submittedOnNote"; final String submittedOnNoteParameterName = "submittedOnNote";
if (fromApiJsonHelper.parameterExists(submittedOnNoteParameterName, element)) { if (fromApiJsonHelper.parameterExists(submittedOnNoteParameterName, element)) {
final String submittedOnNote = fromApiJsonHelper.extractStringNamed(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 // 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() baseDataValidator.reset().parameter(transactionProcessingStrategyIdParameterName).value(transactionProcessingStrategyId).notNull()
.integerGreaterThanZero(); .integerGreaterThanZero();
final LoanChargeCommand[] charges = extractInToLoanChargeCommands(element); // charges
if (charges != null) { final String chargesParameterName = "charges";
for (LoanChargeCommand loanChargeCommand : charges) { if (element.isJsonObject() && fromApiJsonHelper.parameterExists(chargesParameterName, element)) {
try { final JsonObject topLevelJsonElement = element.getAsJsonObject();
loanChargeCommand.validateForCreate(); final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
} catch (PlatformApiDataValidationException e) { final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
dataValidationErrors.addAll(e.getErrors());
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", if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); } "Validation errors exist.", dataValidationErrors); }
} }
@@ -412,6 +474,69 @@ public final class LoanApplicationCommandFromApiJsonHelper {
.notExceedingLengthOf(500); .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) { if (!atLeastOneParameterPassedForUpdate) {
final Object forceError = null; final Object forceError = null;
baseDataValidator.reset().anyOfNotNull(forceError); baseDataValidator.reset().anyOfNotNull(forceError);
@@ -420,38 +545,4 @@ public final class LoanApplicationCommandFromApiJsonHelper {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); } "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. * 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")); "chargeCalculationType", "specifiedDueDate", "locale", "dateFormat"));
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@@ -49,12 +49,13 @@ public final class LoanChargeCommandFromApiJsonDeserializer extends AbstractFrom
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters); fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json); final JsonElement element = fromApiJsonHelper.parse(json);
final Long id = fromApiJsonHelper.extractLongNamed("id", element);
final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", element); final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", element);
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element); final BigDecimal amount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element);
final Integer chargeTimeType = fromApiJsonHelper.extractIntegerWithLocaleNamed("chargeTimeType", element); final Integer chargeTimeType = fromApiJsonHelper.extractIntegerWithLocaleNamed("chargeTimeType", element);
final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerWithLocaleNamed("chargeCalculationType", 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.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate; 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.JsonCommand;
import org.mifosplatform.infrastructure.core.api.JsonQuery; import org.mifosplatform.infrastructure.core.api.JsonQuery;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; 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.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.security.exception.NoAuthorizationException; import org.mifosplatform.infrastructure.security.exception.NoAuthorizationException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; 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.AccountNumberGenerator;
import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory; import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory;
import org.mifosplatform.portfolio.client.domain.Client; 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.domain.NoteRepository;
import org.mifosplatform.portfolio.client.exception.ClientNotFoundException; import org.mifosplatform.portfolio.client.exception.ClientNotFoundException;
import org.mifosplatform.portfolio.fund.domain.Fund; 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.command.LoanStateTransitionCommand;
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine; import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; 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.LoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus; 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.LoanNotFoundException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData; import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator; import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService; import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
@@ -68,6 +71,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
private final ClientRepository clientRepository; private final ClientRepository clientRepository;
private final LoanProductRepository loanProductRepository; private final LoanProductRepository loanProductRepository;
private final LoanChargeAssembler loanChargeAssembler; private final LoanChargeAssembler loanChargeAssembler;
private final LoanCollateralAssembler loanCollateralAssembler;
private final AprCalculator aprCalculator; private final AprCalculator aprCalculator;
private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory; private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory;
@@ -76,9 +80,10 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer, final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer,
final LoanStateTransitionCommandFromApiJsonDeserializer loanStateTransitionCommandFromApiJsonDeserializer, final LoanStateTransitionCommandFromApiJsonDeserializer loanStateTransitionCommandFromApiJsonDeserializer,
final AprCalculator aprCalculator, final LoanAssembler loanAssembler, final LoanChargeAssembler loanChargeAssembler, final AprCalculator aprCalculator, final LoanAssembler loanAssembler, final LoanChargeAssembler loanChargeAssembler,
final LoanRepository loanRepository, final NoteRepository noteRepository, final LoanCollateralAssembler loanCollateralAssembler, final LoanRepository loanRepository,
final LoanScheduleCalculationPlatformService calculationPlatformService, final ClientRepository clientRepository, final NoteRepository noteRepository, final LoanScheduleCalculationPlatformService calculationPlatformService,
final LoanProductRepository loanProductRepository, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory) { final ClientRepository clientRepository, final LoanProductRepository loanProductRepository,
final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory) {
this.context = context; this.context = context;
this.fromJsonHelper = fromJsonHelper; this.fromJsonHelper = fromJsonHelper;
this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.fromApiJsonDeserializer = fromApiJsonDeserializer;
@@ -86,6 +91,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
this.aprCalculator = aprCalculator; this.aprCalculator = aprCalculator;
this.loanAssembler = loanAssembler; this.loanAssembler = loanAssembler;
this.loanChargeAssembler = loanChargeAssembler; this.loanChargeAssembler = loanChargeAssembler;
this.loanCollateralAssembler = loanCollateralAssembler;
this.loanRepository = loanRepository; this.loanRepository = loanRepository;
this.noteRepository = noteRepository; this.noteRepository = noteRepository;
this.calculationPlatformService = calculationPlatformService; this.calculationPlatformService = calculationPlatformService;
@@ -149,9 +155,15 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final Loan existingLoanApplication = retrieveLoanBy(loanId); 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"; final String clientIdParamName = "clientId";
if (changes.containsKey(clientIdParamName)) { if (changes.containsKey(clientIdParamName)) {
@@ -179,6 +191,20 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
existingLoanApplication.updateFund(fund); 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"; final String strategyIdParamName = "transactionProcessingStrategyId";
if (changes.containsKey(strategyIdParamName)) { if (changes.containsKey(strategyIdParamName)) {
final Long strategyId = command.longValueOfParameterNamed(strategyIdParamName); final Long strategyId = command.longValueOfParameterNamed(strategyIdParamName);
@@ -193,6 +219,12 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
existingLoanApplication.updateLoanCharges(loanCharges); 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")) { if (changes.containsKey("recalculateLoanSchedule")) {
changes.remove("recalculateLoanSchedule"); changes.remove("recalculateLoanSchedule");
@@ -245,8 +277,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
} }
logAsErrorUnexpectedDataIntegrityException(dve); logAsErrorUnexpectedDataIntegrityException(dve);
throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue", throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue", "Unknown data integrity issue with resource.");
"Unknown data integrity issue with resource.");
} }
private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) { private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) {
@@ -261,7 +292,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final Loan loan = retrieveLoanBy(loanId); 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()); List<Note> relatedNotes = this.noteRepository.findByLoanId(loan.getId());
this.noteRepository.deleteInBatch(relatedNotes); this.noteRepository.deleteInBatch(relatedNotes);
@@ -6,6 +6,8 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import org.joda.time.LocalDate; 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.api.JsonCommand;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.organisation.staff.domain.Staff; 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.DefaultLoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; 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.LoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus; import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
@@ -49,16 +52,18 @@ public class LoanAssembler {
private final FundRepository fundRepository; private final FundRepository fundRepository;
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository; private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
private final StaffRepository staffRepository; private final StaffRepository staffRepository;
private final LoanChargeAssembler loanChargeAssembler; private final CodeValueRepositoryWrapper codeValueRepository;
private final LoanScheduleAssembler loanScheduleAssembler; private final LoanScheduleAssembler loanScheduleAssembler;
private final LoanChargeAssembler loanChargeAssembler;
private final LoanCollateralAssembler loanCollateralAssembler;
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@Autowired @Autowired
public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository, public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository,
final ClientRepository clientRepository, final GroupRepository groupRepository, final FundRepository fundRepository, final ClientRepository clientRepository, final GroupRepository groupRepository, final FundRepository fundRepository,
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository, final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
final StaffRepository staffRepository, final LoanScheduleAssembler loanScheduleAssembler, final StaffRepository staffRepository, final CodeValueRepositoryWrapper codeValueRepository,
final LoanChargeAssembler loanChargeAssembler) { final LoanScheduleAssembler loanScheduleAssembler, final LoanChargeAssembler loanChargeAssembler, final LoanCollateralAssembler loanCollateralAssembler) {
this.fromApiJsonHelper = fromApiJsonHelper; this.fromApiJsonHelper = fromApiJsonHelper;
this.loanProductRepository = loanProductRepository; this.loanProductRepository = loanProductRepository;
this.clientRepository = clientRepository; this.clientRepository = clientRepository;
@@ -66,8 +71,10 @@ public class LoanAssembler {
this.fundRepository = fundRepository; this.fundRepository = fundRepository;
this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository; this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
this.staffRepository = staffRepository; this.staffRepository = staffRepository;
this.codeValueRepository = codeValueRepository;
this.loanScheduleAssembler = loanScheduleAssembler; this.loanScheduleAssembler = loanScheduleAssembler;
this.loanChargeAssembler = loanChargeAssembler; this.loanChargeAssembler = loanChargeAssembler;
this.loanCollateralAssembler = loanCollateralAssembler;
} }
public Loan assembleFrom(final JsonCommand command) { public Loan assembleFrom(final JsonCommand command) {
@@ -86,6 +93,7 @@ public class LoanAssembler {
final Long fundId = fromApiJsonHelper.extractLongNamed("fundId", element); final Long fundId = fromApiJsonHelper.extractLongNamed("fundId", element);
final Long loanOfficerId = fromApiJsonHelper.extractLongNamed("loanOfficerId", element); final Long loanOfficerId = fromApiJsonHelper.extractLongNamed("loanOfficerId", element);
final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element); final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
final Long loanPurposeId = fromApiJsonHelper.extractLongNamed("loanPurposeId", element);
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId); final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); } if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
@@ -93,7 +101,11 @@ public class LoanAssembler {
final Fund fund = findFundByIdIfProvided(fundId); final Fund fund = findFundByIdIfProvided(fundId);
final Staff loanOfficer = findLoanOfficerByIdIfProvided(loanOfficerId); final Staff loanOfficer = findLoanOfficerByIdIfProvided(loanOfficerId);
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId); 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 Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element);
final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element); final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
@@ -106,24 +118,24 @@ public class LoanAssembler {
client = this.clientRepository.findOne(clientId); client = this.clientRepository.findOne(clientId);
if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); } if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); }
loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy, loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanProduct, fund, loanOfficer, loanPurpose,
loanSchedule, loanCharges); loanTransactionProcessingStrategy, loanSchedule, loanCharges, collateral);
} }
if (groupId != null) { if (groupId != null) {
group = this.groupRepository.findOne(groupId); group = this.groupRepository.findOne(groupId);
if (group == null || group.isDeleted()) { throw new GroupNotFoundException(groupId); } if (group == null || group.isDeleted()) { throw new GroupNotFoundException(groupId); }
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy, loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanProduct, fund, loanOfficer,
loanSchedule, loanCharges); loanTransactionProcessingStrategy, loanSchedule, loanCharges);
} }
if (client != null && group != null){ if (client != null && group != null) {
if (!group.hasClientAsMember(client)) { throw new ClientNotInGroupException(clientId, groupId); } if (!group.hasClientAsMember(client)) { throw new ClientNotInGroupException(clientId, groupId); }
loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy, loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanProduct, fund, loanOfficer,
loanSchedule, loanCharges); loanTransactionProcessingStrategy, loanSchedule, loanCharges);
} }
final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element); final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element);
@@ -141,6 +153,14 @@ public class LoanAssembler {
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses); 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) { public Fund findFundByIdIfProvided(final Long fundId) {
Fund fund = null; Fund fund = null;
if (fundId != null) { if (fundId != null) {
@@ -89,10 +89,7 @@ public class LoanChargeAssembler {
loanCharges.add(loanCharge); loanCharges.add(loanCharge);
} }
} }
} else {
// no charges passed, use existing ones against loan
} }
} }
return loanCharges; 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> { private static final class LoanMapper implements RowMapper<LoanBasicDetailsData> {
public String loanSchema() { 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, " 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, " + " l.loanpurpose_cv_id as loanPurposeId, cv.code_value as loanPurposeName,"
+ " c.office_id as clientOfficeId, g.id as groupId, g.name as groupName, g.office_id as groupOfficeId," + " 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.submittedon_date as submittedOnDate,"
+ " l.total_charges_due_at_disbursement_derived as totalDisbursementCharges," + " 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," + " 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" + " 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" + " 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" + " 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 @Override
@@ -407,8 +410,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final Long fundId = JdbcSupport.getLong(rs, "fundId"); final Long fundId = JdbcSupport.getLong(rs, "fundId");
final String fundName = rs.getString("fundName"); final String fundName = rs.getString("fundName");
final Long loanOfficerId = JdbcSupport.getLong(rs, "loanOfficerId"); final Long loanOfficerId = JdbcSupport.getLong(rs, "loanOfficerId");
final String loanOfficerName = rs.getString("loanOfficerName"); 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 Long loanProductId = JdbcSupport.getLong(rs, "loanProductId");
final String loanProductName = rs.getString("loanProductName"); final String loanProductName = rs.getString("loanProductName");
final String loanProductDescription = rs.getString("loanProductDescription"); final String loanProductDescription = rs.getString("loanProductDescription");
@@ -468,12 +476,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
Collection<LoanChargeData> charges = null; Collection<LoanChargeData> charges = null;
return new LoanBasicDetailsData(id, accountNo, externalId, clientId, clientName, clientOfficeId, groupId, groupName, return new LoanBasicDetailsData(id, accountNo, externalId, clientId, clientName, clientOfficeId, groupId, groupName,
groupOfficeId, loanProductId, loanProductName, loanProductDescription, fundId, fundName, closedOnDate, submittedOnDate, groupOfficeId, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId,
approvedOnDate, expectedDisbursementDate, actualDisbursementDate, expectedMaturityDate, expectedFirstRepaymentOnDate, loanPurposeName, closedOnDate, submittedOnDate, approvedOnDate, expectedDisbursementDate, actualDisbursementDate,
interestChargedFromDate, currencyData, principal, inArrearsTolerance, numberOfRepayments, repaymentEvery, expectedMaturityDate, expectedFirstRepaymentOnDate, interestChargedFromDate, currencyData, principal,
interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, inArrearsTolerance, numberOfRepayments, repaymentEvery, interestRatePerPeriod, annualInterestRate,
interestType, interestCalculationPeriodType, status, lifeCycleStatusDate, termFrequency, termPeriodFrequencyType, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType,
transactionStrategyId, charges, loanOfficerId, loanOfficerName, totalDisbursementCharges); 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.domain.LoanTransactionRepository;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException; import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException; 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.LoanOfficerUnassignmentException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanChargeCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanaccount.serialization.LoanChargeCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanStateTransitionCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanaccount.serialization.LoanStateTransitionCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanTransactionCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanaccount.serialization.LoanTransactionCommandFromApiJsonDeserializer;
@@ -78,8 +78,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanStateTransitionCommandFromApiJsonDeserializer loanStateTransitionCommandFromApiJsonDeserializer, final LoanStateTransitionCommandFromApiJsonDeserializer loanStateTransitionCommandFromApiJsonDeserializer,
final LoanTransactionCommandFromApiJsonDeserializer loanTransactionCommandFromApiJsonDeserializer, final LoanTransactionCommandFromApiJsonDeserializer loanTransactionCommandFromApiJsonDeserializer,
final LoanChargeCommandFromApiJsonDeserializer loanChargeCommandFromApiJsonDeserializer, final LoanChargeCommandFromApiJsonDeserializer loanChargeCommandFromApiJsonDeserializer,
final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, final LoanAssembler loanAssembler,
final LoanAssembler loanAssembler,
final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository, final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository,
final NoteRepository noteRepository, final ChargeRepository chargeRepository, final LoanChargeRepository loanChargeRepository, final NoteRepository noteRepository, final ChargeRepository chargeRepository, final LoanChargeRepository loanChargeRepository,
final ApplicationCurrencyRepository applicationCurrencyRepository, final ApplicationCurrencyRepository applicationCurrencyRepository,
@@ -129,11 +128,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Map<String, Object> changes = loan.disburse(command, defaultLoanLifecycleStateMachine(), currency); final Map<String, Object> changes = loan.disburse(command, defaultLoanLifecycleStateMachine(), currency);
if (!changes.isEmpty()) { 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) // loan transactions would not have an Id before save)
final Set<Long> existingLoanTransactionIds = new HashSet<Long>(); final Set<Long> existingLoanTransactionIds = new HashSet<Long>();
for(LoanTransaction loanTransaction:loan.getLoanTransactions()){ for (LoanTransaction loanTransaction : loan.getLoanTransactions()) {
if(!(loanTransaction.getId()==null)){ if (!(loanTransaction.getId() == null)) {
existingLoanTransactionIds.add(loanTransaction.getId()); existingLoanTransactionIds.add(loanTransaction.getId());
} }
} }
@@ -147,8 +147,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
// make a call to accounting // make a call to accounting
if (loan.isAccountingEnabledOnLoanProduct()) { if (loan.isAccountingEnabledOnLoanProduct()) {
/*** /***
* Variable holds list of all newly created loan transactions during * Variable holds list of all newly created loan transactions
* this disbursal * during this disbursal
**/ **/
List<LoanTransaction> newLoanTransactions = new ArrayList<LoanTransaction>(); List<LoanTransaction> newLoanTransactions = new ArrayList<LoanTransaction>();
for (LoanTransaction loanTransaction : loan.getLoanTransactions()) { for (LoanTransaction loanTransaction : loan.getLoanTransactions()) {
@@ -277,7 +277,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount")); changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount"));
boolean transactionIsRepayment= transactionToAdjust.isRepayment(); boolean transactionIsRepayment = transactionToAdjust.isRepayment();
final LoanTransaction newTransactionDetail = loan.adjustExistingTransaction(transactionDate, transactionAmount, final LoanTransaction newTransactionDetail = loan.adjustExistingTransaction(transactionDate, transactionAmount,
defaultLoanLifecycleStateMachine(), transactionToAdjust); defaultLoanLifecycleStateMachine(), transactionToAdjust);
@@ -297,7 +297,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
changes.put("locale", command.locale()); changes.put("locale", command.locale());
changes.put("dateFormat", command.dateFormat()); 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) { if (loan.isAccountingEnabledOnLoanProduct() && transactionIsRepayment) {
List<LoanTransaction> newLoanTransactions = new ArrayList<LoanTransaction>(); List<LoanTransaction> newLoanTransactions = new ArrayList<LoanTransaction>();
if (newTransactionDetail.getId() != null) { if (newTransactionDetail.getId() != null) {
@@ -509,8 +510,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
public CommandProcessingResult addLoanCharge(final Long loanId, final JsonCommand command) { public CommandProcessingResult addLoanCharge(final Long loanId, final JsonCommand command) {
this.context.authenticatedUser(); this.context.authenticatedUser();
// FIXME - KW - update loan charge api validation
final LoanChargeCommand loanChargeCommand = this.loanChargeCommandFromApiJsonDeserializer.commandFromApiJson(command.json()); final LoanChargeCommand loanChargeCommand = this.loanChargeCommandFromApiJsonDeserializer.commandFromApiJson(command.json());
loanChargeCommand.validateForCreate(); // loanChargeCommand.validateForCreate();
final Loan loan = retrieveLoanBy(loanId); final Loan loan = retrieveLoanBy(loanId);
@@ -709,12 +711,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.context.authenticatedUser(); this.context.authenticatedUser();
final LoanUpdateCommand loanUpdateCommand = this.loanUpdateCommandFromApiJsonDeserializer.commandFromApiJson(command final LoanUpdateCommand loanUpdateCommand = this.loanUpdateCommandFromApiJsonDeserializer.commandFromApiJson(command.json());
.json());
loanUpdateCommand.validate(); loanUpdateCommand.validate();
final LocalDate dateOfLoanOfficerunAssigned = command.localDateValueOfParameterNamed("unassignedDate"); final LocalDate dateOfLoanOfficerunAssigned = command.localDateValueOfParameterNamed("unassignedDate");
final Loan loan = retrieveLoanBy(loanId); final Loan loan = retrieveLoanBy(loanId);
@@ -738,11 +738,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private LoanDTO populateLoanDTO(Loan loan, List<LoanTransaction> loanTransactions) { private LoanDTO populateLoanDTO(Loan loan, List<LoanTransaction> loanTransactions) {
List<LoanTransactionDTO> loanTransactionDTOs = new ArrayList<LoanTransactionDTO>(); List<LoanTransactionDTO> loanTransactionDTOs = new ArrayList<LoanTransactionDTO>();
for (LoanTransaction loanTransaction : loanTransactions) { for (LoanTransaction loanTransaction : loanTransactions) {
LoanTransactionDTO loanTransactionDTO = new LoanTransactionDTO(loanTransaction.getId().toString(), LoanTransactionDTO loanTransactionDTO = new LoanTransactionDTO(loanTransaction.getId().toString(), loanTransaction.getDateOf(),
loanTransaction.getDateOf(), loanTransaction.getAmount(), loanTransaction.getPrincipalPortion(), loanTransaction.getAmount(), loanTransaction.getPrincipalPortion(), loanTransaction.getInterestPortion(),
loanTransaction.getInterestPortion(), loanTransaction.getFeePortion(), loanTransaction.getPenaltyChargesPortion(), loanTransaction.getFeePortion(), loanTransaction.getPenaltyChargesPortion(), loanTransaction.isDisbursement(),
loanTransaction.isDisbursement(), loanTransaction.isRepayment(), loanTransaction.isRepaymentAtDisbursement(), loanTransaction.isRepayment(), loanTransaction.isRepaymentAtDisbursement(), loanTransaction.isContra(),
loanTransaction.isContra(), loanTransaction.isWriteOff()); loanTransaction.isWriteOff());
loanTransactionDTOs.add(loanTransactionDTO); loanTransactionDTOs.add(loanTransactionDTO);
} }
return new LoanDTO(loan.getId(), loan.productId(), loan.getOfficeId(), loan.isCashBasedAccountingEnabledOnLoanProduct(), return new LoanDTO(loan.getId(), loan.productId(), loan.getOfficeId(), loan.isCashBasedAccountingEnabledOnLoanProduct(),